Repository: laowantong/mocodo Branch: master Commit: 720053a53c60 Files: 1279 Total size: 6.4 MB Directory structure: gitextract_p929mom5/ ├── .gitignore ├── LICENSE ├── README.md ├── doc/ │ ├── build_doc.sh │ ├── examples/ │ │ ├── four_random_layouts.mcd │ │ ├── landing.mcd │ │ ├── option_syntax_1.abnf │ │ └── option_syntax_2.abnf │ ├── fr_cheat_sheet.md │ ├── fr_refman.html │ ├── fr_refman.ipynb │ ├── readme/ │ │ └── ccp.mcd │ ├── tutorial.ipynb │ └── tutorial_lib/ │ ├── tuto-0000.mcd │ ├── tuto-0001.mcd │ ├── tuto-0002.mcd │ ├── tuto-0003.mcd │ ├── tuto-0004.mcd │ ├── tuto-0005.mcd │ ├── tuto-0006.mcd │ ├── tuto-0007.mcd │ ├── tuto-0008.mcd │ ├── tuto-0009.mcd │ ├── tuto-0010.mcd │ ├── tuto-0011.mcd │ ├── tuto-0012.mcd │ ├── tuto-0013.mcd │ ├── tuto-0014.mcd │ ├── tuto-0015.mcd │ ├── tuto-0016.mcd │ ├── tuto-0017.mcd │ ├── tuto-0018.mcd │ ├── tuto-0019.mcd │ ├── tuto-0020.mcd │ ├── tuto-0021.mcd │ ├── tuto-0022.mcd │ ├── tuto-0023.mcd │ ├── tuto-0024.mcd │ ├── tuto-0025.mcd │ ├── tuto-0026.mcd │ ├── tuto-0027.mcd │ ├── tuto-0028.mcd │ ├── tuto-0029.mcd │ ├── tuto-0030.mcd │ ├── tuto-0031.mcd │ ├── tuto-0032.mcd │ ├── tuto-0033.mcd │ ├── tuto-0034.mcd │ ├── tuto-0035.mcd │ ├── tuto-0036.mcd │ ├── tuto-0037.mcd │ └── tuto-0038.mcd ├── index.php ├── laowantong.vscode-mocodo/ │ ├── .vscode/ │ │ └── launch.json │ ├── .vscodeignore │ ├── LICENSE │ ├── README.md │ ├── highlighting_zoo.mcd │ ├── language-configuration.json │ ├── package.json │ ├── syntaxes/ │ │ ├── mocodo.tmLanguage.json │ │ └── mocodo.tmLanguage.yaml │ └── vsc-extension-quickstart.md ├── mocodo/ │ ├── __init__.py │ ├── __main__.py │ ├── api.py │ ├── argument_parser.py │ ├── association.py │ ├── attribute.py │ ├── common.py │ ├── constraint.py │ ├── convert/ │ │ ├── __init__.py │ │ ├── _ast.py │ │ ├── _chen.py │ │ ├── _crow.py │ │ ├── _data_dict.py │ │ ├── _prompt.py │ │ ├── _share.py │ │ ├── _uml.py │ │ ├── crow_gv.py │ │ ├── crow_mmd.py │ │ ├── read_template.py │ │ └── relations.py │ ├── dev/ │ │ ├── json_to_yaml_templates.py │ │ ├── update_sql_reserved_words.py │ │ └── update_transfo_metadata.py │ ├── diagram_link.py │ ├── entity.py │ ├── font_metrics.py │ ├── grid.py │ ├── guess_title.py │ ├── inheritance.py │ ├── leg.py │ ├── magic.py │ ├── mcd.py │ ├── mcd_to_svg.py │ ├── mocodo_error.py │ ├── parse_mcd.py │ ├── phantom.py │ ├── resources/ │ │ ├── colors/ │ │ │ ├── blank.json │ │ │ ├── brewer+1.json │ │ │ ├── brewer+2.json │ │ │ ├── brewer+3.json │ │ │ ├── brewer+4.json │ │ │ ├── brewer+5.json │ │ │ ├── brewer+6.json │ │ │ ├── brewer+7.json │ │ │ ├── brewer+8.json │ │ │ ├── brewer+9.json │ │ │ ├── brewer-1.json │ │ │ ├── brewer-2.json │ │ │ ├── brewer-3.json │ │ │ ├── brewer-4.json │ │ │ ├── brewer-5.json │ │ │ ├── brewer-6.json │ │ │ ├── brewer-7.json │ │ │ ├── brewer-8.json │ │ │ ├── brewer-9.json │ │ │ ├── bw-alpha.json │ │ │ ├── bw.json │ │ │ ├── dark-desert.json │ │ │ ├── dark-ocean.json │ │ │ ├── dark-pond.json │ │ │ ├── dark.json │ │ │ ├── desert.json │ │ │ ├── gray.json │ │ │ ├── keepsake.json │ │ │ ├── mondrian.json │ │ │ ├── ocean.json │ │ │ ├── pond.json │ │ │ ├── wb.json │ │ │ └── xinnian.json │ │ ├── default_datatypes_en.tsv │ │ ├── default_datatypes_fr.tsv │ │ ├── font_metrics.json │ │ ├── grammar.lark │ │ ├── i18n/ │ │ │ ├── messages_de.mo │ │ │ ├── messages_fr.mo │ │ │ ├── messages_fr.po │ │ │ └── messages_zh.mo │ │ ├── lorem/ │ │ │ ├── disparition.txt │ │ │ ├── en4.txt │ │ │ ├── fr.txt │ │ │ ├── fr5.txt │ │ │ └── lorem.txt │ │ ├── pristine_sandbox.mcd │ │ ├── prompts/ │ │ │ └── chat/ │ │ │ ├── cards_examples/ │ │ │ │ ├── 1_input.mcd │ │ │ │ ├── 1_output.mcd │ │ │ │ ├── 2_input.mcd │ │ │ │ ├── 2_output.mcd │ │ │ │ ├── 3_input.mcd │ │ │ │ └── 3_output.mcd │ │ │ ├── cards_fr.md │ │ │ ├── types_examples/ │ │ │ │ ├── 1_input.mcd │ │ │ │ ├── 1_output.mcd │ │ │ │ ├── 2_input.mcd │ │ │ │ └── 2_output.mcd │ │ │ └── types_fr.md │ │ ├── relation_templates/ │ │ │ ├── _index.json │ │ │ ├── d2.yaml │ │ │ ├── dbml-b.yaml │ │ │ ├── dbml.yaml │ │ │ ├── ddl.yaml │ │ │ ├── debug.yaml │ │ │ ├── dependencies.yaml │ │ │ ├── diagram-c.yaml │ │ │ ├── diagram.yaml │ │ │ ├── html-b.yaml │ │ │ ├── html-bc.yaml │ │ │ ├── html-bce.yaml │ │ │ ├── html-be.yaml │ │ │ ├── html-c.yaml │ │ │ ├── html-ce.yaml │ │ │ ├── html-e.yaml │ │ │ ├── html.yaml │ │ │ ├── latex-b.yaml │ │ │ ├── latex-bc.yaml │ │ │ ├── latex-bce.yaml │ │ │ ├── latex-be.yaml │ │ │ ├── latex-c.yaml │ │ │ ├── latex-ce.yaml │ │ │ ├── latex-e.yaml │ │ │ ├── latex.yaml │ │ │ ├── markdown-b.yaml │ │ │ ├── markdown-bc.yaml │ │ │ ├── markdown-bce.yaml │ │ │ ├── markdown-be.yaml │ │ │ ├── markdown-c.yaml │ │ │ ├── markdown-ce.yaml │ │ │ ├── markdown-e.yaml │ │ │ ├── markdown.yaml │ │ │ ├── md-b.yaml │ │ │ ├── md-bc.yaml │ │ │ ├── md-bce.yaml │ │ │ ├── md-be.yaml │ │ │ ├── md-c.yaml │ │ │ ├── md-ce.yaml │ │ │ ├── md-e.yaml │ │ │ ├── md.yaml │ │ │ ├── mld-b.yaml │ │ │ ├── mld-bc.yaml │ │ │ ├── mld-bce.yaml │ │ │ ├── mld-be.yaml │ │ │ ├── mld-c.yaml │ │ │ ├── mld-ce.yaml │ │ │ ├── mld-e.yaml │ │ │ ├── mld.yaml │ │ │ ├── ms_sql-b.yaml │ │ │ ├── ms_sql.yaml │ │ │ ├── mssql-b.yaml │ │ │ ├── mssql.yaml │ │ │ ├── mysql-b.yaml │ │ │ ├── mysql.yaml │ │ │ ├── oracle-b.yaml │ │ │ ├── oracle.yaml │ │ │ ├── oracle_db-b.yaml │ │ │ ├── oracle_db.yaml │ │ │ ├── postgres-b.yaml │ │ │ ├── postgres.yaml │ │ │ ├── postgresql-b.yaml │ │ │ ├── postgresql.yaml │ │ │ ├── sql.yaml │ │ │ ├── sql_server-b.yaml │ │ │ ├── sql_server.yaml │ │ │ ├── sqlite-b.yaml │ │ │ ├── sqlite.yaml │ │ │ ├── sqlserver-b.yaml │ │ │ ├── sqlserver.yaml │ │ │ ├── tex-b.yaml │ │ │ ├── tex-bc.yaml │ │ │ ├── tex-bce.yaml │ │ │ ├── tex-be.yaml │ │ │ ├── tex-c.yaml │ │ │ ├── tex-ce.yaml │ │ │ ├── tex-e.yaml │ │ │ ├── tex.yaml │ │ │ ├── text-b.yaml │ │ │ ├── text-bc.yaml │ │ │ ├── text-bce.yaml │ │ │ ├── text-be.yaml │ │ │ ├── text-c.yaml │ │ │ ├── text-ce.yaml │ │ │ ├── text-e.yaml │ │ │ ├── text.yaml │ │ │ ├── txt-b.yaml │ │ │ ├── txt-bc.yaml │ │ │ ├── txt-bce.yaml │ │ │ ├── txt-be.yaml │ │ │ ├── txt-c.yaml │ │ │ ├── txt-ce.yaml │ │ │ ├── txt-e.yaml │ │ │ └── txt.yaml │ │ ├── rendering_services.json │ │ ├── shapes/ │ │ │ ├── arial.json │ │ │ ├── copperplate.json │ │ │ ├── georgia.json │ │ │ ├── mondrian.json │ │ │ ├── sans.json │ │ │ ├── serif.json │ │ │ ├── times.json │ │ │ ├── trebuchet.json │ │ │ ├── verdana.json │ │ │ └── xinnian.json │ │ └── transformations.json │ ├── rewrite/ │ │ ├── __init__.py │ │ ├── _arrange.py │ │ ├── _drain.py │ │ ├── _drown.py │ │ ├── _explode.py │ │ ├── _grow.py │ │ ├── _split.py │ │ ├── arrange_bb.py │ │ ├── arrange_ga.py │ │ ├── arrows.py │ │ ├── cards.py │ │ ├── constraints.py │ │ ├── cross.py │ │ ├── damerau_levenshtein.py │ │ ├── fitness.py │ │ ├── guess_entities.py │ │ ├── obfuscate.py │ │ ├── op_tk.py │ │ └── types.py │ └── tools/ │ ├── __init__.py │ ├── graphviz_tools.py │ ├── load_mini_yaml.py │ ├── parser_tools.py │ ├── pluralize_fr.py │ ├── string_tools.py │ └── various.py ├── pyproject.toml ├── setup.py ├── test/ │ ├── __init__.py │ ├── launch_tests.sh │ ├── test_api.py │ ├── test_argument_parser.py │ ├── test_arrange_bb.py │ ├── test_arrange_ga.py │ ├── test_association.py │ ├── test_convert_chen.py │ ├── test_cross.py │ ├── test_data/ │ │ ├── small_pool.txt │ │ └── templates/ │ │ ├── bad_array_element.yaml │ │ ├── bad_circular_1.yaml │ │ ├── bad_circular_2.yaml │ │ ├── bad_no_order.yaml │ │ ├── bad_non_increasing_order.yaml │ │ ├── bad_non_numeric_order.yaml │ │ ├── bad_not_an_object.yaml │ │ ├── bad_object_value.yaml │ │ ├── child.yaml │ │ ├── expected_child.yaml │ │ ├── expected_grandchild.yaml │ │ ├── grandchild.yaml │ │ └── root.yaml │ ├── test_data_dict.py │ ├── test_entity.py │ ├── test_fitness.py │ ├── test_grid.py │ ├── test_guess_title.py │ ├── test_inheritance.py │ ├── test_mcd.py │ ├── test_mcd_dimensions.py │ ├── test_parser_tools.py │ ├── test_parser_tools_snapshot.txt │ ├── test_read_template.py │ ├── test_relations.py │ ├── test_rewrite_cards.py │ ├── test_rewrite_decompose.py │ ├── test_rewrite_grow.py │ ├── test_rewrite_labels.py │ ├── test_rewrite_types.py │ ├── test_string_tools.py │ ├── test_zoo.py │ └── zoo/ │ ├── alt/ │ │ ├── _alt_0.mcd │ │ ├── ddl/ │ │ │ ├── alt_0_ddl.d2 │ │ │ ├── alt_0_ddl.dbml │ │ │ └── alt_0_ddl.sql │ │ ├── exported/ │ │ │ ├── alt_0_erd_chen.gv │ │ │ ├── alt_0_erd_chen.txt │ │ │ ├── alt_0_erd_crow.gv │ │ │ ├── alt_0_erd_crow.mmd │ │ │ └── alt_0_uml.puml │ │ ├── mld/ │ │ │ ├── alt_0_debug.tsv │ │ │ ├── alt_0_dependencies.gv │ │ │ ├── alt_0_mld.html │ │ │ ├── alt_0_mld.mcd │ │ │ ├── alt_0_mld.md │ │ │ ├── alt_0_mld.tex │ │ │ └── alt_0_mld.txt │ │ └── rewritten/ │ │ ├── alt_0_rw_create_df_arrows=across.mcd │ │ ├── alt_0_rw_drain.mcd │ │ ├── alt_0_rw_drown.mcd │ │ ├── alt_0_rw_explode_arity=2,weak.mcd │ │ ├── alt_0_rw_explode_arity=2.5,weak.mcd │ │ ├── alt_0_rw_explode_arity=2.5.mcd │ │ ├── alt_0_rw_explode_arity=2.mcd │ │ ├── alt_0_rw_explode_arity=3,weak.mcd │ │ ├── alt_0_rw_explode_arity=3.mcd │ │ └── alt_0_rw_split.mcd │ ├── basic/ │ │ ├── _basic_0.mcd │ │ ├── _basic_1.mcd │ │ ├── _basic_2.mcd │ │ ├── ddl/ │ │ │ ├── basic_0_ddl.d2 │ │ │ ├── basic_0_ddl.dbml │ │ │ ├── basic_0_ddl.sql │ │ │ ├── basic_1_ddl.d2 │ │ │ ├── basic_1_ddl.dbml │ │ │ ├── basic_1_ddl.sql │ │ │ ├── basic_2_ddl.d2 │ │ │ ├── basic_2_ddl.dbml │ │ │ └── basic_2_ddl.sql │ │ ├── exported/ │ │ │ ├── basic_0_erd_chen.gv │ │ │ ├── basic_0_erd_chen.txt │ │ │ ├── basic_0_erd_crow.gv │ │ │ ├── basic_0_erd_crow.mmd │ │ │ ├── basic_0_uml.puml │ │ │ ├── basic_1_erd_chen.gv │ │ │ ├── basic_1_erd_chen.txt │ │ │ ├── basic_1_erd_crow.gv │ │ │ ├── basic_1_erd_crow.mmd │ │ │ ├── basic_1_uml.puml │ │ │ ├── basic_2_erd_chen.gv │ │ │ ├── basic_2_erd_chen.txt │ │ │ ├── basic_2_erd_crow.gv │ │ │ ├── basic_2_erd_crow.mmd │ │ │ └── basic_2_uml.puml │ │ ├── mld/ │ │ │ ├── basic_0_debug.tsv │ │ │ ├── basic_0_dependencies.gv │ │ │ ├── basic_0_mld.html │ │ │ ├── basic_0_mld.mcd │ │ │ ├── basic_0_mld.md │ │ │ ├── basic_0_mld.tex │ │ │ ├── basic_0_mld.txt │ │ │ ├── basic_1_debug.tsv │ │ │ ├── basic_1_dependencies.gv │ │ │ ├── basic_1_mld.html │ │ │ ├── basic_1_mld.mcd │ │ │ ├── basic_1_mld.md │ │ │ ├── basic_1_mld.tex │ │ │ ├── basic_1_mld.txt │ │ │ ├── basic_2_debug.tsv │ │ │ ├── basic_2_dependencies.gv │ │ │ ├── basic_2_mld.html │ │ │ ├── basic_2_mld.mcd │ │ │ ├── basic_2_mld.md │ │ │ ├── basic_2_mld.tex │ │ │ └── basic_2_mld.txt │ │ └── rewritten/ │ │ ├── basic_0_rw_create_df_arrows=across.mcd │ │ ├── basic_0_rw_drain.mcd │ │ ├── basic_0_rw_drown.mcd │ │ ├── basic_0_rw_explode_arity=2,weak.mcd │ │ ├── basic_0_rw_explode_arity=2.5,weak.mcd │ │ ├── basic_0_rw_explode_arity=2.5.mcd │ │ ├── basic_0_rw_explode_arity=2.mcd │ │ ├── basic_0_rw_explode_arity=3,weak.mcd │ │ ├── basic_0_rw_explode_arity=3.mcd │ │ ├── basic_0_rw_split.mcd │ │ ├── basic_1_rw_create_df_arrows=across.mcd │ │ ├── basic_1_rw_drain.mcd │ │ ├── basic_1_rw_drown.mcd │ │ ├── basic_1_rw_explode_arity=2,weak.mcd │ │ ├── basic_1_rw_explode_arity=2.5,weak.mcd │ │ ├── basic_1_rw_explode_arity=2.5.mcd │ │ ├── basic_1_rw_explode_arity=2.mcd │ │ ├── basic_1_rw_explode_arity=3,weak.mcd │ │ ├── basic_1_rw_explode_arity=3.mcd │ │ ├── basic_1_rw_split.mcd │ │ ├── basic_2_rw_create_df_arrows=across.mcd │ │ ├── basic_2_rw_drain.mcd │ │ ├── basic_2_rw_drown.mcd │ │ ├── basic_2_rw_explode_arity=2,weak.mcd │ │ ├── basic_2_rw_explode_arity=2.5,weak.mcd │ │ ├── basic_2_rw_explode_arity=2.5.mcd │ │ ├── basic_2_rw_explode_arity=2.mcd │ │ ├── basic_2_rw_explode_arity=3,weak.mcd │ │ ├── basic_2_rw_explode_arity=3.mcd │ │ └── basic_2_rw_split.mcd │ ├── cluster_NN1/ │ │ ├── _cluster_NN1_0.mcd │ │ ├── ddl/ │ │ │ ├── cluster_NN1_0_ddl.d2 │ │ │ ├── cluster_NN1_0_ddl.dbml │ │ │ └── cluster_NN1_0_ddl.sql │ │ ├── exported/ │ │ │ ├── cluster_NN1_0_erd_chen.gv │ │ │ ├── cluster_NN1_0_erd_chen.txt │ │ │ ├── cluster_NN1_0_erd_crow.mmd │ │ │ └── cluster_NN1_0_uml.puml │ │ ├── mld/ │ │ │ ├── cluster_NN1_0_debug.tsv │ │ │ ├── cluster_NN1_0_dependencies.gv │ │ │ ├── cluster_NN1_0_mld.html │ │ │ ├── cluster_NN1_0_mld.mcd │ │ │ ├── cluster_NN1_0_mld.md │ │ │ ├── cluster_NN1_0_mld.tex │ │ │ └── cluster_NN1_0_mld.txt │ │ └── rewritten/ │ │ ├── cluster_NN1_0_rw_create_df_arrows=across.mcd │ │ ├── cluster_NN1_0_rw_drain.mcd │ │ ├── cluster_NN1_0_rw_drown.mcd │ │ ├── cluster_NN1_0_rw_explode_arity=2,weak.mcd │ │ ├── cluster_NN1_0_rw_explode_arity=2.5,weak.mcd │ │ ├── cluster_NN1_0_rw_explode_arity=2.5.mcd │ │ ├── cluster_NN1_0_rw_explode_arity=2.mcd │ │ ├── cluster_NN1_0_rw_explode_arity=3,weak.mcd │ │ ├── cluster_NN1_0_rw_explode_arity=3.mcd │ │ └── cluster_NN1_0_rw_split.mcd │ ├── complex/ │ │ ├── _complex.mcd │ │ ├── ddl/ │ │ │ ├── complex_ddl.d2 │ │ │ ├── complex_ddl.dbml │ │ │ └── complex_ddl.sql │ │ ├── exported/ │ │ │ ├── complex_erd_chen.gv │ │ │ ├── complex_erd_chen.txt │ │ │ ├── complex_erd_crow.mmd │ │ │ └── complex_uml.puml │ │ ├── mld/ │ │ │ ├── complex_debug.tsv │ │ │ ├── complex_dependencies.gv │ │ │ ├── complex_mld.html │ │ │ ├── complex_mld.mcd │ │ │ ├── complex_mld.md │ │ │ ├── complex_mld.tex │ │ │ └── complex_mld.txt │ │ └── rewritten/ │ │ ├── complex_rw_create_df_arrows=across.mcd │ │ ├── complex_rw_drain.mcd │ │ ├── complex_rw_drown.mcd │ │ ├── complex_rw_explode_arity=2,weak.mcd │ │ ├── complex_rw_explode_arity=2.5,weak.mcd │ │ ├── complex_rw_explode_arity=2.5.mcd │ │ ├── complex_rw_explode_arity=2.mcd │ │ ├── complex_rw_explode_arity=3,weak.mcd │ │ ├── complex_rw_explode_arity=3.mcd │ │ └── complex_rw_split.mcd │ ├── drain/ │ │ ├── _drain.mcd │ │ ├── ddl/ │ │ │ ├── drain_ddl.d2 │ │ │ ├── drain_ddl.dbml │ │ │ └── drain_ddl.sql │ │ ├── exported/ │ │ │ ├── drain_erd_chen.gv │ │ │ ├── drain_erd_chen.txt │ │ │ ├── drain_erd_crow.gv │ │ │ ├── drain_erd_crow.mmd │ │ │ └── drain_uml.puml │ │ ├── mld/ │ │ │ ├── drain_debug.tsv │ │ │ ├── drain_dependencies.gv │ │ │ ├── drain_mld.html │ │ │ ├── drain_mld.mcd │ │ │ ├── drain_mld.md │ │ │ ├── drain_mld.tex │ │ │ └── drain_mld.txt │ │ └── rewritten/ │ │ ├── drain_rw_create_df_arrows=across.mcd │ │ ├── drain_rw_drain.mcd │ │ ├── drain_rw_drown.mcd │ │ ├── drain_rw_explode_arity=2,weak.mcd │ │ ├── drain_rw_explode_arity=2.5,weak.mcd │ │ ├── drain_rw_explode_arity=2.5.mcd │ │ ├── drain_rw_explode_arity=2.mcd │ │ ├── drain_rw_explode_arity=3,weak.mcd │ │ ├── drain_rw_explode_arity=3.mcd │ │ └── drain_rw_split.mcd │ ├── empty_attrs/ │ │ ├── _empty_attrs_0.mcd │ │ ├── ddl/ │ │ │ ├── empty_attrs_0_ddl.d2 │ │ │ ├── empty_attrs_0_ddl.dbml │ │ │ └── empty_attrs_0_ddl.sql │ │ ├── exported/ │ │ │ ├── empty_attrs_0_erd_chen.gv │ │ │ ├── empty_attrs_0_erd_chen.txt │ │ │ ├── empty_attrs_0_erd_crow.gv │ │ │ ├── empty_attrs_0_erd_crow.mmd │ │ │ └── empty_attrs_0_uml.puml │ │ ├── mld/ │ │ │ ├── empty_attrs_0_debug.tsv │ │ │ ├── empty_attrs_0_dependencies.gv │ │ │ ├── empty_attrs_0_mld.html │ │ │ ├── empty_attrs_0_mld.mcd │ │ │ ├── empty_attrs_0_mld.md │ │ │ ├── empty_attrs_0_mld.tex │ │ │ └── empty_attrs_0_mld.txt │ │ └── rewritten/ │ │ ├── empty_attrs_0_rw_create_df_arrows=across.mcd │ │ ├── empty_attrs_0_rw_drain.mcd │ │ ├── empty_attrs_0_rw_drown.mcd │ │ ├── empty_attrs_0_rw_explode_arity=2,weak.mcd │ │ ├── empty_attrs_0_rw_explode_arity=2.5,weak.mcd │ │ ├── empty_attrs_0_rw_explode_arity=2.5.mcd │ │ ├── empty_attrs_0_rw_explode_arity=2.mcd │ │ ├── empty_attrs_0_rw_explode_arity=3,weak.mcd │ │ ├── empty_attrs_0_rw_explode_arity=3.mcd │ │ └── empty_attrs_0_rw_split.mcd │ ├── gerund/ │ │ ├── _gerund_0.mcd │ │ ├── _gerund_1.mcd │ │ ├── ddl/ │ │ │ ├── gerund_0_ddl.d2 │ │ │ ├── gerund_0_ddl.dbml │ │ │ ├── gerund_0_ddl.sql │ │ │ ├── gerund_1_ddl.d2 │ │ │ ├── gerund_1_ddl.dbml │ │ │ └── gerund_1_ddl.sql │ │ ├── exported/ │ │ │ ├── gerund_0_erd_chen.gv │ │ │ ├── gerund_0_erd_chen.txt │ │ │ ├── gerund_0_erd_crow.gv │ │ │ ├── gerund_0_erd_crow.mmd │ │ │ ├── gerund_0_uml.puml │ │ │ ├── gerund_1_erd_chen.gv │ │ │ ├── gerund_1_erd_chen.txt │ │ │ ├── gerund_1_erd_crow.gv │ │ │ ├── gerund_1_erd_crow.mmd │ │ │ └── gerund_1_uml.puml │ │ ├── mld/ │ │ │ ├── gerund_0_debug.tsv │ │ │ ├── gerund_0_dependencies.gv │ │ │ ├── gerund_0_mld.html │ │ │ ├── gerund_0_mld.mcd │ │ │ ├── gerund_0_mld.md │ │ │ ├── gerund_0_mld.tex │ │ │ ├── gerund_0_mld.txt │ │ │ ├── gerund_1_debug.tsv │ │ │ ├── gerund_1_dependencies.gv │ │ │ ├── gerund_1_mld.html │ │ │ ├── gerund_1_mld.mcd │ │ │ ├── gerund_1_mld.md │ │ │ ├── gerund_1_mld.tex │ │ │ └── gerund_1_mld.txt │ │ └── rewritten/ │ │ ├── gerund_0_rw_create_df_arrows=across.mcd │ │ ├── gerund_0_rw_drain.mcd │ │ ├── gerund_0_rw_drown.mcd │ │ ├── gerund_0_rw_explode_arity=2,weak.mcd │ │ ├── gerund_0_rw_explode_arity=2.5,weak.mcd │ │ ├── gerund_0_rw_explode_arity=2.5.mcd │ │ ├── gerund_0_rw_explode_arity=2.mcd │ │ ├── gerund_0_rw_explode_arity=3,weak.mcd │ │ ├── gerund_0_rw_explode_arity=3.mcd │ │ ├── gerund_0_rw_split.mcd │ │ ├── gerund_1_rw_create_df_arrows=across.mcd │ │ ├── gerund_1_rw_drain.mcd │ │ ├── gerund_1_rw_drown.mcd │ │ ├── gerund_1_rw_explode_arity=2,weak.mcd │ │ ├── gerund_1_rw_explode_arity=2.5,weak.mcd │ │ ├── gerund_1_rw_explode_arity=2.5.mcd │ │ ├── gerund_1_rw_explode_arity=2.mcd │ │ ├── gerund_1_rw_explode_arity=3,weak.mcd │ │ ├── gerund_1_rw_explode_arity=3.mcd │ │ └── gerund_1_rw_split.mcd │ ├── inheritance/ │ │ ├── _inheritance_0.mcd │ │ ├── _inheritance_1.mcd │ │ ├── _inheritance_2.mcd │ │ ├── _inheritance_3.mcd │ │ ├── _inheritance_4.mcd │ │ ├── _inheritance_5.mcd │ │ ├── ddl/ │ │ │ ├── inheritance_0_ddl.d2 │ │ │ ├── inheritance_0_ddl.dbml │ │ │ ├── inheritance_0_ddl.sql │ │ │ ├── inheritance_1_ddl.d2 │ │ │ ├── inheritance_1_ddl.dbml │ │ │ ├── inheritance_1_ddl.sql │ │ │ ├── inheritance_2_ddl.d2 │ │ │ ├── inheritance_2_ddl.dbml │ │ │ ├── inheritance_2_ddl.sql │ │ │ ├── inheritance_3_ddl.d2 │ │ │ ├── inheritance_3_ddl.dbml │ │ │ ├── inheritance_3_ddl.sql │ │ │ ├── inheritance_4_ddl.d2 │ │ │ ├── inheritance_4_ddl.dbml │ │ │ ├── inheritance_4_ddl.sql │ │ │ ├── inheritance_5_ddl.d2 │ │ │ ├── inheritance_5_ddl.dbml │ │ │ └── inheritance_5_ddl.sql │ │ ├── exported/ │ │ │ ├── inheritance_0_erd_chen.gv │ │ │ ├── inheritance_0_erd_chen.txt │ │ │ ├── inheritance_0_erd_crow.gv │ │ │ ├── inheritance_0_erd_crow.mmd │ │ │ ├── inheritance_0_uml.puml │ │ │ ├── inheritance_1_erd_chen.gv │ │ │ ├── inheritance_1_erd_chen.txt │ │ │ ├── inheritance_1_erd_crow.gv │ │ │ ├── inheritance_1_erd_crow.mmd │ │ │ ├── inheritance_1_uml.puml │ │ │ ├── inheritance_2_erd_chen.gv │ │ │ ├── inheritance_2_erd_chen.txt │ │ │ ├── inheritance_2_erd_crow.gv │ │ │ ├── inheritance_2_erd_crow.mmd │ │ │ ├── inheritance_2_uml.puml │ │ │ ├── inheritance_3_erd_chen.gv │ │ │ ├── inheritance_3_erd_chen.txt │ │ │ ├── inheritance_3_erd_crow.gv │ │ │ ├── inheritance_3_erd_crow.mmd │ │ │ ├── inheritance_3_uml.puml │ │ │ ├── inheritance_4_erd_chen.gv │ │ │ ├── inheritance_4_erd_chen.txt │ │ │ ├── inheritance_4_erd_crow.gv │ │ │ ├── inheritance_4_erd_crow.mmd │ │ │ ├── inheritance_4_uml.puml │ │ │ ├── inheritance_5_erd_chen.gv │ │ │ ├── inheritance_5_erd_chen.txt │ │ │ ├── inheritance_5_erd_crow.gv │ │ │ ├── inheritance_5_erd_crow.mmd │ │ │ └── inheritance_5_uml.puml │ │ ├── mld/ │ │ │ ├── inheritance_0_debug.tsv │ │ │ ├── inheritance_0_dependencies.gv │ │ │ ├── inheritance_0_mld.html │ │ │ ├── inheritance_0_mld.mcd │ │ │ ├── inheritance_0_mld.md │ │ │ ├── inheritance_0_mld.tex │ │ │ ├── inheritance_0_mld.txt │ │ │ ├── inheritance_1_debug.tsv │ │ │ ├── inheritance_1_dependencies.gv │ │ │ ├── inheritance_1_mld.html │ │ │ ├── inheritance_1_mld.mcd │ │ │ ├── inheritance_1_mld.md │ │ │ ├── inheritance_1_mld.tex │ │ │ ├── inheritance_1_mld.txt │ │ │ ├── inheritance_2_debug.tsv │ │ │ ├── inheritance_2_dependencies.gv │ │ │ ├── inheritance_2_mld.html │ │ │ ├── inheritance_2_mld.mcd │ │ │ ├── inheritance_2_mld.md │ │ │ ├── inheritance_2_mld.tex │ │ │ ├── inheritance_2_mld.txt │ │ │ ├── inheritance_3_debug.tsv │ │ │ ├── inheritance_3_dependencies.gv │ │ │ ├── inheritance_3_mld.html │ │ │ ├── inheritance_3_mld.mcd │ │ │ ├── inheritance_3_mld.md │ │ │ ├── inheritance_3_mld.tex │ │ │ ├── inheritance_3_mld.txt │ │ │ ├── inheritance_4_debug.tsv │ │ │ ├── inheritance_4_dependencies.gv │ │ │ ├── inheritance_4_mld.html │ │ │ ├── inheritance_4_mld.mcd │ │ │ ├── inheritance_4_mld.md │ │ │ ├── inheritance_4_mld.tex │ │ │ ├── inheritance_4_mld.txt │ │ │ ├── inheritance_5_debug.tsv │ │ │ ├── inheritance_5_dependencies.gv │ │ │ ├── inheritance_5_mld.html │ │ │ ├── inheritance_5_mld.mcd │ │ │ ├── inheritance_5_mld.md │ │ │ ├── inheritance_5_mld.tex │ │ │ └── inheritance_5_mld.txt │ │ └── rewritten/ │ │ ├── inheritance_0_rw_create_df_arrows=across.mcd │ │ ├── inheritance_0_rw_drain.mcd │ │ ├── inheritance_0_rw_drown.mcd │ │ ├── inheritance_0_rw_explode_arity=2,weak.mcd │ │ ├── inheritance_0_rw_explode_arity=2.5,weak.mcd │ │ ├── inheritance_0_rw_explode_arity=2.5.mcd │ │ ├── inheritance_0_rw_explode_arity=2.mcd │ │ ├── inheritance_0_rw_explode_arity=3,weak.mcd │ │ ├── inheritance_0_rw_explode_arity=3.mcd │ │ ├── inheritance_0_rw_split.mcd │ │ ├── inheritance_1_rw_create_df_arrows=across.mcd │ │ ├── inheritance_1_rw_drain.mcd │ │ ├── inheritance_1_rw_drown.mcd │ │ ├── inheritance_1_rw_explode_arity=2,weak.mcd │ │ ├── inheritance_1_rw_explode_arity=2.5,weak.mcd │ │ ├── inheritance_1_rw_explode_arity=2.5.mcd │ │ ├── inheritance_1_rw_explode_arity=2.mcd │ │ ├── inheritance_1_rw_explode_arity=3,weak.mcd │ │ ├── inheritance_1_rw_explode_arity=3.mcd │ │ ├── inheritance_1_rw_split.mcd │ │ ├── inheritance_2_rw_create_df_arrows=across.mcd │ │ ├── inheritance_2_rw_drain.mcd │ │ ├── inheritance_2_rw_drown.mcd │ │ ├── inheritance_2_rw_explode_arity=2,weak.mcd │ │ ├── inheritance_2_rw_explode_arity=2.5,weak.mcd │ │ ├── inheritance_2_rw_explode_arity=2.5.mcd │ │ ├── inheritance_2_rw_explode_arity=2.mcd │ │ ├── inheritance_2_rw_explode_arity=3,weak.mcd │ │ ├── inheritance_2_rw_explode_arity=3.mcd │ │ ├── inheritance_2_rw_split.mcd │ │ ├── inheritance_3_rw_create_df_arrows=across.mcd │ │ ├── inheritance_3_rw_drain.mcd │ │ ├── inheritance_3_rw_drown.mcd │ │ ├── inheritance_3_rw_explode_arity=2,weak.mcd │ │ ├── inheritance_3_rw_explode_arity=2.5,weak.mcd │ │ ├── inheritance_3_rw_explode_arity=2.5.mcd │ │ ├── inheritance_3_rw_explode_arity=2.mcd │ │ ├── inheritance_3_rw_explode_arity=3,weak.mcd │ │ ├── inheritance_3_rw_explode_arity=3.mcd │ │ ├── inheritance_3_rw_split.mcd │ │ ├── inheritance_4_rw_create_df_arrows=across.mcd │ │ ├── inheritance_4_rw_drain.mcd │ │ ├── inheritance_4_rw_drown.mcd │ │ ├── inheritance_4_rw_explode_arity=2,weak.mcd │ │ ├── inheritance_4_rw_explode_arity=2.5,weak.mcd │ │ ├── inheritance_4_rw_explode_arity=2.5.mcd │ │ ├── inheritance_4_rw_explode_arity=2.mcd │ │ ├── inheritance_4_rw_explode_arity=3,weak.mcd │ │ ├── inheritance_4_rw_explode_arity=3.mcd │ │ ├── inheritance_4_rw_split.mcd │ │ ├── inheritance_5_rw_create_df_arrows=across.mcd │ │ ├── inheritance_5_rw_drain.mcd │ │ ├── inheritance_5_rw_drown.mcd │ │ ├── inheritance_5_rw_explode_arity=2,weak.mcd │ │ ├── inheritance_5_rw_explode_arity=2.5,weak.mcd │ │ ├── inheritance_5_rw_explode_arity=2.5.mcd │ │ ├── inheritance_5_rw_explode_arity=2.mcd │ │ ├── inheritance_5_rw_explode_arity=3,weak.mcd │ │ ├── inheritance_5_rw_explode_arity=3.mcd │ │ └── inheritance_5_rw_split.mcd │ ├── inheritance_weak/ │ │ ├── _strong_child_0.mcd │ │ ├── _strong_child_1.mcd │ │ ├── _strong_child_2.mcd │ │ ├── ddl/ │ │ │ ├── strong_child_0_ddl.d2 │ │ │ ├── strong_child_0_ddl.dbml │ │ │ ├── strong_child_0_ddl.sql │ │ │ ├── strong_child_1_ddl.d2 │ │ │ ├── strong_child_1_ddl.dbml │ │ │ ├── strong_child_1_ddl.sql │ │ │ ├── strong_child_2_ddl.d2 │ │ │ ├── strong_child_2_ddl.dbml │ │ │ └── strong_child_2_ddl.sql │ │ ├── exported/ │ │ │ ├── strong_child_0_erd_chen.gv │ │ │ ├── strong_child_0_erd_chen.txt │ │ │ ├── strong_child_0_erd_crow.gv │ │ │ ├── strong_child_0_erd_crow.mmd │ │ │ ├── strong_child_0_uml.puml │ │ │ ├── strong_child_1_erd_chen.gv │ │ │ ├── strong_child_1_erd_chen.txt │ │ │ ├── strong_child_1_erd_crow.gv │ │ │ ├── strong_child_1_erd_crow.mmd │ │ │ ├── strong_child_1_uml.puml │ │ │ ├── strong_child_2_erd_chen.gv │ │ │ ├── strong_child_2_erd_chen.txt │ │ │ ├── strong_child_2_erd_crow.gv │ │ │ ├── strong_child_2_erd_crow.mmd │ │ │ └── strong_child_2_uml.puml │ │ ├── mld/ │ │ │ ├── strong_child_0_debug.tsv │ │ │ ├── strong_child_0_dependencies.gv │ │ │ ├── strong_child_0_mld.html │ │ │ ├── strong_child_0_mld.mcd │ │ │ ├── strong_child_0_mld.md │ │ │ ├── strong_child_0_mld.tex │ │ │ ├── strong_child_0_mld.txt │ │ │ ├── strong_child_1_debug.tsv │ │ │ ├── strong_child_1_dependencies.gv │ │ │ ├── strong_child_1_mld.html │ │ │ ├── strong_child_1_mld.mcd │ │ │ ├── strong_child_1_mld.md │ │ │ ├── strong_child_1_mld.tex │ │ │ ├── strong_child_1_mld.txt │ │ │ ├── strong_child_2_debug.tsv │ │ │ ├── strong_child_2_dependencies.gv │ │ │ ├── strong_child_2_mld.html │ │ │ ├── strong_child_2_mld.mcd │ │ │ ├── strong_child_2_mld.md │ │ │ ├── strong_child_2_mld.tex │ │ │ └── strong_child_2_mld.txt │ │ └── rewritten/ │ │ ├── strong_child_0_rw_create_df_arrows=across.mcd │ │ ├── strong_child_0_rw_drain.mcd │ │ ├── strong_child_0_rw_drown.mcd │ │ ├── strong_child_0_rw_explode_arity=2,weak.mcd │ │ ├── strong_child_0_rw_explode_arity=2.5,weak.mcd │ │ ├── strong_child_0_rw_explode_arity=2.5.mcd │ │ ├── strong_child_0_rw_explode_arity=2.mcd │ │ ├── strong_child_0_rw_explode_arity=3,weak.mcd │ │ ├── strong_child_0_rw_explode_arity=3.mcd │ │ ├── strong_child_0_rw_split.mcd │ │ ├── strong_child_1_rw_create_df_arrows=across.mcd │ │ ├── strong_child_1_rw_drain.mcd │ │ ├── strong_child_1_rw_drown.mcd │ │ ├── strong_child_1_rw_explode_arity=2,weak.mcd │ │ ├── strong_child_1_rw_explode_arity=2.5,weak.mcd │ │ ├── strong_child_1_rw_explode_arity=2.5.mcd │ │ ├── strong_child_1_rw_explode_arity=2.mcd │ │ ├── strong_child_1_rw_explode_arity=3,weak.mcd │ │ ├── strong_child_1_rw_explode_arity=3.mcd │ │ ├── strong_child_1_rw_split.mcd │ │ ├── strong_child_2_rw_create_df_arrows=across.mcd │ │ ├── strong_child_2_rw_drain.mcd │ │ ├── strong_child_2_rw_drown.mcd │ │ ├── strong_child_2_rw_explode_arity=2,weak.mcd │ │ ├── strong_child_2_rw_explode_arity=2.5,weak.mcd │ │ ├── strong_child_2_rw_explode_arity=2.5.mcd │ │ ├── strong_child_2_rw_explode_arity=2.mcd │ │ ├── strong_child_2_rw_explode_arity=3,weak.mcd │ │ ├── strong_child_2_rw_explode_arity=3.mcd │ │ └── strong_child_2_rw_split.mcd │ ├── landing/ │ │ ├── _landing.mcd │ │ ├── ddl/ │ │ │ ├── landing_ddl.d2 │ │ │ ├── landing_ddl.dbml │ │ │ └── landing_ddl.sql │ │ ├── exported/ │ │ │ ├── landing_erd_chen.gv │ │ │ ├── landing_erd_chen.txt │ │ │ ├── landing_erd_crow.gv │ │ │ ├── landing_erd_crow.mmd │ │ │ └── landing_uml.puml │ │ ├── mld/ │ │ │ ├── landing_debug.tsv │ │ │ ├── landing_dependencies.gv │ │ │ ├── landing_mld.html │ │ │ ├── landing_mld.mcd │ │ │ ├── landing_mld.md │ │ │ ├── landing_mld.tex │ │ │ └── landing_mld.txt │ │ └── rewritten/ │ │ ├── landing_rw_create_df_arrows=across.mcd │ │ ├── landing_rw_drain.mcd │ │ ├── landing_rw_drown.mcd │ │ ├── landing_rw_explode_arity=2,weak.mcd │ │ ├── landing_rw_explode_arity=2.5,weak.mcd │ │ ├── landing_rw_explode_arity=2.5.mcd │ │ ├── landing_rw_explode_arity=2.mcd │ │ ├── landing_rw_explode_arity=3,weak.mcd │ │ ├── landing_rw_explode_arity=3.mcd │ │ └── landing_rw_split.mcd │ ├── minimal/ │ │ ├── _minimal.mcd │ │ ├── ddl/ │ │ │ ├── minimal_ddl.d2 │ │ │ ├── minimal_ddl.dbml │ │ │ └── minimal_ddl.sql │ │ ├── exported/ │ │ │ ├── minimal_erd_chen.gv │ │ │ ├── minimal_erd_chen.txt │ │ │ ├── minimal_erd_crow.gv │ │ │ ├── minimal_erd_crow.mmd │ │ │ └── minimal_uml.puml │ │ ├── mld/ │ │ │ ├── minimal_debug.tsv │ │ │ ├── minimal_dependencies.gv │ │ │ ├── minimal_mld.html │ │ │ ├── minimal_mld.mcd │ │ │ ├── minimal_mld.md │ │ │ ├── minimal_mld.tex │ │ │ └── minimal_mld.txt │ │ └── rewritten/ │ │ ├── minimal_rw_create_df_arrows=across.mcd │ │ ├── minimal_rw_drain.mcd │ │ ├── minimal_rw_drown.mcd │ │ ├── minimal_rw_explode_arity=2,weak.mcd │ │ ├── minimal_rw_explode_arity=2.5,weak.mcd │ │ ├── minimal_rw_explode_arity=2.5.mcd │ │ ├── minimal_rw_explode_arity=2.mcd │ │ ├── minimal_rw_explode_arity=3,weak.mcd │ │ ├── minimal_rw_explode_arity=3.mcd │ │ └── minimal_rw_split.mcd │ ├── protected/ │ │ ├── _protected_0.mcd │ │ ├── ddl/ │ │ │ ├── protected_0_ddl.d2 │ │ │ ├── protected_0_ddl.dbml │ │ │ └── protected_0_ddl.sql │ │ ├── exported/ │ │ │ ├── protected_0_erd_chen.gv │ │ │ ├── protected_0_erd_chen.txt │ │ │ ├── protected_0_erd_crow.gv │ │ │ ├── protected_0_erd_crow.mmd │ │ │ └── protected_0_uml.puml │ │ ├── mld/ │ │ │ ├── protected_0_debug.tsv │ │ │ ├── protected_0_dependencies.gv │ │ │ ├── protected_0_mld.html │ │ │ ├── protected_0_mld.mcd │ │ │ ├── protected_0_mld.md │ │ │ ├── protected_0_mld.tex │ │ │ └── protected_0_mld.txt │ │ └── rewritten/ │ │ ├── protected_0_rw_create_df_arrows=across.mcd │ │ ├── protected_0_rw_drain.mcd │ │ ├── protected_0_rw_drown.mcd │ │ ├── protected_0_rw_explode_arity=2,weak.mcd │ │ ├── protected_0_rw_explode_arity=2.5,weak.mcd │ │ ├── protected_0_rw_explode_arity=2.5.mcd │ │ ├── protected_0_rw_explode_arity=2.mcd │ │ ├── protected_0_rw_explode_arity=3,weak.mcd │ │ ├── protected_0_rw_explode_arity=3.mcd │ │ └── protected_0_rw_split.mcd │ ├── reflexive/ │ │ ├── _reflexive_0.mcd │ │ ├── ddl/ │ │ │ ├── reflexive_0_ddl.d2 │ │ │ ├── reflexive_0_ddl.dbml │ │ │ └── reflexive_0_ddl.sql │ │ ├── exported/ │ │ │ ├── reflexive_0_erd_chen.gv │ │ │ ├── reflexive_0_erd_chen.txt │ │ │ ├── reflexive_0_erd_crow.gv │ │ │ ├── reflexive_0_erd_crow.mmd │ │ │ └── reflexive_0_uml.puml │ │ ├── mld/ │ │ │ ├── reflexive_0_debug.tsv │ │ │ ├── reflexive_0_dependencies.gv │ │ │ ├── reflexive_0_mld.html │ │ │ ├── reflexive_0_mld.mcd │ │ │ ├── reflexive_0_mld.md │ │ │ ├── reflexive_0_mld.tex │ │ │ └── reflexive_0_mld.txt │ │ └── rewritten/ │ │ ├── reflexive_0_rw_create_df_arrows=across.mcd │ │ ├── reflexive_0_rw_drain.mcd │ │ ├── reflexive_0_rw_drown.mcd │ │ ├── reflexive_0_rw_explode_arity=2,weak.mcd │ │ ├── reflexive_0_rw_explode_arity=2.5,weak.mcd │ │ ├── reflexive_0_rw_explode_arity=2.5.mcd │ │ ├── reflexive_0_rw_explode_arity=2.mcd │ │ ├── reflexive_0_rw_explode_arity=3,weak.mcd │ │ ├── reflexive_0_rw_explode_arity=3.mcd │ │ └── reflexive_0_rw_split.mcd │ ├── split/ │ │ ├── _split.mcd │ │ ├── ddl/ │ │ │ ├── split_ddl.d2 │ │ │ ├── split_ddl.dbml │ │ │ └── split_ddl.sql │ │ ├── exported/ │ │ │ ├── split_erd_chen.gv │ │ │ ├── split_erd_chen.txt │ │ │ ├── split_erd_crow.gv │ │ │ ├── split_erd_crow.mmd │ │ │ └── split_uml.puml │ │ ├── mld/ │ │ │ ├── split_debug.tsv │ │ │ ├── split_dependencies.gv │ │ │ ├── split_mld.html │ │ │ ├── split_mld.mcd │ │ │ ├── split_mld.md │ │ │ ├── split_mld.tex │ │ │ └── split_mld.txt │ │ └── rewritten/ │ │ ├── split_rw_create_df_arrows=across.mcd │ │ ├── split_rw_drain.mcd │ │ ├── split_rw_drown.mcd │ │ ├── split_rw_explode_arity=2,weak.mcd │ │ ├── split_rw_explode_arity=2.5,weak.mcd │ │ ├── split_rw_explode_arity=2.5.mcd │ │ ├── split_rw_explode_arity=2.mcd │ │ ├── split_rw_explode_arity=3,weak.mcd │ │ ├── split_rw_explode_arity=3.mcd │ │ └── split_rw_split.mcd │ ├── ternary_unicity/ │ │ ├── _ternary_unicity_0.mcd │ │ ├── ddl/ │ │ │ ├── ternary_unicity_0_ddl.d2 │ │ │ ├── ternary_unicity_0_ddl.dbml │ │ │ └── ternary_unicity_0_ddl.sql │ │ ├── exported/ │ │ │ ├── ternary_unicity_0_erd_chen.gv │ │ │ ├── ternary_unicity_0_erd_chen.txt │ │ │ ├── ternary_unicity_0_erd_crow.gv │ │ │ ├── ternary_unicity_0_erd_crow.mmd │ │ │ └── ternary_unicity_0_uml.puml │ │ ├── mld/ │ │ │ ├── ternary_unicity_0_debug.tsv │ │ │ ├── ternary_unicity_0_dependencies.gv │ │ │ ├── ternary_unicity_0_mld.html │ │ │ ├── ternary_unicity_0_mld.mcd │ │ │ ├── ternary_unicity_0_mld.md │ │ │ ├── ternary_unicity_0_mld.tex │ │ │ └── ternary_unicity_0_mld.txt │ │ └── rewritten/ │ │ ├── ternary_unicity_0_rw_create_df_arrows=across.mcd │ │ ├── ternary_unicity_0_rw_drain.mcd │ │ ├── ternary_unicity_0_rw_drown.mcd │ │ ├── ternary_unicity_0_rw_explode_arity=2,weak.mcd │ │ ├── ternary_unicity_0_rw_explode_arity=2.5,weak.mcd │ │ ├── ternary_unicity_0_rw_explode_arity=2.5.mcd │ │ ├── ternary_unicity_0_rw_explode_arity=2.mcd │ │ ├── ternary_unicity_0_rw_explode_arity=3,weak.mcd │ │ ├── ternary_unicity_0_rw_explode_arity=3.mcd │ │ └── ternary_unicity_0_rw_split.mcd │ ├── triple_111/ │ │ ├── _triple_111_0.mcd │ │ ├── _triple_111_1.mcd │ │ ├── ddl/ │ │ │ ├── triple_111_0_ddl.d2 │ │ │ ├── triple_111_0_ddl.dbml │ │ │ ├── triple_111_0_ddl.sql │ │ │ ├── triple_111_1_ddl.d2 │ │ │ ├── triple_111_1_ddl.dbml │ │ │ └── triple_111_1_ddl.sql │ │ ├── exported/ │ │ │ ├── triple_111_0_erd_chen.gv │ │ │ ├── triple_111_0_erd_chen.txt │ │ │ ├── triple_111_0_erd_crow.mmd │ │ │ ├── triple_111_0_uml.puml │ │ │ ├── triple_111_1_erd_chen.gv │ │ │ ├── triple_111_1_erd_chen.txt │ │ │ ├── triple_111_1_erd_crow.mmd │ │ │ └── triple_111_1_uml.puml │ │ ├── mld/ │ │ │ ├── triple_111_0_debug.tsv │ │ │ ├── triple_111_0_dependencies.gv │ │ │ ├── triple_111_0_mld.html │ │ │ ├── triple_111_0_mld.mcd │ │ │ ├── triple_111_0_mld.md │ │ │ ├── triple_111_0_mld.tex │ │ │ ├── triple_111_0_mld.txt │ │ │ ├── triple_111_1_debug.tsv │ │ │ ├── triple_111_1_dependencies.gv │ │ │ ├── triple_111_1_mld.html │ │ │ ├── triple_111_1_mld.mcd │ │ │ ├── triple_111_1_mld.md │ │ │ ├── triple_111_1_mld.tex │ │ │ └── triple_111_1_mld.txt │ │ └── rewritten/ │ │ ├── triple_111_0_rw_create_df_arrows=across.mcd │ │ ├── triple_111_0_rw_drain.mcd │ │ ├── triple_111_0_rw_drown.mcd │ │ ├── triple_111_0_rw_explode_arity=2,weak.mcd │ │ ├── triple_111_0_rw_explode_arity=2.5,weak.mcd │ │ ├── triple_111_0_rw_explode_arity=2.5.mcd │ │ ├── triple_111_0_rw_explode_arity=2.mcd │ │ ├── triple_111_0_rw_explode_arity=3,weak.mcd │ │ ├── triple_111_0_rw_explode_arity=3.mcd │ │ ├── triple_111_0_rw_split.mcd │ │ ├── triple_111_1_rw_create_df_arrows=across.mcd │ │ ├── triple_111_1_rw_drain.mcd │ │ ├── triple_111_1_rw_drown.mcd │ │ ├── triple_111_1_rw_explode_arity=2,weak.mcd │ │ ├── triple_111_1_rw_explode_arity=2.5,weak.mcd │ │ ├── triple_111_1_rw_explode_arity=2.5.mcd │ │ ├── triple_111_1_rw_explode_arity=2.mcd │ │ ├── triple_111_1_rw_explode_arity=3,weak.mcd │ │ ├── triple_111_1_rw_explode_arity=3.mcd │ │ └── triple_111_1_rw_split.mcd │ ├── triple_N11/ │ │ ├── _triple_N11_0.mcd │ │ ├── _triple_N11_1.mcd │ │ ├── ddl/ │ │ │ ├── triple_N11_0_ddl.d2 │ │ │ ├── triple_N11_0_ddl.dbml │ │ │ ├── triple_N11_0_ddl.sql │ │ │ ├── triple_N11_1_ddl.d2 │ │ │ ├── triple_N11_1_ddl.dbml │ │ │ └── triple_N11_1_ddl.sql │ │ ├── exported/ │ │ │ ├── triple_N11_0_erd_chen.gv │ │ │ ├── triple_N11_0_erd_chen.txt │ │ │ ├── triple_N11_0_erd_crow.mmd │ │ │ ├── triple_N11_0_uml.puml │ │ │ ├── triple_N11_1_erd_chen.gv │ │ │ ├── triple_N11_1_erd_chen.txt │ │ │ ├── triple_N11_1_erd_crow.mmd │ │ │ └── triple_N11_1_uml.puml │ │ ├── mld/ │ │ │ ├── triple_N11_0_debug.tsv │ │ │ ├── triple_N11_0_dependencies.gv │ │ │ ├── triple_N11_0_mld.html │ │ │ ├── triple_N11_0_mld.mcd │ │ │ ├── triple_N11_0_mld.md │ │ │ ├── triple_N11_0_mld.tex │ │ │ ├── triple_N11_0_mld.txt │ │ │ ├── triple_N11_1_debug.tsv │ │ │ ├── triple_N11_1_dependencies.gv │ │ │ ├── triple_N11_1_mld.html │ │ │ ├── triple_N11_1_mld.mcd │ │ │ ├── triple_N11_1_mld.md │ │ │ ├── triple_N11_1_mld.tex │ │ │ └── triple_N11_1_mld.txt │ │ └── rewritten/ │ │ ├── triple_N11_0_rw_create_cifs.mcd │ │ ├── triple_N11_0_rw_create_df_arrows=across.mcd │ │ ├── triple_N11_0_rw_drain.mcd │ │ ├── triple_N11_0_rw_drown.mcd │ │ ├── triple_N11_0_rw_explode_arity=2,weak.mcd │ │ ├── triple_N11_0_rw_explode_arity=2.5,weak.mcd │ │ ├── triple_N11_0_rw_explode_arity=2.5.mcd │ │ ├── triple_N11_0_rw_explode_arity=2.mcd │ │ ├── triple_N11_0_rw_explode_arity=3,weak.mcd │ │ ├── triple_N11_0_rw_explode_arity=3.mcd │ │ ├── triple_N11_0_rw_split.mcd │ │ ├── triple_N11_1_rw_create_cifs.mcd │ │ ├── triple_N11_1_rw_create_df_arrows=across.mcd │ │ ├── triple_N11_1_rw_drain.mcd │ │ ├── triple_N11_1_rw_drown.mcd │ │ ├── triple_N11_1_rw_explode_arity=2,weak.mcd │ │ ├── triple_N11_1_rw_explode_arity=2.5,weak.mcd │ │ ├── triple_N11_1_rw_explode_arity=2.5.mcd │ │ ├── triple_N11_1_rw_explode_arity=2.mcd │ │ ├── triple_N11_1_rw_explode_arity=3,weak.mcd │ │ ├── triple_N11_1_rw_explode_arity=3.mcd │ │ └── triple_N11_1_rw_split.mcd │ ├── triple_NN1/ │ │ ├── _triple_NN1_0.mcd │ │ ├── _triple_NN1_1.mcd │ │ ├── ddl/ │ │ │ ├── triple_NN1_0_ddl.d2 │ │ │ ├── triple_NN1_0_ddl.dbml │ │ │ ├── triple_NN1_0_ddl.sql │ │ │ ├── triple_NN1_1_ddl.d2 │ │ │ ├── triple_NN1_1_ddl.dbml │ │ │ └── triple_NN1_1_ddl.sql │ │ ├── exported/ │ │ │ ├── triple_NN1_0_erd_chen.gv │ │ │ ├── triple_NN1_0_erd_chen.txt │ │ │ ├── triple_NN1_0_erd_crow.mmd │ │ │ ├── triple_NN1_0_uml.puml │ │ │ ├── triple_NN1_1_erd_chen.gv │ │ │ ├── triple_NN1_1_erd_chen.txt │ │ │ ├── triple_NN1_1_erd_crow.mmd │ │ │ └── triple_NN1_1_uml.puml │ │ ├── mld/ │ │ │ ├── triple_NN1_0_debug.tsv │ │ │ ├── triple_NN1_0_dependencies.gv │ │ │ ├── triple_NN1_0_mld.html │ │ │ ├── triple_NN1_0_mld.mcd │ │ │ ├── triple_NN1_0_mld.md │ │ │ ├── triple_NN1_0_mld.tex │ │ │ ├── triple_NN1_0_mld.txt │ │ │ ├── triple_NN1_1_debug.tsv │ │ │ ├── triple_NN1_1_dependencies.gv │ │ │ ├── triple_NN1_1_mld.html │ │ │ ├── triple_NN1_1_mld.mcd │ │ │ ├── triple_NN1_1_mld.md │ │ │ ├── triple_NN1_1_mld.tex │ │ │ └── triple_NN1_1_mld.txt │ │ └── rewritten/ │ │ ├── triple_NN1_0_rw_create_df_arrows=across.mcd │ │ ├── triple_NN1_0_rw_drain.mcd │ │ ├── triple_NN1_0_rw_drown.mcd │ │ ├── triple_NN1_0_rw_explode_arity=2,weak.mcd │ │ ├── triple_NN1_0_rw_explode_arity=2.5,weak.mcd │ │ ├── triple_NN1_0_rw_explode_arity=2.5.mcd │ │ ├── triple_NN1_0_rw_explode_arity=2.mcd │ │ ├── triple_NN1_0_rw_explode_arity=3,weak.mcd │ │ ├── triple_NN1_0_rw_explode_arity=3.mcd │ │ ├── triple_NN1_0_rw_split.mcd │ │ ├── triple_NN1_1_rw_create_df_arrows=across.mcd │ │ ├── triple_NN1_1_rw_drain.mcd │ │ ├── triple_NN1_1_rw_drown.mcd │ │ ├── triple_NN1_1_rw_explode_arity=2,weak.mcd │ │ ├── triple_NN1_1_rw_explode_arity=2.5,weak.mcd │ │ ├── triple_NN1_1_rw_explode_arity=2.5.mcd │ │ ├── triple_NN1_1_rw_explode_arity=2.mcd │ │ ├── triple_NN1_1_rw_explode_arity=3,weak.mcd │ │ ├── triple_NN1_1_rw_explode_arity=3.mcd │ │ └── triple_NN1_1_rw_split.mcd │ ├── triple_NNN/ │ │ ├── _triple_NNN_0.mcd │ │ ├── _triple_NNN_1.mcd │ │ ├── ddl/ │ │ │ ├── triple_NNN_0_ddl.d2 │ │ │ ├── triple_NNN_0_ddl.dbml │ │ │ ├── triple_NNN_0_ddl.sql │ │ │ ├── triple_NNN_1_ddl.d2 │ │ │ ├── triple_NNN_1_ddl.dbml │ │ │ └── triple_NNN_1_ddl.sql │ │ ├── exported/ │ │ │ ├── triple_NNN_0_erd_chen.gv │ │ │ ├── triple_NNN_0_erd_chen.txt │ │ │ ├── triple_NNN_0_erd_crow.mmd │ │ │ ├── triple_NNN_0_uml.puml │ │ │ ├── triple_NNN_1_erd_chen.gv │ │ │ ├── triple_NNN_1_erd_chen.txt │ │ │ ├── triple_NNN_1_erd_crow.mmd │ │ │ └── triple_NNN_1_uml.puml │ │ ├── mld/ │ │ │ ├── triple_NNN_0_debug.tsv │ │ │ ├── triple_NNN_0_dependencies.gv │ │ │ ├── triple_NNN_0_mld.html │ │ │ ├── triple_NNN_0_mld.mcd │ │ │ ├── triple_NNN_0_mld.md │ │ │ ├── triple_NNN_0_mld.tex │ │ │ ├── triple_NNN_0_mld.txt │ │ │ ├── triple_NNN_1_debug.tsv │ │ │ ├── triple_NNN_1_dependencies.gv │ │ │ ├── triple_NNN_1_mld.html │ │ │ ├── triple_NNN_1_mld.mcd │ │ │ ├── triple_NNN_1_mld.md │ │ │ ├── triple_NNN_1_mld.tex │ │ │ └── triple_NNN_1_mld.txt │ │ └── rewritten/ │ │ ├── triple_NNN_0_rw_create_df_arrows=across.mcd │ │ ├── triple_NNN_0_rw_drain.mcd │ │ ├── triple_NNN_0_rw_drown.mcd │ │ ├── triple_NNN_0_rw_explode_arity=2,weak.mcd │ │ ├── triple_NNN_0_rw_explode_arity=2.5,weak.mcd │ │ ├── triple_NNN_0_rw_explode_arity=2.5.mcd │ │ ├── triple_NNN_0_rw_explode_arity=2.mcd │ │ ├── triple_NNN_0_rw_explode_arity=3,weak.mcd │ │ ├── triple_NNN_0_rw_explode_arity=3.mcd │ │ ├── triple_NNN_0_rw_split.mcd │ │ ├── triple_NNN_1_rw_create_df_arrows=across.mcd │ │ ├── triple_NNN_1_rw_drain.mcd │ │ ├── triple_NNN_1_rw_drown.mcd │ │ ├── triple_NNN_1_rw_explode_arity=2,weak.mcd │ │ ├── triple_NNN_1_rw_explode_arity=2.5,weak.mcd │ │ ├── triple_NNN_1_rw_explode_arity=2.5.mcd │ │ ├── triple_NNN_1_rw_explode_arity=2.mcd │ │ ├── triple_NNN_1_rw_explode_arity=3,weak.mcd │ │ ├── triple_NNN_1_rw_explode_arity=3.mcd │ │ └── triple_NNN_1_rw_split.mcd │ └── weak/ │ ├── _weak_0.mcd │ ├── _weak_1.mcd │ ├── _weak_2.mcd │ ├── ddl/ │ │ ├── weak_0_ddl.d2 │ │ ├── weak_0_ddl.dbml │ │ ├── weak_0_ddl.sql │ │ ├── weak_1_ddl.d2 │ │ ├── weak_1_ddl.dbml │ │ ├── weak_1_ddl.sql │ │ ├── weak_2_ddl.d2 │ │ ├── weak_2_ddl.dbml │ │ └── weak_2_ddl.sql │ ├── exported/ │ │ ├── weak_0_erd_chen.gv │ │ ├── weak_0_erd_chen.txt │ │ ├── weak_0_erd_crow.gv │ │ ├── weak_0_erd_crow.mmd │ │ ├── weak_0_uml.puml │ │ ├── weak_1_erd_chen.gv │ │ ├── weak_1_erd_chen.txt │ │ ├── weak_1_erd_crow.gv │ │ ├── weak_1_erd_crow.mmd │ │ ├── weak_1_uml.puml │ │ ├── weak_2_erd_chen.gv │ │ ├── weak_2_erd_chen.txt │ │ ├── weak_2_erd_crow.gv │ │ ├── weak_2_erd_crow.mmd │ │ └── weak_2_uml.puml │ ├── mld/ │ │ ├── weak_0_debug.tsv │ │ ├── weak_0_dependencies.gv │ │ ├── weak_0_mld.html │ │ ├── weak_0_mld.mcd │ │ ├── weak_0_mld.md │ │ ├── weak_0_mld.tex │ │ ├── weak_0_mld.txt │ │ ├── weak_1_debug.tsv │ │ ├── weak_1_dependencies.gv │ │ ├── weak_1_mld.html │ │ ├── weak_1_mld.mcd │ │ ├── weak_1_mld.md │ │ ├── weak_1_mld.tex │ │ ├── weak_1_mld.txt │ │ ├── weak_2_debug.tsv │ │ ├── weak_2_dependencies.gv │ │ ├── weak_2_mld.html │ │ ├── weak_2_mld.mcd │ │ ├── weak_2_mld.md │ │ ├── weak_2_mld.tex │ │ └── weak_2_mld.txt │ └── rewritten/ │ ├── weak_0_rw_create_df_arrows=across.mcd │ ├── weak_0_rw_drain.mcd │ ├── weak_0_rw_drown.mcd │ ├── weak_0_rw_explode_arity=2,weak.mcd │ ├── weak_0_rw_explode_arity=2.5,weak.mcd │ ├── weak_0_rw_explode_arity=2.5.mcd │ ├── weak_0_rw_explode_arity=2.mcd │ ├── weak_0_rw_explode_arity=3,weak.mcd │ ├── weak_0_rw_explode_arity=3.mcd │ ├── weak_0_rw_split.mcd │ ├── weak_1_rw_create_df_arrows=across.mcd │ ├── weak_1_rw_drain.mcd │ ├── weak_1_rw_drown.mcd │ ├── weak_1_rw_explode_arity=2,weak.mcd │ ├── weak_1_rw_explode_arity=2.5,weak.mcd │ ├── weak_1_rw_explode_arity=2.5.mcd │ ├── weak_1_rw_explode_arity=2.mcd │ ├── weak_1_rw_explode_arity=3,weak.mcd │ ├── weak_1_rw_explode_arity=3.mcd │ ├── weak_1_rw_split.mcd │ ├── weak_2_rw_create_df_arrows=across.mcd │ ├── weak_2_rw_drain.mcd │ ├── weak_2_rw_drown.mcd │ ├── weak_2_rw_explode_arity=2,weak.mcd │ ├── weak_2_rw_explode_arity=2.5,weak.mcd │ ├── weak_2_rw_explode_arity=2.5.mcd │ ├── weak_2_rw_explode_arity=2.mcd │ ├── weak_2_rw_explode_arity=3,weak.mcd │ ├── weak_2_rw_explode_arity=3.mcd │ └── weak_2_rw_split.mcd └── web/ ├── ace-builds/ │ ├── ace.js │ ├── ext-language_tools.js │ ├── mode-mocodo.js │ ├── mode-text.js │ ├── text_highlight_rules.js │ └── theme-chrome.js ├── favicons/ │ ├── browserconfig.xml │ └── manifest.json ├── flashlight.js ├── generate.php ├── get_from_lib.php ├── js.cookie.js ├── mocodo.js ├── purge.php ├── reset.css ├── rewrite.php └── style.css ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.pyc build/* dist/* MANIFEST.in mocodo.egg-info/* params.json README.rst sandbox/* meta/* doc/mocodo_notebook doc/.ipynb* sandbox*.* paper/* *.lp *.log .DS_Store magic/build/* magic/dist/* magic/mocodo_magic.egg-info/* magic/README.rst web/sessions/* web/lib/* .pytest_cache/* .ipynb_checkpoints/* */.ipynb_checkpoints/* poetry.lock mocodo_web.py Untitled.ipynb *.vsix mocodo/resources/relation_templates/_graph.gv mocodo/resources/relation_templates/_graph.svg EGC2024 looping/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017- Aristide Grange Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ **Octobre 2025.** Basthon prend maintenant en charge Mocodo 4.3.2. **Mars 2025.** Mocodo [4.3](https://github.com/laowantong/mocodo/releases/tag/4.3.2) commence à surfer sur la vague des IA hallucinogénératives : il peut maintenant composer des _prompts_ à copier-coller sous ChatGPT (ou autre) pour réécrire le MCD en cours avec le type des attributs ou l'explication des cardinalités. **Septembre 2024.** Basthon prend maintenant en charge Mocodo 4.2.8, qui simplifie l'appel à la fonction `mocodo()` de `mocodo.magic`. Voir l'[exemple mis à jour](https://notebook.basthon.fr/?from=examples/python3-mocodo.ipynb) dans la galerie de Basthon. **Janvier 2024.** Mocodo [4.2](https://github.com/laowantong/mocodo/releases/tag/4.2.0) est maintenant disponible sous [Basthon](https://basthon.fr). Après [Mocodo _online_](https://www.mocodo.net), Basthon constitue donc une deuxième manière d'utiliser Mocodo sans rien installer. Elle a l'avantage de donner accès à l'intégralité des fonctionnalités du logiciel. Merci à son auteur, Romain Casati ! **Novembre 2023.** Mocodo [4.1](https://github.com/laowantong/mocodo/releases/tag/4.1.0) gère les bibliothèques de MCD. Un enseignant peut par exemple placer sur son propre serveur la correction d'un MCD sous un nom « secret », que le moment venu il communiquera oralement à ses étudiants pour leur permettre de le récupérer (soit sous Mocodo online, soit en ligne de commande). **Octobre 2023.** Mocodo [4](https://github.com/laowantong/mocodo/releases/tag/4.0.4) introduit la gestion manuelle et automatique des contraintes d'optionalité et d'unicité, améliore les interfaces graphique et en ligne de commande, et ajoute un grand nombre de fonctionnalités : coloration syntaxique, liens de partage, exportation en UML, en notation de Chen et _crow's foot_, génération de MCD aléatoires, décomposition des associations et autres opérations de réécriture. **Septembre 2022.** Mocodo 3 introduit l'[héritage](https://laowantong.github.io/mocodo/doc/fr_refman.html#Héritage-(ou-spécialisation)), l'[agrégation](https://laowantong.github.io/mocodo/doc/fr_refman.html#Agrégation-(ou-pseudo-entité)), les [calques](https://laowantong.github.io/mocodo/doc/fr_refman.html#Héritage-(ou-spécialisation)), les [sorties PDF et PNG](https://laowantong.github.io/mocodo/doc/fr_refman.html#Héritage-(ou-spécialisation)), etc. : [3.0](https://github.com/laowantong/mocodo/releases/tag/3.0), [3.1](https://github.com/laowantong/mocodo/releases/tag/3.1.0), [3.2](https://github.com/laowantong/mocodo/releases/tag/3.2.0). ------ [Documentation](https://laowantong.github.io/mocodo/doc/fr_refman.html) sur une seule page, incluant la sortie de [`mocodo --help`](https://laowantong.github.io/mocodo/doc/fr_refman.html#mocodo---help) et l'[aide-mémoire des transformations](https://laowantong.github.io/mocodo/doc/fr_refman.html#Aide-mémoire-des-arguments-de---transform). ---- ![](logos/banner.svg) Mocodo est un logiciel d'aide à l'enseignement et à l'apprentissage des [bases de données relationnelles](https://fr.wikipedia.org/wiki/Base_de_données_relationnelle). - En entrée, il prend un [MCD](https://fr.wikipedia.org/wiki/Modèle_entité-association) (modèle conceptuel de données) décrit dans un langage dédié minimaliste. - En sortie, il produit un diagramme entité-association et, à la demande, un [MLD](https://fr.wikipedia.org/wiki/Merise_(informatique)#MLD_:_modèle_logique_des_données) (schéma relationnel, sous forme graphique ou textuelle), un [DDL](https://fr.wikipedia.org/wiki/Langage_de_définition_de_données) (script SQL de création de la base), un [diagramme de classes UML](https://fr.wikipedia.org/wiki/Diagramme_de_classes), etc. - En bonus, il est capable de réarranger automatiquement votre MCD de façon esthétique, et de lui appliquer des opérations de réécriture qui vont du mondain (typographie) à l'académique (décomposition d'associations), en passant par le merveilleux (inférence de types, génération d'exercices et d'exemples). Vous pouvez utiliser Mocodo : - sur son site web [Mocodo _online_](https://www.mocodo.net) ; - dans un document [Jupyter Notebook](https://jupyter.org), avec ou [sans](https://basthon.fr) installation ; - en ligne de commande ; - comme une bibliothèque Python. ## Tracé du modèle conceptuel Ci-dessous, un exemple d'utilisation sous Jupyter Notebook. L'appel du programme est en première ligne ; le texte-source proprement dit, lignes suivantes. En sortie, le diagramme conceptuel, égayé au passage par l'option `--colors` : ![png](doc/readme/ccp_mcd.png) ![svg](doc/readme/ccp_mcd.svg) ## Opérations de conversion On peut récupérer ce texte-source avec `--input` pour lui appliquer diverses opérations. Ainsi, l'appel suivant génère et affiche son MLD, son diagramme relationnel et son DDL : ``` %mocodo --input ccp --transform mld diagram ddl --colors desert ``` --- - **CLIENT** (Réf. client, Nom, Prénom, Adresse) - **COMMANDE** (Num. commande, Date, Montant, _#Réf. client_) - **INCLURE** (_#Num. commande_, _#Réf. produit_, Quantité) - **PRODUIT** (Réf. produit, Libellé, Prix unitaire) --- ![svg](doc/readme/ccp_mld.svg) --- ```sql CREATE TABLE CLIENT ( PRIMARY KEY (ref_client), ref_client VARCHAR(8) NOT NULL, nom VARCHAR(255), prenom VARCHAR(255), adresse VARCHAR(255) ); CREATE TABLE COMMANDE ( PRIMARY KEY (num_commande), num_commande VARCHAR(8) NOT NULL, date DATE, montant DECIMAL(10,2), ref_client VARCHAR(8) NOT NULL ); CREATE TABLE INCLURE ( PRIMARY KEY (num_commande, ref_produit), num_commande VARCHAR(8) NOT NULL, ref_produit VARCHAR(8) NOT NULL, quantite INTEGER ); CREATE TABLE PRODUIT ( PRIMARY KEY (ref_produit), ref_produit VARCHAR(8) NOT NULL, libelle VARCHAR(50), prix_unitaire DECIMAL(10,2) ); ALTER TABLE COMMANDE ADD FOREIGN KEY (ref_client) REFERENCES CLIENT (ref_client); ALTER TABLE INCLURE ADD FOREIGN KEY (ref_produit) REFERENCES PRODUIT (ref_produit); ALTER TABLE INCLURE ADD FOREIGN KEY (num_commande) REFERENCES COMMANDE (num_commande); ``` Dans la suite, pour épargner la frappe, les options `--input` et `--transform` seront respectivement abrégées en `-i` et `-t`. Les opérations de conversion ne se limitent pas forcément au schéma relationnel. En voici une qui extrait un dictionnaire des données, par défaut sous la forme d'un tableau Markdown à trois colonnes : ``` %mocodo -i ccp -t data_dict ``` --- | Entité ou association | Libellé de l'attribut | Type | |:----------------------|:----------------------|:--------------| | CLIENT | Adresse | VARCHAR(255) | | " | Nom | VARCHAR(255) | | " | Prénom | VARCHAR(255) | | " | Réf. client | VARCHAR(8) | | COMMANDE | Date | DATE | | " | Montant | DECIMAL(10,2) | | " | Num. commande | VARCHAR(8) | | INCLURE | Quantité | INTEGER | | PRODUIT | Libellé | VARCHAR(50) | | " | Prix unitaire | DECIMAL(10,2) | | " | Réf. produit | VARCHAR(8) | Une autre qui transcrit le MCD dans la notation _crow's foot_ (`crow`) pour [Mermaid](http://mermaid.js.org) (`mmd`) : ``` %mocodo -i ccp -t crow:mmd ``` --- ```mmd erDiagram CLIENT { VARCHAR(8) ref_client PK VARCHAR(255) nom VARCHAR(255) prenom VARCHAR(255) adresse } COMMANDE { VARCHAR(8) num_commande PK DATE date DECIMAL(10-2) montant } INCLURE { INTEGER quantite PK } PRODUIT { VARCHAR(8) ref_produit PK VARCHAR(50) libelle DECIMAL(10-2) prix_unitaire } CLIENT ||--o{ COMMANDE: PASSER INCLURE }|..|| COMMANDE: DF INCLURE }o..|| PRODUIT: DF ``` Le rendu des diagrammes décrits dans des langages-tiers (comme Mermaid) n'est pas directement pris en charge, mais peut être délégué (`--defer`) de façon transparente au service web approprié. Dans ce cas, c'est la sortie graphique qui est affichée : ``` %mocodo -i ccp -t crow:mmd --defer ``` --- ![svg](doc/readme/ccp_erd_crow.svg) ## Opérations de réécriture Une **réécriture** transforme un MCD Mocodo en un autre MCD Mocodo (au contraire d'une **conversion**, qui produit un animal d'une espèce différente). Heureusement, l'utilisateur n'a pas à réfléchir si la transformation qu'il souhaite appliquer est une réécriture ou une conversion : dans les deux cas, il invoque `-t` (c'est-à-dire `--transform`), et Mocodo se débrouille. En guise de premier exemple de réécriture, mettons les noms des entités et associations (`boxes`) en majuscules, et les libellés (`labels`) en ASCII et _snake case_ : ``` %mocodo -i ccp -t upper:boxes ascii:labels snake:labels --colors brewer+3 ``` ![svg](doc/readme/ccp_mcd_ascii.svg) --- %%mocodo --colors brewer+3 CLIENT: ref_client [VARCHAR(8)], nom [VARCHAR(255)], prenom [VARCHAR(255)], adresse [VARCHAR(255)] PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: num_commande [VARCHAR(8)], date [DATE], montant [DECIMAL(10,2)] INCLURE, 1N COMMANDE, 0N PRODUIT: quantite [INTEGER] PRODUIT: ref_produit [VARCHAR(8)], libelle [VARCHAR(50)], prix_unitaire [DECIMAL(10,2)] Remarquez que l'exécution d'une réécriture affiche, au-dessous du diagramme, le code-source résultant. Celui-ci est précédé de la commande magique originale, _privée de l'option `-i` et de toute opération de réécriture_. Ces dispositions permettent de continuer à travailler directement dessus si on le copie-colle dans une autre cellule. Plusieurs opérations de réécriture de nature sémantique sont également offertes. Par exemple, on peut décomposer un MCD quelconque en un MCD équivalent, mais n'employant que des dépendances fonctionnelles et des entités faibles : ``` %mocodo -i ccp --select mcd -t explode:weak,arity=2 arrange:wide --seed=3 --colors brewer+3 ``` ![svg](doc/readme/ccp_mcd_explode.svg) Notez la sous-option `arrange:wide`. Elle a procédé à une réorganisation aléatoire des boîtes, ce que l'insertion de deux nouvelles associations de dépendance fonctionnelles avait rendu nécessaire. Quant à l'option `--seed=3`, elle garantit que le résultat sera le même à chaque exécution. Pour vous familiariser avec Mocodo, le plus simple est d'utiliser [sa version en ligne](https://www.mocodo.net). Vous pourrez ensuite vous plonger dans la [documentation](https://laowantong.github.io/mocodo/doc/fr_refman.html). ================================================ FILE: doc/build_doc.sh ================================================ # Export directly from the notebook File > Save and export notebook as > HTML ================================================ FILE: doc/examples/four_random_layouts.mcd ================================================ :: Tincidunt1, 11 Ante1, 0N Massa1 :::::: Euismod1, 0N Massa1, 0N Massa1 Massa1: metus, posuere Convallis1, 0N Aliquet1, 0N Ante1: vestibulum Ante1: vitae, tempor ::: Risus2, 1N Aliquet2, 0N Massa2 Massa2: metus, posuere Euismod2, 0N Massa2, 0N Massa2 : Risus1, 1N Aliquet1, 0N Massa1 Aliquet1: hendrerit, metus, lacus, quis Rutrum1, 0N Gravida1, 0N Ante1, 0N Aliquet1: faucibus, curae Gravida1: ornare : Ultrices2, 01 Aliquet2, 0N Aliquet2 Aliquet2: hendrerit, metus, lacus, quis Convallis2, 0N Aliquet2, 0N Ante2: vestibulum Tincidunt2, 11 Ante2, 0N Massa2 :: Ultrices1, 01 Aliquet1, 0N Aliquet1 ::: Gravida2: ornare Rutrum2, 0N Gravida2, 0N Ante2, 0N Aliquet2: faucibus, curae Ante2: vitae, tempor : Ultrices3, 01 Aliquet3, 0N Aliquet3 Aliquet3: hendrerit, metus, lacus, quis Risus3, 1N Aliquet3, 0N Massa3 ::: Risus4, 1N Aliquet4, 0N Massa4 Aliquet4: hendrerit, metus, lacus, quis Ultrices4, 01 Aliquet4, 0N Aliquet4 : Rutrum3, 0N Gravida3, 0N Ante3, 0N Aliquet3: faucibus, curae Convallis3, 0N Aliquet3, 0N Ante3: vestibulum Massa3: metus, posuere Euismod3, 0N Massa3, 0N Massa3 : Euismod4, 0N Massa4, 0N Massa4 Massa4: metus, posuere Convallis4, 0N Aliquet4, 0N Ante4: vestibulum Rutrum4, 0N Gravida4, 0N Ante4, 0N Aliquet4: faucibus, curae Gravida4: ornare Gravida3: ornare Ante3: vitae, tempor Tincidunt3, 11 Ante3, 0N Massa3 ::: Tincidunt4, 11 Ante4, 0N Massa4 Ante4: vitae, tempor :: ================================================ FILE: doc/examples/landing.mcd ================================================ AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise PIÈCE: réf. pièce, libellé pièce COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, _11 AYANT-DROIT, 0N EMPLOYÉ EMPLOYÉ: matricule, nom employé PROJET: num. projet, nom projet FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT TRAVAILLER, 0N EMPLOYÉ, 1N PROJET SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ ================================================ FILE: doc/examples/option_syntax_1.abnf ================================================ start = opt " " (arg *("," arg)) arg = subopt [":" subarg *("," subarg)] subarg = subsubarg ["=" subsubopt] ================================================ FILE: doc/examples/option_syntax_2.abnf ================================================ start = opt " " ((subopt [":" (subsubarg ["=" subsubopt]) *("," (subsubarg ["=" subsubopt]))]) *("," (subopt [":" (subsubarg ["=" subsubopt]) *("," (subsubarg ["=" subsubopt]))]))) ================================================ FILE: doc/fr_cheat_sheet.md ================================================ ### Opérations de conversion | Sous-option | Description | Exemples | Explications | | :-- | :-- | :-- | :-- | | ast | crée l'arbre de syntaxe abstraite du texte source (pour le débogage) | | | | chen | convertit le modèle conceptuel dans la notation de Chen | `` chen `` | sans attributs | | | | `` chen:attrs `` | avec attributs | | | | `` chen:attrs --defer `` | calcule le rendu graphique via un service web | | | | `` chen:layout=circo,mindist=2,scale=0.6 `` | ajoute des options arbitraires pour Graphviz | | crow | convertit le modèle conceptuel dans la notation crow's foot | `` crow `` | format Graphviz | | | | `` crow --defer `` | calcule le rendu graphique via un service web | | | | `` crow:mmd `` | format Mermaid | | | | `` crow:mermaid `` | idem | | d2 | convertit le modèle conceptuel en un schéma relationnel au format D2 | | | | data_dict | extrait tous les attributs du MCD dans une table | `` data_dict `` | tableau Markdown, trois colonnes | | | | `` data_dict:label `` | liste Markdown, une colonne | | | | `` data_dict:label,type='Description' `` | deux colonnes, un libellé personnalisé | | | | `` data_dict:label='Attribut',type='Description' `` | deux colonnes, deux libellés personnalisés | | | | `` data_dict:**box**='Entité ou
association',label,`type`=`'Type de données'` `` | mise en forme de certains libellés | | | | `` data_dict:tsv `` | tableau TSV, trois colonnes | | | | `` data_dict:tsv,label `` | liste des attributs séparés par des retours à la ligne | | dbml | convertit le modèle conceptuel en un schéma relationnel au format DBML | `` dbml `` | version de base | | | | `` dbml:b `` | avec _boilerplate_ | | debug | liste des informations internes relatives à la conversion en schéma relationnel | | | | dependencies | convertit le modèle conceptuel en un graphe de dépendances | | | | diagram | convertit le modèle conceptuel en un diagramme relationnel au format Mocodo | `` diagram `` | version de base | | | | `` diagram:c `` | avec contraintes d'unicité et d'optionalité | | html | convertit le modèle conceptuel en un schéma relationnel au format HTML | `` html `` | version de base | | | | `` html:b `` | avec _boilerplate_ | | | | `` html:c `` | avec contraintes d'unicité et d'optionalité | | | | `` html:e `` | avec explications | | | | `` html:bce `` | avec _boilerplate_, contraintes et explications | | latex | convertit le modèle conceptuel en un schéma relationnel au format LaTeX | `` latex `` | version de base | | | | `` latex:b `` | avec _boilerplate_ | | | | `` latex:c `` | avec contraintes d'unicité et d'optionalité | | | | `` latex:e `` | avec explications | | | | `` latex:bce `` | avec _boilerplate_, contraintes et explications | | markdown | convertit le modèle conceptuel en un schéma relationnel au format Markdown | `` markdown `` | version de base | | | | `` markdown:c `` | avec contraintes d'unicité et d'optionalité | | | | `` markdown:e `` | avec explications | | | | `` markdown:ce `` | avec contraintes et explications | | mssql | convertit le modèle conceptuel en un modèle physique pour Microsoft SQL Server | `` mssql `` | version de base | | | | `` mssql:b `` | avec _boilerplate_ | | mysql | convertit le modèle conceptuel en un modèle physique pour MySQL | `` mysql `` | version de base | | | | `` mysql:b `` | avec _boilerplate_ | | oracle | convertit le modèle conceptuel en un modèle physique pour Oracle DB | `` oracle `` | version de base | | | | `` oracle:b `` | avec _boilerplate_ | | postgresql | convertit le modèle conceptuel en un modèle physique pour PostgreSQL | `` postgresql `` | version de base | | | | `` postgresql:b `` | avec _boilerplate_ | | prompt | génère un prompt pour demander à une IA de compléter le MCD | `` prompt:cards `` | avec les explications des cardinalités | | | | `` prompt:types `` | avec les types des attributs | | relation | convertit le modèle conceptuel en schéma relationnel avec le gabarit donné | `` relation:path/to/my_template.yaml `` | chemin relatif, extension obligatoire | | share | encode le MCD dans une URL pour Mocodo online | `` qr --defer `` | génère un QR code via un service web | | sql | convertit le modèle conceptuel en un modèle physique pour SQL | | | | sqlite | convertit le modèle conceptuel en un modèle physique pour SQLite | `` sqlite `` | version de base | | | | `` sqlite:b `` | avec _boilerplate_ | | text | convertit le modèle conceptuel en un schéma relationnel au format texte | `` text `` | version de base | | | | `` text:c `` | avec contraintes d'unicité et d'optionalité | | | | `` html:e `` | avec explications | | | | `` html:ce `` | avec contraintes et explications | | uml | convertit le modèle conceptuel en diagramme de classes UML | `` uml `` | format PlantUML | | | | `` uml:plantuml `` | idem | | | | `` uml --defer `` | calcule le rendu graphique via un service web | | | | `` uml:plantuml=- `` | supprime les styles par défaut | | | | `` uml:plantuml='skinparam backgroundColor yellow\nskinparam classAttributeFontName Arial\n' `` | ajoute des styles personnalisés | ### Opérations de réécriture | Sous-option | Description | Exemples | Explications | | :-- | :-- | :-- | :-- | | arrange | réarrange la disposition, soit par Branch & Bound, soit avec un algorithme génétique | `` arrange `` | B&B sans contraintes | | | | `` arrange:timeout=60 `` | B&B limité à une minute | | | | `` arrange:wide `` | B&B privilégiant la largeur | | | | `` arrange:current `` | B&B sur la grille courante | | | | `` arrange:balanced=0 `` | B&B sur la plus petite grille équilibrée | | | | `` arrange:balanced=1 `` | B&B sur la seconde plus petite grille équilibrée | | | | `` arrange:algo=ga `` | algorithme génétique | | ascii | réécrit les éléments donnés en ASCII | `` ascii:roles,labels `` | rôles, libellés des boîtes et des attributs en ASCII | | camel | réécrit les éléments donnés en camelCase | | | | capitalize | réécrit les éléments donnés en capitalisant la première lettre de chaque mot | | | | casefold | réécrit les éléments donnés en minuscules, mais plus agressivement que « lower » | | | | create | essaie d'inférer les types, entités, CIFs ou flèches de DF à partir des éléments existants | `` guess:types `` | deviner les types manquants | | | | `` create:types= `` | remplacer les types manquants par `[]` | | | | `` create:types=TODO `` | remplacer les types manquants par `[TODO]` | | | | `` make:entities `` | réparer l'oubli d'entités référencées dans des associations | | | | `` create:dfs `` | mettre des DF partout où c'est possible | | | | `` add:df_arrows `` | ajouter des flèches aux DF 11 | | | | `` add:cifs `` | ajouter les CIF correspondant aux agrégats | | | | `` add:cifs=light `` | même chose en visualisation allégée | | | | `` add:roles `` | mettre comme rôles le nom des associations partout où c'est utile | | delete | supprime les éléments donnés quand c'est possible | `` empty `` | ne garde que la structure et le nom des boîtes | | | | `` delete:types,notes,attrs,cards `` | idem | | | | `` delete:cards `` | remplace les cardinalités par `XX` | | | | `` delete:card_prefixes `` | supprime les marqueurs d'entités faibles et d'agrégats | | | | `` delete:dfs `` | supprime les entités indépendantes dont tous les attributs sont identifiants (et les DF qui les relient) | | drain | déplace tout attribut d'association (1,1) vers l'entité appropriée | | | | drown | remplace tous les noms d'éléments par un libellé générique numéroté | | | | echo | réécrit le texte source tel quel | | | | explode | décompose toute association n-aire (*,N) en n associations binaires | `` explode arrange `` | décomposer les non-DF ternaires et plus, puis réarranger | | | | `` explode:arity=3 arrange `` | idem | | | | `` explode:weak arrange `` | idem, avec création d'entités faibles | | | | `` explode:arity=2.5 arrange `` | étendre aux non-DF binaires porteuses d'attributs | | | | `` explode:arity=2 arrange `` | étendre à toutes les non-DF binaires | | fix | essaie de corriger les erreurs courantes dans les éléments donnés | `` fix:cards `` | normaliser les cardinalités en 01, 11, 0N et 1N | | flip | applique au diagramme une symétrie verticale, horizontale ou diagonale | `` flip:v `` | symétrie verticale | | | | `` flip:h `` | symétrie horizontale | | | | `` flip:d `` | symétrie selon la seconde diagonale | | | | `` flip:vhd `` | symétrie selon la première diagonale | | | | `` flip:dhv `` | idem (ordre indifférent) | | grow | ajoute des entités et associations aléatoires (par défaut : 10 nouvelles associations) | `` grow arrange `` | ajouter des éléments avec les paramètres par défaut, puis réarranger | | | | `` grow:n=10 `` | nombre total d'associations à ajouter (défaut) | | | | `` grow:arity_1=2 `` | nombre d'associations réflexives (défaut) | | | | `` grow:arity_3=2 `` | nombre d'associations ternaires (défaut) | | | | `` grow:arity_4=0 `` | nombre d'associations quaternaires (défaut) | | | | `` grow:doubles=1 `` | nombre d'associations liant deux mêmes entités (défaut) | | | | `` grow:composite_ids=1 `` | nombre d'identifiants composites (défaut) | | | | `` grow:ent_attrs=4 `` | nombre maximal d'attributs par entité (défaut) | | | | `` grow:assoc_attrs=2 `` | nombre maximal d'attributs par association (défaut) | | | | `` grow:'*1-*N'=3 `` | nombre d'associations `*1-*N` (défaut) | | | | `` grow:'01-11'=1 `` | nombre d'associations `01-11` (défaut) | | | | `` grow:'_11-*N'=1 `` | une entité faible (zéro par défaut) | | | | `` grow:'/1N-*N'=1 `` | un agrégat (zéro par défaut) | | | | `` grow:from_scratch arrange `` | à partir d'un MCD vide | | | | `` grow:grow:n=9,from_scratch,ent_attrs=3 obfuscate:labels=en4 create:roles lower:roles arrange `` | créer un MCD d'entraînement à la conversion en relationnel | | lower | réécrit les éléments donnés en minuscules | `` lower:attrs,roles `` | attributs et rôles en minuscules | | pascal | réécrit les élements donnés en PascalCase | | | | prefix | préfixe les éléments donnés avec la chaîne donnée | `` prefix:roles='-' `` | force les rôles à remplacer le nom des clés étrangères lors du passage au relationnel | | randomize | garde la structure, mais randomise les éléments donnés quand c'est possible | `` obfuscate `` | libellés remplacés par du Lorem Ipsum | | | | `` obfuscate:labels=lorem `` | idem | | | | `` obfuscate:labels=disparition `` | idem, lexique du roman de Perec | | | | `` obfuscate:labels=en4 `` | idem, mots anglais de 4 lettres (SFW) | | | | `` obfuscate:attrs=fr,boxes=fr5 `` | idem, mots français de longueur quelconque pour les attributs, de 5 lettres pour les boîtes | | | | `` randomize:types `` | types randomisés avec les fréquences de `default_datatypes_fr.tsv`. | | replace | réécrit les éléments donnés en appliquant le motif « recherche/remplacement » donné | `` replace:boxes='DIRIGER/RÉPONDRE DE' `` | renomme une boîte | | | | `` replace:texts='personel/personnel' `` | corrige une faute d'orthographe | | | | `` replace:replace:texts='_/ ' `` | remplace les tirets bas par des espaces | | | | `` replace:types='VARCHAR/VARCHAR2' `` | modifie un nom de type | | | | `` replace:cards=0N/1N `` | remplace toutes les cardinalités 0N par 1N | | | | `` replace:cards=1N//1N `` | crée des agrégats un peu partout | | | | `` replace:cards='0/X' replace:cards='11/X1' replace:cards='1N/XN' `` | masque les cardinalités minimales | | | | `` delete:card_prefixes replace:cards=11/_11 `` | ajoute des marqueurs d'entités faibles | | slice | réécrit les éléments donnés en n'en gardant qu'une tranche donnée | `` slice:boxes=5:10 `` | de l'indice 5 (inclus) à l'indice 10 (exclu) | | | | `` slice:boxes=5: `` | supprime les 5 premiers caractères | | | | `` slice:boxes=:5 `` | ne garde que les 5 premiers caractères | | | | `` slice:boxes=:-5 `` | supprime les 5 derniers caractères | | | | `` slice:boxes=: `` | équivalent de `echo` | | | | `` slice:boxes= `` | idem | | | | `` slice:boxes `` | idem | | snake | réécrit les éléments donnés en snake_case | | | | split | décompose toute association n-aire (*,1) en n-1 associations binaires | `` split arrange `` | décomposer, puis réarranger | | suffix | suffixe les éléments donnés avec la chaîne donnée | `` suffix:boxes=1 `` | Ajoute un suffixe numérique au nom des boîtes en vue de mettre un MCD et sa copie sur le même diagramme. | | swapcase | réécrit les éléments donnés en inversant la casse de chaque lettre | | | | title | réécrit les éléments donnés en mettant la première lettre de chaque mot en majuscule | | | | truncate | tronque les éléments donnés à la longueur donnée (par défaut : 64) | `` truncate:boxes=10 `` | tronque les noms des boîtes à 10 caractères | | upper | réécrit les éléments donnés en majuscules | `` upper:boxes `` | noms des boîtes en majuscules | ================================================ FILE: doc/fr_refman.html ================================================ fr_refman
================================================ FILE: doc/fr_refman.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": { "toc": true }, "source": [ "

Mocodo

\n", "
" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "init_cell": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Mocodo 4.3.2 loaded.\n" ] } ], "source": [ "%reload_ext mocodo" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "init_cell": true }, "outputs": [], "source": [ "from IPython import display\n", "from pathlib import Path\n", "import os\n", "if Path.cwd().name != \"mocodo_notebook\":\n", " Path(\"mocodo_notebook\").mkdir(parents=True, exist_ok=True)\n", " os.chdir(\"mocodo_notebook\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![](https://cdn.rawgit.com/laowantong/mocodo/master/logos/banner.svg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mocodo est un logiciel d'aide à l'enseignement et à l'apprentissage des [bases de données relationnelles](https://fr.wikipedia.org/wiki/Base_de_données_relationnelle).\n", "\n", "- En entrée, il prend un [MCD](https://fr.wikipedia.org/wiki/Modèle_entité-association) (modèle conceptuel de données) décrit dans un langage dédié minimaliste.\n", "- En sortie, il produit un diagramme entité-association et, à la demande, un [MLD](https://fr.wikipedia.org/wiki/Merise_%28informatique%29%23MLD_%3A_mod%C3%A8le_logique_des_donn%C3%A9es) (schéma relationnel, sous forme graphique ou textuelle), un [DDL](https://fr.wikipedia.org/wiki/Langage_de_définition_de_données) (script SQL de création de la base), un [diagramme de classes UML](https://fr.wikipedia.org/wiki/Diagramme_de_classes), etc.\n", "- En bonus, il est capable de réarranger automatiquement votre MCD de façon esthétique, et de lui appliquer des opérations de réécriture qui vont du mondain (typographie) à l'académique (décomposition d'associations), en passant par le merveilleux (inférence de types, génération d'exercices et d'exemples).\n", "\n", "Vous pouvez utiliser Mocodo :\n", "\n", "- à distance, sans rien installer, avec [Mocodo _online_](https://www.mocodo.net) ;\n", "- en local, comme n'importe quel programme Python ;\n", "- dans un document [Jupyter Notebook](https://jupyter.org) (à l'instar de cette documentation)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tracé du modèle conceptuel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ci-dessous, un exemple d'utilisation sous Jupyter Notebook. L'appel du programme est en première ligne ; le texte-source proprement dit, lignes suivantes. En sortie, le diagramme conceptuel, égayé au passage par l'option `--colors` :" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPasser\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tInclure\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProduit\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --colors ocean\n", "Client: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)]\n", "Passer, 0N Client, 11 Commande\n", "Commande: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)]\n", "Inclure, 1N Commande, 0N Produit: Quantité [INTEGER]\n", "Produit: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Opérations de conversion" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'appel précédent a automatiquement enregistré le texte-source sous le nom de `sandbox.mcd`. Renommons-le `ccp.mcd` pour y avoir accès tout au long de ce document." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "Path(\"sandbox.mcd\").rename(\"ccp.mcd\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On peut le récupérer avec `--input` pour lui appliquer diverses opérations. Ainsi, l'appel suivant génère et affiche son MLD, son diagramme relationnel et son DDL :" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Client** (Réf. client, Nom, Prénom, Adresse)\n", "- **Commande** (Num. commande, Date, Montant, _#Réf. client_)\n", "- **Inclure** (_#Num. commande_, _#Réf. produit_, Quantité)\n", "- **Produit** (Réf. produit, Libellé, Prix unitaire)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_mld.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE Client (\n", " PRIMARY KEY (Ref_client),\n", " Ref_client VARCHAR(8) NOT NULL,\n", " Nom VARCHAR(255),\n", " Prenom VARCHAR(255),\n", " Adresse VARCHAR(255)\n", ");\n", "\n", "CREATE TABLE Commande (\n", " PRIMARY KEY (Num_commande),\n", " Num_commande VARCHAR(8) NOT NULL,\n", " Date DATE,\n", " Montant DECIMAL(10,2),\n", " Ref_client VARCHAR(8) NOT NULL\n", ");\n", "\n", "CREATE TABLE Inclure (\n", " PRIMARY KEY (Num_commande, Ref_produit),\n", " Num_commande VARCHAR(8) NOT NULL,\n", " Ref_produit VARCHAR(8) NOT NULL,\n", " Quantite INTEGER\n", ");\n", "\n", "CREATE TABLE Produit (\n", " PRIMARY KEY (Ref_produit),\n", " Ref_produit VARCHAR(8) NOT NULL,\n", " Libelle VARCHAR(50),\n", " Prix_unitaire DECIMAL(10,2)\n", ");\n", "\n", "ALTER TABLE Commande ADD FOREIGN KEY (Ref_client) REFERENCES Client (Ref_client);\n", "\n", "ALTER TABLE Inclure ADD FOREIGN KEY (Ref_produit) REFERENCES Produit (Ref_produit);\n", "ALTER TABLE Inclure ADD FOREIGN KEY (Num_commande) REFERENCES Commande (Num_commande);\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo --input ccp --transform mld diagram ddl --colors mondrian" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans la suite, pour épargner la frappe, les options `--input` et `--transform` seront respectivement abrégées en `-i` et `-t`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les opérations de conversion ne se limitent pas forcément au schéma relationnel. En voici une qui extrait un dictionnaire des données, par défaut sous la forme d'un tableau Markdown à trois colonnes :" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_data_dict_3.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "| Entité ou association | Libellé de l'attribut | Type |\n", "|:----------------------|:----------------------|:--------------|\n", "| Client | Adresse | VARCHAR(255) |\n", "| \" | Nom | VARCHAR(255) |\n", "| \" | Prénom | VARCHAR(255) |\n", "| \" | Réf. client | VARCHAR(8) |\n", "| Commande | Date | DATE |\n", "| \" | Montant | DECIMAL(10,2) |\n", "| \" | Num. commande | VARCHAR(8) |\n", "| Inclure | Quantité | INTEGER |\n", "| Produit | Libellé | VARCHAR(50) |\n", "| \" | Prix unitaire | DECIMAL(10,2) |\n", "| \" | Réf. produit | VARCHAR(8) |\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t data_dict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Une autre qui transcrit le MCD dans la notation _crow's foot_ (`crow`) pour [Mermaid](http://mermaid.js.org) (`mmd`) :" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_erd_crow.mmd\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```mmd\n", "erDiagram\n", " Client {\n", " VARCHAR(8) Ref_client PK\n", " VARCHAR(255) Nom\n", " VARCHAR(255) Prenom\n", " VARCHAR(255) Adresse\n", " }\n", " Commande {\n", " VARCHAR(8) Num_commande PK\n", " DATE Date\n", " DECIMAL(10-2) Montant\n", " }\n", " Inclure {\n", " INTEGER Quantite PK\n", " }\n", " Produit {\n", " VARCHAR(8) Ref_produit PK\n", " VARCHAR(50) Libelle\n", " DECIMAL(10-2) Prix_unitaire\n", " }\n", " Client ||--o{ Commande: Passer\n", " Inclure }|..|| Commande: DF\n", " Inclure }o..|| Produit: DF\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t crow:mmd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le rendu des diagrammes décrits dans des langages-tiers (comme Mermaid, PlantUML, etc.) n'est pas directement pris en charge, mais peut être délégué (`--defer`) de façon transparente au service web approprié. Dans ce cas, c'est la sortie graphique qui est affichée :" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_erd_crow.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t crow:plantuml --defer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Opérations de réécriture" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Une **réécriture** transforme un MCD Mocodo en un autre MCD Mocodo (au contraire d'une **conversion**, qui produit un animal d'une espèce différente).\n", "\n", "Heureusement, l'utilisateur n'a pas à réfléchir si la transformation qu'il souhaite appliquer est une réécriture ou une conversion : dans les deux cas, il invoque `-t` (c'est-à-dire `--transform`), et Mocodo se débrouille.\n", "\n", "En guise de premier exemple de réécriture, mettons les noms des entités et associations (`boxes`) en majuscules, et les libellés (`labels`) en ASCII et _snake case_ :" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPASSER\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tINCLURE\n", "\t\tQuantite\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRef_client\n", "\t\n", "\tNom\n", "\tPrenom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tNum_commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tRef_produit\n", "\t\n", "\tLibelle\n", "\tPrix_unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp.mcd\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "%%mocodo --colors brewer+3\n", "CLIENT: Ref_client [VARCHAR(8)], Nom [VARCHAR(255)], Prenom [VARCHAR(255)], Adresse [VARCHAR(255)]\n", "PASSER, 0N CLIENT, 11 COMMANDE\n", "COMMANDE: Num_commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)]\n", "INCLURE, 1N COMMANDE, 0N PRODUIT: Quantite [INTEGER]\n", "PRODUIT: Ref_produit [VARCHAR(8)], Libelle [VARCHAR(50)], Prix_unitaire [DECIMAL(10,2)]\n" ] } ], "source": [ "%mocodo -i ccp -t upper:boxes ascii:labels snake:labels --colors brewer+3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remarquez que l'exécution d'une réécriture affiche, au-dessous du diagramme, le code-source résultant. Celui-ci est précédé de la commande magique originale, _privée de l'option `-i` et de toute opération de réécriture_. Ces dispositions permettent de continuer à travailler directement dessus si on le copie-colle dans une autre cellule." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plusieurs opérations de réécriture de nature sémantique sont également offertes. Par exemple, on peut décomposer un MCD quelconque en un MCD équivalent, mais n'employant que des dépendances fonctionnelles et des entités faibles :" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPasser\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tInclure\n", "\tQuantité\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProduit\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp.mcd\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "%%mocodo --seed=3 --colors brewer+3\n", "Client: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)]\n", "Passer, 0N Client, 11 Commande\n", "Commande: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)]\n", "DF, _11 Inclure, 1N Commande\n", "Inclure: _Quantité [INTEGER]\n", "DF, _11 Inclure, 0N Produit\n", "Produit: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)]\n" ] } ], "source": [ "%mocodo -i ccp -t explode:weak,arity=2 arrange:wide --seed=3 --colors brewer+3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notez l'argument `arrange:wide`. Il a procédé à une réorganisation aléatoire des boîtes, ce que l'insertion de deux nouvelles associations de dépendance fonctionnelles avait rendu nécessaire. Quant à l'option `--seed=3`, elle garantit que le résultat sera le même à chaque exécution." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Après cet aperçu de quelques-unes des fonctionnalités de Mocodo, entrons dans les détails." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Installation et lancement du programme" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Comme nous l'avons dit, vous pouvez utiliser Mocodo sans rien installer. Il vous suffit d'aller sur [mocodo.net](https://www.mocodo.net) et de commencer à taper votre MCD. Appuyez à tout moment sur le ventilateur pour rafraîchir les diagrammes conceptuel et relationnel. Quand le résultat vous convient, appuyez sur le bouton de téléchargement pour récupérer une archive ZIP contenant tous les fichiers d'entrée et de sortie spécifiés.\n", "\n", "Mocodo _online_ est conçu pour une utilisation occasionnelle et/ou interactive, et son interface vise avant tout à la simplicité. Vous n'avez donc accès qu'aux options essentielles du programme. Si vous en voulez davantage, tant en termes de paramétrage que de calcul ou de fonctionnalités, nous vous conseillons d'installer Mocodo sur votre machine." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Installation minimale" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mocodo est un programme écrit en [Python 3](https://www.python.org). Si vous êtes sous macOS ou Linux, il est déjà installé. Dans le cas contraire, vous devrez peut-être [le faire](https://www.python.org/downloads/).\n", "\n", "Une fois Python présent sur votre machine, tapez sous un terminal:\n", "\n", " python -m pip install mocodo\n", "\n", "Puis testez-le :\n", "\n", " mocodo\n", "\n", "Invoqué sous cette forme, Mocodo récupère le texte d'entrée du MCD dans le répertoire courant sous le nom de `sandbox.mcd`. Si ce fichier n'existe pas, il y sera automatiquement créé avec un MCD d'exemple. Par la suite, vous n'aurez qu'à le garder ouvert sous un éditeur de texte, afin de le modifier à votre fantaisie avant de relancer la commande.\n", "\n", "Si votre système se plaint que cette commande n'existe pas, localisez le fichier `mocodo` et ajoutez à votre `PATH` le chemin du répertoire contenant:\n", "- [sous Linux ou macOS](http://www.commentcamarche.net/faq/3585-bash-la-variable-d-environnement-path#v-ajouter-un-repertoire-a-la-variable-path) ;\n", "- [sous Windows](http://sametmax.com/ajouter-un-chemin-a-la-variable-denvironnement-path-sous-windows/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Installation complète (recommandé)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Installez la [distribution Anaconda](https://www.anaconda.com/download), qui contient Python 3, Jupyter Notebook et bien plus encore.\n", "\n", "\n", "- La ligne donnée précédemment installe en fait, en plus de Mocodo, sa « commande magique » pour [Jupyter Notebook](https://jupyter.org). Nous l'avons déjà invoquée plusieurs fois dans ce document :\n", "\n", " python -m pip install mocodo\n", "\n", "- Mocodo fonctionne parfaitement sans aucune dépendance. Cependant, si vous souhaitez :\n", " - utiliser l'option `--svg_to` pour générer des figures en PDF ou en PNG ;\n", " - copier directement dans le presse-papier le résultat d'une réécriture ;\n", "\n", " ... vous pouvez installer les dépendances optionnelles aux bibliothèques [CairoSVG](https://cairosvg.org) et [Pyperclip](https://github.com/asweigart/pyperclip) ainsi :\n", " \n", " python -m pip install 'mocodo[svg,clipboard]'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour mettre la « commande magique » `mocodo` à disposition d'un _notebook_ donné, évaluez dans celui-ci la cellule suivante:\n", "\n", " %reload_ext mocodo\n", "\n", "Techniquement, `%load_ext mocodo` suffit, mais cette forme vous épargnera un message d'erreur si vous réévaluez ultérieurement la cellule.\n", "\n", "Pour tester la commande :" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tMISSION\n", "\taccomplie\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "MISSION: accomplie" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour charger automatiquement `mocodo` à chaque ouverture d'un notebook (ce qui dispense d'évaluer `%load_ext mocodo`) :\n", "\n", "- exécutez sous un terminal :\n", "\n", "```\n", "ipython profile create\n", "```\n", "\t\n", "- éditez le fichier créé (p. ex.: `~/.ipython/profile_default/ipython_config.py`) en remplaçant la ligne suivante :\n", "\n", "```\n", "# c.TerminalIPythonApp.extensions = []\n", "```\n", "\n", " par celle-ci :\n", "\n", "```\n", "c.InteractiveShellApp.extensions = [\"mocodo\"]\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Utilisation par importation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nouveauté de la version 4.1.3.** Un _wrapper_ minimaliste permet de simuler programmatiquement l'appel à la commande `mocodo`." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "from mocodo.api import mocodo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ainsi, l'équivalent de :\n", "\n", "```\n", "mocodo -i ccp --colors mondrian\n", "```\n", "\n", "... s'écrira :" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "messages = mocodo(\"-i ccp --colors mondrian\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Par défaut (second argument `quiet=True`), la fonction n'affiche pas les messages de succès, mais renvoie leur concaténation :" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fichier de sortie « ~/Dropbox/Sites/mocodo/doc/mocodo_notebook/ccp.svg » généré avec succès.\n" ] } ], "source": [ "print(messages)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plus important, les fichiers de sortie sont bel bien créés :" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPasser\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tInclure\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProduit\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.SVG(\"ccp.svg\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si vous travaillez sous une implantation de Jupyter Notebook qui ne prend pas en charge les commandes magiques, l'importation conseillée est :" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "from mocodo.magic import mocodo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Elle permet de simuler les commandes magiques, aussi bien celles qui sont réduites à une ligne :" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPasser\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tInclure\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProduit\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mocodo(\"%mocodo -i ccp --colors ocean\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... que celles qui portent sur toute une cellule :" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tMission\n", "\taccomplie\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mocodo(\"\"\"\n", "%%mocodo --colors pond\n", "Mission: accomplie\n", "\"\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le préfixe `\"%mocodo\"` ou `\"%%mocodo\"` est ignoré, mais l'autoriser permet de copier toute une ligne ou toute une cellule (vraiment) magique, et de la coller directement comme argument de la fonction." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nouveauté de la version 4.2.** Avec cette technique, Mocodo peut être utilisé sous [Basthon](https://basthon.fr) v. 0.62.12 ou ultérieure. Basthon est une plateforme permettant de travailler sous Jupyter Notebook sans avoir à rien installer. Son auteur, Romain Casati, a ajouté un exemple d'utilisation de Mocodo dans la [galerie](https://basthon.fr/galerie.html) du logiciel, à tester [ici](https://notebook.basthon.fr/?from=examples/python3-mocodo.ipynb). Sous Mocodo _online_, cliquez sur le symbole de lecture de Basthon (dans le pied de page) pour ouvrir un notebook avec le MCD en cours d'édition." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Syntaxe de description d'un MCD" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Notions élémentaires" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Entités, associations, attributs, identifiants, cardinalités" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPASSER\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tINCLURE\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "CLIENT: Réf. client, Nom, Prénom, Adresse\n", "PASSER, 0N CLIENT, 11 COMMANDE\n", "COMMANDE: Num. commande, Date, Montant\n", "INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité\n", "PRODUIT: Réf. produit, Libellé, Prix unitaire" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Passons en revue les principes de la syntaxe. Ils ne devraient pas poser trop de problèmes.\n", "\n", "**La première ligne** ne fait pas partie de la définition du MCD. Sous Jupyter Notebook, elle dénote l'appel à une « commande magique », qui lance Mocodo sur le reste de la cellule. En dehors d'un notebook, vous n'en avez pas besoin, et toute ligne commençant par un pourcentage (`%`) sera considérée comme un commentaire." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Une entité** est définie par :\n", "\n", "$$\n", "\\overbrace{\\texttt{COMMANDE}}^{\\text{son nom}}\n", "\\quad\n", "\\overbrace{\\texttt{:}}^{\\text{deux-points}}\n", "\\quad\n", "\\overbrace{\\texttt{Num. commande, Date, Montant}}^{\\text{ses attributs séparés par des virgules}}\n", "$$ \n", "\n", "Notez que le premier attribut d'une entité est considéré par défaut comme son identifiant, et donc souligné." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Une association** est définie par :\n", "\n", "$$\n", "\\overbrace{\\texttt{INCLURE}}^{\\text{son nom}}\n", "\\quad\n", "\\overbrace{\\texttt{,}}^{\\text{une virgule}}\n", "\\quad\n", "\\overbrace{\n", " \\underbrace{\\texttt{1N}}_{\\text{cardinalités}}\n", " \\underbrace{\\texttt{COMMANDE}}_{\\text{entité}}\n", " \\quad\n", " ,\n", " \\quad\n", " \\underbrace{\\texttt{0N}}_{\\text{cardinalités}}\n", " \\underbrace{\\texttt{PRODUIT}}_{\\text{entité}}\n", "}^{\\text{ses pattes séparées par des virgules}}\n", "\\quad\n", "\\overbrace{\\texttt{:}}^{\\text{deux-points}}\n", "\\quad\n", "\\overbrace{\\texttt{Quantité}}^{\\text{ses attributs séparés par des virgules}}\n", "$$ \n", "\n", "Notez que pour les associations sans attributs (comme PASSER), le deux-points est facultatif." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Astuce.** Si vous recopiez un MCD ou que vous l'avez bien en tête, commencez par les associations et, à tout moment, faites apparaître les entités manquantes (double clic sur le lapin magique sous Mocodo online). Elles auront comme identifiant le nom de l'entité en minuscules, par défaut précédé de « id. » (sauf pour les entités DATE et PÉRIODE). Vous n'aurez plus qu'à remplir les autres attributs :" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRéserver\n", "\t\tDurée\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tid. client\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tChambre\n", "\tid. chambre\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDate\n", "\tdate\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox.mcd\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "%%mocodo \n", "Réserver, 1N Client, 1N Chambre, 0N Date: Durée\n", "\n", "Client: id. client, \n", "Chambre: id. chambre, \n", "Date: date\n" ] } ], "source": [ "%%mocodo -t guess:entities\n", "Réserver, 1N Client, 1N Chambre, 0N Date: Durée" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dépendances fonctionnelles entre entités" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lorsque l'une des cardinalités maximales d'une association binaire est 1 (ou quelquefois 0), on désigne cette association sous le nom de **dépendance fonctionnelle**. On la figure souvent par un cercle portant le symbole DF : cela évite de se creuser la tête pour trouver un nom à une association qui (_spoiler_) disparaîtra corps et biens au moment du passage au relationnel." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tINCLURE\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "CLIENT: Réf. client, Nom, Prénom, Adresse\n", "DF, 0N CLIENT, 11 COMMANDE\n", "COMMANDE: Num. commande, Date, Montant\n", "INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité\n", "PRODUIT: Réf. produit, Libellé, Prix unitaire" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Associations réflexives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quelquefois appelées **circulaires**, **unaires** ou **récursives**, elles associent une entité à elle-même. Voici par exemple une représentation des filiations patrilinéaires (le 01 permet d'« arrêter » la remontée des ancêtres) :" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tENGENDRER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tHOMME\n", "\tNum. SS\n", "\t\n", "\tNom\n", "\tPrénom\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "HOMME: Num. SS, Nom, Prénom\n", "ENGENDRER, 0N HOMME, 01 HOMME" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Identifiants composites" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Préfixez d'un tiret bas (`_`) les second, troisième, etc. attributs pour les inclure dans l'identifiant." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tGRATTE-CIEL\n", "\tlatitude\n", "\t\n", "\tlongitude\n", "\t\n", "\tnom\n", "\thauteur\n", "\tannée de construction\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "GRATTE-CIEL: latitude, _longitude, nom, hauteur, année de construction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Flèches sur les pattes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suffixez d'un chevron (`<` ou `>`) les cardinalités de la patte concernée. La direction indiquée se lit en partant de l'association et en allant vers l'entité." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPeut recevoir\n", "\t\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tAppartient\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEngendre\n", "\t\n", "\t0,N\n", "\t2,2\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tGroupe sanguin\n", "\ttype de sang\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPersonne\n", "\tNum. SS\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tSexe\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "Peut recevoir, 1N> Groupe sanguin, 1N< Groupe sanguin\n", "Groupe sanguin: type de sang\n", "Appartient, 11> Personne, 0N Groupe sanguin\n", "Personne: Num. SS, Nom, Prénom, Sexe\n", "Engendre, 0N< Personne, 22> Personne" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Types de données" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Comme on l'a vu dans l'introduction, chaque attribut peut être assorti d'un type entre crochets, qui servira lors de la conversion en SQL. À partir de la version 4.0, Mocodo est capable de deviner le type des attributs usuels à partir de leur nom :" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_data_dict_3.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "| Entité ou association | Libellé de l'attribut | Type |\n", "|:----------------------|:----------------------|:--------------|\n", "| CLIENT | Adresse | VARCHAR(30) |\n", "| \" | Nom | VARCHAR(255) |\n", "| \" | Prénom | VARCHAR(255) |\n", "| \" | Réf. client | VARCHAR(8) |\n", "| COMMANDE | Date | DATE |\n", "| \" | Montant | DECIMAL(10,2) |\n", "| \" | Num. commande | VARCHAR(8) |\n", "| INCLURE | Quantité | INTEGER |\n", "| PRODUIT | Libellé | VARCHAR(50) |\n", "| \" | Prix unitaire | DECIMAL(10,2) |\n", "| \" | Réf. produit | VARCHAR(8) |\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t guess:types data_dict --select cv\n", "CLIENT: Réf. client, Nom, Prénom, Adresse\n", "PASSER, 0N CLIENT, 11 COMMANDE\n", "COMMANDE: Num. commande, Date, Montant\n", "INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité\n", "PRODUIT: Réf. produit, Libellé, Prix unitaire" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nouveauté de la version 4.3.** Avec l'option `-t prompt:types`, Mocodo compose un _prompt_ à copier-coller sous ChatGPT (ou autre) pour compléter le MCD avec les types manquants. Ce _prompt_ est trop long pour être reproduit ici : il consiste en une présentation de la syntaxe de Mocodo, des instructions sur le travail demandé, plusieurs exemples corrigés, et enfin le MCD à compléter. Voici par exemple les types générés par [Claude 3.5 Haiku](https://claude.ai/chat/d4fb8fa7-fb68-43ae-b255-fa10dcc5cb84) pour le MCD de la page d'accueil de Mocodo online :" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_data_dict_3.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "| Entité ou association | Libellé de l'attribut | Type |\n", "|:----------------------|:----------------------|:-------------|\n", "| AYANT-DROIT | lien | VARCHAR(50) |\n", "| \" | nom ayant-droit | VARCHAR(100) |\n", "| COMPOSER | quantité | INTEGER |\n", "| DÉPARTEMENT | nom département | VARCHAR(100) |\n", "| \" | num. département | VARCHAR(20) |\n", "| EMPLOYÉ | matricule | VARCHAR(20) |\n", "| \" | nom employé | VARCHAR(100) |\n", "| FOURNIR | qté fournie | INTEGER |\n", "| PIÈCE | libellé pièce | VARCHAR(255) |\n", "| \" | réf. pièce | VARCHAR(20) |\n", "| PROJET | nom projet | VARCHAR(255) |\n", "| \" | num. projet | VARCHAR(20) |\n", "| REQUÉRIR | qté requise | INTEGER |\n", "| SOCIÉTÉ | num. société | VARCHAR(20) |\n", "| \" | raison sociale | VARCHAR(255) |\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t data_dict\n", "AYANT-DROIT: nom ayant-droit [VARCHAR(100)], lien [VARCHAR(50)]\n", "DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET\n", "REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise [INTEGER]\n", "PIÈCE: réf. pièce [VARCHAR(20)], libellé pièce [VARCHAR(255)]\n", "COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité [INTEGER]\n", "\n", "DF, _11 AYANT-DROIT, 0N EMPLOYÉ\n", "EMPLOYÉ: matricule [VARCHAR(20)], nom employé [VARCHAR(100)]\n", "PROJET: num. projet [VARCHAR(20)], nom projet [VARCHAR(255)]\n", "FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie [INTEGER]\n", "\n", "DÉPARTEMENT: num. département [VARCHAR(20)], nom département [VARCHAR(100)]\n", "EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT\n", "TRAVAILLER, 0N EMPLOYÉ, 1N PROJET\n", "SOCIÉTÉ: num. société [VARCHAR(20)], raison sociale [VARCHAR(255)]\n", "CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ce problème d'inférence n'est pas très difficile pour une IA. Les résultats devraient être très bons, surtout en comparaison de ceux de la version purement algorithmique, qui sont généralement incomplets :" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_data_dict_3.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "| Entité ou association | Libellé de l'attribut | Type |\n", "|:----------------------|:----------------------|:-------------|\n", "| AYANT-DROIT | lien | |\n", "| \" | nom ayant-droit | VARCHAR(255) |\n", "| COMPOSER | quantité | INTEGER |\n", "| DÉPARTEMENT | nom département | VARCHAR(255) |\n", "| \" | num. département | VARCHAR(8) |\n", "| EMPLOYÉ | matricule | |\n", "| \" | nom employé | VARCHAR(255) |\n", "| FOURNIR | qté fournie | INTEGER |\n", "| PIÈCE | libellé pièce | |\n", "| \" | réf. pièce | VARCHAR(8) |\n", "| PROJET | nom projet | VARCHAR(255) |\n", "| \" | num. projet | VARCHAR(8) |\n", "| REQUÉRIR | qté requise | INTEGER |\n", "| SOCIÉTÉ | num. société | VARCHAR(8) |\n", "| \" | raison sociale | |\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t guess:types data_dict --select cv\n", "AYANT-DROIT: nom ayant-droit, lien\n", "DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET\n", "REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise\n", "PIÈCE: réf. pièce, libellé pièce\n", "COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité\n", "\n", "DF, _11 AYANT-DROIT, 0N EMPLOYÉ\n", "EMPLOYÉ: matricule, nom employé\n", "PROJET: num. projet, nom projet\n", "FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie\n", "\n", "DÉPARTEMENT: num. département, nom département\n", "EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT\n", "TRAVAILLER, 0N EMPLOYÉ, 1N PROJET\n", "SOCIÉTÉ: num. société, raison sociale\n", "CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tracé d'un MCD : principes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mocodo combine les avantages de l'approche _diagram as code_ (comme PlantUML, Mermaid ou Graphviz), avec la liberté de positionnement offerte par les logiciels WYSIWYG (_what you see is what you get_).\n", "\n", "Sa grande originalité est de se baser sur l'ordre et la séparation des lignes de la description pour définir une mise en page qui se révèle suffisante dans la majorité des cas." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Le pont aux ânes du langage Mocodo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Premier principe : les boîtes (entités et associations) définies sur des **lignes consécutives** sont tracées sur **une seule rangée**.\n", "\n", "Si l'on écrit les définitions sur des lignes consécutives, on se retrouve donc vite avec des chevauchements qui ne sautent pas forcément aux yeux :" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPASSER\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tINCLURE\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "CLIENT: Réf. client, Nom, Prénom, Adresse\n", "COMMANDE: Num. commande, Date, Montant\n", "PRODUIT: Réf. produit, Libellé, Prix unitaire\n", "PASSER, 0N CLIENT, 11 COMMANDE\n", "INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'option `--detect_overlaps` (active par défaut sous Mocodo online) signale le problème et donne la solution :" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Mocodo Err.29 - Mauvaise disposition des boîtes :\n", " - La patte « INCLURE — COMMANDE » chevauche « PRODUIT ».\n", " - La patte « INCLURE — PRODUIT » chevauche « PASSER ».\n", " - La patte « PASSER — CLIENT » chevauche « COMMANDE ».\n", " - Les pattes « PASSER — COMMANDE » et « INCLURE — COMMANDE » se chevauchent.\n", "Pour corriger cela, réordonnez et/ou sautez des lignes dans le texte-source, soit à la\n", "main, soit à l'aide de l'option -t arrange (tablette de chocolat sous Mocodo online)." ] } ], "source": [ "%mocodo -i sandbox --detect_overlaps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Limitation.** Seuls les chevauchements mettant en jeu des pattes horizontales ou verticales sont détectés." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### MCD sur plusieurs lignes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Deuxième principe : pour commencer une nouvelle rangée, il faut sauter une ligne :" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEROS\n", "\t\tmetus\n", "\t\tcongue\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tNIBH\n", "\t\n", "\t1,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSCELERISQUE LOREM\n", "\tblandit\n", "\t\n", "\telit\n", "\tligula\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPELLENTESQUE IPSUM\n", "\ttincidunt\n", "\t\n", "\tbibendum\n", "\tconsequat\n", "\tinteger\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "SCELERISQUE LOREM: blandit, elit, ligula\n", "EROS, 11 SCELERISQUE LOREM, 1N PELLENTESQUE IPSUM: metus, congue\n", "\n", "NIBH, 1N SCELERISQUE LOREM, 11 PELLENTESQUE IPSUM\n", "PELLENTESQUE IPSUM: tincidunt, bibendum, consequat, integer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les centres des boîtes sont placés aux intersections d'une grille invisible. Ils sont donc alignés aussi bien horizontalement que verticalement. De plus, dans un but esthétique, le dessin fait l'objet d'une compression dans les deux dimensions. Par exemple, ci-dessus, un espace horizontal négatif a été créé entre le bord droit de l'entité de gauche et le bord gauche de l'entité de droite." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Mise en miroir" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mocodo permet de calculer facilement les symétriques d'un MCD :\n", "\n", "- verticalement : `-t flip:v` ;\n", "- horizontalement : `-t flip:h` ;\n", "- selon la seconde diagonale (transposition) : `-t flip:d`.\n", "- selon la première diagonale (transposition) : `-t flip:vhd`." ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEROS\n", "\t\tmetus\n", "\t\tcongue\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tNIBH\n", "\t\n", "\t1,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSCELERISQUE LOREM\n", "\tblandit\n", "\t\n", "\telit\n", "\tligula\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPELLENTESQUE IPSUM\n", "\ttincidunt\n", "\t\n", "\tbibendum\n", "\tconsequat\n", "\tinteger\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --select mcd -t flip:h" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Corrections de la version 4.0.**\n", "- Les sens de « horizontal » et « vertical » sont permutés pour être plus conformes à l'intuition.\n", "- La documentation parlait de première diagonale, c'était la seconde, et vice-versa." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Astuce.** Transposez le MCD temporairement pour réaliser plus facilement certaines opérations d'édition en colonne, en particulier sous Mocodo online." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Insertion d'espacements" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mocodo centre les rangées qui contiennent moins de boîtes que les autres. Ce comportement n'est pas toujours idéal :" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tUltrices\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRisus\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEuismod\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tConvallis\n", "\t\tvestibulum\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTincidunt\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRutrum\n", "\t\tfaucibus\n", "\t\tcurae\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAliquet\n", "\thendrerit\n", "\t\n", "\tmetus\n", "\tlacus\n", "\tquis\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tMassa\n", "\tmetus\n", "\t\n", "\tposuere\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tGravida\n", "\tornare\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAnte\n", "\tvitae\n", "\t\n", "\ttempor\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo \n", "Ultrices, 01 Aliquet, 0N Aliquet\n", "Aliquet: hendrerit, metus, lacus, quis\n", "Risus, 1N Aliquet, 0N Massa\n", "Massa: metus, posuere\n", "Euismod, 0N Massa, 0N Massa\n", "\n", "Convallis, 0N Aliquet, 0N Ante: vestibulum\n", "Tincidunt, 11 Ante, 0N Massa\n", "\n", "Gravida: ornare\n", "Rutrum, 0N Gravida, 0N Ante, 0N Aliquet: faucibus, curae\n", "Ante: vitae, tempor" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'utilisateur peut alors spécifier les espacements qu'il désire en complétant les rangées par des boîtes invisibles dont le seul rôle est de « pousser » les autres à l'emplacement voulu. Cela se fait en insérant manuellement des lignes réduites au caractère deux-points :" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tUltrices\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRisus\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEuismod\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tConvallis\n", "\t\tvestibulum\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTincidunt\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRutrum\n", "\t\tfaucibus\n", "\t\tcurae\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAliquet\n", "\thendrerit\n", "\t\n", "\tmetus\n", "\tlacus\n", "\tquis\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tMassa\n", "\tmetus\n", "\t\n", "\tposuere\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tGravida\n", "\tornare\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAnte\n", "\tvitae\n", "\t\n", "\ttempor\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "Ultrices, 01 Aliquet, 0N Aliquet\n", "Aliquet: hendrerit, metus, lacus, quis\n", "Risus, 1N Aliquet, 0N Massa\n", "Massa: metus, posuere\n", "Euismod, 0N Massa, 0N Massa\n", "\n", ":\n", ":\n", "Convallis, 0N Aliquet, 0N Ante: vestibulum\n", "Tincidunt, 11 Ante, 0N Massa\n", "\n", "Gravida: ornare\n", "Rutrum, 0N Gravida, 0N Ante, 0N Aliquet: faucibus, curae\n", ":\n", "Ante: vitae, tempor" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Réarrangement automatique" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cependant, en général, ces manipulations sont inutiles : Mocodo est capable de calculer tout seul des plongements à la fois compacts et esthétiques. Voici une compilation des réarrangements automatiques du MCD précédent produits par :\n", "```\n", "%mocodo -i sandbox -t arrange --seed 1\n", "%mocodo -i sandbox -t arrange --seed 2\n", "%mocodo -i sandbox -t arrange --seed 3\n", "%mocodo -i sandbox -t arrange --seed 4\n", "```" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTincidunt\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEuismod\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tConvallis\n", "\t\tvestibulum\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRisus\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEuismod\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRisus\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRutrum\n", "\t\tfaucibus\n", "\t\tcurae\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tUltrices\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tConvallis\n", "\t\tvestibulum\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTincidunt\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tUltrices\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRutrum\n", "\t\tfaucibus\n", "\t\tcurae\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tUltrices\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRisus\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRisus\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tUltrices\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRutrum\n", "\t\tfaucibus\n", "\t\tcurae\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tConvallis\n", "\t\tvestibulum\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEuismod\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEuismod\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tConvallis\n", "\t\tvestibulum\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRutrum\n", "\t\tfaucibus\n", "\t\tcurae\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTincidunt\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTincidunt\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tMassa\n", "\tmetus\n", "\t\n", "\tposuere\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAnte\n", "\tvitae\n", "\t\n", "\ttempor\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tMassa\n", "\tmetus\n", "\t\n", "\tposuere\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAliquet\n", "\thendrerit\n", "\t\n", "\tmetus\n", "\tlacus\n", "\tquis\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tGravida\n", "\tornare\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAliquet\n", "\thendrerit\n", "\t\n", "\tmetus\n", "\tlacus\n", "\tquis\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tGravida\n", "\tornare\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAnte\n", "\tvitae\n", "\t\n", "\ttempor\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAliquet\n", "\thendrerit\n", "\t\n", "\tmetus\n", "\tlacus\n", "\tquis\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAliquet\n", "\thendrerit\n", "\t\n", "\tmetus\n", "\tlacus\n", "\tquis\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tMassa\n", "\tmetus\n", "\t\n", "\tposuere\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tMassa\n", "\tmetus\n", "\t\n", "\tposuere\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tGravida\n", "\tornare\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tGravida\n", "\tornare\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAnte\n", "\tvitae\n", "\t\n", "\ttempor\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAnte\n", "\tvitae\n", "\t\n", "\ttempor\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ../examples/four_random_layouts --scale 0.8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Extensions du modèle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Entité faible (ou identification relative)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Préfixez d'un tiret bas (`_`) une cardinalité 11 pour indiquer que l'entité distinguée est faible." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,N\n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tŒUVRE\n", "\tCote œuvre\n", "\t\n", "\tTitre\n", "\tDate parution\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEXEMPLAIRE\n", "\tNum. exemplaire\n", "\t\n", "\tÉtat du livre\n", "\tDate d’achat\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "ŒUVRE: Cote œuvre, Titre, Date parution\n", "DF, 1N ŒUVRE, _11 EXEMPLAIRE\n", "EXEMPLAIRE: Num. exemplaire, État du livre, Date d'achat" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans le diagramme, les identifiants (ou plutôt, discriminateurs) d'une telle entité seront soulignés en pointillés, tandis que le 11 sera par défaut souligné d'un trait plein.\n", "\n", "Notez qu'un discriminateur n'est pas toujours obligatoire :" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEmployé\n", "\tNum. employé\n", "\t\n", "\tNom employé\n", "\tPrénom employé\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tConjoint\n", "\tNom conjoint\n", "\tPrénom conjoint\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo \n", "Employé: Num. employé, Nom employé, Prénom employé\n", "Df, _11 Conjoint, 01 Employé\n", "Conjoint: _Nom conjoint, Prénom conjoint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Son absence implique cependant que l'occurrence de l'entité forte qui « renforce » une occurrence de l'entité faible ne peut en renforcer une autre : d'où la cardinalité maximale 1 sur la patte distinguant EMPLOYÉ.\n", "\n", "Mocodo rejettera donc la version « polygame » du MCD précédent (avec 0N à la place de 01) :" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Mocodo Err.50 - L'entité faible « CONJOINT » devrait avoir un discriminateur." ] } ], "source": [ "%%mocodo\n", "Employé: Num. employé, Nom employé, Prénom employé\n", "Df, _11 Conjoint, 0N Employé\n", "Conjoint: _Nom conjoint, Prénom conjoint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Héritage (généralisation / spécialisation)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'héritage permet de regrouper dans une entité, dite « mère », les attributs communs à plusieurs autres entités, dites « filles », qui se distinguent les unes des autres par des attributs spécifiques." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour définir une spécialisation, insérez une ligne spécifiant :\n", "\n", "\n", "$$\n", "\\overbrace{\\texttt{/}\\,\\texttt{XT}\\,\\texttt{\\\\}}^{\\text{sa nature entre barres obliques}}\n", "\\qquad\n", "\\overbrace{\\texttt{<}\\,\\texttt{-}}^{\\text{son type de flèche}}\n", "\\qquad\n", "\\overbrace{\\texttt{mère}}^{\\text{une entité}}\n", "\\qquad\n", "\\overbrace{\\texttt{,}}^{\\text{une virgule}}\n", "\\qquad\n", "\\overbrace{\\texttt{fille 1, fille 2, fille 3}}^{\\text{des entités séparées par des virgules}}\n", "\\qquad\n", "\\overbrace{\\texttt{: discriminateur}}^{\\text{optionnellement, un attribut}}\n", "$$ " ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tXT\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPersonne\n", "\tnum SS\n", "\t\n", "\tnom\n", "\tprénom\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tHomme\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFemme\n", "\tnom de jeune fille\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "Personne: num SS, nom, prénom\n", "\n", "/XT\\ Personne <- Homme, Femme: sexe\n", "\n", "Homme: \n", ":\n", "Femme: nom de jeune fille" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inscrivez dans le triangle les symboles de votre choix pour prendre une part active aux guerres culturelles de notre temps :" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "| Totalité | Exclusion | Symboles | Exemple de population | Toute personne est : |\n", "|----------|-----------|:--------:|:---------------------:|:---------------------|\n", "| oui | oui | `/XT\\` | ♂♂♂♂♂♂♂
♀♀♀♀♀♀♀♀♀♀
| - soit un homme
- soit une femme |\n", "| non | oui | `/X\\` | ♂♂♂♂♂♂♂♂
♀♀♀♀♀♀
○○○○
| - soit un homme
- soit une femme
- soit aucun des deux |\n", "| oui | non | `/T\\` | ♂♂♂♂♂♂
♀♀♀♀♀♀♀♀
⚥⚥⚥⚥
| - soit un homme
- soit une femme
- soit les deux à la fois |\n", "| non | non | `/\\` | ♂♂♂♂♂
♀♀♀♀♀♀
○○○○
⚥⚥⚥⚥
| - soit un homme
- soit une femme
- soit aucun des deux
- soit les deux à la fois |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Contrainte inter-associations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour définir une telle contrainte, ajoutez n'importe où une ligne spécifiant :\n", "\n", "$$\n", "\\overbrace{\\texttt{(}\\,\\texttt{FOO}\\,\\texttt{)}}^{\\text{son nom entre parenthèses}}\n", "\\quad\n", "\\overbrace{\n", " \\texttt{-}\\,\\texttt{>}\n", " \\texttt{boîte 1,}\n", " \\quad\n", " \\texttt{.}\\,\\texttt{.}\n", " \\texttt{boîte 2,}\n", " \\quad\n", " \\texttt{-}\\,\\texttt{-}\n", " \\texttt{boîte 3,}\n", " \\quad\n", " \\texttt{boîte 4}\n", "}^{\\text{les types de liens et les boîtes qu'elle met en jeu, séparés par des virgules}}\n", "\\quad\n", "\\overbrace{\\texttt{:}\\,\\,\\texttt{horizontale, verticale}}^{\\text{optionnellement, ses coordonnées}}\n", "$$ \n", "\n", "**Précisions.**\n", "- Comme leur nom ne l'indique pas, ces contraintes peuvent aussi bien porter sur des entités que des associations.\n", "- Le nom d'une contrainte peut être formé de zéro à trois symboles quelconques.\n", "- Un lien est soit vide (trait invisible), soit formé d'un ou plusieurs symboles `-` (trait plein) ou `.` (trait pointillé), optionnellement précédés d'un symbole `<` (flèche vers la contrainte) et/ou suivis d'un symbole `>` (flèche vers la boîte).\n", "- Au contraire des boîtes (entités et associations), les contraintes ne sont pas... contraintes par la grille sous-jacente : la mise en page du MCD n'en tient absolument pas compte. Celui de l'exemple ci-dessous a donc dû être « aéré » avec des `:` pour leur faire de la place.\n", "- Les clauses de définition des contraintes peuvent apparaître n'importe où. Notez cependant que tout réarrangement automatique les enverra systématiquement à la fin du texte-source.\n", "- Chaque coordonnée du centre de la contrainte peut être exprimée sous la forme :\n", " - d'une référence à une boîte sur laquelle l'aligner horizontalement ou verticalement (respectivement) ;\n", " - d'un pourcentage de la largeur ou de la hauteur (respectivement) du MCD.\n", "- En l'absence de coordonnées, la contrainte est placée au barycentre des boîtes qu'elle relie.\n", "\n", "\n", "Voici un exemple concret, adapté de la Fig. 7.37 de _Merise, deuxième génération_ (Dominique Nanci et Bernard Espinasse, 4e éd., 2001) :" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tI\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tLouer\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tStocker\n", "\t\tquantité\n", "\t\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tComposer\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDépôt\n", "\tnum dépôt\n", "\t\n", "\tsurface\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tnum. commande\n", "\t\n", "\tdate\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tArticle\n", "\tréf. article\n", "\t\n", "\tprix\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Article** (réf. article, prix)\n", "- **Commande** (num. commande, date, _#num dépôt_)\n", "- **Composer** (_#num. commande_, _#réf. article_)\n", "- **Dépôt** (num dépôt, surface)\n", "- **Stocker** (_#num dépôt_, _#réf. article_, quantité)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", ":::\n", "Dépôt: num dépôt, surface\n", "\n", ":\n", "Louer, 11 Commande, 0N Dépôt\n", ":\n", "Stocker, 1N Dépôt, 1N Article: quantité\n", "\n", "Commande: num. commande, date\n", "Composer, 1N Commande, 0N Article\n", ":\n", "Article: réf. article, prix\n", "\n", "(I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer, Commande" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notez que pour Mocodo, ces contraintes sont purement décoratives : le passage au niveau relationnel ou physique n'en tient pas compte." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Agrégat (ou pseudo-entité)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans le MCD ci-dessous, le petit rond et l'enveloppe pointillée indiquent qu'une réservation d'une chambre donnée à une date donnée ne peut être faite que par un seul client :" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRéserver\n", "\t\tDurée\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDate\n", "\tDate\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tChambre\n", "\tNum. chambre\n", "\t\n", "\tPrix\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tId. client\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --colors ocean # changement de palette pour faire apparaître un fond semi-transparent \n", "Date: Date\n", "Réserver, /1N Client, 1N Chambre, 0N Date: Durée\n", "Chambre: Num. chambre, Prix\n", "\n", "Client: Id. client" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La syntaxe est minimale : une simple barre oblique `/` avant la cardinalité de l'entité-cible.\n", "\n", "S'il y a au maximum deux entités à agréger, et que l'angle formé par l'association et les entités est plat ou rectangle, une enveloppe de points matérialise la pseudo-entité.\n", "\n", "Sinon, pour des raisons de simplicité du code et de clarté du diagramme, l'enveloppe n'est pas affichée. Cependant, le petit rond subsiste et le reste des traitements est bien sûr inchangé.\n", "\n", "**Remarques.**\n", "- La représentation traditionnelle demanderait à insérer une association DF entre CLIENT et RÉSERVER. Mais permettre l'association d'une entité et d'une association demanderait à apporter au code de Mocodo des changements conséquents, pour un bénéfice qui ne nous saute pas forcément aux yeux.\n", "- Les agrégats sur association binaire sont soit inutiles (sur une DF), soit impossibles (sur une non-DF, ils reviennent à permettre à une même occurrence d'entité de renforcer plusieurs occurrences d'une entité faible sans discriminateur). Mocodo les interdit dans les deux cas." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### CIF à unicité complète" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La notion d'agrégation n'a pas très bonne presse dans les milieux autorisés. Ceux-ci lui préfèrent généralement celle (équivalente) de contrainte d'intégrité fonctionnelle à unicité complète. Pour rentrer dans leurs bonnes grâces (et désactiver du même coup la visualisation de l'enveloppe et du petit rond), il suffit d'expliciter la contrainte correspondante dans le MCD précédent. Mocodo peut le faire pour vous :" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tCIF\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRéserver\n", "\t\tDurée\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDate\n", "\tDate\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tChambre\n", "\tNum. chambre\n", "\t\n", "\tPrix\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tId. client\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox.mcd\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "%%mocodo --seed=12 --colors ocean\n", "Date: Date\n", "Réserver, /1N Client, 1N Chambre, 0N Date: Durée\n", "Chambre: Num. chambre, Prix\n", "\n", "-INVISIBLE_1, XX Client, XX Client\n", "Client: Id. client\n", ":\n", "\n", "(CIF) ..Réserver, ->Client, --Chambre, --Date: INVISIBLE_1, INVISIBLE_1\n" ] } ], "source": [ "%mocodo -i sandbox -t create:cifs arrange --seed=12 --colors ocean" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remarquez que la visibilité de la CIF est assurée par le positionnement de celle-ci sur une boîte invisible introduite à cet effet : si vous rectifiez ce positionnement, vous pouvez bien sûr supprimer cette boîte.\n", "\n", "[Vous avez également le droit](https://www.developpez.net/forums/d2099422/general-developpement/alm/outils/looping/cif-depliees-compactees/) d'alléger la visualisation des CIFs _complètes_ en transformant les traits pleins `\"--\"` des entités émettrices en traits invisibles `\"\"`. Là encore, Mocodo peut le faire pour vous :" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tCIF\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRéserver\n", "\t\tDurée\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDate\n", "\tDate\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tChambre\n", "\tNum. chambre\n", "\t\n", "\tPrix\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tId. client\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox.mcd\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "%%mocodo --seed=12 --colors ocean\n", "Date: Date\n", "Réserver, /1N Client, 1N Chambre, 0N Date: Durée\n", "Chambre: Num. chambre, Prix\n", "\n", "-INVISIBLE_1, XX Client, XX Client\n", "Client: Id. client\n", ":\n", "\n", "(CIF) ..Réserver, ->Client, Chambre, Date: INVISIBLE_1, INVISIBLE_1\n" ] } ], "source": [ "%mocodo -i sandbox -t create:cifs=light arrange --seed=12 --colors ocean" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Limitation.** Au niveau fonctionnel, c'est toujours la barre oblique qui conditionne le traitement des CIF. Par conséquent, les CIF à unicité _incomplète_ ne sont prises en charge qu'au niveau visuel." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Identifiants explicites dans les associations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mocodo 4.1.1 relaxe par défaut (sauf dans la version en ligne) la contrainte de Merise restreignant aux entités la présence d'identifiants. Si vous l'osez, vous pouvez donc obtenir le même schéma relationnel avec le MCD « allégé » suivant :" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRéserver\n", "\t\tDate\n", "\t\t\n", "\t\tDurée\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tId. client\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tChambre\n", "\tNum. chambre\n", "\t\n", "\tPrix\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Chambre** (Num. chambre, Prix)\n", "- **Réserver** (Id. client, _#Num. chambre_, Date, Durée)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Client: Id. client\n", "Réserver, 1N Client, 0N Chambre: _Date, Durée\n", "Chambre: Num. chambre, Prix" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'introduction de cette possibilité, surtout destinée à simplifier le plongement des MCD touffus, est discutée [ici](#Par-suppression-de-ces-mêmes-entités)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Conversion d'un MCD" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Passage au relationnel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il se fait en deux étapes:\n", "- la création d'une représentation **interne** complète du MLD ;\n", "- la traduction de celle-ci en une représentation **externe** dans le ou les formats de sortie souhaités." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour construire la **représentation interne**, l'algorithme de base réalise la séquence d'opérations suivante :\n", "\n", "1. Pour chaque entité, une relation (table) de même nom et de mêmes attributs est créée. Le ou les identifiants de l'entité constituent la clé primaire de la relation.\n", "2. Toute relation issue d'une entité faible est renforcée, c'est-à-dire que la clé primaire de l'entité qu'elle détermine fonctionnellement vient s'adjoindre à sa clé primaire, au besoin de façon récursive.\n", "3. Les associations sont traitées ainsi :\n", " 1. si toutes les pattes de l'association portent la cardinalité maximale N, une relation de même nom et de mêmes attributs est créée. Sa clé primaire est constituée de l'ensemble des clés primaires des relations issues des entités mises en jeu ;\n", " 2. dans le cas contraire, c'est-à-dire si l'une des pattes de l'association porte la cardinalité (1,1), ou à défaut (0,1), l'entité distinguée se voit adjoindre :\n", " 1. en tant que clés étrangères, l'ensemble des clés primaires des autres entités mises en jeu;\n", " 2. en tant que simples attributs, l'ensemble des attributs de l'association.\n", "\n", "**Remarque.** Un couple de cardinalités non standard, c'est-à-dire distinct de (0,1), (1,1), (0,N) et (1,N), est traité comme (0,1) si son deuxième symbole est un 1, et comme (0,N) sinon. Cela couvre en particulier les cas (\\*, 1), (\\*,N), (?,?) et (X,X)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour construire la **représentation externe**, Mocodo formate la représentation interne à l'aide d'un gabarit. Près d'une centaine de gabarits sont fournis, qui permettent de produire quatre grandes catégories de résultats :\n", "1. le schéma relationnel dans toutes les combinaisons de formats (HTML, Markdown, texte brut, $\\LaTeX$) et d'options (avec ou sans explications, avec ou sans _boilerplate_, avec ou sans visualisation des contraintes d'unicité et d'optionalité) ;\n", "2. le diagramme relationnel au format Mocodo, avec ou sans visualisation des contraintes d'unicité et d'optionalité, ou dans des formats externes, comme DBML ou D2.\n", "3. le DDL en SQL ANSI et dans les principaux dialectes (Microsoft SQL Server, MySQL, Oracle DB, PostgreSQL, SQLite), avec ou sans _boilerplate_ de création de la base ;\n", "4. le graphe des dépendances." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Niveau logique" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Schéma relationnel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La transformation `-t mld` opère la conversion d'un MCD (modèle conceptuel de données) en un MLD (modèle logique de données), autrement appelé **schéma relationnel** :" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Client** (Réf. client, Nom, Prénom, Adresse)\n", "- **Commande** (Num. commande, Date, Montant, _#Réf. client_)\n", "- **Inclure** (_#Num. commande_, _#Réf. produit_, Quantité)\n", "- **Produit** (Réf. produit, Libellé, Prix unitaire)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t mld" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sous Jupyter Notebook, comme pour toutes les opérations de conversion, le tracé du MCD est omis par défaut. Dans le cas très fréquent où l'on a besoin de visualiser simultanément le MCD et le MLD, on peut invoquer l'option `-t` sans arguments (ou encore `--mld`) :" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPasser\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tInclure\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProduit\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Client** (Réf. client, Nom, Prénom, Adresse)\n", "- **Commande** (Num. commande, Date, Montant, _#Réf. client_)\n", "- **Inclure** (_#Num. commande_, _#Réf. produit_, Quantité)\n", "- **Produit** (Réf. produit, Libellé, Prix unitaire)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Diagramme relationnel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La plupart des SGBD offrent une représentation hybride (graphique / texte) de la base sous la forme de tables liées par des flèches. Nous l'appelons **diagramme relationnel**. Sous Mocodo :" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_mld.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp --colors mondrian -t diagram" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le nom du fichier généré avant rendu graphique se termine par `_mld.mcd`. L'extension `.mcd` signifie que Mocodo peut le prendre comme texte-source (bien que ce ne soit pas un schéma conceptuel). Examinons son contenu :" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
%%mocodo\n",
       ":\n",
       "Client: Réf. client, Nom, Prénom, Adresse\n",
       ":\n",
       "Commande: Num. commande, Date, Montant, #Réf. client > Client > Réf. client\n",
       ":\n",
       "Inclure: #Num. commande > Commande > Num. commande, _#Réf. produit > Produit > Réf. produit, Quantité\n",
       ":\n",
       "Produit: Réf. produit, Libellé, Prix unitaire\n",
       ":\n",
       "
\n" ], "text/latex": [ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", "\\PYZpc{}\\PYZpc{}mocodo\n", ":\n", "Client: Réf. client, Nom, Prénom, Adresse\n", ":\n", "Commande: Num. commande, Date, Montant, \\PYZsh{}Réf. client \\PYZgt{} Client \\PYZgt{} Réf. client\n", ":\n", "Inclure: \\PYZsh{}Num. commande \\PYZgt{} Commande \\PYZgt{} Num. commande, \\PYZus{}\\PYZsh{}Réf. produit \\PYZgt{} Produit \\PYZgt{} Réf. produit, Quantité\n", ":\n", "Produit: Réf. produit, Libellé, Prix unitaire\n", ":\n", "\\end{Verbatim}\n" ], "text/plain": [ "%%mocodo\n", ":\n", "Client: Réf. client, Nom, Prénom, Adresse\n", ":\n", "Commande: Num. commande, Date, Montant, #Réf. client > Client > Réf. client\n", ":\n", "Inclure: #Num. commande > Commande > Num. commande, _#Réf. produit > Produit > Réf. produit, Quantité\n", ":\n", "Produit: Réf. produit, Libellé, Prix unitaire\n", ":" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.Code(\"ccp_mld.mcd\", language=\"text\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La syntaxe d'un MLD est effectivement la même que celle d'un MCD, à ceci près que les associations sont remplacées par des liens allant de l'attribut `a1` de l'entité `E1` à l'attribut `a2` de l'entité `E2`, et notés : `E1: ... a1 > E2 > a2`.\n", "\n", "Les relations sont placées dans le même ordre que les boîtes du MCD d'origine. Vous devrez quelquefois les réarranger (automatiquement ou manuellement) pour obtenir un résultat plus esthétique. Notez que des boîtes invisibles ont été automatiquement insérées une colonne sur deux afin de laisser de la place aux flèches." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Limitation.** Les clés étrangères composites sont actuellement représentées comme si elles étaient séparées (autant de flèches que de parties)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nouveautés de la version 4.0.**\n", "- Ce qui se notait `E1: ... a1->E2->a2` se note maintenant `E1: ... a1 > E2 > a2`.\n", "- Sous Jupyter Notebook, un seule étape suffit pour visualiser le diagramme relationnel.\n", "- Le nom du fichier intermédiaire se termine par `_mld.mcd` au lieu de `.mld`. L'ancienne extension est abandonnée." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Niveau physique : DDL" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### SQL" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La principale raison de vivre d'un MCD est de se métamorphoser _in fine_ en une chatoyante base de données relationnelles. Cela se fait au moyen d'un sous-ensemble du langage SQL appelé DDL (_Data Definition Language_). Sous Mocodo :" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE Client (\n", " PRIMARY KEY (Ref_client),\n", " Ref_client VARCHAR(8) NOT NULL,\n", " Nom VARCHAR(255),\n", " Prenom VARCHAR(255),\n", " Adresse VARCHAR(255)\n", ");\n", "\n", "CREATE TABLE Commande (\n", " PRIMARY KEY (Num_commande),\n", " Num_commande VARCHAR(8) NOT NULL,\n", " Date DATE,\n", " Montant DECIMAL(10,2),\n", " Ref_client VARCHAR(8) NOT NULL\n", ");\n", "\n", "CREATE TABLE Inclure (\n", " PRIMARY KEY (Num_commande, Ref_produit),\n", " Num_commande VARCHAR(8) NOT NULL,\n", " Ref_produit VARCHAR(8) NOT NULL,\n", " Quantite INTEGER\n", ");\n", "\n", "CREATE TABLE Produit (\n", " PRIMARY KEY (Ref_produit),\n", " Ref_produit VARCHAR(8) NOT NULL,\n", " Libelle VARCHAR(50),\n", " Prix_unitaire DECIMAL(10,2)\n", ");\n", "\n", "ALTER TABLE Commande ADD FOREIGN KEY (Ref_client) REFERENCES Client (Ref_client);\n", "\n", "ALTER TABLE Inclure ADD FOREIGN KEY (Ref_produit) REFERENCES Produit (Ref_produit);\n", "ALTER TABLE Inclure ADD FOREIGN KEY (Num_commande) REFERENCES Commande (Num_commande);\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t sql" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notez le `NOT NULL` des clés primaires. Il s'agit d'une redondance, mais aussi d'une bonne pratique. Non seulement parce qu'_explicit is better than implicit_ : elle peut se révéler utile, par exemple pour désactiver les contraintes de clé primaire pendant une maintenance tout en continuant à interdire le remplissage avec des `NULL`. Si cela vous gêne, nous montrons en annexe comment l'éliminer." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Principales nouveautés de la version 4.0.**\n", "- Génération automatique des contraintes `UNIQUE`, `NOT NULL` et `NULL` appropriées.\n", "- Les libellés sont convertis en ASCII, et toute suite de caractères qui ne sont ni des lettres, ni des chiffres, ni des tirets bas sont remplacés par un unique tiret bas. Cela simplifie la référence aux tables et aux champs, et évite de noyer le DDL sous un essaim de délimiteurs (qui plus est spécifiques à chaque dialecte). Ainsi, « Réf. client » se trouvera transformé en `Ref_client`.\n", "- Vos choix de casse (minuscules, MAJUSCULES, _snake_case_, _camelCase_, _PascalCase_, etc.) sont respectés." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Inférence de type" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Avec l'option de réécriture `-t create:types` Mocodo essaiera d'inférer des libellés des attributs tout type manquant. La langue définie avec l'option `--language` est prioritaire, mais l'anglais prend la relève en cas d'échec." ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox.mcd\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "%%mocodo --select rw\n", "Customer: Customer ID [VARCHAR(8)], Last Name [VARCHAR(255)], First Name [VARCHAR(255)], Address [VARCHAR(30)]\n", "Make Order, 0N Customer, 11 Order\n", "Order: Order Number [VARCHAR(8)], Date [DATE], Amount [DECIMAL(10,2)]\n", "INCLUDE, 1N Order, 0N Product: Quantity [INTEGER]\n", "Product: Product ID [VARCHAR(8)], Description [TEXT], Unit Price [DECIMAL(10,2)]\n" ] } ], "source": [ "%%mocodo -t create:types --select rw\n", "Customer: Customer ID, Last Name, First Name, Address\n", "Make Order, 0N Customer, 11 Order\n", "Order: Order Number, Date, Amount\n", "INCLUDE, 1N Order, 0N Product: Quantity\n", "Product: Product ID, Description, Unit Price" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il va de soi que les types inférés devront être systématiquement contrôlés, et parfois corrigés ou complétés. Une discussion sur StackOverflow, [Common MySQL fields and their appropriate data types](https://stackoverflow.com/questions/354763/common-mysql-fields-and-their-appropriate-data-types), a servi de point de départ. La liste des types utilisée est [celle de Wikipedia](https://en.wikibooks.org/wiki/Structured_Query_Language/Data_Types). Consultez les correspondances spécifiées dans `mocodo/resources/default_datatypes_fr.tsv`, etc. pour plus de détails, et n'hésitez pas à [ouvrir le débat](https://github.com/laowantong/mocodo/issues/new) si vous avez des corrections ou des suggestions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si vous préférez tout typer à la main, vous pouvez au moins créer les « cases » à remplir :" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox.mcd\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "%%mocodo --select rw\n", "Customer: Customer ID [], Last Name [], First Name [], Address []\n", "Make Order, 0N Customer, 11 Order\n", "Order: Order Number [], Date [], Amount []\n", "INCLUDE, 1N Order, 0N Product: Quantity []\n", "Product: Product ID [], Description [], Unit Price []\n" ] } ], "source": [ "%%mocodo -t create:types=[] --select rw\n", "Customer: Customer ID, Last Name, First Name, Address\n", "Make Order, 0N Customer, 11 Order\n", "Order: Order Number, Date, Amount\n", "INCLUDE, 1N Order, 0N Product: Quantity\n", "Product: Product ID, Description, Unit Price" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Dialectes de SQL" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'argument `sql` peut être remplacé par un nom de dialecte parmi `mysql`, `oracle`, `postgresql`, `sqlite` et (nouveauté de la version 4) `mssql`. Quelques transformations seront alors appliquées au code-source généré. Du fait de l'uniformisation de la syntaxe apportée par la version 4, elles sont assez minimes.\n", "\n", "Notez les points suivants :\n", "- Mocodo ne touche pas aux types spécifiés par l'utilisateur. Une traduction inter-dialectes automatique est envisageable dans le futur, mais c'est un problème qui n'a en réalité aucune solution satisfaisante.\n", "- Mocodo protège les noms de tables ou de colonnes qui se trouveraient faire partie des mots réservés par le dialecte-cible. Par exemple, ci-dessous, les noms `USER` et `MEMBER` sont réservés en MySQL. Les listes viennent du site [modern-sql.com](https://modern-sql.com) de Markus Winand, et plus précisément de la page https://modern-sql.com/reserved-words-empirical-list.\n", "- Avec la sous-sous-option `b`, un _boilerplate_ de création de la base est ajouté en préambule." ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl_mysql.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE DATABASE IF NOT EXISTS `MCD`\n", " CHARACTER SET utf8mb4\n", " COLLATE utf8mb4_general_ci\n", ";\n", "USE `MCD`;\n", "\n", "CREATE TABLE `USER` (\n", " PRIMARY KEY (username),\n", " username VARCHAR(42) NOT NULL,\n", " mail VARCHAR(42),\n", " `member` VARCHAR(42)\n", ");\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t mysql:b\n", "USER: username, mail, member" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Notes sur les règles de passage" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Génération de contraintes d'optionalité et d'unicité" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Avec la sous-sous-option `c`, Mocodo peut faire apparaître dès le niveau logique certaines contraintes du niveau physique. Les notations sont les suivantes :\n", "\n", "| Contrainte | niveau logique | niveau physique |\n", "|---|:--|:--|\n", "| non-optionalité | attribut! | `NOT NULL` |\n", "| optionalité | attribut? | `NULL` |\n", "| unicité | attribut $^{u1\\ u2...}$ | `UNIQUE` |\n", "\n", "Par exemple, ci-dessous, la clé étrangère _#Réf_client_ est maintenant marquée comme **non optionnelle** :" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Client** (Réf. client, Nom, Prénom, Adresse)\n", "- **Commande** (Num. commande, Date, Montant, _#Réf. client!_)\n", "- **Inclure** (_#Num. commande_, _#Réf. produit_, Quantité)\n", "- **Produit** (Réf. produit, Libellé, Prix unitaire)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t mld:c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La clé étrangère _#id entreprise_ est marquée comme **optionnelle** :" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEnvoyer\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntreprise\n", "\tid. entreprise\n", "\t\n", "\traison\n", "\tactivité\n", "\tadresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tParticipant\n", "\tid. inscrit\n", "\t\n", "\tnom\n", "\tadresse\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Entreprise** (id. entreprise, raison, activité, adresse)\n", "- **Participant** (id. inscrit, nom, adresse, _#id. entreprise?_)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c\n", "Entreprise: id. entreprise, raison, activité, adresse\n", "Envoyer, 0N Entreprise, 01 Participant\n", "Participant: id. inscrit, nom, adresse" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La clé étrangère _#id employé_ est marquée comme **unique** :" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tDIRIGER\n", "\t\n", "\t1,1\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEMPLOYÉ\n", "\tid. employé\n", "\t\n", "\tnom employé\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDÉPARTEMENT\n", "\tid. département\n", "\t\n", "\tnom département\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **DÉPARTEMENT** (id. département, nom département, _#id. employé!_ u1)\n", "- **EMPLOYÉ** (id. employé, nom employé)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c\n", "EMPLOYÉ: id. employé, nom employé\n", "DIRIGER, 11 DÉPARTEMENT, 01 EMPLOYÉ\n", "DÉPARTEMENT: id. département, nom département" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Ajout manuel de contraintes d'optionalité et d'unicité" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les contraintes `NOT NULL` ou `NULL` spécifiées dans les types peuvent également faire l'objet d'une visualisation au niveau relationnel :" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Personne** (id. personne, nom!, prénom, nom de jeune fille?)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t mld:c\n", "Personne: id. personne [VARCHAR(8)], nom [VARCHAR(255) NOT NULL], prénom [VARCHAR(255)], nom de jeune fille [VARCHAR(255) NULL]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour les contraintes d'unicité, qui peuvent s'appliquer à _plusieurs groupes_ d'attributs potentiellement _non disjoints_, la notation est un peu plus complexe. Voici un exemple [proposé par Fabien Duchateau](https://github.com/laowantong/mocodo/issues/79) :" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\tID\n", "\t\n", "\tNom\n", "\t1\n", "\tPrénom\n", "\t1\n", "\tAdresse\n", "\t\n", "\tMail\n", "\t2\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **CLIENT** (Réf. client, Nom u1, Prénom u1, Adresse, Mail u2)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c --shapes trebuchet # changement de police de caractères pour mieux distinguer les 1 des I\n", "CLIENT: Réf. client, 1_Nom, 1_Prénom, Adresse, 2_Mail" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "C'est l'occasion d'introduire quelques définitions :\n", "\n", "- On appelle **identifiant candidat** d'une entité tout sous-ensemble minimal d'attributs dont chaque occurrence est unique.\n", "- Parmi ces sous-ensembles, l'un est élu **identifiant** (tout court), souligné, et appelé à devenir clé primaire lors du passage au relationnel.\n", "- Les candidats malheureux sont appelés **identifiants alternatifs**.\n", "\n", "Ainsi, dans l'exemple précédent, l'ensemble des identifiants candidats de CLIENT est constitué de :\n", "\n", "- l'identifiant proprement dit _Réf. client_ ;\n", "- l'identifiant alternatif (_Nom_, _Prénom_) (dont on supposera pour les besoins de la cause qu'il assure l'unicité) ;\n", "- l'identifiant alternatif _Mail_." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Par défaut, dès la première déclaration d'un identifiant alternatif, Mocodo fait apparaître une gouttière à gauche des attributs de **toutes** les entités. Y sont portés :\n", "\n", "- un symbole « ID » (resp. « id ») pour l'identifiant fort (resp. faible).\n", "- des chiffres de 1 à 9 correspondant aux numéros des identifiants alternatifs." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La notation de Mocodo permet de faire de n'importe quels sous-ensembles d'attributs des identifiants alternatifs. Ci-dessous, le premier identifiant alternatif est le triplet (_bar_, _biz_, _quux_), le second (_biz_, _buz_, _quux_) et le troisième (_qux_, _quux_) :" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFOO\n", "\tfoo\n", "\tID\n", "\t\n", "\tbar\n", "\t1\n", "\tbiz\n", "\t1 2\n", "\tbuz\n", "\t2\n", "\tqux\n", "\t3\n", "\tquux\n", "\t1 2 3\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **FOO** (foo, bar u1, biz u1 u2, buz u2, qux u3, quux u1 u2 u3)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE FOO (\n", " PRIMARY KEY (foo),\n", " foo VARCHAR(42) NOT NULL,\n", " bar VARCHAR(42),\n", " biz VARCHAR(42),\n", " buz VARCHAR(42),\n", " qux VARCHAR(42),\n", " quux VARCHAR(42),\n", " UNIQUE (bar, biz, quux),\n", " UNIQUE (biz, buz, quux),\n", " UNIQUE (qux, quux)\n", ");\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t markdown:c sql --shapes trebuchet\n", "FOO: foo, 1_bar, 12_biz, 2_buz, 3_qux, 123_quux" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Examinons le cas exceptionnel où l'identifiant à souligner a un attribut commun avec un identifiant alternatif. Ce dernier devant être distinct et minimal, cela implique que l'identifiant est composite." ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntité 1\n", "\tfoo\n", "\t1 ID\n", "\t\n", "\tbar\n", "\tID\n", "\t\n", "\tbiz\n", "\t1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntité 2\n", "\tfoo\n", "\tID\n", "\t\n", "\tbar\n", "\t1\n", "\tbiz\n", "\t1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntité 3\n", "\tfoo\n", "\tID\n", "\t\n", "\tbar\n", "\t1 ID\n", "\t\n", "\tbiz\n", "\t1\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --shapes trebuchet\n", "Entité 1_: 1_foo, _bar, 1_biz\n", "Entité 2_: foo, 1_bar, 1_biz\n", "Entité 3_: foo, 01_bar, 1_biz" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Si l'attribut commun est en tête de liste (ici, _foo_, entité 1), rien ne change, on écrit `1_foo`.\n", "- S'il n'est pas en tête de liste (ici, _bar_, entité 2), l'écriture `1_bar` dénote déjà l'appartenance à un identifiant alternatif. Elle ne peut dénoter simultanément l'appartenance à l'identifiant à souligner.\n", "- C'est pourquoi on doit donc expliciter l'appartenance à l'identifiant à souligner qui, comme on l'a vu, est numéroté `0` (entité 3)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Un sous-cas demande encore réflexion : celui où le premier attribut ne fait pas partie de l'identifiant à souligner (entité 4). Si l'on veut alors que ce premier attribut appartienne à une clé alternative, il faut expliciter le `0` (entité 5)." ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntité 4\n", "\tfoo\n", "\t\n", "\tbar\n", "\tID\n", "\t\n", "\tbiz\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntité 5\n", "\tfoo\n", "\t1\n", "\tbar\n", "\tID\n", "\t\n", "\tbiz\n", "\t1\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --shapes trebuchet\n", "Entité 40: _foo, _bar, biz\n", "Entité 50: 01_foo, _bar, 1_biz" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En résumé, on explicite le `0`, soit pour **empêcher le soulignement du premier attribut**, soit pour **forcer le soulignement d'un attribut suivant**.\n", "\n", "Cela devrait vous rappeler quelque chose… Remplacez `0` par `_` dans la phrase précédente et vous retrouverez la règle que vous avez appris à connaître et à aimer : on explicite le `_`, soit pour empêcher le soulignement du premier attribut, soit pour forcer le soulignement d'un attribut suivant." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Gestion des entités faibles (identification relative)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans ce joli exemple dû à Idris NEUMANN, [_Initiation à la conception de bases de données relationnelles avec MERISE_](http://ineumann.developpez.com/tutoriels/merise/initiation-merise/#LIV-A), les renforcements successifs aboutissent à faire entrer l'identifiant de RUE dans celui de APPARTEMENT, alors même que ces entités sont séparées par non moins de trois associations :" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tComposer\n", "\t\n", "\t0,N\n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tAppartenir\n", "\t\n", "\t1,N\n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tSe situer\n", "\t\n", "\t0,N\n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAppartement\n", "\tnum appart.\n", "\t\n", "\tnb pièces\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tÉtage\n", "\tnum étage\n", "\t\n", "\tnb appartements\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tImmeuble\n", "\tnum immeuble\n", "\t\n", "\tnb étages\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tRue\n", "\tcode rue\n", "\t\n", "\tnom rue\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Appartement** (_#code rue_, _#num immeuble_, _#num étage_, num appart., nb pièces)\n", "- **Étage** (_#code rue_, _#num immeuble_, num étage, nb appartements)\n", "- **Immeuble** (_#code rue_, num immeuble, nb étages)\n", "- **Rue** (code rue, nom rue)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Appartement: num appart., nb pièces\n", "Composer, 0N Étage, _11 Appartement\n", "Étage: num étage, nb appartements\n", "Appartenir, 1N Immeuble, _11 Étage\n", "Immeuble: num immeuble, nb étages\n", "Se situer, 0N Rue, _11 Immeuble\n", "Rue: code rue, nom rue" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le résultat apparaît plus clairement sur le diagramme relationnel :" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t diagram --colors mondrian" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Une phase préliminaire de l'algorithme de passage au relationnel consiste à « renforcer » les entités faibles de façon à traiter uniquement des identifiants forts dans la suite. Cela permet la gestion des renforcements en cascade comme ci-dessus, ainsi que la détection des problèmes de renforcement cyclique :" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPick\n", "\t\n", "\t0,N\n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tZone\n", "\t\n", "\t1,N\n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tLand\n", "\ttrue\n", "\t\n", "\thold\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPeer\n", "\tfoot\n", "\t\n", "\tcity\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "Pick, 0N Land, _11 Peer\n", "Land: true, hold\n", "\n", "Peer: foot, city\n", "Zone, 1N Peer, _11 Land" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Mocodo Err.17 - Cycle d'entités faibles dans \"Land\", \"Peer\"." ] } ], "source": [ "%mocodo -i sandbox -t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Gestion de l'héritage" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La bonne gestion d'un héritage est primordiale, tant du point de vue de la préservation du patrimoine, que de l'optimisation des ressources financières, de la minimisation des conflits familiaux et du respect des dernières volontés du défunt. Je blague.\n", "\n", "Adaptons ici un exemple d'un [cours de Stéphane Crozat](https://stph.scenari-community.org/bdd/rel3.pdf). Nous reprenons sa terminologie et donnons en parallèle celle introduite par Martin Fowler dans _Patterns of Enterprise Application Architecture_, Addison-Wesley (2003).\n", "\n", "Ci-dessous, l'héritage est considéré comme _total_ (tout document est soit un ouvrage, soit une thèse, soit les deux). Crozat passe en revue trois mécanismes possibles pour le passage au relationnel." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "L'**héritage par référence** (en anglais, [_Class Table Inheritance_](http://martinfowler.com/eaaCatalog/classTableInheritance.html) ou _Table Per Type Inheritance_) se note en Mocodo par une flèche simple allant vers les filles. Une référence à la table-mère sera ajoutée dans chaque table-fille comme clé étrangère. L'intérêt de cette solution est en raison directe du nombre d'attributs non identifiants de l'entité-mère." ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tT\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDOCUMENT\n", "\tcote\n", "\t\n", "\ttitre\n", "\tauteur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tOUVRAGE\n", "\téditeur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTHÈSE\n", "\tdiscipline\n", "\tuniversité\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **DOCUMENT** (cote, titre, auteur)\n", "- **OUVRAGE** (_#cote_, éditeur)\n", "- **THÈSE** (_#cote_, discipline, université)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c\n", "DOCUMENT: cote, titre, auteur\n", " \n", "OUVRAGE: éditeur\n", "/T\\ DOCUMENT -> OUVRAGE, THÈSE\n", "THÈSE: discipline, université" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "L'**héritage par absorption dans les tables-filles** (en anglais, [_Concrete Table Inheritance_](http://martinfowler.com/eaaCatalog/concreteTableInheritance.html) ou _Table Per Concrete Inheritance_) se note en doublant la flèche :\n", "\n", "1. les attributs de la table-mère sont reproduits dans chacune des filles. Pour chaque document qui est à la fois un ouvrage et une thèse, il y aura donc duplication des triplets (cote, titre, auteur).\n", "2. la table-mère disparaît. Pour éviter de perdre les informations sur les documents qui ne seraient ni des ouvrages, ni des thèses, Mocodo lève une erreur si l'héritage n'est pas total (T ou XT)." ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tT\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDOCUMENT\n", "\tcote\n", "\t\n", "\ttitre\n", "\tauteur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tOUVRAGE\n", "\téditeur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTHÈSE\n", "\tdiscipline\n", "\tuniversité\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **OUVRAGE** (cote, titre, auteur, éditeur)\n", "- **THÈSE** (cote, titre, auteur, discipline, université)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c\n", "DOCUMENT: cote, titre, auteur\n", " \n", "OUVRAGE: éditeur\n", "/T\\ DOCUMENT => OUVRAGE, THÈSE\n", "THÈSE: discipline, université" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "L'**héritage par absorption dans la table-mère** (en anglais, [_Single Table Inheritance_](http://martinfowler.com/eaaCatalog/singleTableInheritance.html) ou _Table Per Hierarchy Inheritance_) se note par une flèche simple allant vers la mère. Les tables-filles disparaissent, et leurs attributs migrent dans la mère avec une contrainte d'optionalité (point d'interrogation en relationnel, `NULL` en SQL)." ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tX\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDOCUMENT\n", "\tcote\n", "\t\n", "\ttitre\n", "\tauteur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tOUVRAGE\n", "\téditeur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTHÈSE\n", "\tdiscipline\n", "\tuniversité\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **DOCUMENT** (cote, titre, auteur, discriminateur?, éditeur?, discipline?, université?)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE DOCUMENT (\n", " PRIMARY KEY (cote),\n", " cote VARCHAR(42) NOT NULL,\n", " titre VARCHAR(42),\n", " auteur VARCHAR(42),\n", " discriminateur ENUM ('OUVRAGE', 'THÈSE') NULL,\n", " editeur VARCHAR(42) NULL,\n", " discipline VARCHAR(42) NULL,\n", " universite VARCHAR(42) NULL\n", ");\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c sql\n", "DOCUMENT: cote, titre, auteur\n", "\n", "OUVRAGE: éditeur\n", "/X\\ DOCUMENT <- OUVRAGE, THÈSE: discriminateur [ENUM ('OUVRAGE', 'THÈSE')]\n", "THÈSE: discipline, université" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ci-dessus, nous avons donné comme « attribut » au triangle d'héritage un « discriminateur » de type `ENUM`. Cela ajoute à la table un champ optionnel ou non (selon que l'héritage est partiel ou total) permettant de déterminer à quel type concret d'une occurrence on a affaire.\n", "\n", "C'est par défaut un `UNSIGNED INT`, qui pourra prendre les valeurs :\n", "- 1 = OUVRAGE, 2 = THÈSE, pour l'héritage exclusif ;\n", "- 1 ($2^0$) = OUVRAGE, 2 ($2^1$) = THÈSE, 3 ($2^0 | 2^1$) = OUVRAGE + THÈSE, pour l'héritage non exclusif." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternativement à l'emploi d'un discriminateur, Mocodo peut ajouter un drapeau booléen par table-fille. Cela se note en doublant la flèche :" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tT\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDOCUMENT\n", "\tcote\n", "\t\n", "\ttitre\n", "\tauteur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tOUVRAGE\n", "\téditeur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTHÈSE\n", "\tdiscipline\n", "\tuniversité\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **DOCUMENT** (cote, titre, auteur, est ouvrage!, éditeur?, est these!, discipline?, université?)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE DOCUMENT (\n", " PRIMARY KEY (cote),\n", " cote VARCHAR(42) NOT NULL,\n", " titre VARCHAR(42),\n", " auteur VARCHAR(42),\n", " est_ouvrage BOOLEAN NOT NULL,\n", " editeur VARCHAR(42) NULL,\n", " est_these BOOLEAN NOT NULL,\n", " discipline VARCHAR(42) NULL,\n", " universite VARCHAR(42) NULL\n", ");\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c sql\n", "DOCUMENT: cote, titre, auteur\n", "\n", "OUVRAGE: éditeur\n", "/T\\ DOCUMENT <= OUVRAGE, THÈSE\n", "THÈSE: discipline, université" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Gestion de l'agrégation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Un agrégat simple autour d'une association dont toutes les cardinalités maximales sont N se traduit en relationnel comme la même association non agrégée, mais avec une clé primaire réduite :" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRÉSERVER\n", "\t\tdurée\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDATE\n", "\tdate\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCHAMBRE\n", "\tnum. chambre\n", "\t\n", "\tprix\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tid. client\n", "\t\n", "\tnom client\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **CHAMBRE** (num. chambre, prix)\n", "- **CLIENT** (id. client, nom client)\n", "- **RÉSERVER** (_#num. chambre_, date, _#id. client!_, durée)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c\n", "DATE: date\n", "RÉSERVER, /1N CLIENT, 1N CHAMBRE, 0N DATE: durée\n", "CHAMBRE: num. chambre, prix\n", "\n", "CLIENT: id. client, nom client" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Un agrégat simple autour d'une association dont la cardinalité maximale « de sortie » est 1 se traduit en relationnel comme la même association non agrégée, mais avec une contrainte d'unicité sur la clé étrangère. Ci-dessous, au lieu d'avoir simplement _num résa_ $\\implies$ (_num voilier_, _num semaine_), on a donc en plus (_num voilier_, _num semaine_) $\\implies$ _num résa_ :" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tOffrir\n", "\t\ttarif\n", "\t\n", "\t1,1\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tVoilier\n", "\tnum voilier\n", "\t\n", "\tlongueur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSemaine\n", "\tnum semaine\n", "\t\n", "\tdate début\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tRéservation\n", "\tnum résa\n", "\t\n", "\tarrhes\n", "\tdate résa\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Réservation** (num résa, arrhes, date résa, _#num voilier!_ u1, _#num semaine!_ u1, tarif)\n", "- **Semaine** (num semaine, date début)\n", "- **Voilier** (num voilier, longueur)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c\n", "Voilier: num voilier, longueur\n", "Offrir, /11 Réservation, 0N Voilier, 0N Semaine: tarif\n", "Semaine: num semaine, date début\n", "\n", "Réservation: num résa, arrhes, date résa" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le cas des agrégats multiples, plus rare, est étudié en annexe." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Ajustement des règles de passage" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Supprimer ou maintenir une table facultative" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le passage au relationnel supprime automatiquement les tables réduites à une clé primaire, pourvu qu'aucun composant de celle-ci ne soit clé étrangère. Si l'on souhaite maintenir certaines de ces tables, on préfixe d'un `+` l'entité concernée. Ainsi, ci-dessous, Date est supprimée, mais pas Thème." ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Aborder** (_#thème_, _#id. formation_)\n", " - Le champ _thème_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Thème_.\n", " - Le champ _id. formation_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Formation_.\n", "\n", "- **Animateur** (num. animateur, nom animateur)\n", " - Le champ _num. animateur_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Animateur_.\n", " - Le champ _nom animateur_ était déjà un simple attribut de l'entité _Animateur_.\n", "\n", "- **Formation** (id. formation, durée)\n", " - Le champ _id. formation_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Formation_.\n", " - Le champ _durée_ était déjà un simple attribut de l'entité _Formation_.\n", "\n", "- **Intervenir** (_#num. animateur_, _#id. formation_, date, nb heures)\n", " - Le champ _num. animateur_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Animateur_.\n", " - Le champ _id. formation_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Formation_.\n", " - Le champ _date_ fait partie de la clé primaire de la table. Sa table d'origine (_Date_) ayant été supprimée, il n'est pas considéré comme clé étrangère.\n", " - Le champ _nb heures_ était déjà un simple attribut de l'association _Intervenir_.\n", "\n", "- **Thème** (thème)\n", " - Le champ _thème_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Thème_.\n", "
\n", "----\n", "\n", "\n", "**NB.** La table _Date_ a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tIntervenir\n", "\t\tnb heures\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tAborder\n", "\t\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAnimateur\n", "\tnum. animateur\n", "\t\n", "\tnom animateur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFormation\n", "\tid. formation\n", "\t\n", "\tdurée\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tThème\n", "\tthème\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDate\n", "\tdate\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select mld mcd -t mld:e\n", "Animateur: num. animateur, nom animateur\n", "Intervenir, 1N Animateur, 1N Formation, 1N Date: nb heures\n", "Formation: id. formation, durée\n", "Aborder, 1N Thème, 1N Formation\n", "+Thème: thème\n", "\n", "Date: date\n", ":" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notez l'explication de la suppression (en NB) et celle de la perte du caractère étranger de l'attribut _date_ (dans la table Intervenir). Pour une discussion sur cette problématique, cf. [issue #66](https://github.com/laowantong/mocodo/issues/66)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Forcer une table pour éviter un champ optionnel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Une association de dépendance fonctionnelle ne donne normalement pas lieu à une création de table. Pour reprendre un exemple vu plus haut :" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEnvoyer\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntreprise\n", "\tid. entreprise\n", "\t\n", "\traison\n", "\tactivité\n", "\tadresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tParticipant\n", "\tid. inscrit\n", "\t\n", "\tnom\n", "\tadresse\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Entreprise** (id. entreprise, raison, activité, adresse)\n", "- **Participant** (id. inscrit, nom, adresse, _#id. entreprise?_)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c\n", "Entreprise: id. entreprise, raison, activité, adresse\n", "Envoyer, 0N Entreprise, 01 Participant\n", "Participant: id. inscrit, nom, adresse" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans le cas où les particuliers sont beaucoup plus nombreux que les employés d'entreprise, la clé étrangère _#id. entreprise_ est presque toujours à `NULL`. C'est une perte d'espace de stockage. On peut entourer l'association de crochets droits préfixer d'un `+` (à partir de la version 4) l'association pour forcer sa conversion en table. Mocodo produit alors une visualisation intermédiaire entre entité et association :" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEnvoyer\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntreprise\n", "\tid. entreprise\n", "\t\n", "\traison\n", "\tactivité\n", "\tadresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tParticipant\n", "\tid. inscrit\n", "\t\n", "\tnom\n", "\tadresse\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Entreprise** (id. entreprise, raison, activité, adresse)\n", "- **Envoyer** (_#id. inscrit_, _#id. entreprise!_)\n", "- **Participant** (id. inscrit, nom, adresse)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --select all -t mld:c --colors mondrian\n", "Entreprise: id. entreprise, raison, activité, adresse\n", "+Envoyer, 0N Entreprise, 01 Participant\n", "Participant: id. inscrit, nom, adresse" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cela permet du même coup d'éviter un champ optionnel, dont la gestion peut être délicate (notamment sous Microsoft SQL Server qui, au mépris du standard SQL, ne semble pas convaincu qu'une colonne `UNIQUE` peut contenir plus d'un `NULL` !)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Préciser le rôle d'une clé étrangère" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ce MCD modélise la soutenance de stage des étudiants, ainsi que la visite d'amitié et de contrôle dont les honore leur enseignant responsable :" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tSoutenir\n", "\t\tnote stage\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRépondre de\n", "\t\n", "\t0,N\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tÉtudiant\n", "\tnum. étudiant\n", "\t\n", "\tnom\n", "\tcoordonnées\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDate\n", "\tdate\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEnseignant\n", "\tnum. enseignant\n", "\t\n", "\tnom\n", "\tcoordonnées\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Enseignant** (num. enseignant, nom, coordonnées)\n", "- **Étudiant** (num. étudiant, nom, coordonnées, date 1, note stage, date 2, _#num. enseignant_)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Soutenir, 01 Étudiant, 0N Date: note stage\n", "Étudiant: num. étudiant, nom, coordonnées\n", "\n", "Date: date\n", "Répondre de, 0N Date, 11 Étudiant, 0N Enseignant\n", "Enseignant: num. enseignant, nom, coordonnées" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Force est de constater que la table Étudiant laisse quelque peu à désirer du point de vue sémantique :\n", "\n", "- on ne sait pas à quoi correspondent les attributs _date 1_ et _date 2_ : ils ne peuvent en tout cas certainement pas être laissés en l'état ;\n", "- ensuite, on pourrait avoir envie d'expliciter la raison pour laquelle un enseignant apparaît parmi les attributs d'un étudiant.\n", "\n", "Ces précisions peuvent être apportées en insérant, entre la cardinalité et l'entité des pattes appropriées, une note entre crochets, appelée **rôle**. Ces rôles seront utilisés pour compléter le nom des clés étrangères correspondantes. Cela permet de réintroduire la sémantique perdue lors de la disparition des associations de dépendance fonctionnelle par lesquelles elle ont migré :" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Enseignant** (num. enseignant, nom, coordonnées)\n", "- **Étudiant** (num. étudiant, nom, coordonnées, date soutenance, note stage, date visite resp, _#num. ens. resp._)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t mld\n", "Soutenir, 01 Étudiant, 0N [soutenance] Date: note stage\n", "Étudiant: num. étudiant, nom, coordonnées\n", "\n", "Date: date\n", "Répondre de, 0N [+ visite resp] Date, 11 Étudiant, 0N [-num. ens. resp.] Enseignant\n", "Enseignant: num. enseignant, nom, coordonnées" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La composition du rôle est entièrement paramétrable :\n", "\n", "- _date soutenance_ : par défaut, le rôle est concaténé à la clé, avec un séparateur dépendant du gabarit (espace par défaut, tiret bas pour SQL).\n", "- _date visite resp._ : `+` en préfixe supprime le séparateur.\n", "- _num. ens. resp._ : `-` en préfixe supprime le nom de la clé et le remplace par le rôle.\n", "\n", "Pour ne pas surcharger le dessin, le rôle n'est pas affiché à côté du lien, mais il apparaît au survol de la cardinalité." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Correction de la version 4.0.** Dans une association réflexive hiérarchique, c'est désormais le rôle porté par la patte `*N` qui sert à rétablir la sémantique. Auparavant c'était l'inverse, en contradiction avec le traitement des associations binaires et n-aires : si vous avez utilisé des rôles dans une association réflexive avant la version 4.0.0, vous devez donc les permutez pour que Mocodo les traite correctement. Reprenons la filiation patrilinéaire :" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tENGENDRER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tHOMME\n", "\tnum. SS\n", "\t\n", "\tnom\n", "\tprénom\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **HOMME** (num. SS, nom, prénom, _#num. SS père_)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "HOMME: num. SS, nom, prénom\n", "ENGENDRER, 0N [père] HOMME, 01 [fils] HOMME" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Orienter la migration dans les DF à double sens" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lorsque toutes les cardinalités d'une dépendance fonctionnelle sont 11 (ou toutes 01), le sens de migration est spécifié à coût zéro en référençant l'entité réceptrice en tête de la liste des entités énumérées dans la clause de définition de l'association :" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **AUTHENTICATION** (email, password hash, salt)\n", "- **USER** (user id, name, pseudo, _#email_)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE AUTHENTICATION (\n", " PRIMARY KEY (email),\n", " email VARCHAR(255) NOT NULL,\n", " password_hash BINARY(64),\n", " salt BINARY(16)\n", ");\n", "\n", "CREATE TABLE USER (\n", " PRIMARY KEY (user_id),\n", " user_id VARCHAR(8) NOT NULL,\n", " name VARCHAR(100),\n", " pseudo VARCHAR(100),\n", " email VARCHAR(255) NOT NULL,\n", " UNIQUE (email)\n", ");\n", "\n", "ALTER TABLE USER ADD FOREIGN KEY (email) REFERENCES AUTHENTICATION (email);\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t mld diagram sql --colors mondrian\n", "USER: user id [VARCHAR(8)], name [VARCHAR(100)], pseudo [VARCHAR(100)]\n", "DF, 11 USER, 11 AUTHENTICATION\n", "AUTHENTICATION: email [VARCHAR(255)], password hash [BINARY(64)], salt [BINARY(16)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans la version avec identification relative, c'est l'entité faible qui doit être placée en tête :" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **AUTHENTICATION** (_#user id_, email, password hash, salt)\n", "- **USER** (user id, name, pseudo)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE AUTHENTICATION (\n", " PRIMARY KEY (user_id, email),\n", " user_id VARCHAR(8) NOT NULL,\n", " email VARCHAR(255) NOT NULL,\n", " password_hash BINARY(64),\n", " salt BINARY(16)\n", ");\n", "\n", "CREATE TABLE USER (\n", " PRIMARY KEY (user_id),\n", " user_id VARCHAR(8) NOT NULL,\n", " name VARCHAR(100),\n", " pseudo VARCHAR(100)\n", ");\n", "\n", "ALTER TABLE AUTHENTICATION ADD FOREIGN KEY (user_id) REFERENCES USER (user_id);\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t mld diagram sql --colors mondrian\n", "USER: user id [VARCHAR(8)], name [VARCHAR(100)], pseudo [VARCHAR(100)]\n", "DF, _11 AUTHENTICATION, 11 USER\n", "AUTHENTICATION: email [VARCHAR(255)], password hash [BINARY(64)], salt [BINARY(16)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les traitements alternatifs (fusion en une seule table ou migration dans les deux sens) ne sont pas pris en charge par Mocodo. La dernière pratique ressortit à la phase d'optimisation et de dénormalisation de la base, qui est en dehors de sa juridiction. La seule chose que vous pouvez faire, à des fins d'illustration, est de reprendre le MLD généré pour y ajouter à la main la migration inverse. Notez cependant que les schémas logique et physique générés seront alors dépourvus de toute clé étrangère (et nécessiteront donc également d'être retouchés à la main)." ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tUSER\n", "\tuser id\n", "\t\n", "\tname\n", "\tpseudo\n", "\t#email\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAUTHENTICATION\n", "\temail\n", "\t\n", "\tpassword hash\n", "\tsalt\n", "\t#user id\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **AUTHENTICATION** (email, password hash, salt, user id)\n", "- **USER** (user id, name, pseudo, email)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE AUTHENTICATION (\n", " PRIMARY KEY (email),\n", " email VARCHAR(255) NOT NULL,\n", " password_hash BINARY(64),\n", " salt BINARY(16),\n", " user_id VARCHAR(42)\n", ");\n", "\n", "CREATE TABLE USER (\n", " PRIMARY KEY (user_id),\n", " user_id VARCHAR(8) NOT NULL,\n", " name VARCHAR(100),\n", " pseudo VARCHAR(100),\n", " email VARCHAR(42)\n", ");\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t mld sql --colors mondrian --select all\n", ":\n", "USER: user id [VARCHAR(8)], name [VARCHAR(100)], pseudo [VARCHAR(100)], #email > AUTHENTICATION > email\n", ":\n", "AUTHENTICATION: email [VARCHAR(255)], password hash [BINARY(64)], salt [BINARY(16)], #user id > USER > user id\n", ":" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Autres sorties relationnelles" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Graphe des dépendances" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La table A **dépend** de la table B lorsque A possède une clé étrangère qui est clé primaire de B. Cette notion est utile dans le cas où l'on doit importer une base de données à partir d'un ensemble de fichiers CSV (ou autres). Si l'on veut éviter de perdre le bénéfice du contrôles de clés étrangères (en faisant, p. ex. sous MySQL, `SET FOREIGN_KEY_CHECKS = 0`), il conviendra de lire ces fichiers dans un [ordre topologique](https://fr.wikipedia.org/wiki/Tri_topologique) quelconque. Mocodo peut générer un graphe des dépendances qui met cet ordre en évidence :" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_dependencies.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t dependencies --defer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Même si un tel graphe n'est pas forcément [sans circuits](https://fr.wikipedia.org/wiki/Graphe_orienté_acyclique), remplir les tables dans le sens de lecture des langues latines minimisera le recours à la désactivation des contraintes de clés étrangères." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Diagramme relationnel / DDL BDML" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "D'après [son site officiel](https://dbml.dbdiagram.io) :\n", "\n", "> DBML (_Database Markup Language_) est un DSL (langage dédié) open-source conçu pour définir et documenter les schémas et structures de base de données. Il vise la simplicité, la cohérence et la lisibilité.\n", "\n", "Mocodo parle maintenant DBML :" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_ddl.dbml\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```dbml\n", "Table \"Client\" {\n", " \"Réf. client\" VARCHAR(8) [pk, NOT NULL]\n", " \"Nom\" VARCHAR(255)\n", " \"Prénom\" VARCHAR(255)\n", " \"Adresse\" VARCHAR(255)\n", "}\n", "\n", "Table \"Commande\" {\n", " \"Num. commande\" VARCHAR(8) [pk, NOT NULL]\n", " \"Date\" DATE\n", " \"Montant\" DECIMAL(10,2)\n", " \"Réf. client\" VARCHAR(8) [NOT NULL]\n", "}\n", "\n", "Table \"Inclure\" {\n", " \"Num. commande\" VARCHAR(8) [NOT NULL]\n", " \"Réf. produit\" VARCHAR(8) [NOT NULL]\n", " \"Quantité\" INTEGER\n", " Indexes {\n", " (\"Num. commande\", \"Réf. produit\") [pk]\n", " }\n", "}\n", "\n", "Table \"Produit\" {\n", " \"Réf. produit\" VARCHAR(8) [pk, NOT NULL]\n", " \"Libellé\" VARCHAR(50)\n", " \"Prix unitaire\" DECIMAL(10,2)\n", "}\n", "\n", "Ref:\"Commande\".\"Réf. client\" > \"Client\".\"Réf. client\"\n", "Ref:\"Inclure\".\"Num. commande\" > \"Commande\".\"Num. commande\"\n", "Ref:\"Inclure\".\"Réf. produit\" > \"Produit\".\"Réf. produit\"\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t dbml --title CCP" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "À notre connaissance, il n'existe pas actuellement d'API publique de rendu des diagrammes DBML. Pour les visualiser, copiez-collez la sortie sur [Dbdiagram.io](https://dbdiagram.io/d/651077feffbf5169f065359b), ou installez un [plugin VS-Code](https://marketplace.visualstudio.com/items?itemName=nicolas-liger.dbml-viewer) :" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "1*1**1CLIENTRéf. clientVARCHAR(8)NomVARCHAR(255)PrénomVARCHAR(255)AdresseVARCHAR(255)COMMANDENum. commandeVARCHAR(8)DateDATEMontantDECIMAL(10,2)Réf. clientVARCHAR(8)INCLURENum. commandeVARCHAR(8)Réf. produitVARCHAR(8)QuantitéINTEGERPRODUITRéf. produitVARCHAR(8)LibelléVARCHAR(50)Prix unitaireDECIMAL(10,2)" ], "text/plain": [ "" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.SVG(\"../examples/dbml.svg\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Diagramme relationnel D2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[D2](https://d2lang.com) est un langage de description de diagrammes de différents types. La prise en charge des diagrammes relationnels est minimale, mais le projet est jeune (actuellement en version 0.6.1, open source depuis novembre 2022). Son point fort est TALA, son algorithme de plongement **propriétaire**, que vous pouvez tester [ici](https://play.d2lang.com/?script=1JVBb9owFMfv-RRPOW0HOKBWmqg0KaLZlgkS5oZKVVVVHjWTt8QOjjmgivv4Js3n8BebYljjBDBh7NJj5Pzfe3-_n99zvTsvjDvXKApitw_PDoAuqg_5PHmU-HtCHAA3xVLQ6SIhbh9uPTT44qF3F7338Fy1oA_3maApFsvHX2R5BTMuCP3Byo-HVRmD8RTwEjPZeRKcSiNS7_KyEcqIpLUJJcyWOoxiCCfD4cpZOY47iEbj6MZHFkNCFbMuZFS9TAlMeZrxXBWmuw-nmdsTDzN5RsD5AjNJpSrcPgRh7H_2kfZ2rdZjD8X-yA9tDWOLtAtPqsiwkCQlTFoqaV522agD0rJTugx_NB5Gd2p9LjP7cpM0S_hSO6_lPdHWvXGnV6-EPGwQ-RRNUBjYCNGpMsF_EmuS9lj8exRdSs6nVBWydi2nMiVVATO-EIySBlbjQP0e-C0fzAksJbQci6rYVV70NiSNUfTVP4ry0UbsA2lH9BejVzRBkDzjLC9zWUdbjSW3BMndkoT8bxO1Rm-FJc2AIPMFzZsM3ESDQK1j66NujWKzHwLTnDMtxc273g0MqXoRVpeHGxIj79YLhkPr_P8vC-3srpblmgu4a9QFnY9QjVnzxFhw3UOLTKu3j7r-01G53ltWvVHV7kDWytqW2vdXNYG7tXvc5N2MhPqJqajVYy21kaUiV6sq4nfOnaqK_dPC1p9qJrR3Z2ra-zvsYPuEjvs03ktb_mqSlgb_BAAA__8%3D&layout=tala&theme=5&sketch=0&). Celui-ci requérant une licence payante, si vous déférez le rendu, c'est le plongement par défaut (Dagre, celui de Mermaid) qui sera utilisé." ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_ddl.d2\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```d2\n", "\"Client\": { shape: sql_table\n", " \"Réf. client\": VARCHAR(8) {constraint: PK}\n", " \"Nom\": VARCHAR(255) \n", " \"Prénom\": VARCHAR(255) \n", " \"Adresse\": VARCHAR(255) \n", "}\n", "\n", "\"Commande\": { shape: sql_table\n", " \"Num. commande\": VARCHAR(8) {constraint: PK}\n", " \"Date\": DATE \n", " \"Montant\": DECIMAL(10,2) \n", " \"Réf. client\": VARCHAR(8) {constraint: [FK; NOT NULL]}\n", "}\n", "\n", "\"Inclure\": { shape: sql_table\n", " \"Num. commande\": VARCHAR(8) {constraint: [PK; FK]}\n", " \"Réf. produit\": VARCHAR(8) {constraint: [PK; FK]}\n", " \"Quantité\": INTEGER \n", "}\n", "\n", "\"Produit\": { shape: sql_table\n", " \"Réf. produit\": VARCHAR(8) {constraint: PK}\n", " \"Libellé\": VARCHAR(50) \n", " \"Prix unitaire\": DECIMAL(10,2) \n", "}\n", "\n", "\"Commande\".\"Réf. client\" -> \"Client\".\"Réf. client\"\n", "\"Inclure\".\"Num. commande\" -> \"Commande\".\"Num. commande\"\n", "\"Inclure\".\"Réf. produit\" -> \"Produit\".\"Réf. produit\"\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t d2" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_ddl.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t d2 --defer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Autres conversions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Diagramme de classes UML" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "À tout seigneur tout honneur, Mocodo peut traduire votre MCD en UML. Ce formalisme graphique n'a, curieusement, pas de DSL textuel officiel. Nous nous rabattons donc sur le standard de fait, PlantUML :" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_uml.puml\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```puml\n", "@startuml \"CCP\"\n", "\n", "!define Table(x) class \"x\" << (T,#FFFFFF) >>\n", "!define pk(x) x\n", "\n", "Table(\"Client\") {\n", " {field} + pk(Réf. client) VARCHAR(8)\n", " {field} + Nom VARCHAR(255)\n", " {field} + Prénom VARCHAR(255)\n", " {field} + Adresse VARCHAR(255)\n", "}\n", "\n", "\"Client\" \"1\" --- \"*\" \"Commande\": \"Passer\"\n", "\n", "Table(\"Commande\") {\n", " {field} + pk(Num. commande) VARCHAR(8)\n", " {field} + Date DATE\n", " {field} + Montant DECIMAL(10,2)\n", "}\n", "\n", "\"Commande\" \"*\" --- \"1..*\" \"Produit\": \"Inclure\"\n", "(\"Commande\", \"Produit\") .. \"Inclure\"\n", "Table(\"Inclure\") {\n", " {field} + Quantité INTEGER\n", "}\n", "\n", "Table(\"Produit\") {\n", " {field} + pk(Réf. produit) VARCHAR(8)\n", " {field} + Libellé VARCHAR(50)\n", " {field} + Prix unitaire DECIMAL(10,2)\n", "}\n", "\n", "@enduml\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp --title CCP -t uml:plantuml=- # '-' supprime un préambule par défaut qui ne nous intéresse pas ici" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_uml.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t uml --defer --colors brewer+5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ERD avec la convention _Look across_" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nouveauté de la version 4.0.** Force est de reconnaître que de nos jours, les MCD à la sauce Merise ne sont plus goûtés que par une poignée d'irréductibles Gaulois (et contractuellement leurs étudiants). Dans le cadre de son projet secret de domination planétaire, Mocodo commence à faire du pied à des notations mieux comprises du reste de l'univers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Notation de Chen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Un MCD peut être converti en un ERD (_Entity-Relationship Diagram_) dans la notation de Chen, sans ses attributs ou avec :" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t chen --defer --colors ocean" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t chen:attrs --defer --colors ocean" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Notation _crow's foot_" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans le même ordre d'idées, Mocodo peut générer des ERD dans l'astucieuse notation introduite en 1976 par Gordon Everest. Par défaut, le format du fichier intermédiaire est là encore Graphviz :" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_erd_crow.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t crow --defer --colors brewer+3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il est également possible de demander une sortie au format Mermaid. Vous en avez un exemple dans l'introduction, nous ne le répétons pas ici. Le DSL de Mermaid est de plus haut niveau, non encombré d'informations de style. Cependant, le format Graphviz peut être préféré pour plusieurs raisons :\n", "\n", "- on peut lui appliquer une palette de couleurs de Mocodo (cf. ci-dessus) ;\n", "- il gère les accents ;\n", "- il admet la virgule dans les types (Mermaid demande [actuellement](https://github.com/mermaid-js/mermaid/issues/1546) à transformer `DECIMAL(10,2)` en `DECIMAL(10-2)`) ;\n", "- il peut produire de meilleurs plongements (notamment en jouant sur la valeur de l'option `--seed`) ;\n", "- ceux-ci peuvent-être rectifiés à la main (quoique péniblement)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dictionnaire des données" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous pouvez extraire sous forme de table diverses informations sur les attributs de votre MCD.\n", "\n", "Un format possible est TSV. Dans ce cas, sous Jupyter Notebook, si la bibliothèque `pandas` est installée, elle sera rendue comme un _dataframe_ :" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_data_dict_3.tsv\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
 Entité ou associationLibellé de l'attributType
0ClientAdresseVARCHAR(255)
1ClientNomVARCHAR(255)
2ClientPrénomVARCHAR(255)
3ClientRéf. clientVARCHAR(8)
4CommandeDateDATE
5CommandeMontantDECIMAL(10,2)
6CommandeNum. commandeVARCHAR(8)
7InclureQuantitéINTEGER
8ProduitLibelléVARCHAR(50)
9ProduitPrix unitaireDECIMAL(10,2)
10ProduitRéf. produitVARCHAR(8)
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t data_dict:tsv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le format par défaut est Markdown. Vous pouvez préciser en sous-sous-option tout ou partie des colonnes suivantes dans l'ordre où vous les souhaitez :\n", "\n", "- `label`: le libellé de l'attribut ;\n", "- `type` : son type ou un descriptif (auquel cas il conviendra de changer le nom de la colonne) ;\n", "- `box` : le nom de l'entité ou association où il se trouve.\n", "\n", "Entourez ces noms de colonnes de balises Markdown pour les mettre en forme (pas d'incidence en TSV). Faites-les suivre de `=\"Nom de colonne personnalisé\"` pour éviter la valeur par défaut (dépendante de l'option `language`). Dans l'exemple ci-dessous, les sous-sous-options se décodent ainsi :\n", "\n", "- `**box**=\"Entité ou association\"` : boîtes en colonne 1, en-tête personnalisé, cellules en gras ;\n", "- `label` : libellé des attributs en colonne 2 ;\n", "- \\``type`\\``=`\\``Type`\\` : types en colonne 3, en-tête personnalisé et cellules dans une police non proportionnelle." ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_data_dict_3.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "| **Entité ou
association** | Libellé de l'attribut | ``Type de données`` |\n", "|:-----------------------------|:----------------------|:--------------------|\n", "| **Client** | Adresse | `VARCHAR(255)` |\n", "| **\"** | Nom | `VARCHAR(255)` |\n", "| **\"** | Prénom | `VARCHAR(255)` |\n", "| **\"** | Réf. client | `VARCHAR(8)` |\n", "| **Commande** | Date | `DATE` |\n", "| **\"** | Montant | `DECIMAL(10,2)` |\n", "| **\"** | Num. commande | `VARCHAR(8)` |\n", "| **Inclure** | Quantité | `INTEGER` |\n", "| **Produit** | Libellé | `VARCHAR(50)` |\n", "| **\"** | Prix unitaire | `DECIMAL(10,2)` |\n", "| **\"** | Réf. produit | `VARCHAR(8)` |\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t data_dict:**box**=\"Entité ou
association\",label,`type`=`\"Type de données\"`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Aménités.**\n", "\n", "- Au format Markdown, les répétitions de cellules de la _première_ colonne sont remplacées par un guillemet itératif.\n", "- Le tableau est automatiquement trié selon sa première, puis éventuellement deuxième et troisième colonnes.\n", "- Si une seule colonne est demandée, la ligne d'en-tête n'est pas générée et, en Markdown, c'est une liste qui est produite." ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_data_dict_1.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- Adresse\n", "- Date\n", "- Libellé\n", "- Montant\n", "- Nom\n", "- Num. commande\n", "- Prix unitaire\n", "- Prénom\n", "- Quantité\n", "- Réf. client\n", "- Réf. produit\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t data_dict:label" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nouveautés de la version 4.0.**\n", "- Sélection, ordre et nommage des colonnes, mise en forme Markdown, tri, guillemets itératifs.\n", "- Remplacement par un algorithme dédié du gabarit relationnel utilisé (abusivement) dans les versions antérieures." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### URL de partage" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous pouvez demander à encoder le texte-source de votre MCD dans un lien vers Mocodo online, qui le composera automatiquement dans la zone d'entrée :" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_url.url\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "https://www.mocodo.net/?mcd=eNptkMEKgkAQhu89xRwLJDQQwtuySgm5mEgX6bDlBgu6xrYLvZLP4Ys1ahZYt_m_Yeb_Z2glhTIBZF17W8N1UFCcSEb3JFtuV2cHWFN_ycb3e5bqrlV_OCm1eDzEjC9SjlA74DKgg4UDnge0qWuuSrGYigCYrTHFW85yhNwgCkkeoUgaZXgfNYxonJDD0nOdDTrF6lpZLXA_--wffFPdlFbipUeLc9J0LRQxy6NdlGG-qTm-4T7Kmf9BXkRV9XMT9t3xF_IJVknDpRY_gV4LRG5Z\n", "\n" ] } ], "source": [ "%mocodo -i ccp -t share" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Mocodo pour la pédagogie" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Vue en extension" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Un chiffre ou tiret bas à la fin du nom d'une boîte (entité ou association) est utilisé en interne pour distinguer cette boîte des autres, mais n'est pas affiché dans le diagramme conceptuel. Cela peut servir à produire une vue en extension d'un MCD.\n", "\n", "Voici par exemple le MCD que j'utilise en cours pour introduire la notion d'entité faible (à gauche, vue en compréhension, à droite vue en extension):" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,N\n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t \n", "\t \n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t \n", "\t \n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t \n", "\t \n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tŒUVRE\n", "\tcote\n", "\t\n", "\ttitre\n", "\tdate de publication\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tŒUVRE\n", "\t612.NAT.34\n", "\t\n", "\tJ’apprends à lire à mes souris blanches\n", "\tmai 1975\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEXEMPLAIRE\n", "\tnuméro d’exemplaire\n", "\t\n", "\tétat\n", "\tdate d’achat\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEXEMPLAIRE\n", "\t1\n", "\t\n", "\tbon état\n", "\t12/6/1975\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEXEMPLAIRE\n", "\t2\n", "\t\n", "\tbon état\n", "\t1/8/1977\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEXEMPLAIRE\n", "\t3\n", "\t\n", "\treliure rongée\n", "\t3/4/2005\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "ŒUVRE1: cote, titre, date de publication\n", ":::\n", "ŒUVRE2: 612.NAT.34, J'apprends à lire à mes souris blanches, mai 1975\n", ":\n", " \n", "DF, 1N ŒUVRE1, _11 EXEMPLAIRE1\n", "::\n", "DF, XX ŒUVRE2, XX EXEMPLAIRE2\n", "DF, XX ŒUVRE2, XX EXEMPLAIRE3\n", "DF, XX ŒUVRE2, XX EXEMPLAIRE4\n", "\n", "EXEMPLAIRE1: numéro d'exemplaire, état, date d'achat\n", "::\n", "EXEMPLAIRE2: 1, bon état, 12/6/1975\n", "EXEMPLAIRE3: 2, bon état, 1/8/1977\n", "EXEMPLAIRE4: 3, reliure rongée, 3/4/2005" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mocodo n'interdit pas la conversion en relationnel d'un tel MCD, mais celle-ci n'a aucun sens." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si l'on veut garder les cardinalités sans les afficher, on peut les préfixer d'un `-`. Le résultat de la conversion en relationnel peut alors être interprété comme l'ensemble des lignes des différentes tables." ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t \n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t \n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t \n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tŒUVRE\n", "\t612.NAT.34\n", "\t\n", "\tJ’apprends à lire à mes souris blanches\n", "\tmai 1975\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEXEMPLAIRE\n", "\t1\n", "\t\n", "\tbon état\n", "\t12/6/1975\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEXEMPLAIRE\n", "\t2\n", "\t\n", "\tbon état\n", "\t1/8/1977\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEXEMPLAIRE\n", "\t3\n", "\t\n", "\treliure rongée\n", "\t3/4/2005\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **EXEMPLAIRE** (_#612.NAT.34_, 1, bon état, 12/6/1975)\n", "- **EXEMPLAIRE** (_#612.NAT.34_, 2, bon état, 1/8/1977)\n", "- **EXEMPLAIRE** (_#612.NAT.34_, 3, reliure rongée, 3/4/2005)\n", "- **ŒUVRE** (612.NAT.34, J'apprends à lire à mes souris blanches, mai 1975)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "ŒUVRE: 612.NAT.34, J'apprends à lire à mes souris blanches, mai 1975\n", " \n", "DF, -1N ŒUVRE, -_11 EXEMPLAIRE1\n", "DF, -1N ŒUVRE, -_11 EXEMPLAIRE2\n", "DF, -1N ŒUVRE, -_11 EXEMPLAIRE3\n", "\n", "EXEMPLAIRE1: 1, bon état, 12/6/1975\n", "EXEMPLAIRE2: 2, bon état, 1/8/1977\n", "EXEMPLAIRE3: 3, reliure rongée, 3/4/2005" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## MCD interactif" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Afficher des explications au survol" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les débutants ont souvent des doutes sur la sémantique de telle ou telle cardinalité. Cette information peut être incluse dans le texte-source en annotant les pattes correspondantes. Survolez les cardinalités du MCD ci-dessous pour faire apparaître leur description." ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tInclure\n", "\t\tquantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tParrainer\n", "\t\tdate parrainage\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProduit\n", "\tréf. produit\n", "\t\n", "\tlibellé\n", "\tprix unitaire\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tnum. commande\n", "\t\n", "\tdate\n", "\tmontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tréf. client\n", "\t\n", "\tnom\n", "\tadresse\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N [Une commande inclut au moins un produit.] Commande, 0N [Un produit peut être commandé un nombre quelconque de fois.] Produit: quantité\n", "Commande: num. commande, date, montant\n", "DF, 0N [Un client peut passer zéro (prospect) ou plusieurs commandes.] Client, 11 [Une commande est passée par un et un seul client.] Commande\n", "Client: réf. client, nom, adresse\n", "Parrainer, 01 [Un client peut avoir été parrainé ou non.] Client, 0N [Un client peut parrainer d'autres clients.] Client : date parrainage" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nouveauté de la version 4.3.** Avec l'option `-t prompt:cards`, Mocodo compose un _prompt_ à copier-coller sous ChatGPT (ou autre) pour compléter le MCD avec de telles descriptions. Ce _prompt_ est trop long pour être reproduit ici : il consiste en une présentation de la syntaxe de Mocodo, des instructions sur le travail demandé, plusieurs exemples corrigés, et enfin le MCD à compléter. Voici par exemple les explications générées par [DeepSeek](https://chat.deepseek.com) pour le MCD de la page d'accueil de Mocodo online :" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tDIRIGER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tREQUÉRIR\n", "\t\tqté requise\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCOMPOSER\n", "\t\tquantité\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tFOURNIR\n", "\t\tqté fournie\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEMPLOYER\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTRAVAILLER\n", "\t\n", "\t0,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCONTRÔLER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAYANT-DROIT\n", "\tnom ayant-droit\n", "\t\n", "\tlien\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPIÈCE\n", "\tréf. pièce\n", "\t\n", "\tlibellé pièce\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEMPLOYÉ\n", "\tmatricule\n", "\t\n", "\tnom employé\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPROJET\n", "\tnum. projet\n", "\t\n", "\tnom projet\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDÉPARTEMENT\n", "\tnum. département\n", "\t\n", "\tnom département\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSOCIÉTÉ\n", "\tnum. société\n", "\t\n", "\traison sociale\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "AYANT-DROIT: nom ayant-droit, lien\n", "DIRIGER, 0N [Un employé peut diriger zéro ou un projet.] EMPLOYÉ, 01 [Un projet est dirigé par au plus un employé.] PROJET\n", "REQUÉRIR, 1N [Un projet requiert au moins une pièce.] PROJET, 0N [Une pièce peut être requise par plusieurs projets.] PIÈCE: qté requise\n", "PIÈCE: réf. pièce, libellé pièce\n", "COMPOSER, 0N [Une pièce peut être composée de plusieurs autres pièces.] PIÈCE, 0N [Une pièce peut entrer dans la composition de plusieurs autres pièces.] PIÈCE: quantité\n", "\n", "DF, _11 [Un ayant-droit est associé à un et un seul employé.] AYANT-DROIT, 0N [Un employé peut avoir plusieurs ayants-droit.] EMPLOYÉ\n", "EMPLOYÉ: matricule, nom employé\n", "PROJET: num. projet, nom projet\n", "FOURNIR, 1N [Un projet est fourni par au moins une société.] PROJET, 1N [Une pièce est fournie par au moins une société.] PIÈCE, 1N [Une société fournit au moins une pièce pour un projet.] SOCIÉTÉ: qté fournie\n", "\n", "DÉPARTEMENT: num. département, nom département\n", "EMPLOYER, 11 [Un employé appartient à un et un seul département.] EMPLOYÉ, 1N [Un département emploie au moins un employé.] DÉPARTEMENT\n", "TRAVAILLER, 0N [Un employé peut travailler sur plusieurs projets.] EMPLOYÉ, 1N [Un projet implique au moins un employé.] PROJET\n", "SOCIÉTÉ: num. société, raison sociale\n", "CONTRÔLER, 0N< [Une société peut en contrôler une autre.] SOCIÉTÉ, 01 [Une société est contrôlée par au plus une autre société.] SOCIÉTÉ" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le résultat est en général plus qu'honorable, mais demande à être relu attentivement pour vérifier l'adhérence au MCD original, corriger d'éventuelles hallucinations ou apporter des précisions. Ci-dessus, en l'occurrence, une société peut en contrôler _plusieurs autres_ (et non _une autre_)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Différence syntaxique entre rôles et explications.** Remarquez que la syntaxe est la même que pour les rôles, qui sont en plus utilisés lors du passage au relationnel. Comment Mocodo fait-il la différence ? En appliquant les règles suivantes :\n", "\n", "- si la note de patte commence par `+` ou `-`, ou qu'elle ne contient aucun espace, c'est un rôle.\n", "- sinon, c'est une description de cardinalité." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Explication de contraintes.** Le même principe s'applique aux contraintes (survolez le Ⓘ) :" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tI\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tFournir\n", "\t\tquantité\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRequérir\n", "\t\tquantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjet\n", "\tnum. projet\n", "\t\n", "\tnom projet\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSociété\n", "\tnum. société\n", "\t\n", "\traison sociale\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPièce\n", "\tréf. pièce\n", "\t\n", "\tlibellé pièce\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "Projet: num. projet, nom projet\n", ":\n", "Fournir, 1N Projet, 1N Pièce, 1N Société: quantité\n", "Société: num. société, raison sociale\n", "\n", "Requérir, 1N Projet, 0N Pièce: quantité\n", ":\n", "Pièce: réf. pièce, libellé pièce\n", "\n", "(I) [Toute pièce fournie doit avoir été requise.] ..Pièce, ->Requérir, --Fournir, Projet" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Limitations.**\n", "- Non pris en charge par les éditeurs de SVG comme Inkscape.\n", "- Ne semble pas fonctionner dans une page HTML statique (comme la version HTML de ce document sous GitHub).\n", "- Nécessite de faire confiance à un notebook (Trust notebook) pour s'afficher à la réouverture." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dévoiler un MCD par étapes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nouveauté de la version 3.** Il est possible de faire apparaître progressivement les différentes « boîtes » constituant un MCD. Pour cela, il suffit d'indenter (décaler vers la droite à l'aide d'espaces ou de tabulations) au moins une ligne. Les éléments correspondants seront alors répartis sur autant de « calques » qu'il y a de niveaux d'indentations." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Voici par exemple un exercice consistant en la description du « réel perçu » d'une entreprise de VPC :\n", "\n", "> 1. Un produit est connu par une référence, un libellé et un prix unitaire.\n", "> 1. Toute commande inclut un produit ou plusieurs, chacun en une certaine quantité.\n", "> 1. Un client peut passer zéro (_client potentiel_) ou plusieurs commandes.\n", "> 1. Un client peut entrer dans la base par parrainage d'un autre client.\n", "\n", "L'enseignant peut le présenter pas à pas en suivant les étapes de l'énoncé :" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tParrainer\n", "\t\tdate parrainage\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tInclure\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProduit\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --colors ocean\n", " Parrainer, 01 Client, 0N Client : date parrainage\n", "Produit: Réf. produit, Libellé, Prix unitaire\n", " Inclure, 1N Commande, 0N Produit: Quantité\n", " \n", " Client: Réf. client, Nom, Prénom, Adresse\n", " DF, 0N Client, 11 Commande\n", " Commande: Num. commande, Date, Montant" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Remarques.**\n", "- Pas de règle sur la taille de l'indentation. Pour Mocodo, autant de niveaux d'indentation distincts, autant de calques.\n", "- Les différents calques sont codés directement dans le SVG. L'interaction ne nécessite donc aucun logiciel spécifique.\n", "- Sous Mocodo online, le MCD est toujours présenté entièrement dévoilé. Cela permet de voir directement le résultat d'une modification du texte-source.\n", "- Pour ajouter facilement de l'interactivité à un MCD existant :\n", " 1. commencez par indenter au maximum toutes les lignes ;\n", " 1. effacez l'indentation des lignes du premier calque ;\n", " 1. décalez votre curseur de $n$ caractères vers la droite ;\n", " 1. placez-vous tour à tour sur les lignes à intégrer au deuxième calque et effacez les espaces surnuméraires ;\n", " 1. recommencez à l'étape 3 jusqu'au dernier calque.\n", " \n", " Depuis la version 4.0, l'éditeur de Mocodo online vous permet de créer des curseurs multiples, ce qui simplifie encore ces opérations.\n", "\n", "**Limitations.**\n", "- Pas de granularité plus fine que la ligne (entité ou association avec toutes ses pattes et cardinalités).\n", "- Pas de prise en charge des touches directionnelles. Cela serait sans doute possible, mais difficilement compatible avec la présence de plusieurs MCD interactifs sur une même page (comme dans cette documentation)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Éviter qu'une interaction sur un SVG ne s'applique à un autre.**\n", "\n", "Dans le cas très rare où plusieurs SVG interactifs générés **à partir du même texte-source** coexistent sur une même page web, une interaction opérée sur l'un s'applique également à tous les autres. Par exemple, cliquer sur l'un des ronds gris de l'une des figures ci-dessous agira sur les deux figures :" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFOO\n", "\tfoo\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tBAR\n", "\tbar\n", "\t\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "FOO: foo\n", " BAR: bar" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFOO\n", "\tfoo\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tBAR\n", "\tbar\n", "\t\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "FOO: foo\n", " BAR: bar" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ce problème trahit une « collision » : différents éléments du [DOM](https://fr.wikipedia.org/wiki/Document_Object_Model) se sont vus attribuer la même empreinte (obtenue par hachage du texte-source). La solution est de passer un entier discriminant qui, par concaténation, fera de ces empreintes de véritables identifiants." ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFOO\n", "\tfoo\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tBAR\n", "\tbar\n", "\t\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --uid_suffix 1\n", "FOO: foo\n", " BAR: bar" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFOO\n", "\tfoo\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tBAR\n", "\tbar\n", "\t\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --uid_suffix 2\n", "FOO: foo\n", " BAR: bar" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Explications du passage au relationnel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les gabarits de conversion en MLD (à savoir `html`, `markdown`, `latex` et `text`) admettent une sous-sous-option `e` qui accompagne le résultat par des explications détaillées du mécanisme de passage :" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Client** (Réf. client, Nom, Prénom, Adresse)\n", " - Le champ _Réf. client_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Client_.\n", " - Les champs _Nom_, _Prénom_ et _Adresse_ étaient déjà de simples attributs de l'entité _Client_.\n", "\n", "- **Commande** (Num. commande, Date, Montant, _#Réf. client_)\n", " - Le champ _Num. commande_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Commande_.\n", " - Les champs _Date_ et _Montant_ étaient déjà de simples attributs de l'entité _Commande_.\n", " - Le champ _Réf. client_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _Passer_ à partir de l'entité _Client_ en perdant son caractère identifiant.\n", "\n", "- **Inclure** (_#Num. commande_, _#Réf. produit_, Quantité)\n", " - Le champ _Num. commande_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Commande_.\n", " - Le champ _Réf. produit_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Produit_.\n", " - Le champ _Quantité_ était déjà un simple attribut de l'association _Inclure_.\n", "\n", "- **Produit** (Réf. produit, Libellé, Prix unitaire)\n", " - Le champ _Réf. produit_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Produit_.\n", " - Les champs _Libellé_ et _Prix unitaire_ étaient déjà de simples attributs de l'entité _Produit_.\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t markdown:e" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous avons essayé d'être aussi précis que possible, tout en « factorisant » avec soin les lignes consécutives suseptibles de l'être. Vous pouvez adapter ces explications à votre enseignement en modifiant une copie du gabarit `html-ce.yaml`, dont les autres sont dérivés." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## MCD à compléter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les MCD à trous sont des exercices classiques d'introduction aux bases de données." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Supprimer le marquage d'un identifiant" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour éviter le marquage automatique du premier attribut d'une entité comme identifiant, il suffit de le préfixer par un tiret bas (`_`) : ce caractère est donc un commutateur, qui souligne un attribut non souligné par défaut, et désouligne un attribut souligné par défaut." ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPASSER\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tINCLURE\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tNum. commande\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tRéf. produit\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "CLIENT: _Réf. client, Nom, Prénom, Adresse\n", "PASSER, 0N CLIENT, 11 COMMANDE\n", "COMMANDE: _Num. commande, Date, Montant\n", "INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité\n", "PRODUIT: _Réf. produit, Libellé, Prix unitaire" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Masquer un couple de cardinalités" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous pouvez masquer n'importe quelles cardinalités en les remplaçant par `XX` ou en les préfixant d'un `-` :" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPASSER\n", "\t\n", "\t \n", "\t \n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tINCLURE\n", "\t\tQuantité\n", "\t\n", "\t \n", "\t \n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "CLIENT: Réf. client, Nom, Prénom, Adresse\n", "PASSER, XX CLIENT, XX COMMANDE\n", "COMMANDE: Num. commande, Date, Montant\n", "INCLURE, -1N COMMANDE, -0N PRODUIT: Quantité\n", "PRODUIT: Réf. produit, Libellé, Prix unitaire" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nouveauté de la version 4.0.** Si la cardinalité comporte un et un seul `X`, l'autre caractère sera affiché tout seul." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Masquer un attribut" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous pouvez mettre deux virgules consécutives pour réserver la place d'un attribut manquant. Les espaces insécables sont préservés, ce qui permet de réserver plus d'espace horizontal, cf. ci-dessous premier attribut vide de INCLURE." ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPASSER\n", "\t\n", "\t \n", "\t \n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tINCLURE\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "CLIENT: Réf. client,,, \n", "PASSER, XX CLIENT, XX COMMANDE\n", "COMMANDE: , Date, Montant\n", "INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité,,,,\n", "PRODUIT: Réf. produit, Libellé, Prix unitaire" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Régression de la version 4.0.** Les espaces insécables ne sont plus préservés. Il n'y a donc plus d'autre moyen de réserver davantage d'espace horizontal que d'employer le style `blank` (paragraphe suivant)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Ne faire apparaître que le squelette du schéma conceptuel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous pouvez transformer en exercice à trous n'importe quel MCD en rendant complètement transparentes les couleurs des attributs, associations et cardinalités. Le style `blank` a été prédéfini à cet effet:" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPasser\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tInclure\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProduit\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp --colors=blank" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Attention, n'utilisez cette méthode que pour la projection : l'information textuelle est toujours présente, susceptible d'être sélectionnée et collée ailleurs. Vous pouvez bien sûr empêcher cette possibilité en convertissant le SVG en PNG, mais le plus simple est d'appliquer une réécriture `empty` :" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPasser\n", "\t\n", "\t \n", "\t \n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tInclure\n", "\t\n", "\t \n", "\t \n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProduit\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp.mcd\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "%%mocodo \n", "Client: , , , \n", "Passer, XX Client, XX Commande\n", "Commande: , , \n", "Inclure, XX Commande, XX Produit: \n", "Produit: , ,\n" ] } ], "source": [ "%mocodo -i ccp -t empty # équivalent de \"delete:types,notes,attrs,cards\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Obfuscation d'un MCD donné" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Obfusquer un MCD consiste à vider celui-ci de sa sémantique de surface en substituant des chaînes aléatoires à tous les libellés. Cela permet de créer des exemples d'illustration de telle ou telle notion « pure » sans risquer de voir son public se focaliser sur des détails-métier (c'est l'équivalent de la [variable méta-syntaxique](https://fr.wikipedia.org/wiki/Variable_métasyntaxique) `foobar` dans le contexte de la pédagogie de la programmation)." ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tLibellum\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tMulieres\n", "\t\tCarus\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tScriptam\n", "\tSpicula\n", "\t\n", "\tPersequitur\n", "\tDefensor\n", "\tFrontem\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFuit\n", "\tArtes\n", "\t\n", "\tLeporis\n", "\tSociis\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTheatro\n", "\tReperiri\n", "\t\n", "\tSerpentes\n", "\tAmissum\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp --seed=1 --select mcd -t obfuscate # raccourci pour \"obfuscate:labels\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En argument, vous pouvez ajouter le chemin d'un fichier texte quelconque où puiser les mots de substitution. Par exemple, le texte du `README` de ce projet :" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tNotebook\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEspèce\n", "\t\tTexte\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tVersion\n", "\tCardinalités\n", "\t\n", "\tDécomposition\n", "\tRelationnelle\n", "\tMaster\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAffichée\n", "\tBonus\n", "\t\n", "\tIncluant\n", "\tSvg\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSpécialisation\n", "\tÉgalement\n", "\t\n", "\tDélégué\n", "\tExportation\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t obfuscate:labels=../../README.md --seed=1 --select mcd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mocodo essaie d'abord de trouver ce fichier à l'endroit indiqué. En cas d'échec, il le cherche (avec extension `.txt` facultative) parmi ceux distribués avec le logiciel, à savoir:\n", "\n", "- `\"lorem.txt\"` (6464 mots) : le [faux-texte](https://fr.wikipedia.org/wiki/Faux-texte) le plus courant, augmenté d'une sélection des 10000 mots latins les plus courants [compilés par Kyle P. Johnson](https://kyle-p-johnson.com/blog/2015/04/23/most-common-greek-latin-words.html), le tout privé de ses doublons et des mots de moins de 3 lettres (`\"lorem_ipsum.txt\"` avant la version 4.0).\n", "- `\"fr.txt\"` (3396 mots) : une liste des 4000 mots français les plus courants, privée de ceux comportant une apostrophe ou moins de 4 lettres. Source : http://wortschatz.uni-leipzig.de/index.html _via_ [Wikitionary](https://fr.wiktionary.org/wiki/Wiktionnaire:Listes_de_fréquence). Nouveauté de la version 4.0.\n", "- `\"fr5.txt\"` (464 mots): la liste de `\"fr.txt\"`, restreinte aux mots de 5 lettres. Nouveauté de la version 4.0.\n", "- `\"en4.txt\"` (640 mots): une sélection (SFW) de mots anglais de quatre lettres (`\"four_letter_words.txt\"` avant la version 4.0).\n", "- `\"disparition.txt\"` (7489 mots) : le lexique du [célèbre roman lipogrammatique](https://fr.wikipedia.org/wiki/La_Disparition_(roman)) de Georges Perec, privé des mots de moins de 4 lettres.\n", "\n", "En cas de nouvel échec, il se rabat sur `\"lorem.txt\"`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**NB.** L'algorithme s'assure que la [distance de Damerau-Levenshtein](https://fr.wikipedia.org/wiki/Distance_de_Damerau-Levenshtein) entre deux libellés de substitution quelconques est d'au moins 3. En clair, cela signifie que, si vous donnez en examen un exercice de conversion en relationnel basé sur un tel MCD, les erreurs de transcription d'un étudiant stressé, inattentif, illettré, dyslexique, roublard, ou tout cela à la fois, ne devraient pas vous empêcher de lui octroyer les points qui lui reviennent." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Croissance stochastique" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous pouvez créer un MCD partiellement aléatoire à partir d'un MCD donné en lui ajoutant un nombre `n` d'associations (avec les entités nécessaires) :" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTernaire 9\n", "\t\n", "\t0,N\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTernaire 7\n", "\t\n", "\t1,1\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tPasser\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tInclure\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRéflexive 11\n", "\t\tat 11 1\n", "\t\n", "\t1,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRéflexive 10\n", "\t\tat 10 1\n", "\t\n", "\t1,1\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntité 8\n", "\tid 8 1\n", "\t\n", "\tat 8 2\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntité 6\n", "\tid 6 1\n", "\t\n", "\tat 6 2\n", "\tat 6 3\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProduit\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t grow:n=4 arrange --seed=1 --select mcd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Des sous-options pré-définies (données ci-dessous avec leur valeur par défaut après le `=`) permettent de spécifier finement le nombre désiré :\n", " - d'associations réflexives (`arity_1=2`) ;\n", " - d'associations ternaires (`arity_3=2`) ;\n", " - d'associations quaternaires (`arity_4=0`) ;\n", " - d'associations doubles, i.e., associant le même couple d'entités (`doubles=1`) ;\n", " - d'identifiants composites (`composites=1`) ;\n", " - d'attributs maximum par entité (`ent_attrs=4`) ;\n", " - d'attributs maximum par association (`assoc_attrs=2`).\n", "\n", "- On ne peut pas préciser directement le nombre d'associations binaires : si le nombre total des autres associations spécifiées n'arrivent pas à `n`, elles viennent en complément.\n", "\n", "- Des sous-options de forme plus ou moins libre décrivent les cardinalités. Par exemple, `_11-*N=2` créera deux entités faibles et `/*N-*N` un agrégat. Les associations de complément sont `*N-*N`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Avec la sous-option `from_scratch`, le MCD de départ est vide. À titre d'exemple, voici la transformation complexe invoquée par Mocodo online pour créer un MCD d'entraînement à la conversion au relationnel, accompagné de cette dernière. Notez la création de rôles par défaut : ils permettent de simuler le rétablissement de la sémantique des associations disparues." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%mocodo --seed=2 --mld -t grow:from_scratch,arity_3=1,_11-*N=1 obfuscate create:roles lower:roles arrange" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il n'est pas impossible que le MCD résultant soit incorrect (p. ex., apparition d'une identification relative circulaire), mais les contrôles effectués _a priori_ et _a posteriori_ devraient dans la majorité des cas produire quelque chose de raisonnable." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Génération d'un QR code" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Impressionnez votre public en accompagnant l'option `-t share` de `--defer` :" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_url.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t share --defer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Limitation.** Comme il faut un mobile pour scanner ce QR code, et que Mocodo online n'est pas vraiment adapté aux mobiles, l'intérêt réel de cette fonctionnalité est pour l'instant discutable." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Bibliothèque de MCD en ligne" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous avez créé avec Mocodo toute une bibliothèque de MCD, certains pour illustrer des points de votre cours, d'autres comme solutions d'exercices. À partir de la version 4.1, vous pouvez donner accès à tel ou tel de ces MCD en communiquant simplement son nom à vos étudiants." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Accès sous Mocodo online" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour comprendre le fonctionnement, accédons à un MCD de la bibliothèque par défaut : copiez-collez la chaîne `def_weak-974e` comme titre du MCD dans l'onglet _Entrée_ de [Mocodo online](https://www.mocodo.net). Le texte-source est mis à jour avec le contenu d'un fichier `def_weak-974e.mcd` stocké en ligne :\n", "\n", "![](examples/lib_demo.png)\n", "\n", "**Remarques.**\n", "- Vous avez sans doute déjà utilisé cette fonctionnalité sans le savoir en parcourant le tutoriel, dont les MCD, appelés `tuto-0000`, `tuto-0001`, etc., sont récupérés de la même manière.\n", "- Tous les noms de fichiers de la bibliothèque par défaut se terminent par un code de quatre caractères alphanumériques : cette disposition évite qu'un utilisateur ne tombe dessus par hasard, et protège certains MCD (typiquement, les solutions d'exercices) de la curiosité naturelle des étudiants. Au moment de la correction, je leur donne simplement le nom complet, et ils récupèrent directement la solution, ce qui dégage du temps pour des activités plus éducatives que la recopie.\n", "\n", "Bien sûr, la plupart des MCD de la bibliothèque par défaut n'intéressent que mon enseignement, et n'ont pas vocation à être diffusés au-delà du cercle très select des _happy few_ qui en bénéficient. \n", "Pour mettre votre propre bibliothèque à la disposition de vos propres étudiants :\n", "\n", "1. placez-la dans un répertoire dédié d'un serveur sur lequel vous avez les droits ;\n", "2. copiez-collez l'URL de ce répertoire dans le champ _Bibliothèque de MCD_ (onglet _Options_). Désormais, c'est à cette adresse que Mocodo essaiera de trouver les MCD quand vous mettrez à jour leur titre ;\n", "3. vos étudiants devront eux-mêmes avoir renseigné le champ en question, ce qui peut poser des problèmes à certains. Facilitez-leur la tâche en leur donnant le lien suivant (tout ce qui suit `lib=` doit bien sûr être remplacé par l'adresse de votre répertoire distant) :\n", "\n", " ```\n", " https://www.mocodo.net/?lib=https://your_server.com/path/to/your/mcd/directory\n", " ```\n", "\n", " Ils n'auront plus qu'à cliquer pour remplir automatiquement le champ approprié, lequel persistera dans les cookies de leur navigateur." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Accès sous terminal ou Jupyter Notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'option `--input` (ou `-i`) est surchargée pour aller chercher un MCD sur internet s'il ne se trouve pas dans le répertoire local. Pour commencer la démonstration, assurons-nous que le fichier en question n'existe pas en local :" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "path = Path(\"def_weak-974e.mcd\")\n", "path.unlink(missing_ok=True)\n", "path.is_file()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Récupérons-le de façon transparente dans la bibliothèque par défaut (avec ou sans extension `.mcd`) :" ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,N\n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tOEuvre\n", "\tCote oeuvre\n", "\t\n", "\tTitre\n", "\tDate parution\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tExemplaire\n", "\tNum. exemplaire\n", "\t\n", "\tEtat du livre\n", "\tDate d’achat\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i def_weak-974e.mcd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notez qu'au passage, pour le rendre disponible hors ligne, le fichier a été sauvegardé sur votre machine :" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "path.is_file()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**NB.** Si le paramètre de `-i` est un chemin de la forme `path/to/file.mcd` (avec ou sans extension `.mcd`), c'est la dernière partie du chemin (à savoir `file.mcd`) qui sera récupérée sur le serveur. Par contre, c'est le chemin complet qui déterminera l'emplacement de la sauvegarde." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour utiliser votre propre répertoire distant, passez son URL à l'option `--lib` :\n", "\n", "```\n", "mocodo -i your_diagram.mcd --lib https://your_server.com/path/to/your/mcd/directory\n", "```\n", "\n", "Nous vous conseillons d'ajouter cette option dans votre fichier de paramètres personnalisé `params.json`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Compléments sur l'aspect visuel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Styles" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plusieurs styles prédéfinis sont distribués avec l'application. Un style se définit comme la combinaison d'une palette de couleurs (répertoire `colors`) avec un dictionnaire de polices et de dimensions (répertoire `shapes`).\n", "\n", "Vous pouvez bien sûr créer vos propres styles en vous inspirant des fichiers fournis. Si vous êtes particulièrement content d'un style, soumettez-le pour inclusion dans une prochaine distribution." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Réglages.** Une approximation de la largeur des caractères des différentes polices a été pré-calculée sous macOS. Il est possible qu'elle soit inexacte, en particulier sous Windows ou Linux. Dans ce cas, en particulier, les traits de soulignement n'atteindront pas ou excéderont la largeur des libellés soulignés. Vous pouvez contourner le problème en appliquant un facteur multiplicatif avec l'argument `--adjust_width`)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Paramétrage du réarrangement automatique" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il est possible de demander à Mocodo de chercher tout seul une « bonne » permutation des définitions des boîtes, ce qui à la main deviendrait vite difficile.\n", "\n", "Le critère que nous avons retenu pour évaluer la qualité du tracé est double :\n", "\n", "1. les liens ne doivent pas se couper ;\n", "2. leur longueur cumulée doit être minimale.\n", "\n", "Actuellement, deux algorithmes d'arrangement sont fournis :\n", "\n", "- un algorithme exact (`bb`, pour _Branch & Bound_), qui ne trouve que des solutions satisfaisant au premier critère ;\n", "- un algorithme approché (`ga`, pour _Genetic Algorithm_), réservé aux cas où il est impossible d'y satisfaire. L'algorithme va alors chercher des solutions où les liens se coupent seulement le moins possible.\n", "\n", "**Remarque.** Mocodo met en œuvre une technique de réarrangement originale : en contraignant la position des boîtes aux intersections d'une grille invisible, il transforme un classique [problème de plongement](https://en.wikipedia.org/wiki/Graph_embedding) en un [problème d'affectation](https://fr.wikipedia.org/wiki/Problème_d%27affectation), ce qui permet de satisfaire de façon efficace à un certain nombre de contraintes esthétiques pertinentes (planarité, compacité, etc.)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous illustrerons les algorithmes et leurs paramètres sur le MCD d'accueil de Mocodo online." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le réarrangement automatique peut se faire sans contraintes, ou dans les limites d'une grille spécifiée par l'utilisateur." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Méthode exacte (_branch & bound_)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Arrangement non contraint (par défaut)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le réarrangement dit organique consiste à choisir une première boîte au hasard, puis à essayer d'agréger les autres sans se préoccuper de contenir le tout dans une grille prédéterminée :" ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEMPLOYER\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tDIRIGER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTRAVAILLER\n", "\t\n", "\t0,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tREQUÉRIR\n", "\t\tqté requise\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCONTRÔLER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tFOURNIR\n", "\t\tqté fournie\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCOMPOSER\n", "\t\tquantité\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAYANT-DROIT\n", "\tnom ayant-droit\n", "\t\n", "\tlien\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEMPLOYÉ\n", "\tmatricule\n", "\t\n", "\tnom employé\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDÉPARTEMENT\n", "\tnum. département\n", "\t\n", "\tnom département\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPROJET\n", "\tnum. projet\n", "\t\n", "\tnom projet\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSOCIÉTÉ\n", "\tnum. société\n", "\t\n", "\traison sociale\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPIÈCE\n", "\tréf. pièce\n", "\t\n", "\tlibellé pièce\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ../examples/landing --select mcd --seed=2 -t arrange" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cela donne un plongement sur une grille $5\\times4$, ce qui est loin d'être optimal. Cependant, l'arrangement organique fournit souvent un bon point de départ pour chercher soi-même une permutation plus esthétique ou mettant en évidence certaines propriétés du MCD." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Arrangement dans la grille courante" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le réarrangement automatique des boîtes peut s'opérer dans les limites de la grille courante ; c'est-à-dire que le MCD résultant aura (au plus) le même nombre de colonnes et de rangées que le texte de départ (ici, $4\\times5$) :" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEMPLOYER\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTRAVAILLER\n", "\t\n", "\t0,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tREQUÉRIR\n", "\t\tqté requise\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tDIRIGER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tFOURNIR\n", "\t\tqté fournie\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCONTRÔLER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCOMPOSER\n", "\t\tquantité\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEMPLOYÉ\n", "\tmatricule\n", "\t\n", "\tnom employé\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPROJET\n", "\tnum. projet\n", "\t\n", "\tnom projet\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDÉPARTEMENT\n", "\tnum. département\n", "\t\n", "\tnom département\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPIÈCE\n", "\tréf. pièce\n", "\t\n", "\tlibellé pièce\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAYANT-DROIT\n", "\tnom ayant-droit\n", "\t\n", "\tlien\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSOCIÉTÉ\n", "\tnum. société\n", "\t\n", "\traison sociale\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ../examples/landing --select mcd --seed=2 -t arrange:current" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Arrangement en privilégiant la largeur" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans un document paginé, on cherche en général à utiliser en priorité l'espace horizontal. La version 4.0 introduit à cet effet une option de réarrangement « en largeur d'abord ». Le MCD d'accueil s'y prête particulièrement bien :" ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tDIRIGER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tFOURNIR\n", "\t\tqté fournie\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCONTRÔLER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEMPLOYER\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTRAVAILLER\n", "\t\n", "\t0,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tREQUÉRIR\n", "\t\tqté requise\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCOMPOSER\n", "\t\tquantité\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAYANT-DROIT\n", "\tnom ayant-droit\n", "\t\n", "\tlien\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEMPLOYÉ\n", "\tmatricule\n", "\t\n", "\tnom employé\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPROJET\n", "\tnum. projet\n", "\t\n", "\tnom projet\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSOCIÉTÉ\n", "\tnum. société\n", "\t\n", "\traison sociale\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDÉPARTEMENT\n", "\tnum. département\n", "\t\n", "\tnom département\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPIÈCE\n", "\tréf. pièce\n", "\t\n", "\tlibellé pièce\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ../examples/landing --select mcd --seed=2 -t arrange:wide=8 --scale 0.8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ici, l'utilisateur a demandé $8$ boîtes en largeur (c'est aussi la valeur par défaut). L'algorithme commence par calculer le nombre de lignes minimal correspondant, sachant qu'il a $14$ boîtes à placer : c'est $\\lceil14/8\\rceil=2$. Il cherche donc un plongement sur une grille $8\\times2$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Arrangement dans une grille équilibrée minimale" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On peut chercher à faire tenir le MCD dans la plus petite grille possible, tout en maintenant entre hauteur et largeur un rapport « équilibré » (ou proche du [nombre d'or](https://fr.wikipedia.org/wiki/Nombre_d%27or)). Par exemple, un MCD de 13 boîtes (entités ou associations) peut tenir dans les grilles:\n", "\n", "- $13\\times1$;\n", "- $7\\times2$, ce qui laisse 1 case vide;\n", "- $5\\times3$, ce qui laisse 2 cases vides;\n", "- $4\\times4$, ce qui laisse 3 cases vides;\n", "- etc.\n", "\n", "Les deux premières grilles étant non équilibrées, on retiendra la plus petite des suivantes, de dimensions $5\\times3$.\n", "\n", "La table ci-dessous énumère les dimensions des grilles minimales d'équilibre supérieur à 0,5 pour tous les MCD comportant moins de 100 boîtes. On peut y vérifier par exemple que le MCD de taille 13 (en gras) se trouve effectivement aux coordonnées (5, 3)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "| | **1** | **2** | **3** | **4** | **5** | **6** | **7** | **8** | **9** | **10** | **11** | **12** | **13** |\n", "|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n", "| **1** | 1 | 2 | 3 | | | | | | | | | | |\n", "| **2** | | 4 | 5, 6 | | | | | | | | | | |\n", "| **3** | | | 7, 8, 9 | 10, 11, 12 | **13**, 14, 15 | | | | | | | | |\n", "| **4** | | | | 16 | 17, 18, 19, 20 | 21, 22, 23, 24 | 26, 27, 28 | | | | | | |\n", "| **5** | | | | | 25 | 29, 30 | 31, 32, 33, 34, 35 | 37, 38, 39, 40 | 43, 44, 45 | | | | |\n", "| **6** | | | | | | 36 | 41, 42 | 46, 47, 48 | 50, 51, 52, 53, 54 | 57, 58, 59, 60 | 65, 66 | | |\n", "| **7** | | | | | | | 49 | 55, 56 | 61, 62, 63 | 67, 68, 69, 70 | 73, 74, 75, 76, 77 | 82, 83, 84 | 91 |\n", "| **8** | | | | | | | | 64 | 71, 72 | 78, 79, 80 | 85, 86, 87, 88 | 92, 93, 94, 95, 96 | |\n", "| **9** | | | | | | | | | 81 | 89, 90 | 97, 98, 99 | | |\n", "| **10** | | | | | | | | | | 100 | | | |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le MCD d'accueil ayant déjà été arrangé avec l'option `-t arrange:balanced`, nous ne le reproduisons pas ici. Sachez cependant qu'au cas où, pour une raison ou une autre, la plus petite grille équilibrée ne convient pas, il est possible de passer à la $i^\\text{e}$ suivante en mettant $i$ en sous-sous-argument. Ici, pour $14$ boîtes, `balanced=1` permet donc de passer de la grille $5\\times3$ (prévue pour $13$, $14$ et $15$ boîtes) à la grille $4\\times4$ (prévue pour $16$ boîtes)." ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tEMPLOYER\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTRAVAILLER\n", "\t\n", "\t0,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tDIRIGER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tREQUÉRIR\n", "\t\tqté requise\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCONTRÔLER\n", "\t\n", "\t0,N\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCOMPOSER\n", "\t\tquantité\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tFOURNIR\n", "\t\tqté fournie\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEMPLOYÉ\n", "\tmatricule\n", "\t\n", "\tnom employé\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tAYANT-DROIT\n", "\tnom ayant-droit\n", "\t\n", "\tlien\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDÉPARTEMENT\n", "\tnum. département\n", "\t\n", "\tnom département\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPROJET\n", "\tnum. projet\n", "\t\n", "\tnom projet\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPIÈCE\n", "\tréf. pièce\n", "\t\n", "\tlibellé pièce\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSOCIÉTÉ\n", "\tnum. société\n", "\t\n", "\traison sociale\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ../examples/landing --select mcd --seed=1 -t arrange:balanced=1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " #### Limitations de la méthode exacte" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le réarrangement exact ne fonctionnera jamais sur les MCD non planaires ou n'admettant aucun plongement planaire dans les limites de la grille spécifiée (par les sous-options `current`, `wide` ou `balanced`).\n", "\n", "Rappelons qu'un graphe est dit [planaire](https://fr.wikipedia.org/wiki/Graphe_planaire) lorsqu'il en existe au moins un arrangement sans croisement. Le graphe non planaire comportant le plus petit nombre de liens est connu sous le doux nom de [$K_{3,3}$](https://www.rodhilton.com/2011/10/29/why-the-complete-bipartite-graph-k33-is-not-planar/) :" ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRHONCUS\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tSODALES\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tQUIS ENIM\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDIGNISSIM\n", "\tnec sem\n", "\t\n", "\tnunc\n", "\tvulputate\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tIMPERDIET\n", "\ta praesent\n", "\t\n", "\tnibh\n", "\tsemper\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTINCIDUNT\n", "\tfaucibus\n", "\t\n", "\torci\n", "\tcursus\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "DIGNISSIM: nec sem, nunc, vulputate\n", "IMPERDIET: a praesent, nibh, semper\n", "TINCIDUNT: faucibus, orci, cursus\n", "\n", "RHONCUS, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT\n", "SODALES, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT\n", "QUIS ENIM, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Son réarrangement par _Branch & bound_ échouera donc nécessairement :" ] }, { "cell_type": "code", "execution_count": 121, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Mocodo Err.9 - Impossible de calculer un plongement planaire satisfaisant la contrainte\n", "donnée." ] } ], "source": [ "%mocodo -i sandbox --seed=1 -t arrange:balanced" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Comme on voit, Mocodo ne cherche pas à savoir si la non-planarité est intrinsèque au graphe, ou résulte des dimensions de la grille imposée pour le plongement. Pour en avoir le cœur net, tentez un arrangement non contraint :" ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Mocodo Err.41 - Impossible de calculer un plongement planaire." ] } ], "source": [ "%mocodo -i sandbox --seed=1 -t arrange" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**NB.** Plusieurs paramètres permettent de régler le fonctionnement de l'algorithme exact (_Branch and Bound_) :\n", "\n", "- `call_limit` : nombre maximal d'appels pour une boîte de départ donnée (défaut: `10000`).\n", "- `min_objective` : meilleur objectif esthétique pour la mise en page (défaut: `0`).\n", "- `max_objective` : pire objectif esthétique pour la mise en page (défaut: `15`).\n", "\n", "Réservés aux spécialistes, ils ne sont pas décrits dans cette documentation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Méthode heuristique (algorithme génétique)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans tous les cas où la méthode exacte ne produit pas de résultat satisfaisant, on pourra se rabattre sur une heuristique qui, au lieu d'interdire les croisements, cherchera simplement à en minimiser le nombre." ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRHONCUS\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tSODALES\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tQUIS ENIM\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDIGNISSIM\n", "\tnec sem\n", "\t\n", "\tnunc\n", "\tvulputate\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tIMPERDIET\n", "\ta praesent\n", "\t\n", "\tnibh\n", "\tsemper\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTINCIDUNT\n", "\tfaucibus\n", "\t\n", "\torci\n", "\tcursus\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox.mcd\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "%%mocodo --seed=1\n", "DIGNISSIM: nec sem, nunc, vulputate\n", "RHONCUS, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT\n", "IMPERDIET: a praesent, nibh, semper\n", "\n", "SODALES, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT\n", "TINCIDUNT: faucibus, orci, cursus\n", "QUIS ENIM, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT\n" ] } ], "source": [ "%mocodo -i sandbox --seed=1 -t arrange:algo=ga" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'amélioration ne saute pas forcément aux yeux, mais il n'y a plus que trois croisements au lieu de neuf. Ce plongement constitue en tout cas un bon point de départ pour un réarrangement manuel. Il ne reste en effet plus qu'à insérer quelques boîtes invisibles:" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRHONCUS\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tSODALES\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tQUIS ENIM\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDIGNISSIM\n", "\tnec sem\n", "\t\n", "\tnunc\n", "\tvulputate\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tIMPERDIET\n", "\ta praesent\n", "\t\n", "\tnibh\n", "\tsemper\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTINCIDUNT\n", "\tfaucibus\n", "\t\n", "\torci\n", "\tcursus\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "RHONCUS, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT\n", "\n", "DIGNISSIM: nec sem, nunc, vulputate\n", ":::\n", "IMPERDIET: a praesent, nibh, semper\n", "\n", "SODALES, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT\n", ":::\n", "QUIS ENIM, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT\n", "\n", "TINCIDUNT: faucibus, orci, cursus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**NB.** Plusieurs paramètres permettent de régler le fonctionnement de l'algorithme génétique :\n", "\n", "- `population_size` : nombre d'individus à faire évoluer (défaut: `1000`).\n", "- `crossover_rate` : taux de croisement, entre 0 et 1 (défaut: `0.9`).\n", "- `mutation_rate` : taux de mutation, entre 0 et 1 (défaut: `0.06`).\n", "- `sample_size` : taille de l'échantillon pour les tournois (défaut: `7`).\n", "- `max_generations` : nombre maximal de générations (défaut: `300`).\n", "- `plateau` : nombre maximal de générations consécutives sans amélioration (défaut: `30`).\n", "\n", "Réservés aux spécialistes, ils ne sont pas décrits dans cette documentation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Amélioration du plongement" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Une entité liée à de nombreuses associations peut limiter ou même empêcher les possibilités de réarrangement sans croisements." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Par duplication d'entités réduites à leur identifiant" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lorsque cette entité est indépendante et réduite à son identifiant, elle est vouée à disparaître lors du passage au relationnel. Il n'y a donc aucun inconvénient à en créer plusieurs, et cela peut avoir l'avantage de faciliter (ou même de rendre possible) l'obtention d'une bonne mise en page.\n", "\n", "Prenons comme exemple un MCD extrait d'une [étude de François de Sainte Marie](https://fsmrel.developpez.com/basesrelationnelles/ullman-cthrsg/), et présenté comme suit :\n", "\n", "![](examples/ullman_cthrsg_mcd_sans_cif_80.png)\n", "\n", "Le graphe sous-jacent étant non planaire, des croisements sont inévitables (ici entre certaines pattes des associations SHR, CT et THR). Cependant, cette non-planarité résulte elle-même de la mise en jeu d'une même entité Période dans quatre associations triples. La monnayer en quatre entités Période numérotées augmente l'ordre du graphe, mais le rend planaire, le tout sans impact sur le schéma relationnel :" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tSHR\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCSG\n", "\t\n", "\t0,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCHS\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCHR\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCT\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTHR\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tÉtudiant\n", "\tid. étudiant\n", "\t\n", "\tnom étudiant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPériode\n", "\theure\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tNiveau\n", "\tniveau\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCours\n", "\tid. cours\n", "\t\n", "\tnom cours\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSalle\n", "\tid. salle\n", "\t\n", "\tnom salle\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPériode\n", "\theure\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPériode\n", "\theure\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProfesseur\n", "\tid. professeur\n", "\t\n", "\tnom professeur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPériode\n", "\theure\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **CHR** (heure, _#id. salle_, _#id. cours_)\n", "- **CHS** (heure, _#id. cours_, _#id. étudiant_)\n", "- **Cours** (id. cours, nom cours, _#id. professeur_)\n", "- **CSG** (niveau, _#id. cours_, _#id. étudiant_)\n", "- **Étudiant** (id. étudiant, nom étudiant)\n", "- **Professeur** (id. professeur, nom professeur)\n", "- **Salle** (id. salle, nom salle)\n", "- **SHR** (heure, _#id. salle_, _#id. étudiant_)\n", "- **THR** (heure, _#id. salle_, _#id. professeur_)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Étudiant: id. étudiant, nom étudiant\n", ":\n", ":\n", "SHR, 0N Période3, 0N Salle, 0N Étudiant\n", "Période3: heure\n", "\n", ":\n", "CSG, 0N Niveau, 1N Cours, 1N Étudiant\n", "Niveau: niveau\n", ":\n", "\n", "CHS, 0N Période2, 0N Cours, 0N Étudiant\n", "Cours: id. cours, nom cours\n", "CHR, 0N Période4, 0N Salle, 0N Cours\n", "Salle: id. salle, nom salle\n", "\n", "Période2: heure\n", "CT, 11 Cours, 1N Professeur\n", "Période4: heure\n", ":\n", ":\n", "\n", ":\n", "Professeur: id. professeur, nom professeur\n", ":\n", "THR, 0N Période1, 0N Salle, 0N Professeur\n", "Période1: heure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ce genre de problème (et sa solution) se présente typiquement lorsqu'une entité DATE réduite à un identifiant _date_ est associée à un grand nombre d'entités n'ayant rien à voir entre elles. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Par suppression de ces mêmes entités" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inversement, toujours étant donné que ce type d'entité est destiné à disparaître lors du passage au relationnel, on peut décider de les supprimer par anticipation. Pour cela, il faut accepter que les associations puissent être porteuses d'identifiants (ce qui n'est pas prévu par Merise). Cela permet d'obtenir un schéma conceptuel beaucoup plus lisible, toujours sans impact sur le schéma relationnel :" ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCSG\n", "\t\tniveau\n", "\t\t\n", "\t\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCT\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCHS\n", "\t\theure\n", "\t\t\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCHR\n", "\t\theure\n", "\t\t\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tSHR\n", "\t\theure\n", "\t\t\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTHR\n", "\t\theure\n", "\t\t\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCours\n", "\tid. cours\n", "\t\n", "\tnom cours\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProfesseur\n", "\tid. professeur\n", "\t\n", "\tnom professeur\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tÉtudiant\n", "\tid. étudiant\n", "\t\n", "\tnom étudiant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSalle\n", "\tid. salle\n", "\t\n", "\tnom salle\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **CHR** (_#id. salle_, _#id. cours_, heure)\n", "- **CHS** (_#id. cours_, _#id. étudiant_, heure)\n", "- **Cours** (id. cours, nom cours, _#id. professeur_)\n", "- **CSG** (_#id. cours_, _#id. étudiant_, niveau)\n", "- **Étudiant** (id. étudiant, nom étudiant)\n", "- **Professeur** (id. professeur, nom professeur)\n", "- **Salle** (id. salle, nom salle)\n", "- **SHR** (_#id. salle_, _#id. étudiant_, heure)\n", "- **THR** (_#id. salle_, _#id. professeur_, heure)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "CSG, 1N Cours, 1N Étudiant: _niveau\n", ":\n", "Cours: id. cours, nom cours\n", "CT, 11 Cours, 1N Professeur\n", "\n", ":\n", "CHS, 0N Cours, 0N Étudiant: _heure\n", "CHR, 0N Salle, 0N Cours: _heure\n", "Professeur: id. professeur, nom professeur\n", " \n", "Étudiant: id. étudiant, nom étudiant\n", "SHR, 0N Salle, 0N Étudiant: _heure\n", "Salle: id. salle, nom salle\n", "THR, 0N Salle, 0N Professeur: _heure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notez qu'avec cette technique, les cardinalités minimales des pattes distinguées par les entités supprimées sont perdues dès le niveau conceptuel (sachant qu'elles l'auraient été de toute façon lors du passage au relationnel)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Comme la notion d'identifiant explicite d'association enfreint le standard Merise, et risque de semer la discorde entre professeurs et étudiants, elle est par défaut désactivée sous Mocodo online. En ligne de commande, elle est interdite à la demande :" ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Mocodo Err.52 - L'association « CSG » ne peut pas avoir d'identifiant." ] } ], "source": [ "%mocodo -i sandbox --no_assoc_ids" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Ajustement de l'aspect de certains éléments" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Format des cardinalités" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Par défaut, les cardinalités sont séparées par une virgule, et celles des entités faibles soulignées, mais cela peut être changé :" ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t(1/N)\n", "\t(1/1)(R)\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tŒUVRE\n", "\tCote œuvre\n", "\t\n", "\tTitre\n", "\tDate parution\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEXEMPLAIRE\n", "\tNum. exemplaire\n", "\t\n", "\tÉtat du livre\n", "\tDate d’achat\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --card_format=({min_card}/{max_card}) --strengthen_card=(1/1)(R)\n", "ŒUVRE: Cote œuvre, Titre, Date parution\n", "DF, 1N ŒUVRE, _11 EXEMPLAIRE\n", "EXEMPLAIRE: Num. exemplaire, État du livre, Date d'achat" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Format des clés étrangères" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La mise en forme des clés étrangères affichées dans le diagramme relationnel peut être paramétrée, par exemple pour enlever le préfixe par défaut (`#`) :" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "ccp_mld.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i ccp -t diagram --fk_format={label}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Symbole de dépendance fonctionnelle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il est possible d'activer l'encerclement d'un autre sigle que DF. C'est ce sigle qui devra alors apparaître en entrée, par exemple:" ] }, { "cell_type": "code", "execution_count": 130, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tCIF\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tINCLURE\n", "\t\tQuantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tNum. commande\n", "\t\n", "\tDate\n", "\tMontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tRéf. produit\n", "\t\n", "\tLibellé\n", "\tPrix unitaire\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --df CIF\n", "CLIENT: Réf. client, Nom, Prénom, Adresse\n", "CIF, 0N CLIENT, 11 COMMANDE\n", "COMMANDE: Num. commande, Date, Montant\n", "INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité\n", "PRODUIT: Réf. produit, Libellé, Prix unitaire" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Comme le cercle est alors un peu plus grand, on peut vouloir régler (_a priori_ une fois pour toutes) le ratio défini dans l'objet `shapes` du fichier appelé par défaut `sandbox_geo.json`):\n", "\n", "```\n", "\"df_text_height_ratio\" : 1.00,\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Basculement des cardinalités et inflexion des pattes rectilignes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mocodo est capable de détecter certaines configurations de pattes dont les cardinalités présentent un risque élevé de collision. Il procède alors à deux types d'ajustements:\n", "\n", "1. [Issue 25](https://github.com/laowantong/mocodo/issues/25). Les cardinalités d'une patte verticale ou horizontale sont envoyées de l'autre côté de la patte. Par exemple, dans le plongement du MCD précédent, la présence de la patte oblique _BLANDIT-VIVAMUS_ envoie les cardinalités de _VELIT-BLANDIT_ à l'opposé de leur position par défaut (à droite d'une patte verticale ou en bas d'une patte horizontale).\n", "2. [Issue 27](https://github.com/laowantong/mocodo/issues/27). Les pattes obliques sont infléchies de façon à ménager plus d'espace pour afficher deux couples de cardinalités. Ici, l'inflexion de la patte _DF-CONGUE_ permet à ses cardinalités de coexister sans problème avec celles de l'association réflexive.\n", "\n", "Ces ajustements automatiques résolvent les problèmes les plus courants. Toutefois, étant antérieurs au tracé proprement dit, ils peuvent seulement réduire les risques de collision, et non les prévenir totalement. Ils peuvent même en produire d'autres. Ainsi, autour des entités monstrueusement pattues, des collisions qui ne se seraient pas produites par défaut seront parfois observées. L'utilisateur a alors deux possibilités:\n", "\n", "- diminuer la valeur du paramètre `--flex` (par défaut, `0.75`) pour réduire la courbure de l'inflexion automatique, en allant jusqu'à `0` pour la désactiver totalement ;\n", "- modifier à la main les positions des cardinalités en conflit, comme expliqué dans la section suivante.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Positionnement des contraintes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### En ajoutant des liens invisibles" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La possibilité de rendre un trait invisible peut être exploitée pour « attirer » une contrainte vers telle ou telle boîte existante, soit en la répétant dans la définition de la contrainte, soit en l'y intégrant (fictivement) si elle n'en fait pas partie.\n", "\n", "Par exemple, ci-dessous, répéter l'association Stocker et intégrer l'entité Commande permet de réduire la largeur du MCD sans compromettre sa lisibilité :" ] }, { "cell_type": "code", "execution_count": 131, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tI\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tLouer\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tStocker\n", "\t\tquantité\n", "\t\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tComposer\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDépôt\n", "\tnum dépôt\n", "\t\n", "\tsurface\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tnum. commande\n", "\t\n", "\tdate\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tArticle\n", "\tréf. article\n", "\t\n", "\tprix\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", ":::\n", "Dépôt: num dépôt, surface\n", "\n", ":\n", "Louer, 11 Commande, 0N Dépôt\n", ":\n", "Stocker, 1N Dépôt, 1N Article: quantité\n", "\n", "Commande: num. commande, date\n", "Composer, 1N Commande, 0N Article\n", ":\n", "Article: réf. article, prix\n", "\n", "(I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer, Commande, Stocker" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### En ajoutant une boîte invisible" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ces réglages restent relativement fragiles. Une façon d'assurer qu'ils survivront à un réarrangement est de faire coïncider le centre d'une contrainte avec celui d'une boîte supplémentaire, que nous appelons ici « Z » :" ] }, { "cell_type": "code", "execution_count": 132, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tI\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tLouer\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tZ\n", "\t\n", "\t \n", "\t \n", "\t \n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tStocker\n", "\t\tquantité\n", "\t\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tComposer\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDépôt\n", "\tnum dépôt\n", "\t\n", "\tsurface\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tnum. commande\n", "\t\n", "\tdate\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tArticle\n", "\tréf. article\n", "\t\n", "\tprix\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", ":::\n", "Dépôt: num dépôt, surface\n", "\n", ":\n", "Louer, 11 Commande, 0N Dépôt\n", "Z, XX Commande, XX Article, XX Dépôt\n", "Stocker, 1N Dépôt, 1N Article: quantité\n", "\n", "Commande: num. commande, date\n", "Composer, 1N Commande, 0N Article\n", ":\n", "Article: réf. article, prix\n", "\n", "(I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer: Z, Z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il ne reste plus qu'à rendre invisibles l'association Z et ses pattes en préfixant Z d'un signe moins `-` :" ] }, { "cell_type": "code", "execution_count": 133, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tI\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tLouer\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tStocker\n", "\t\tquantité\n", "\t\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tComposer\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDépôt\n", "\tnum dépôt\n", "\t\n", "\tsurface\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCommande\n", "\tnum. commande\n", "\t\n", "\tdate\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tArticle\n", "\tréf. article\n", "\t\n", "\tprix\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", ":::\n", "Dépôt: num dépôt, surface\n", "\n", ":\n", "Louer, 11 Commande, 0N Dépôt\n", "-Z, XX Commande, XX Article, XX Dépôt\n", "Stocker, 1N Dépôt, 1N Article: quantité\n", "\n", "Commande: num. commande, date\n", "Composer, 1N Commande, 0N Article\n", ":\n", "Article: réf. article, prix\n", "\n", "(I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer: Z, Z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cette technique résiste très bien aux réarrangements automatiques." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Gouttière d'identifiants" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Elle apparaît automatiquement (`visibility=auto`) dès qu'il y a au moins un identifiant alternatif. Il est possible de l'en empêcher :" ] }, { "cell_type": "code", "execution_count": 134, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\t\n", "\tNom\n", "\tPrénom\n", "\tAdresse\n", "\tMail\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --gutters ids:visibility=off\n", "CLIENT: Réf. client, 1_Nom, 1_Prénom, Adresse, 2_Mail" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... ou au contraire de la forcer quand elle est superfétatoire :" ] }, { "cell_type": "code", "execution_count": 135, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\tID\n", "\t\n", "\tNom\n", "\t\n", "\tPrénom\n", "\t\n", "\tAdresse\n", "\t\n", "\tMail\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --gutters ids:visibility=on\n", "CLIENT: Réf. client, Nom, Prénom, Adresse, Mail" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous pouvez aussi préciser quelles chaînes dénoteront un identifiant fort (par défaut, `strong=ID`), un identifiant faible (par défaut, `weak=id`) ainsi que les numéros des groupes d'identifiants alternatifs (par défaut, `alts=123456789`, mais vous pouvez lister moins de 9 symboles) :" ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCLIENT\n", "\tRéf. client\n", "\t\n", "\t\n", "\tNom\n", "\t\n", "\tPrénom\n", "\t\n", "\tAdresse\n", "\t\n", "\tMail\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --gutters ids:strong=➤,alts=❶❷❸❹\n", "CLIENT: Réf. client, 1_Nom, 1_Prénom, Adresse, 2_Mail" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Style et direction des pattes de l'héritage" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le type de flèche spécifiant le mécanisme de passage au modèle relationnel (`<=`, `<-`, `->` ou `=>`) fait par défaut l'objet d'une visualisation dès le modèle conceptuel : la ou les pattes vers les entités de destination des attributs migrants sont orientées vers celles-ci ; et la ou les autres pattes sont doublées (pour `<=` et `=>`) ou non (pour `<-` et `->`)." ] }, { "cell_type": "code", "execution_count": 137, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tXT\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPersonne\n", "\tnum SS\n", "\t\n", "\tnom\n", "\tprénom\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tHomme\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFemme\n", "\tnom de jeune fille\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Femme** (num SS, nom, prénom, nom de jeune fille)\n", "- **Homme** (num SS, nom, prénom)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Personne: num SS, nom, prénom\n", "\n", "/XT\\ Personne => Homme, Femme: sexe\n", "\n", "Homme: \n", ":\n", "Femme: nom de jeune fille" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cette visualisation n'est pas conventionnelle : on se borne normalement à ajouter une flèche dirigée vers l'entité-mère.\n", "\n", "Pour désactiver l'embellissement opéré par Mocodo, sans pour autant changer le mécanisme de passage au relationnel, prolongez simplement d'un caractère la flèche : `<==`, `<--`, `-->` ou `==>` :" ] }, { "cell_type": "code", "execution_count": 138, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tXT\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPersonne\n", "\tnum SS\n", "\t\n", "\tnom\n", "\tprénom\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tHomme\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFemme\n", "\tnom de jeune fille\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Femme** (num SS, nom, prénom, nom de jeune fille)\n", "- **Homme** (num SS, nom, prénom)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Personne: num SS, nom, prénom\n", "\n", "/XT\\ Personne ==> Homme, Femme: sexe\n", "\n", "Homme: \n", ":\n", "Femme: nom de jeune fille" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Changement de la version 4.0.** Précédemment, `-->` et `==>` s'écrivaient respectivement `->>` et `=>>`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Retouches fines" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lors du plongement, Mocodo génère systématiquement un fichier (intitulé par défaut `sandbox_geo.json`) répertoriant les positions les plus importantes du dessin. Les autres coordonnées sont calculées relativement à celles-ci." ] }, { "cell_type": "code", "execution_count": 139, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tVELIT\n", "\t\tsollicitudin\n", "\t\n", "\t1,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tVIVAMUS\n", "\t\teleifend\n", "\t\tiaculis\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tBLANDIT\n", "\tconsequat\n", "\t\n", "\tligula\n", "\tnibh\n", "\tconsequat\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tNONUMMY\n", "\tconsequat\n", "\t\n", "\tligula\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo --colors brewer+1\n", "VELIT, 1N> BLANDIT, 11 NONUMMY: sollicitudin\n", "\n", "BLANDIT: consequat, ligula, nibh, consequat\n", ":::\n", "NONUMMY: consequat, ligula\n", " \n", "VIVAMUS, 0N NONUMMY, 0N BLANDIT: eleifend, iaculis" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ouvrons le fichier de géométrie généré :" ] }, { "cell_type": "code", "execution_count": 140, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n",
       "  "width": 444,\n",
       "  "height": 242,\n",
       "  "cx": [\n",
       "    [ "VELIT", 214 ],\n",
       "    [ "BLANDIT", 58 ],\n",
       "    [ "NONUMMY", 378 ],\n",
       "    [ "VIVAMUS", 214 ]\n",
       "  ],\n",
       "  "cy": [\n",
       "    [ "VELIT", 38 ],\n",
       "    [ "BLANDIT", 117 ],\n",
       "    [ "NONUMMY", 117 ],\n",
       "    [ "VIVAMUS", 196 ]\n",
       "  ],\n",
       "  "shift": [\n",
       "    [ "VELIT,BLANDIT,0", 0 ],\n",
       "    [ "VELIT,NONUMMY,0", 0 ],\n",
       "    [ "VIVAMUS,NONUMMY,0", 0 ],\n",
       "    [ "VIVAMUS,BLANDIT,0", 0 ]\n",
       "  ],\n",
       "  "ratio": [\n",
       "    [ "VELIT,BLANDIT,0", 1.0 ]\n",
       "  ]\n",
       "}\n",
       "
\n" ], "text/latex": [ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", "\\PY{p}{\\PYZob{}}\n", "\\PY{+w}{ }\\PY{n+nt}{\\PYZdq{}width\\PYZdq{}}\\PY{p}{:}\\PY{+w}{ }\\PY{l+m+mi}{444}\\PY{p}{,}\n", "\\PY{+w}{ }\\PY{n+nt}{\\PYZdq{}height\\PYZdq{}}\\PY{p}{:}\\PY{+w}{ }\\PY{l+m+mi}{242}\\PY{p}{,}\n", "\\PY{+w}{ }\\PY{n+nt}{\\PYZdq{}cx\\PYZdq{}}\\PY{p}{:}\\PY{+w}{ }\\PY{p}{[}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}VELIT\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{214}\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}BLANDIT\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{58}\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}NONUMMY\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{378}\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}VIVAMUS\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{214}\\PY{+w}{ }\\PY{p}{]}\n", "\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{n+nt}{\\PYZdq{}cy\\PYZdq{}}\\PY{p}{:}\\PY{+w}{ }\\PY{p}{[}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}VELIT\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{38}\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}BLANDIT\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{117}\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}NONUMMY\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{117}\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}VIVAMUS\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{196}\\PY{+w}{ }\\PY{p}{]}\n", "\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{n+nt}{\\PYZdq{}shift\\PYZdq{}}\\PY{p}{:}\\PY{+w}{ }\\PY{p}{[}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}VELIT,BLANDIT,0\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{0}\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}VELIT,NONUMMY,0\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{0}\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}VIVAMUS,NONUMMY,0\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{0}\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}VIVAMUS,BLANDIT,0\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mi}{0}\\PY{+w}{ }\\PY{p}{]}\n", "\\PY{+w}{ }\\PY{p}{],}\n", "\\PY{+w}{ }\\PY{n+nt}{\\PYZdq{}ratio\\PYZdq{}}\\PY{p}{:}\\PY{+w}{ }\\PY{p}{[}\n", "\\PY{+w}{ }\\PY{p}{[}\\PY{+w}{ }\\PY{l+s+s2}{\\PYZdq{}VELIT,BLANDIT,0\\PYZdq{}}\\PY{p}{,}\\PY{+w}{ }\\PY{l+m+mf}{1.0}\\PY{+w}{ }\\PY{p}{]}\n", "\\PY{+w}{ }\\PY{p}{]}\n", "\\PY{p}{\\PYZcb{}}\n", "\\end{Verbatim}\n" ], "text/plain": [ "{\n", " \"width\": 444,\n", " \"height\": 242,\n", " \"cx\": [\n", " [ \"VELIT\", 214 ],\n", " [ \"BLANDIT\", 58 ],\n", " [ \"NONUMMY\", 378 ],\n", " [ \"VIVAMUS\", 214 ]\n", " ],\n", " \"cy\": [\n", " [ \"VELIT\", 38 ],\n", " [ \"BLANDIT\", 117 ],\n", " [ \"NONUMMY\", 117 ],\n", " [ \"VIVAMUS\", 196 ]\n", " ],\n", " \"shift\": [\n", " [ \"VELIT,BLANDIT,0\", 0 ],\n", " [ \"VELIT,NONUMMY,0\", 0 ],\n", " [ \"VIVAMUS,NONUMMY,0\", 0 ],\n", " [ \"VIVAMUS,BLANDIT,0\", 0 ]\n", " ],\n", " \"ratio\": [\n", " [ \"VELIT,BLANDIT,0\", 1.0 ]\n", " ]\n", "}" ] }, "execution_count": 140, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.Code(\"sandbox_geo.json\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On y trouve :\n", "\n", "- un couple de dimensions `width` et `height` définit la taille du MCD ;\n", "- deux listes de couples `cx` et `cy`, les abscisses et ordonnées des centres des boîtes ;\n", "- un dictionnaire `shift`, les positions relatives des cardinalités par rapport à leur position par défaut ;\n", "- une liste `ratio`, les positions des flèches éventuelles : leur valeur peuvent varier de `0.0` (flèche cachée sous la boîte d'origine) à `1.0` (par défaut, pointe de la flèche au contact du bord de la boîte de destination, compte non tenu de l'arrondi s'il s'agit d'une association).\n", "\n", "Copions-collons ce texte, apportons-lui quelques modifications et sauvegardons-le :" ] }, { "cell_type": "code", "execution_count": 141, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting sandbox_geo.json\n" ] } ], "source": [ "%%file sandbox_geo.json\n", "{\n", " \"width\": 444,\n", " \"height\": 182,\n", " \"cx\": [\n", " [ \"VELIT\", 214 ],\n", " [ \"BLANDIT\", 58 ],\n", " [ \"NONUMMY\", 378 ],\n", " [ \"VIVAMUS\", 214 ]\n", " ],\n", " \"cy\": [\n", " [ \"VELIT\", 38 ],\n", " [ \"BLANDIT\", 87 ],\n", " [ \"NONUMMY\", 87 ],\n", " [ \"VIVAMUS\", 136 ]\n", " ],\n", " \"shift\": [\n", " [ \"VELIT,BLANDIT,0\", -30 ],\n", " [ \"VELIT,NONUMMY,0\", -30 ],\n", " [ \"VIVAMUS,NONUMMY,0\", 0 ],\n", " [ \"VIVAMUS,BLANDIT,0\", 0 ]\n", " ],\n", " \"ratio\": [\n", " [ \"VELIT,BLANDIT,0\", 0.5 ]\n", " ]\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il suffit maintenant d'ajouter l'option `--reuse_geo` pour appliquer ces modifications :" ] }, { "cell_type": "code", "execution_count": 142, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tVELIT\n", "\t\tsollicitudin\n", "\t\n", "\t1,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tVIVAMUS\n", "\t\teleifend\n", "\t\tiaculis\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tBLANDIT\n", "\tconsequat\n", "\t\n", "\tligula\n", "\tnibh\n", "\tconsequat\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tNONUMMY\n", "\tconsequat\n", "\t\n", "\tligula\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --colors brewer+1 --reuse_geo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bien entendu, ce genre de manipulations doit rester exceptionnel. Il est en tout cas à réserver à la toute dernière étape de production d'un MCD, puisque la plupart des évolutions subséquentes de son texte-source frapperaient d'obsolescence votre fichier de géométrie." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**NB.** Étant donné que Mocodo produit des SVG, rien ne vous empêche de retoucher ceux-ci avec un logiciel de dessin vectoriel dédié, comme [Inkscape](https://www.inkscape.org/?lang=fr) (libre) ou Adobe Illustrator, Freehand, CorelDRAW, etc. Attention, le SVG généré n'est pas spécialement pensé pour cet usage. Certains éléments sont groupés pour permettre leur déplacement en bloc, mais les liens ne suivent pas ces déplacements, ce qui peut vite devenir fastidieux." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conversion dans d'autres formats graphiques" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Avec l'option `--svg_to` suivie de `pdf` et/ou `png`, Mocodo convertit en PDF et/ou PNG le SVG généré pour le MCD." ] }, { "cell_type": "code", "execution_count": 143, "metadata": {}, "outputs": [], "source": [ "%mocodo -i ccp --svg_to png pdf --select" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le premier format est prévu pour inclusion dans des documents qui ne gèrent pas les SVG :" ] }, { "cell_type": "code", "execution_count": 144, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.IFrame(\"mocodo_notebook/ccp.pdf\", width=\"100%\", height=\"100%\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Attention, seules les polices de caractères les plus courantes apparaîtront correctement sur toutes les plateformes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le second est un format bitmap, donc avec une certaine perte de qualité :" ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlIAAACACAIAAABP1BApAAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nO29eTzU3f//f2axZanIkolQoZBpF5HSolQira7i0hWlq0UrqksoLeq6VJJUpBRxlUIlVLSQCvW21tV10aKSipR9eH3/OL9ev/nM8jLGjBnm3P9wG+ec12vO67zOc85rOY/zIGEYBhAIBAKBkAzIoq4AAoFAIBDdBxr2EAgEAiFBoGEPgUAgEBIEGvYQCAQCIUGgYQ+BQCAQEgQa9hAIBAIhQaBhD4FAIBASBJVbxsaNG589e9adVREhdDo9JCRE1LVAIBAIhNDherf37NkzCRn2JOdIEQgEAsH1bg8AQKfTMzMzu6smIsPa2lrUVUAgEAhEN4He7SEQCARCgkDDHgKBQCAkCDTsIRAIBEKCQMMeAoFAICQINOwhEAgEQoIgmsmJQIgQiVKOig9Iw9qDQDFCAEFPRnd7CDEF6Sm7H9TmPQt0vrhB3DLobg8hvkiIclR8QBrWHgeKEY4Q92R0t4dAIBAICaKrd3t1dXWnT5/+3//+V15eTqFQtLW1zczM5s6dq6ysLCcnJ5AqIhAIBAIhKLp0txcbG6urq7t58+bo6Oh79+7dvXs3Ojp6zZo1WlpaoaGhgqoihmEZGRk8Fk5LS2tsbGRJbGxsvHfvnqDqg0AgEIieC//DXn5+/q+//vr161d9ff1jx45lZGRcvXrVy8tLWVlZV1d33bp1sNi2bdsGDRpkamra1NTEvpO0tDQtLS0tLa309HSYEhQUNOj/oqGh4enpCXP9/f1hYlZWFr4TDw+PQYMGHThwAABw9+5dLS0tlj1oaWndvHmT7yNFIBAIRK+B/4ec27Zta25utrS0zMjIkJaWhon29va//vprVVWVrKwsTJkxY0ZwcPDEiRPxFGZmzJjx8eNHGRmZ6dOnw5TBgwdXVlYaGRlZWFjAFAaD8e+//8LPJiYmlZWVAABXV9d79+5paWkBAOh0ekREhKGhIQBASUnJzs5OVlb22bNnjx8/Hj9+PJ1Ob2pqUlJS4vtIEQgEAtFr4HPYYzAYjx49AgAEBgbiYx7ExMTExMSki9WytraGj0nr6+vJZDLLa0IVFZWKioqpU6dmZWVpamoyZ/n4+MAPBw4cePz4saOj4/bt27tYGQQCgUD0Gvh8yPn169f6+noAgIaGhkDr8/8RGRk5dOjQoUOHDh48+OHDhyy5gYGBixYtevXq1dSpUz9+/CiMCiAQCASiV8LnsKempjZ48GAAQHJyMnsu/qKObwYPHmxhYWFhYWFiYsJyNwkAoFAoFy5cmD9//osXL2xsbKqrq7v4dQgEAoGQEPif0rJw4UIAgK+v76lTpxgMBkysra09ePDg7Nmzs7Ozu1ItGxub6Ojo6Ojou3fvWllZsRegUqmXLl2aPXt2SUmJn59fV74LgUAgEJID/1Na9u3bV1JScuPGDXd39+3bt+vr6zc0NJSWljIYDAqFcvXqVXNzc7xwQUHBzp078X/fvHmzePFiOzs7bju/f/8+c/nHjx+npaWxlJGWlr58+fLcuXN5lzcgJAGkJUUgmAkKCsrLy2NJVFNTmz59uo2NTd++fUVSK1GCcWHy5MmTJ0/mlgtpb29PTEy0sbGBb/hkZGSMjY19fX3Ly8vxMriSgYWYmBgMw1JTU0kkEplMTkpKguXxOSkc6wnnpyxduhTff319vYKCAgDA29ubuW4uLi4AgAULFhAfAo9Hiuh++DsvFy9eVFZWZu8/JBLp4MGD7OVbW1sFUFfxo729PT09vbNboVjoWfB4vuDsd47o6urW1NR09nu7LWpu3brV0NDAktjQ0JCVlUW8IXHLdGnYY6apqam9vZ3Hwt+/f+9UW1dUVPBeuLOgUBdP+DgveXl5MjIyAAB2Lamenl5jYyNe8vbt23Z2durq6mQyWUNDY+7cuXfv3sVzr1y5QqPRaDRabm4unnjz5k2YmJqa2qlikGPHjsH0v//+mzl99+7dMD0zMxNPdHd3p9Fo+/fvh//OmTOHxoS+vr6joyN+pYhh2N69e2n/FzU1tWHDhnWq9TAUCz0NHs/Xly9f4CDn4+Nz/Pjx48ePh4aGrl69GiZGR0djGLZlyxbm/qOlpWVmZrZr167a2lp8P8RR02Ev9fX1pdFoZmZmzHWbOXMmjUb7/fff4b+3bt2CYuu0tDQMw7y9vVVUVFj6toqKCstNTmdbRmBLUcOfGx6B92e8A6fPIBDE8KglPXLkyMaNG0kkkq2t7fDhw4uLi5OTk5OTk8PCwtasWQMAoNFo8AI5Ojp6/PjxcKvTp0/DRBqNBlN4LAZJT0+H6VevXl2wYAGe3qEUFQAwYcKElJQUMzOzkSNHYhhWU1OTkZFx5cqVP//808vLC3SkdkUgIIsWLaLT6fAzhmGXL1+urq5+8+YNAMDIyKiysnL48OGWlpYAgLq6upycnMDAwH/++Sc2NhbwEDUd9tLp06cHBQXNnDmTuUqWlpa3bt0aPXo0/JdFyS0sHTZ/o2VvQnKOtGfR2fPS2toqLy8PAGC+bWKnqqqKQqFISUkx342lpKRQqVQqlfrlyxcMw3Jzc2F0UKnUkpISDMOYVwUqLCyEW/FYDMOwwsJCEonk4OCgpqZGoVBevnyJZ12+fBkAoKKiAgAYOnRoZWUlhmFhYWEAgKtXr8IygYGBAIBDhw7hW5WXlysqKsrLy3/79g3DsJiYGADA2rVrYe6PHz/YHw3xAoqFnkVn7/aWLFni5eXl5eW1cePGadOmwUTYzaKiogAAHh4e+FawexsYGGC8RU2HvfTu3bsAADc3N+a67dmzBwAQGRmJp1CpVHl5eZZD2L9/PwAAf/7RxZZBDgyIXgKPWtJHjx61tbXZ2dkxX3XCfxkMBj6SAQAcHR3b29u3bt2KYdimTZvk5OQWL17MvkNeisFw9fX13bp1a1tbW1BQEEuBzkpRdXR0xo4dW19fX1xcjCcSq10RiLi4uL/++uuvv/4KCQnJyMgYMmTI9u3b7e3t8QIPHjxYu3bt2rVrf/nlFzhXf+rUqaAzUcMMx14qDiC/PdHQ1NR0586dx48ff/jw4f379xwXLBU5ZDJ5wIABI0aMmDx58sSJEykUiqhrRATUkr5+/To5OdnAwIAlNz09HT42gWIbKpW158MUXIoDADA2NlZQUDh37tzy5cvz8vJ27tzJ8TR1WKy8vPzSpUtKSkrZ2dmtra0AgJiYmF27dunp6eFloBS1paXl6tWrNjY2HMdXjrS1teGfBw8eDJ+1vnnzhl3tKrb0iFjgRs+KkdOnTw8bNgwAQCKR9PX11dXVWQqUl5d//vy5ra3t8+fPAICAgAA4i5D3qOEIcy9taWlhzmpububvWLoCutvrbgoKChYvXqyqqmpnZ7dnz56UlJQPHz6IulKcqaury83N3bVrl6Wlpba29p49e+DtlNjCi5Z0woQJAIDr168/fvwY3zA7OxtOKoa5OLt375aWlr5w4YK6ujrBKnfExYKDgxkMRl1d3YYNG3x9fQEADAZj3759LMU6JUV98+bN06dPpaWlR4wYgSd2qHYVN3pQLHCjZ8XImDFjrKysrKysLC0t2cc8AIC7u/vHjx8/ffoEH4G2tLTA66dORQ0OSy/t168fACApKenbt2+wQFtb28WLFwEA3SyiQHd73ce7d++8vb1jY2OVlZVXrFgxf/58a2trKSkpUderA759+5aenh4VFeXn53fq1Kljx47NmzdP1JXiDC9aUhqN5ufn5+/vP2nSpKVLlxoaGpaUlMTFxTEYjD179qipqTHvUFdX193dPTQ0NCAggGAeFkGxjx8/RkVFDRs2LDc3F94K1NbWGhkZRUdH79y5k2WuFrEU9ebNmzU1Ne3t7dXV1deuXfv+/bu3tzezWoMXtauY0ENjgRs9KEZ4gUQiRUVFmZiY7Nu3z9bW1sLCgveoIeilJiYmo0ePzs/PHzZs2NSpU2VlZe/cufP27VttbW34KLX74O+VYG+ie470wYMHampq8vLyO3fuhC94exw5OTmmpqYAgJUrV3aDcIe/88KLlrS9vT0uLs7MzExRUREAoKSkZG5unpCQgBeIi4sDAMyaNQvDsA8fPowfP57BYMAqAQBwBQIvxRwdHQGbfhTOpps3bx7GmxSV5dZNTk5u/PjxoaGh+CbEalfeQbHQRcQzRuAzBsA06YkdaO5maWmJpyxatAgAoKqq+uLFC4yHqOmwl2IYVl1d7eXlNXjwYAqFQiaTNTU1f/nll1evXuEF2JXcEN512BDiliFhGMYxYKytrZ8/fw5PYe8GHmZmZqbwviIuLs7V1VVXVzcpKQk+W++hMBiMwMDAwMDA2bNnx8fH9+nTR3jfZW1tDQDoynlpbm6WlpYmkUgEZZqamjhaYvUCXr9+3VnlT9fbvEN6TSxwo2fFCH90PWra2tra29uFd39P3DLo3Z7Qefjwoaur68SJE3Nycnp6nFOpVH9//5MnT6ampjo6OjK/qRZDZGRkiMc8AEBvHfOAWKpde1MscKNnxQh/dD1qoBxCIJXhA6J3e8K+BxIT4HWBkHj37p2jo6Ourm5iYiJ8o9sLWLVqFZlM/u233/7444+9e/eKujqInkGvjAVuoBgRZ9CUFuHi7e1dX1//4MGDXhbnK1eufPLkyb59+6ytraEwAIEgprfGAjdQjIgt6CGnECkoKIiNjfXy8uqVz3OOHj06bNiwzZs398rHOAjB0rtjgRsoRsQTNOwJkf379ysrK2/dulXUFREK0tLS+/btKywsPHfunKjrghB3encscAPFiHiChj1h0dTUdPPmzUWLFnV11VQxxtHRccyYMSdOnBB1RRBijSTEAjdQjIghfA57mpqaVCpVSkqKRqMtX778/fv3LAU8PT1VVVU/ffoE/y0rKxs6dCiJRMIVJB1SWVlJpVIjIyP5q6HIuX379vfv3x0cHERdEeHi7Oz85MkTtNg/ggAJiQVuoBgRN/ic0sJgMLS0tGbNmpWXlxcTE/P27VvmOZ/fv38/f/58VFQUrt738/N7/fr10qVLoasFL2AYBrUdJSUlRkZGwcHBW7Zs4a+2IuHJkycUCoXjGlG9yezYyclp06ZN169fX79+vcB3/vz5c6HOs0WwICSpLkEsRERE3Lp1CwDg4ODwyy+/CPyrxQEUI90PcU/mfyanvr5+WFhYe3v75MmTs7KyPn/+PGDAAJj15cuXkJAQJycnvHBOTs6UKVPg8mudZciQIatWrZo1a1ZnN2QwGOnp6XxsKBDev3+voaHBcUVgV1fXHTt2sKeHh4fr6urm5+fzPtWtubmZSqVyWwCXOBcHw7Dbt2/jRiSdQktLq3///uhKFkEAQSxMmjTJw8MDAHDgwAH+dt7a2ioOq5oRBBGKEXGjqwIGMpk8cuTIBw8efPnyBR/2dHR0bG1tGxsb5eTkYEpVVRVcpYkPZGRkIiIiOrsVhmFubm7y8vIiHPYGDhzIMQsXe/r4+AwaNAgAgGFYUVFReHh4eXl5UlLSihUrYIHQ0FBoNHXkyBFmb9KamhofH5+rV69WVVVRqdSRI0c6OjpaW1tDl1Hi3KCgIGjnhtPa2tq3b9+XL18CALZu3QpdJSFkMplGo02fPn3z5s3cbkOh7wGfzUSIhChHxQch3TcQxAI+FuIf/P39T506BQC4cOECXOYNAODh4XH9+vV169bhK30nJSWFhITk5+fX1dUNHjx42rRps2bNmjZtmpKS0rZt2y5evKiiopKbm8surN6xY0d0dLSWllZOTg6eaGtrW1RU5ODgcOzYMcBDFBAHETsoRroZ4p7cpSkt9fX1jx49unr1qrq6ur6+PkzMzs7W09MbNGgQ7H8AAEVFxZaWliNHjhCPQI8fPzY0NCSRSCQSydzcHE+vrKwkkUhnzpxh3j+JRJKSkoL7LykpIZFIdnZ2ioqKJBJp2rRp9fX127ZtO3/+fHh4OLQD7sph8ge+siIBixYt8vT09PT0XLt2bVhYmKqqKgAAmh1DoCV3ZWXl1atXmTdcuXLlyZMnR40a5ePjs3z58oaGhp07d0LDxg5zoRN3v3797H4yZ84cTU1NmAtNlqGpsZ2dnYWFxYcPHwIDA1evXs3tKPr164cvqY5AsMNLLOBAu/nKykpXV9e3b9/CRDqdXllZidvN+/r62tvb5+fnOzo6bt261cjIKCoqyt/fX0ZGBgAwY8aMyspKfX19jouJTJ8+vbKyktm5AgBgaWlZWVmJe3x3GAXEQcQOihGxgv+7vbS0NNiV+/bte+HCBbgK1Ldv3+bPnz9gwIDIyMinT58GBwc7OzsfOXJk5cqVtra2BI+2a2pq5syZ09zcvGHDBjk5ufT0dDwLrhoK/3LcP/R8Kioqio6Ozs/P37t3b0RExNChQ+Xl5UePHu3s7KylpcX3YQqVAwcOwKtgeLdXXV0NADAxMYG5RUVFycnJDg4ODx8+jI2N/eOPP6Dmqb29PSUlBQDg7u4+c+bMPn36YBh2+PBh6ABJnItjbW0dGhoKAKivryeTyfh9OcTKyio8PBx+fvz48YQJEwoKCoTcGAjE/4+KikpFRcXUqVOzsrJYhpPnz5/v27dPU1MzLy8PtxS+fv26rq4uHPYERYdRQBxECLGF/7s9PT09Dw8PRUVFaWlp+KQOAJCWllZdXe3g4KClpTV//nx5efnExEQ3NzcymTxmzBiCu72UlJTq6urDhw+HhITs27fv6dOnHItx3D/MCggIcHR09Pf3l5eXLy0t9fDw6NOnj5GRkYeHx+zZs/k+TKFCbHbMzZKbTCbDMo6OjgoKClpaWvPmzevbty+8gCXOxSF24uZmsoxAdA8EdvNZWVkAABcXF3zMAwDY2dmx9PCu02EUIDv7Hgr/d3tDhw4NDw9fvXq1paWlvb39//73PyUlpcrKSgDA3bt38/PzAQCWlpY8mudWVFQAAGxsbIiLEeyfTCYDACgUira2dk1NDd/H1Z0QmB0TW3LHxsba2dllZGQUFRW9evUqJSUlJSWlpKTkr7/+6jAXQuzEzc1kGYHoHgjs5tvb2wEPjt7E8OLx3WEU9FA7e0RX5ep0Ov3YsWOvX7+GgryRI0cCABYvXnzzJ7t27eJlP9ra2gCAmzdvEhfjZf/4ovsUCoWlc4sbBGbHxJbcv/7664gRI2JiYp49e/bt2zeobsQ9RYlzIcRO3NxMlsUHTU3NcePGNTY2sqTT6XSWx7mSxooVK/AX7T0abnbzkyZNAgCcPXv23bt3eGJGRsZvv/3Gy2559/juMArE3M6+G9TVXUGEHVUAS1G7uLhERkaGhYUtXbrUzMxMV1d3165dpaWldDr9/v37AIALFy7ghd+9e+fq6rpp0yaWB492dnYqKire3t4FBQV0Ov3hw4cHDx5k/y6O++d2kgYMGHDr1q2oqKgHDx7gM2J6BMSW3NLS0pcuXYqPj58zZ864ceMYDMbly5cBAFOmTAEAfPjwgSAXhxcnbnaTZWEfOO8wGIynT5+uXLmSRRXDYDAkfP3Dtra2Lt4JiQ8c7ebHjh3r6ekZFhY2cuRINzc3RUXF/Pz8GzduMBiMWbNm4ROeCwoKmHv4mzdvFi9ebGdn11mPb4IoEHM7+25QV3cF4o4qXLk2f+60ampqM2fOxP/9559/+vTpM3To0MbGxrKyMisrK3hZNGTIEOgxTaFQdu7ciWHYo0ePKBTKnj172Pf56NEjOp0OZ3IOGzYMXsqdPn0a/wCLse+/uLgYAHD+/HlYwMjIaOHChRiGJSQkwEeCRkZG3A6kwyPlG2tra2tra45ZHZodE1tyf/78+cyZMwsWLMD7q6amppeXV1NTE4ZhxLlYR07cHZosd+pIuwLxeYGzXgEABw4cYE43MjKysbEReGV6EMuWLdPV1eVv2+6PBVyu9+eff8IUXuzmGQzG6dOnTUxM4O+AmprakiVL7ty5U1tbi2HYunXrOPbwmJgYuHmHHt8dRkFn7exFFSMzZszAMKytrQ3eIldXV+O55eXl+I8qREtLa/r06QKvJDeIO2pTU9OqVauKiorgv62trTdu3OB958Qtw+ew1yENDQ0/fvzgmPXt2zeCDb9///79+/eu7J+Fmpoa4gLdH+oCpKmpqbW1lb9cZioqKviug6hC2sbGhkajkclk5niQ5GHvw4cP1dXVPWvY6yIMBqO5uZmXkt+/f2f/KWAwGC0tLYKqDEEQiXbYw34O5GVlZcwF3r1719DQgP8rLS29YcMGgVeSnc521Pb29uXLl69evZr3ryBuGWEtRS0nJycvL88xi3g5WgUFBV4kPgT7Z6F3m3vJyMhA/QYfucyIoRN3h2hqaiYlJcnKyi5btoxFJgylnH///Tf819DQcOnSpQCA/Px8Eom0cOFCKPF0dHRMT0/X0NAgkUiGhoZQQMINdl1pdXW1i4uLurq6goKClZVVbm4uLNnht/BSjezsbG1tbRKJpK6uDt9scdSnwm8sKioyNTUdOHCgqqpqfHw8TGRXuPY+KBQKj2+dFRQU2H8KBOvxLZ5BJEB1dU5ODuz/ffr0MTU1PXHiBJxeRCKRdu/ebW5uDrsl4B4aHDsqx2hllmsLXITNz7u93bt3d/2LRULPrTmCI6NHjz5//ryTk5O9vX1ubi5+RYUxaT3Bz0dPeEppaWlsbGxKSsrJkyczMjIOHTpUVVX1xx9/XLt2jdu0CHZdaUtLi42NTVlZ2fz585WVlS9dumRlZZWXl2dsbNzht4waNarDasBH9Nu3b7906VJAQMCSJUvgbln0qV5eXg0NDXPnzq2trd2+ffvYsWMDAwO/f//OUeEqjPU2EeKMYNXVsAe6uroOHDjw3r17np6eNTU18JXN3r17FyxYQKPRZGVluYWGnp4ee0cFXKKVOVHgImxkPITo2Tg6Ou7du7esrMzZ2Rlee3ZIUFDQnDlz4DXQb7/95u7uDpUhqamp3DZh15UmJiYWFhYePnw4Pj4+PDz84cOHLS0tzPOwOvwW4gIuLi63b99eu3YtHImrqqrgViz6VABAZmZmRUXFtm3b9u/f7+TkZGxsDAgVrgjJQbDqaoiDg0NQUFBWVpa5ufmRI0dgopmZWVxcXEJCwvnz57mFBseOygsCF2FL1t0eolfi4+NTWlp6/vx5FjULfv3IEQ0NDTk5OVzuoqOjwyKLZoZdV1pWVgYAmDdvHvx3xIgRw4cPh+NQp76FWwE6nZ6YmOjn5wdFY/j0VHZ9KpzzxbIIMt8KWkRvQrDqamYoFMrEiROzs7PhvzNmzMCzuIUGx46KQxytggXd7SF6A6dOnbKwsAgKCiopKQE/BVj46qbc7gLxwaZD2HWlOjo6gEkN+fbt2xcvXsBinf0WjgViYmKcnJwCAgKuXLlCvJWuri4A4MmTJ8y5fCtoEb0PQamrWUhLS1NWVoafmZc/5RYaHDsq4C1aBSvC5lO3p6mpSaPR7t27x7IMHZ1OV1dXhwZaiGfPnkmCFdbz58+huEKEyMjIJCYmjh8/Ht6W0Wg0eXn5CxcuGBoaZmVlvXr1asyYMbzsh3ddaVhY2KBBg7Zs2ZKfn6+pqXn27FkMw9auXSuoI8rMzJSVlTUyMoqLiwMAFBcXc5u7YW1tbWBgEBgY2NLSQqPR4M9HhwrabkZCYoEbIo8RgairIdeuXWtpaYmNjS0sLOQ4Vcre3p5jaFhaWrJ3VMBbtApWhM2/zWxvEgtXVFQI/Mkt/P2VEITUgPCykSNwRhlziqqqanJyMpxORiKRTpw4sW7dunnz5s2YMUNVVRUWZv7LshP4ubKyMjMzc8qUKSwBP2DAgOvXr69evRqG3NChQ/v165eWlrZ69eqIiIj29nY9Pb2EhASod+7wWzosAABwc3O7fv36uHHjHBwctLW1d+3ade7cOY5bSUlJpaamrl+/3t/fv7a2FgCgp6fXp0+fmzdvuru7R0VFtbS0DBkyhEdDOxQLQkK0MUIikSIjI01NTV1dXQsLCzn2Dbw8tyiAXLx4MTIyUk5Obt26dQEBAfj+8QIEocHeUeG27NHKEiN+fn7bt293c3MzMjLqeruRuD1RhZdm3Jyc1NTU4DTrAwcOMA/4xsbGGhoazEsqiD/W1tYVFRWurq6C3W10dLSOjs7du3cFu1sxZMqUKRUVFS4uLoLd7dmzZ3V0dLriJcZgMOrr6zvrVl9XV0egsfnx4wcAgFlj09jY2NzcLAydDLyIlJGRaWxsJJFIHG10OqSxsbG9vZ1HtQ+KBSEhnjFC0Dc4RkF2draFhcW1a9fMzc379+/foX91p0KDl2itra3lcW/E4xf/i5PBKao+Pj4mJiaisnIVFDo6OgK/EIPrxEsIwmjArptnUqnUzo55gAddKUuKnJyckBxnqFQqlF12Zf+d3RbFgpAQwxgh6BvEUYA7ine4f967Hy/RKqiLS/6HPU1NzYMHD1paWi5btiw3N5dlUdHq6uotW7akpqbW19ePHj06ODh4woQJAID8/PwxY8Y4OTmlpqb++PHDwcFhzZo1y5cvr6qqMjAwuH//Pr7oFALBDTSXuOugNuzdCOP8Qtff2NhYOAVUtHTlALs0kxOKhb99+2Zvb19XV4enQ7libGzs5MmTf/nll8LCQisrq6KiIvB/9cIeHh6JiYkLFiwICAgICAh48eLFtWvXulIfBAKBQAiJ/v3729rasnjF9ES66sAAxcK+vr7Ozs74oAXlikePHoVrwq5fv97IyOjgwYPwnTz4KdQdO3bsyZMnoVAXAHD27NnU1FQe3UMQkgy6U0EgiEExQoAAjIfYxcKCUvIiEAgEAiFYBCNXZxELC1DJi0BwREVFBc74kJKSUlVVpdPpPj4+cEETRM+ira1t//79w4cPl5KSIpFImpqay5Yt+/Dhg6jqw2K+2nPp27cv9Etip6CggEqlpqSk8LKfyspKKpUaGRmJf2DO7YnNJZhhD4qFdXR04Ks7XK64Zs2awMBA6LghQCVvN3D//v1BgwaRSKSxY8e+fv2aOaumpsbAwIB5tZem1nsAABSjSURBVP7W1lYqlRobG9vt1RRrXr161adPHwqFcuPGjc7mdgiDwaDRaKtWrXJ1dZ0yZcrHjx/3798/duxYXAOLEBQEZ6rrsdDU1DRp0iQfH59Pnz7NnTvX1dW1X79+sbGxRkZGjx49Etgx/AQu9n/o0CFuBaD56okTJ3C7SuEh7B8ZBoPB4uOKH357e3tbWxuPa9hiGAYL4x/wrO5sLgHC57DHTSwMLVGgXJFOp0dERPzxxx8AAN6VvHwfiQBpbGxcuHDhqFGjMjMzGxoaVq1axVLg5cuXs2bNgsuHQ3jvQ5JDe3t7WFgYjJbO5vICnU4/ceLEqVOn4uPj37596+np+fr1a/aThcNgMJhXF0PwCPGZ6mIsBAcHP3r0aNKkSSUlJVeuXImKiiouLg4ICKipqXF3dxfI2hfM533IkCGrVq3CBVfsXeLLly8hISFOTk5d/15iRPIjw3L4Xafbmkuw8PluD18PnhljY2N8Pufw4cOzsrLY5YqjR49mDh4o/oWIj8g9MTGxqqrq0KFDBgYGPj4+K1asePXq1dChQ5nL5OXlzZ8//8aNGzIyMqKqp5ijr6/fv39//nI7i5SUVGhoaGFhYVpa2ocPHwYOHMhSAMMwNzc3eXn5nq4x7X46PFN8x8KPHz/279/fv3//xMREXApGIpF27dpVVlZ28eLFuLg4Z2dn/qvOdt5lZGQiIiI4ZkF0dHRsbW0bGxuFpMXEEcmPDH74eXl5MKW+vv779+8aGhr87bDbmkuwCHcpajk5uZ7o8vry5UslJSWoRBw7diwA4J9//mEps2/fvoKCAt7NbhDChkQi+fj4AACysrLYPVrZnSolwYW1e+A7FoqKihoaGlavXs0uf965cycA4PHjxxw9SNnPL+Biw8ty3onNS9nNV4WHSH5kmA8fABAQEKCurj5w4MDp06dDNw/QmbjozuYSLMiBgQM1NTXwmTv4ubw9+1yJ4cOHX7t2LSUlBVrdi6CWCDZGjBgBAHj48CH0aA0NDTUwMAgICCgtLYVOlZaWluHh4evXr4dOm7KyspGRke7u7sHBwc+fPxd19XsqfMcCnAEHDQFYMDAwkJaWLikp4ehByn5+8TLQhnfHjh23b9+OiIhgOe/s5qWi6hIi+ZFhaczPnz/7+vrOnTs3IyNj+/bt4KcDLS+N0KMjCA17HNDQ0Pj333/h22AYUewPzQAAlpaWMTExZ86c2bFjR3dXEcEJKIBRUVFh92hlcapELqyChb9YgM9Ov379yp5VU1PT0tLCbREsbh68gM2Gl8ChVLRdQhx+ZI4ePerr65uUlDRq1KiEhATQGXfiHh1BAtDt9T4MDQ2bm5uLi4tNTU2fPn1KIpFYll7DcXJy+vPPPzdu3NjNNURwBJqnjBs3jptHKw5yYRU4fMQCnU6HDzDXrFnDMp0NWruMHj0a/styr0NwftlteHmkm7uEWP3IWFhYFBQUgM40Qo+OIDTscWDOnDk0Gm3jxo2bNm3au3evnZ2dtrb2zp07zc3N2Z04NmzY8Pbt28OHD4ukqj0Lbm0oEOLi4o4fP25hYTFr1qyYmBgXF5fLly9raGhMnDgRFmB2qsSdNjds2ABTGhoahFGrXomgYkFXV3f58uXnzp3buXNnQEAAvqJ/RkaGt7e3mpqah4cHnCXH4kHK8fyygI+jBA6lIuwSYvUjc+fOHXivybERON6O9+gI4nPYw7uUjIyMgYGBn5+fo6Mjt8Kenp4JCQnFxcU9RdshJSUVHx/v5OQ0b948Op0eFhZWXV0dHBwcHx+Pl2G+OA0ODobXPsz07dtXQjRkdXV1gwYN4pjF3ErEbcgHpaWlu3fvZjAY1dXVT548KSgo0NfXj46OJpPJ7B6tJiYmzE6Vx44dEysXVjGH9/PY2Vg4fPhwWVlZUFBQSkrKjBkzFBUVnzx5kpKSoqSkFBsbq6SkpKioyO5ByvH8cqs883n39/fnliXULsEeIwL5kemQ4uJiODkIAmfu4Fy+fPnTp09xcXElJSVQacbRgZajU6O4+Rh3DowLkydPnjx5MrdcAMDw4cN///33yZMnUygUKpVaXFzMsWRdXZ2CgkJCQgK3XYkcbkfa3t7+6dMn+PnQoUOmpqZQusSRpqamly9fMqesW7dORUVFoDUVU9TV1eErd2I6bEMWiHugsrIyhUKBfU9OTs7AwGD37t2NjY0w9+HDhxoaGn379nV1ddXW1lZSUiosLITzIAAARkZGGIaVlZVZWVlBy/IhQ4b8/fffPFasF0Pc5hCBx0JDQ4Ofnx88NZAVK1bAySyQc+fO9e3bl0QizZw5U1VVdcmSJRzPb3FxMQDg/PnzcCsjI6OFCxdiP+e/wPP+7t07AMDp06dhmW7rEtxipIs/MsTni91ILyYmBh7+f//9Z2hoCBMVFRW9vb2bm5vhVuyNgDcaS+uJcwR1MH7xuRkAv/32G/wcEhICANi/fz/HkuXl5XgziSe8hPrq1asvX77cqd3CZnn79i3/NesJNDU1USgUf3//Dkt2tg15OS8EtLa2NjU1YRjW0NCAD4cYhtXU1DAXa2ho+PHjB9/f0ssQYSy0t7efOXMGzmExNjbesWNHREREQUEBzG1tba2trWUuz+38coPlvBNkCbxL8BgjfDRsF2OkoaHh48eP3LJ4bATxjCDilhHATE742pl5HR1mcD1j179IhJw4cYLgKS5H5s6dSyKR4PyoXsydO3fa2tqgmSIxfLRhV6BSqVDkKycnx+xLziIklZOT49F5HAERUiyQSCQ3N7cXL17s27ePQqEcOHDg3r17+KNLdg9SbueXGwQCYmF3CR5jpJsDBAAgJyfHzUWI90boiRHU1WHv9evXoaGhAABzc3MSibR79274Ydq0aYCTnpGjpBTuqrq62sXFRV1dXUFBwcrKKjc3FwCQn59PIpEWLlwIyzs6Oqanp2toaJBIJENDQ25jrTigp6c3evToHvOwm1+SkpKUlJSmTJki6oogxBfeY0FZWdnb2/vZs2f19fXnzp3DJ7n0aFCMiBv8D3tnzpyRlpbW0dGJj4+fPXs2fFm6d+9ebW1tJyengQMHctQzYpwkpYC7My3Wk21pPT098/Lyrly5IuqKCIva2tr4+Hg7Ozv4fB+B4EZnY0FaWlpMVujtIihGxBH+no0CAExNTXft2hUeHn779u22tjaYOGnSJLwMnJLk6+ubnp6elpYmLy/v5+cHPdbPnj2LYRiDwZCXl1+1ahWGYXBG1tGjR+G28O308uXLnz59CgC4du0ahmHQi8TLywuW0dPTW7BgAc8Pe7nSxefjBDAYDGNjY319ffx1cS/D29ubTCbDqxmBI7zzguAGigWBg2JEJBC3DP+6vXHjxgUEBLAkzpgxA/9MoGdkl5Ty4kzb42xpKRTKn3/+OXPmzPXr14eHh4u6OgKmtLT0yJEjK1as4Li4FALBTO+OBW6gGBFPBLw4GfO7ZVzPePMnuP06DvMYBnhwpu1xzz2mT5/u4+Nz8uRJfPnX3sHXr1/t7e379esXFBQkkgrIysrq6+sz27IAAJ49e9anTx87OzuRVAlBTG+NBW6IPEZIP5GVlTU1Ne3Fb1s6ixDX5MT1jKtXrw4PD3d2dibwQusFzrTcCAgImDlzpoeHx+nTp0VdF8Hw9evX+fPnv3379sqVKxwXEuwGWltb//nnH+Ye0tDQsHTp0sbGRoGYtCGEQe+LBW6IQ4yAn+pqMzOz4uLixYsXw7W/menQd5dvOrVn4VWDI/wPexxvvJgT+/Tpc/PmzVGjRkVFRa1ZsyY3N9fR0ZGbzSw3Z9oeYUtLDIVCuXLliq2trbu7u5+fH4vfcY+jtLR0woQJT548OX/+vJmZmWgrc/78eXx+oJeXF3xUzh98m9Ai91re6WWxwA3xiRELC4tjx45lZmYePnyYwWAkJyezFBC48SzHPXcYI8KrBmf4eyXYKTqlZ2xoaCAQlgqD7nkt3Nra6ubmBgAwNTXNzs4W9tcJg5qaGm9vbzk5uYEDB+bk5Aj76zoQnJLJM2bMgCt0/Pvvv5cvXwYAODg4KCsrz5w5s7Pf1d7evnz5cl7WmhHUhuIJioUuIlYxApgWFbl37x4AYPPmzcKuEjsiiRGhrNLSm+jOI01KSho8eDCZTLa1tb106RLLwhPiSVNT082bNz08PJSVlclksouLy/v377vhezsc9pYuXZqTkyMlJWVqaqqsrKyjo1NTU6OiogKHvU+fPq1YsUJNTQ16qj169AhuCCcJz549W0FBAQBgY2Pz48ePLVu24BeCM2fOfPjwoZaWFgBATU3tjz/+4H1D4beKcEGxwB/iGSP4sFdRUbFo0SIAALw69PPzg+t329jYMK83Nn78eFlZWThaf/v2zdDQUEFBobS0FN8hDAF8pUkDA4MlS5ZgXEID3zMvwcVcDZYaYhj28OFDaElIpVK3bt3a9ZZBDgzdyty5c6dOnRoSEnLixInFixeTSCRdXV1VVVVFRUVRV40DNTU179+/r6qqam9vV1JSsrOz8/b2Fqs5aWZmZgcOHNi0aZOUlNTNmzfx5TagDLSsrGz+/PnKysqXLl2ysrLKy8szNjbGmJSj+fn5e/fuxc1IR48e7ezsrKWlBZdq3L59+6VLlwICApYsWTJ8+HBeNhRlW/Q0elYscEPMY+TMmTPR0dGtra0AAGZ19YIFC2g0mqysLMZkPHvt2rXx48fb29vn5OSsX7/+xYsXV65cwZfuBGwutfjgyjE0Fi5cCLN4Dy58z8w1hPrvAQMGREZGPn36NDg42NnZ2dTUtCvNgoa97kZeXn7Hjh0+Pj6PHj3KysoqLi7+/PmzeM7C0NTUHDt2rKam5oQJE6ZMmSKeelsvL6/s7OxJkyaNHz8eT0xMTCwsLDx69Oi6desAAOvXrzcyMjp48OC5c+dgAWhGam9vHxISUlpaGhERsWvXLug4CgDQ1NQ8dOgQhUJRVFS8f/9+VVXV8OHDedkQ0Sl6UCxwQ8xjZOTIkfPmzaPRaMOGDbO2tobKMTMzM6iTBgDA2yyIhoZGcnLypEmTRo4cWV9fv3fv3vnz5/P+XSyhgad7eHjwGFw4zDVMSEiorq5etWqVlpbWoEGDoqOjExMT0bDXIyGTyebm5ubm5qKuSG+AfbHHDmWgxGakwnAxRXADxYLw6FBdzYKpqemaNWuCg4NpNBpcTpId7P9a/uLwGBodWkADnvXffIOGPUQvBJeBQs0MlIFyvHTlaEYqKBdTBEIMIVi5+/Hjx8eOHVNVVa2srNywYcPx48eZc+Fq4CyWv+ywT7DvbHBx1H8L0M+2S8Meg8HQ0dExMDC4ffs2tzIrVqx49OjRy5cvu/JFCESnwGWg+fn5mpqacDE8Yhkos+MoVPjy4WIqIUJsRK/k/fv3cC7006dP/f39w8LChgwZsmnTJrwAjUZjt/zlZc98BxcQjp9tl+TqKSkplZWVd+7cYVdB4rS1tfVWdQ5ChHBTbcJ0bjJQ8PNSlF396efnJyMj4+bmlpub6+bmpqSkNG7cuP/++09bW3vXrl2fPn3iZcPuOHIEgmc6VFcz92oHB4evX79evXp14MCBx48fnzVr1tatW69fv85c+MSJE+Xl5fPmzSssLFRVVWXenCU0mBP5CC78Sznqv7vaLNwe1FpbWwMAMjMzCTaeNWtWamoqAMDT05PldhjH2dk5Jyfnv//+62JFhQcvR4rofgRyXhobG5ubmwm81liora2FhRkMRltbm4yMTGNjI7w+5XHDHg2KhZ6FSM4Xg8Gor69nsT/kBb6DC9LY2Nje3s6jtx9xy/B/t1deXn7r1i04/fTcuXN1dXUsBT5+/AhfWiIQokJOTq5ToxFeWIAupghEb4Ld8pdH+A4uiAD9bPkf9iIiIjAMW7ly5Zo1a378+IFPDQcAFBUVmZqaDhw4UFVVFdoPAQC4mdCSSCQpKSk4a6itrW3OnDlUKlVBQWHZsmUcUzhuiEAgEAgEL/A57LW2tkZFReno6NjY2Li4uMjLyx8/fhw+L21oaJg7d+6bN2+2b9+ekJAwYsQIfKsOTWivXbt2/fp1KOWpqKgAALCncNyw6w2BQCAQCEmAz2EvMTGxqqpKTk5u8+bNu3fvVlZWLisrg/M5MzMzKyoqtm3btn//ficnJ2NjY3wrKEJMSEg4f/58WlpadXW1g4ODlpbW/Pnz5eXlExMT9fT0AABpaWkLFizIzs4GALCncNyw6w2BQCAQCEmATwHDyZMnyWTy+/fvIyMj8cTQ0NBp06ZB2T98jMlChyJEOp0eGhq6devW0aNHnz59etmyZewpwlAvIhAIBEJC4GfYe/ny5d27d5ctWxYTE4Mnzps3Lzk5+fXr13DN0CdPnowbN45lww5FiK2trWvXrrW1tV26dKmrq+v06dP79evHkiIM9SICgUAgJAR+hj04meX3339nTty0aVNycvKJEycCAwMNDAwCAwNbWlpoNBou6WeBowjRxMSkoKBgyZIlOjo6+fn5P378OHPmDEuKMNSLCAQCgZAQ+Bn2zp49O2bMGBb7RGtr61GjRp05c2bPnj2pqanr16/39/evra0FP9/PAU4iRHd396ioqJaWliFDhhw4cEBeXj40NDQ+Pl5ZWXnnzp26urrwISdzCgCAfUP+GwCBQCAQkgQ/wx43NR582QYA0NHRSUpKYsll18UbGBhkZWWxiBDfvXv3+fPnfv36UalUAICtrS1LCrcNEQgEAoHoENEvRS0nJ8eSMmDAgA5TOG6IQCAQCAQxXVqTE4FAIBCInoXo7/YQCG48e/YMrq2H6B6ePXtGp9NFXQtEJ0AxwhHinoyGPYSYgn5/ux86nY6avQeBThY3iHsyGvYQYkpISIioq4BAiDUoRvgDvdtDIBAIhASBhj0EAoFASBBo2EMgEAiEBIGGPQQCgUBIEGjYQyAQCIQEQTSTU0IUIUirhEAgEJID12FPckYCpFVCIBAIyYHEvkI0AoFAIBC9FfRuD4FAIBASBBr2EAgEAiFBoGEPgUAgEBIEGvYQCAQCIUGgYQ+BQCAQEgQa9hAIBAIhQaBhD4FAIBASxP8DQtV/SNcGr9UAAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "execution_count": 145, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.Image(\"ccp.png\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mentionnons une possibilité amusante, actuellement non intégrée à Mocodo : [Svg2Rough.js](https://fskpf.github.io) pourra donner à votre travail une touche finale plus informelle, dans un style « tracé à main levée »." ] }, { "cell_type": "code", "execution_count": 146, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnIAAAB+CAMAAACXrQz1AAAA+VBMVEX///8AAAD9/f0HBwf6+vrv7+9MTEyfn58NDQ0hISFISEjn5+fd3d04ODjQ0NDY2NgsLCzGxsaGhoYmJiYXFxff39+urq5XV1e6urpAQEBERETV1dVmZmbs7OyioqISEhJjY2P19fVpaWlgYGCTk5N+fn56enrj4+P39/fCwsKQkJDz8/Orq6txcXFbW1uKiopUVFS/v7+NjY2ZmZlRUVExMTF0dHScnJyWlpYpKSnp6emxsbGBgYE0NDRubm63t7c8PDx3d3fx8fHKysrNzc2mpqYbGxvh4eG0tLSoqKi8vLykpKTa2tqDg4Nra2sdHR3S0tLFxcVdXV05thBaAAAszElEQVR42uzaZ2/aUBQG4PdcbPaGmL0aB8JeCVAgzLJCSUn+/4+pF47VFqmVoHFVP9+MEEaHY9/j9wKLxWKxWCwWy7+OEwuwWP6iI8VhxBUWfMZT2tx8DaT8X5ah1/ZzfDoSart91tHvDsRKMxwe3xXnvfrkJZ2/bcRiD3a7O5mY+Z4eg58jrnU050wvYLGcE6AtGP81NOqHJ7GZy0Z/wObMrV2R4OOTL5F02x9ijdv8p0m916AVLJZzbijhVvss+uhuTMaVQdeR3e9qwmgaf26XQ8sv/lTgq3dT8niGmQzPL6qFwj3HMYazvDSCxXJOich3e1iFPAyXkqIpLJZzhpTFhfmpDYvlHJ4cuLAQvcJiOadKXVxYmZawWM65JxEX9kx+WIyGW/zXPAEYMOrgwqYUgMXoQAz/HMa4+0J1wfOZoUcKajebksSjGA6HGQmvWPBqkOuVktyjEuWW2/H4dCuFuSslzE3hzQcjauLCRuSF2XD4SPUoTI7x3uV0L959arkTvmBk7bTRxRRhT8KIDj+eHSgx/GQ3rbwlffZiCDgWoOHxCzW6gcm8kjgVdtn+oBIeF3sv6bfYgzsxSygJdkuOsNNyhj0v3o0P4WZHHHT7Uji5qgnCaKvEk69yPuk/ygnlzaYkJZRyPskxrrDIDEs33kDKvwxJ1/Z2JJ3EIZ2lEz7cFXv1l/RtQ9qdcVMQZsWF82LdHiGVMzKTSiJVRC6IVI+xWg1HdlWT7Ha71Wq13++zEoeiL9FrNY3HT6UyVKrK4IvByHaHd16x9Zbb+WkMlfDgTtob84EHXjrx1sjNoNhSH2y2nrVirYdWflKMQ7YiD8wlo++WRL+5PgfV3RL5S9vd2l6dsln3LRrNOW02uqic6ylBSZhVkYhciUavs38+ehiuI/IJRs4idCuS1beUPr2XVDPcEDXEkdBJ56sdojAUewqCm5FuDVmWhjAZB+0XDL+PFeT5pKTMJym/NqA8KwPKaUIZDMRKRRz0tb2a7VS9wpVboXx9yzfCe45B5ovBrDb0xHBtufm5Q5EoLeQpL9ALVDfx9jdqNos1LCgHTZ9IS94CRPfgUu1pgz7FhWxXfdVBPEymTF/wgSJpmFYwj6ujMIzW9fffhQSAZQMrmkDnog0kC4pCI5CN1hlIGFEJsorxQ7tkun+SxCmFD5Sbw7RcL7i2Aokwck2gqrqof1qG6tDZKAOJhyLvv19jTkkOkigFtLW0B92ATPcXvA9+iKYwTCvaw7UNKfvr0a5ISQZFl+bQkdpAAcodXlq+tTOLZ8ozt3ojfKSjFsXdQlchDiazoxL+lKnj9sux3eHavCTAKJjWV9CjPtONccKIGCRb0mxRpjT4iBKu+LQFa0l26DrmCz4/9okmQw6YFoVxbX6Kw+jx9lSXHIOqSR3otJaL05NjuvQuAKXlELBRE0hoLfeVfNA1CWbT/3m8ZDirevZYtwhAwxUAFMoMJ9690HvwJRtdHpoN1WBWHFWgWdTEQZmDZMOzc91Twp8LUQhGTw0o2pSE5kAD6GzqMvn+FCsvrEoPUgVJbZYrGcPOsPlaTqR7/BQ/KiUMJ52RtxqDLPC5C8mrLYwfj3XdNiQsQmWoHr9zd6Z9iSNBGH+ehDNcQYKIIKdccohyjKhcHjiOB67f/8MsdBKM9Di4v9UdZ/+vbAYYUlS6q56uLuZ5wMPYtQsmOVoUYBHh191jdZR1JLhAvwJGpNrNiOtZs9ElWbiHxVMrD0FN24sXNU1rF7a+SzqtcJUn2Dg1owjpFv9+qJSd4V7WjOUaHL2kD8/mI+x4GLSmSP+XdrljKpDkRyEsmmjCvgX6XML0PgXyWOCl3wo3jFMIZtwGDJLGgTmTpqmXr8fecuLsTyggdCz6A+6PDKop+LmklwKwZqMMF7SjEDzyEoIifzRocSTrtNheS94mbQgUP5uK8tQmbwocA1AiYXOWq+frwA+mX9K/hGV+0nqzKlWsOFfx1dgiIMmPwAE5+nHztJ81AxqDDAOKj4xDGgsiKhkwb0R6FMveD8Cc5wPSJ5ysTC/kheWrUuE1LPwqoBwygSEjkZJBNQys2eiOyRzJhBAx2vadlODYdbTzwMlJqXwq67S4YwBOkt3VeklfllTv0OK37ydFgwzsdIdccoIUY0/xYCRcq+KORVsNIQN/hMstP5IkPyJMNqyHsgDiJL1i8eAO1scC19D+e0CVx1bkWgD8DChHGtk3Xe4PquZyZJNGFoDCGTReAq4cdQVrNrrlCHuHPs4iADy2y3WFTW7NSELSaWWhdtcDi5qfzBYrQJ8CNe1qWXuT11B0mnyDlyX7ne0lx+XPYUXp67lc2QdI8iN2V4bx0Q2ckcwBU5L7gDQW1xsrm68J8bvPdL4Mi8BAiDDfhIuO2PiDqrkiHMNCjYkgwIOkuEVcPsbXbGRF9IEhB3WHyyXFCtsxtVlJp5UlDE3DikDc/BjaJDHdr1WBvR/hUwWCYL89jOnDbgWBURwWQduWivLq+/1q9A1Akh9T9F3ARGcKKDHJGRSDaRYAeYy4yugOhb0mDNao3gv1KAHovLGcNI4WW9Oipvv0Kiy2v2A1l02YP1a5awiB6xkziPHGvNr4uo3OTEtcJDkCurbL6bwHcGDORLJOiwfiFR4NH8KXdrnRHJDkx20+w0RReQH0eKYyHmU2xRAgj9Flv3JNVRE3dhwdzm+AHSaAnhXtacygR5NB/msI0b/mkrWXcps5yaYI3/dS+1lqko3sgo8oDaBte6vBPREvHzre1fO2Q4jyuf+9yxX8gCQ/bq3WjDgNIEC6PbxusqmQeXmMDk2exJocBx7pr+CILUC3nOqEBYR43jhKBVxfRYjeVEl4uTIC1ViiBrhoknNLNjJnOZFS3SBnZR6npnR7bLqcrNOKG95JNwn8DpdTItslD/8lheuggneQHgCS/Hi4Wgn6LAIZhnDAXJZRhFiTxykuMNpDes2FFXDtMhb4i8+A33KqfZYxYEUSot3YTD665eMKdaZPdneTyeQkFAoNlwV9uj4YxGL+BfPZzDCMbDZrGLP53O9fPJpV+QpjK/5PD6qkbE3CTUEUso1u2bSnPDceLB87M016wgdHHqzL1l/Rnny4y2WxmfCQH0PCjc0UdUCSH39Qr9rSRwp4ZFlkpzMFfW7J4wb9O6dARgjhIZERXISof1+Os1ZO5uEOYlKF6gnr7/nuP5q2G5sZMwWTK45gyw+NJKl2FGDNRl6WrYUzBOyIlBbICTXZziplnRatIV6RCP0Gl3NtcZYL0Th2493Im1TVbY0LLrEJcdGS/Fj3s+UGkMqyKCauMVyqEDoanEhjpccTK+lwWfkG4O5RZQ5QWTV9S73AhHFp78OFDQhtoNhwi+rQwM3909VfP8bb197OonJ+2iwUE57dyXDgN3w+w6/3QkmtmyimR/3y+eHBiTfTOBv/qF2GU8GbgLsuykIrj1zwrGATZ7xfuVwTAoUEUhr57MKajazd9z2xw143eGDeKxXz4kv5uguyaIZ28ve7XL7LRI7ZD+gzFimQbEBGvkhJfkSY9OVKu6QnL9aKOOAhr0RY414fR+mrWz67A8wt3eN0IAJuslQL/tUnx0CCB8FgJFUL/oM6hyrJhzw+FKVMbk6VG4yvuxxIBVAeyOKajXDMYSbydKczWRVpEbWtnMoOUH1MZrnAyEN2OU17YwL472I5pc3nGcsX+AjiGnmy0eUmgCQ/AuEkSXE3A26hMV4zaSq9R+vjMx7YwmbG3AYUVHSeACGadEQibJJ9/w6gQn5GUuvyc4MI7TyoElxlCn5Tbd3JetZshDFNBnvm7WuQ9B2KeEWgDl2yTit2G5wUe4DiyteXxxUr8aA4ixiN1o6sk4iizP/g+HDrwWwqVyg8jpr9/rRcLi2PQ20dHh4vjwAsz0R5M5m769vGdleNbtpk91MP46NokNt4G3lqD8RhoYSvj8duCLziE0WFzWoFtzSuwCKiAHf7sHDVqoCykwv1PNOIeM/bxCQ20JPX79+OIangMzjhpmTZyz1Y3Cj2H0EIXMq6jbB34NGTreuqbb+boyvzVZfRe/EcWaeVj34UZ7rKTaiqz7fMj5bZkUiZRMbk8/nUJfLT3fgFQZLFPD6OGmV5X0rLfxubt2OmpILPYSS/tZxNfzZ6Ea/IzdqtYvqxOS09LELRb967RSy6cxRdBKP38cqpiEbfxfJs94WYKh/VU/yKNjn9WBPvyzfzh4mPnx9ppMg9fBIKWYLMf3hqQD5TiJwOiU+9qa/IkYKPJURV+bouNzU2eEUHn0Z8w934X5wamI8klfQj2Ry6hDhRHEWom5GLbd2Btde5N6QQmobfxOb+ELfS2vdGOwQFqAYgYZfxyl0VBAkmNlUSfi7yiZ5CDCauunMq6tXE95zpyE4ROPs3LndBVl4VoVrEz6uwUP7qHHw/hUlg+zBzqbwqtr1Xydg0shYn17+sy43m+AW0tipfCmtft0Nw1jG36bYVkIb1R+6bs4zX2VUBglOyuqGS8LPxlX/uckqMTHYUCMJqbM/MidUU5Gi3iuhuHDJVqXBA5oTaWhGqoB7jLUxqPdJW2+pTLukpzmLbBgVbeCFP3uFthlRfwxeWQ5/PsXs00PVebzgMhSbLriW7mubxdLvtdiLRahWLuVw6XXhcZO128Ju53f6+iH2vwpFgxRRiJR79eJuUPcm9FNa+bofgrGO2ywdQoQeCe07gKON1dlWASZbjDfPDe7swiVDdarJkdlmyuhiYbVvMZiSiG0m8Ugmcmp2lqy5FPf/5jnedfo/KYtWUO7Nxcw38VoVEny5sq2FI1NXM5huHDK4VoQoKtJXIDJnc8p7nogDcSRpp735z+qryccxifVsjy3ihRA/eJukvCc4tHpZsmTycl0rl8kIB6jebo8fHQjqdyxWLrVYi0W53l78woC33OicTe6tTbHTO5zPjje5EquHvTbREbjR9OO5ktse1SiGGt+nbl/FSWLvWDsFRxxyz5bswkxA8cQhnGe8Fs9L+wuSXgbdnmNTarfRoen64dW4278k9J9rdZbeWYU+P+Wc+/kt88Z/eggpzuChaX1w6amrIaUBmJMRpyLjZ33zCi1wvQrXLIUNYEiCPHNO+VoXA6XJR8SmjPu68mizwNpoHn4C9O5W6ih6Nz24znf3Dh3L/MdeyOmZZe+3+dAxvQ8YdYV1BbofgrGOe28Ltpe1yVwzBWcZ7QUM6UMNfZtPTVlebDAfzrCrEMEP8rkJokrR+VsHZkmohwHYcTVjsPktPywlvqeXafeVur+1fCRGKLrULOBn5YaImAMVjXpMZjQb5bc3C4QsAj4TAft7FiUuJVoBghOlUYFNESq4XoS5wG+yQeWFMpxDvYVOBwFlsW2PbXKKbeGGTy/0e8oH78E168B57mIW1cjsEZx2zwdM1lzuiB84y3gAl/yY3ZNOfgdw1U3Y5hUXxbXqBI53ZQl7Z8nAW67qw4sZPti+QJuD1wH4evvNMJ69SXGIoG/Jucr0IdUGTmhKiObXOyGRxa0exOu7M2yNvHHAW226zYL6oiBfIL5s+5PR3uZxdWLvWDkHUMTuKwQVXtsudsQhHGa+jq8L7LNOc4bNxcf/n6ZRbzBnn9CJMNa1zf88g55NnF2xcPbX0zB3kCORU2M/DmGSBU+UwzVh/H9hnfpOJnUWowoBqJKxZm5iVcogLhnERUyeyXHAAZ7Fth+f5yrhN1v5nLmcW1q61Q3DUMWOVfYY5WZ0ycJbxOrsq2JDKhmz6c8nz5Of/Z5x/U3clWqnkQLRuutn3fRcBZRFEUVFRcEHccPf/P2ZIIKSxhYZ5MMs9Z47K9LNjdXVSublVVbo8dsNlowQqtnfsUfUaNTLgBS9EOaKSRpTI0/S6J/73tfm0h+3x8lC3NrESoRJV2uBQBFK9theVulLmbXwDSaPY9g0C0Qb9P1yutKTLCWGtqRwC1zGbLvbCJXcfB0YZr6qqoAAl4LPaTZuxiaL7+x/yveF49xBph0dONHWi5KwULS0SicfuZnep6+65d6R6fN5vWh/bAUqEOsGJmNVKyJOCxxjSneLdKLYtg8NetY5YzAkeOV2dO28IeotMyglre0hhrakcgkHHrENTpJBOHEF8KRmvqqpgxBw5ieLIrHlZa9QKC1qLnP2+mPvxAGhxseLBLYafhPgi4SxNPOSBKNVR152hQXTDzRo9HM/1uYUmZkqEKhnB8qdOFBX0iYekut5L9b4Mq9+NYtsu9m+dgNu7vMvJp3YhWdZEijaFe1T+pss5sGsuh2DQMffxoM5w4sInkGdGGa+qqqAA1GguskGy5mWtUVeL54SzrVq7XAA7j2UN27oO193YbYezY/24IYF0hCjdoel1e9xV3FFOtdmtawUD+g9F4CXyYnwJ7PAfTif6wgdGwWhATIguxI1i2wPsUeFMg9PwRrDFLicdLIbJ35/YXA3tHVz8HZdTwtrZcggGHXMFPfEMa3XaQadCxFK4mpXxDs3EFnBtsehb8bLWiCm34pyt5GiNNSjMLufFgXi4CTpBOJQ8xBdNYjlvRbK1b/74zROf0MTCKq87wiNRCYwokh6/nY80HxoKP1zOjaNJrOgT6/vN7nH2Q2TFN+HcPu6mgaZuFNvuQagCO0DGeMq6jHgpB2iesdsOaUMIo7C6yxmFtT/KISgdcwa91v2uD9inahD5/XAbndysjFdVVVAWX1SF57tH1rysNTJ4pimY5GgXu1zBi4NCVbcdOvWLNABtl9E9/GKJi5CAzccN8kknJ0T7aSJ53Ra8RG/81XY1rUtW5mH7IUINRsefFB74UO7z4Ejd8cEmwOHs6jNi2+fxLF4vGRb+yiKXUzlFVUTRHPt3XPfzP+qCKreMeYlY3cxBSgPSrpdiVXHK/vPyAuMcrpdiAzb+mcoQd6ou5XJqdlbC2h/lEJSO2QMBoW/tZ9W+Xsl4DVUVLFxOSfSX5WVDhslyrKtg0qr6AEnyCKaG/w/F0aoimmaXO41yvaUsVR/94AR0B+5Ec7t4ky7vhoWWbrcYvhzWki1e1v/Rc2HzX38yokKLW5p7ckv42tMCwaRiM5UItd+XM5UYI/PG9+LyN9RD545h/YfYloV0UwORAJy0hGEZsrtoiEG+PMMrdPgO+H04PNJwTiYOko6CWqlKOex1kb+MwM1mLw/54LyqMpzuQ/jJ8QcivShRzg6k/UuQJLp0OSWsNZVDUDrmo5S9+NbwTmzjT16YZLyqqoLFwqpexmV5WV/n9KGzRXT7/uSCj8hWyj8c88DPB42ffxz2iAo44pyt5GgXuJwC453OZfuM61ry+rJxz5tnHPDeGVfZ0WGO3Z2+8fGzN94kQXZT8ImRqXJi8/GwmTxiP9pLZXihqPucFWHbAx4ApIJ0AB/yQASamYPswhnFLb2OL9E+8DRzeRLO0xRqBOAkEuVhnNZ0IUh6EPYT+JZwuQJAGwaQtAg5luNlg4JJ9dMJEPUhV20jAoTIC7jTQIDaPiIPwpyzlRytpcutDpk24blYuuJ9dDMS2BZOyAzz0oZ38mptG4XgOEWBuJ2OgcYBuqyL158cpAfteiHVGX3FdwapCz8OjJdXP5wxOkCFAdvsCLaC010n24OPGnAwh+ZbQpJY/ydcrmWxl1+Ol03hku5wTnYUWRwXfPnPcA+DlyiLGOUPibzY4SSa5GjnuNw66WfrcmrMuZlZ7gVXS/H/KPJYMF0dYOsWutjxvOCAHPBSEsOfHGScs5hh2PrwVW04Iqa9Gy8f4Ej87zrgpWN4P0Xw405R0dnoIRJYwuVs/4TL3VmT5Na8rNgyMi1L9ocCsRYl4CGKnIz3jA54KNok+oSDXyc42rkud/WxbpJ0sOitcgARd7Z7du1fb/ueI4QXTMSK8ERWnM42/bg/QJ0o4qYzfFIcA8rA8ZOD3OFGdyCWwxvpPFjrzFweR5KH+P0LJPi13jGR6b4hN+By6Ipu/Zdd7tPC5ZbjZQ+jnClLkd1HHDf8x3SPcCoea4XaJyRsmOqQ4GgtXG6dJGlmUezQb/dui26D9lFIH7O3ZZGyyKWPMmNRpCwejQQzImVxmrPYGCU6nh3v7I3EXeXb/ffiFDtxngM5Fs1wzQwXf4lfOlLfJFwyRhPzPWsijWcHX0g7aeIkzxeeKIfdnxzkJc6J7ND5ekH5bSK3y3h5i398j5gXR9zqoQBuSRzElMAd7tVr7XI54PlFDfqtO8ndzG6XmolpQ7tyeSweEtqh++dJN7uvkD/jreT6VUaLgEUUZNpOy/KyJSdRBSXpck1UKIdvCn4w4lM8udvC5ThnKzjauSTJlWvdJOkrLhcaoOPErxDqXJG7+MDVuR2uzlXiXIODfk+0uVfcQW9lSq0/JlsJThWpBV1nbCb7oD8OMappID7EfiPsTFMYHgrAIfZs/cEMB+nRPrZukaUAX2CDbqIsmOHyCwRrAzu2XrFDXFDDeti/3kaU7nFyHepq+0u5nPHPF9pkVyQihMm8AI7QhIo0znyep3DOMV3+I9hL2ROl4ulueO94q/E0DPkDMY9NFxa30Vyk7LQsL1vCaTiKa+lyL7A33EjSAdzxPQ0hKuJlq4izycIaSVu43DpJ0kecYRHc++HjRstfKRAH6z9+1p7i9w4ucNy9vSqWeONKX5sX4Mg/RIK9m3EBjvf9EVVztHMssh6HE21gJuCNjXvPppFcpqShZzKT9X3IsDSEMPQZAfLweaqTond8znCQ9wB6F1RHmCjV5vSjx3j5Dr9Kc9v4jxmc0WsQgE8j2gUAe4U4PrPRRiPOodpHirG/jt4PP5DJ1XVaAazaz3keR3Tv3bhuyflReJcLQ0dm6wVdUQ0zcH4Adg43lzenUyOId1i8woeJ/Il0DCExWsTLlgAgPM1DZwmI7FA9C6CtDSkm7rwjOFvJ0XLY4DBJCUwkqWT5FOu5CjxwqI6kRSFqtrtTnFjpuD6i2ExF+jRCi4SIeZrgPEYC4u/yJ3N3T8RajHTfJdFXnL5OCyTAao4v7gix5xb/ZGAjeisS5Q6Y8XKKPQ31QI1CfB6Je4h0f7IijokvvkJ1FcsZCSjuLZmRjParNkw+xR1AnNYMve+JZe5q1/Gt46O32yugVqt9fYVCobu7kbMP/H7/gKt4v7iMNzgNuQLVKdf5Osww7gnjhOQ9h1xYK37+d3ol0x+6/5w0F/QzHpnXh3dVB7crCY527hlr0WUmSSXLZyJJLaBuMR/YzI61h8wqyct67vUu+Xz+tp8dTar2dGr0PrgeeMpNlPdrvUm7TzgJzksb8XXq+TJZuxMLVYH9OufEAp+DUC05nqxHk87tfnG7WZLrWfbf3j7QHFj07WQ2+hHL/S30cWxSr5hIUsnyGVjPtbWs2lBqemfhkcfuxFrsvORzwoD8aOX2TWT+2eIoaJfL+kfUiRWg6hC6E9nT7tHxffy6xv4rJMmiV7WpxEv8COCVvzxfvIE236adj96f7ugFuhq9QYkoRq/mjaFTN69OM9Oo+17kRAyrFhLNbMRMkkqWz0iSLg+Gg4UGsNEGEEFmiQICTEQ3vubt3k/siI2x2Bk7tvjemG+Oefh1efn0dD2KwZLDYas1WqBGiTVx3vP7nvf7djjO+MlMt8yntXH39F4wMnJXDUFmEAj9y1QwzUf7e0qXjEJn1/QN/OZz/ChvUoTPW/e8aog9ejcQiTVfohtyPP5sLJKkNtrNisUUlO2YSVLJ8hlJ0hWgdRcaoE4bQAeDpbI1WYz+GSiP2/iBVz9RtnQ56w7AdWbwkgDNgr1UydZ4VbdbAQx7JmLeRJJKls9Ekq7W8ZZdyLB5/EXJ99eP3nT7oOvmZm9vmsp39V0yU8b3ZlFqq5Epa5iO9Uk/T7gi23GaAzXWgr2hfhEVfEhauNz8x9HJTo+FdpXCshs1nSg1iKIJdbs/WfW2g2aSVLJ8ZpLUGqqr8HUeDwNBUfi4oeKHShW8frjlkUft4Ux8Pdcyv5VBKAHoNEwZ3xvFd1uNTFnDJC0dBIHIB1CeYyA11pwzzLlYu9Tlv1gsrPMfh6qKpONKKSxNGRFfiBPlE/J2f9Z/s9Q2k6SS5TOwnqu3a7dhz7brYmLqPBBMPpMG2ABKuJS6ubNJLnbgN5fbxuAGOGWmjO8NotlTI5u1htEetrzojeV3mh6peaxMvGV5Etg6sorl5j8O1XS8in0VErxHaBY1bl5nU93uTwKt77aZJJUsn5H1NMFq453scFbYL/aLWozID9sGXa482RaxYUo82Is7/DrLZcGYQ8PuhOCYURPaGG0ECZ8a2aw1jPa4mqSJfyE1rr3EvNMx+p+oXysYx0o5r9sZeJVi0EUAFjyOB+loBeOLV+xQoW9Mjx9yNlUrydv9WRmcZs9MkkqWz8h6mmB1btctEjHtkj/wRDHNKAbPBl3OMbVY+3kyH1R+60pchE50p3Hb5741uANSTSgTwTeAwxs1MoM1fub1RgpSJFTp40j0YZqMkUrRpIZgVY7V236lDji+pBh0AYC5j0PF3VSH+G6cFV/Ml4C0R6XHJ3Eton11u5Vi+41WRFeyvwTfGouw6eC77tojDyobdLka3DKSvJ4cu/V/c7kr6CIadpPtA4krXEk1oUwE3wDsKTWyH9ZQ9mhNCdNj1F6xw78GJmOkbSBygpAc6zWGlHz/wNWuR4pBFwCY/zjUDGSD+G6cFV8EEqLEiUqPTwpOQ95uJTzsb9Tl1FpdDhMxtMTfSHe4zG10lnucCtEneV8ZMKPLqdImVbH2a2wfDhKPlrBNKhF8A3C71ch+WEPZY2e65b7G8xcaxDdvcowlRHNebMmxNsS1WRApMeh8APMfh4qzKgjTNE353ZnhLh83pMe3OHMrb7dq4daNdrdRBxr3J0QxkXkTbhKdI7lRl1M7P/jFl4H2a63I7Pi7W1RcPe6XW1JNKIWJG0Darkb2wxrKHmdTleETtkKIE3WRk2M8AZ+W9uRYt+CXFbCkGHQBgEWPIzxVkuwpJeZ+UET33alV4vgiHacWt7Pg/kzi94vaMMBoBjtt/e+3ogxgaEv0RMCc4A9ZQ26DLqdiIi3OxLE8Kr+5XAljuzpFxlwML0JNqISJm0DqRI3shzXUuO9QJvJkvdyVAgG8CNWKHGNas1EFR1L5eIyMeG0KJMWgiwDMfxyKM/uUCztXWJ4Gx0vt1CovGFAf5QW3szxUM4vfWR6A73U2IfP9T4oF7wDRDNF37JTTjazk1P8Bl/MAqNFO/BLQCr+43KEYW0Y7Ib481LE3URMqYeL64UsYR6asMVuJqoNPGuDhMQ4Xy40V7jk5xl6Pe8CtVD6eCZfrwkZKDDofwPzHoU9DiZDMROUKy3JQzKUvU6vwQ4ELdNXtVsHN4e+BRh37rbLmqhtbdZ4wWh3qARe8jGgQ1b1jR87Rpl1OCumrPBRhvy6sdu1OZyEXQmSH4/UA9omaUAkT14/et3Fkyhqz1HgIzri+jzycAWKaL35+gws5Rp48xdAUY50urF1ckBKDWp/imh+H8cg9icvHWjjVY3xhLWvdpyvtozC1yg5e6REH6narx7Lmn/s8km4YD1NfS31aFeZe6peOTStJzIqM2Pvcut9XEIgT+aMAXNOM76kwcf0Ibs8fGQEZaSknEJ0kPZ+B41GO0d4moZmfjLWlPYqzO52UGHQunpEi0+MwC82+IJAVCkvxS4MZZZV7LUc57V7dbnUGwxzbXsBBFIMYUV14BavTbCI6W1K1+bKoKAnQoA3gbWEQsKe2r4NyIhv2CnO34p76C5NqQilMXD9cRZqPiCJmHt/sqdMwtCS38/Wr7YjJMcYCPBqokRwrGysjiaQYdAGwSLyam6bCsLft8nHLNv7lhVw/FGBGq3jFf+p2q1Ph5p5eMTxzZwmFHgJuaAFi3TzaZ6QS0UXeupVq07qX/AEitAEAFxYu9+/hYX9hFPJAM0hquKf14XHhoXbF4l4bKemYOpQ7lhd/WIsU7gHY4aA3RLJOxGQiusxbt1BtWqfre4AMrR1PwL/YUMOafZ+PiqkBmf+jR+sDFqqAYojTxnHl+n07EQIAV4C24MrVtTObZmf+NrwyEV3mrS+j2vzC3eImabRuMCCwmsvptBGsrh+kkqlIfsFGa8PZYmsH8EQmkmzduM3/Ln5PYrvbqBJtoUU06A+xtw3tjGQiusxbt1BtWudOkw680ZrxgBQt6XLMzuecWPScTFAStPUK6hC22Ph80qbgt2j0+okkmUkyKZtbCFW50OrfdLXfXe5yst5t4VGW07v1EMlEdJm3bqXalK/O4qUkSWtFGWDLulwIKPDgLkgmKAkarVNQp2PPKijQaTMIAGcWlRVqZCbJpGxuDkyVCy3+zQHYr9RwHAGjy13i20NUqMlEdJm3voxqM2axKW0B17RGHAAXtKzL7QF+oubvTurOzxGprbE8sxltbKhD5SVwYl0/Zg5JxsgCzGwaZtmA08hTxvE6cbmYiPK1h/vBi8spE9Fl3rqValMWoViILeBQp3XhBgjQ0i63DTSIRWWZcKaIIIMEjQrmFip/Gzk4rPucB2jt8CaACLMqWOWneSQZUWvAR3cgxiYrFkrtnoAyzUy2pUc3yA/NxaPbE9eR58zP8Ki+6ZGkTESXeetWqk2ZuLgYQ6yNnrsDELOeB9UrBpxSAEBIEj+SCJpK0NjeB4L3phYqfxcVPFs3TUW3sOY1NQvAbvVLr5GhX0kyIZtLa69Eu+MJRFYslNq9mcqForKj+EgWPDTKDxuI/epy5PlRQr46GPKPZCK6yFu3Um2q4MUCNhcA5zNb3B6P6Xq1WuWFReZcWN8HENFpeZfTcaPxknJFbE2JnwkRJCVoVES7nHeaWqisvUaLwmMHQOK6SmtCtZEGgB1pNisK1UySCdncF3r6OW6EfWXFQqndM1QulJUd+Uey4KGSHyq/NpzGrB0IkyU+sSbckTXCUMHLbhpVO15RJkn8SCJoIkH7wolO7RtTC5XVoXaFVmBbMEKL3By+82pL42JLL7zWkmzJpuo08UJN5bJIHy01x2U4XFBoPpIlnlGhX0mycWHCHfhw0ycBWbFQavdU5UJZ2ZF/JAseGuWHNdxt3OW03aXC6stv/CHyuwGilVwujGQX17CTM02S+JFE0ESCtsu/69hnW6j8Ae6W0gpUA2dprA2u85ZnOf1+jn4lycb1k1gEQUkSyoqFUrunKhfKyo78I1nw0Cg/9KO1nMs9BtbQ2XotYHrBJmoNffrveL2lQCWnotUVXS4NWxIdOMit6ZL4kUTQRIKWAOeO7LMtVP4ALQxoBbD6RSwwqF03HHtvt8Wm/Sb4oMESzk4qcfUmK1otB9X8ykySUaojfkBUuq6sWCi1e6pyoazsKD4SpcCk/NAcWcjcWYV7FaGd3NDKUIms/y2oRtjOINkAeGgXA0n8SCJoIkG7QozoxEczLVT+AE/r2Y4y/t7FXjP+u9rw+rIhSp6F/JnXWCVns3gDLQ6fTSSZlM3FETmWlS5lxUKp3TNULpSVHSktXW5WfujBy3yX8yOiOOI0/V0Es/TfwpT/vkCWKIgUf6uPJPEjiaCJBG0L24GnKHIzLVT+ALz1yn8VYRCZSDKpR0/CVRFZrwKyYqHU7qnKhbKyo/hIuJySH0picp7LMZ+hfEPnkFaGopf/W9jVpmz7ObezKIl7KIkfSQRNJGhVN4AgHDMtVCxgFS/9VyGroJlJsv007fQ8RHpKY8QhKxZK7Z6qXCgrO/KPZMFDo/yQ4WCuyz1DyjxYnT6yJGlBlS+buRK9ZW07EbvxY7Py+L+FqWX1Z52Iib+xkZTEjySCpASNDeKf9FSZaaHyB+D9af+r4Gkqc0gyRrKjCQnIioVSu6cqF04rOxJTBQ+N8kNneZ7LVT80ETJTf1uDW7slSQvKfNlYCkCJqqcAwOTHv6Y0/bfwV3tn25Q2EATg24QAAYJA2sibKC8FJQ6giAQKFXAq0lGo/v8f091AlNbgaIeQ07nnAx9umIFJNsndZvee8SHzjxRIjFdKOtsB6nxTyNXhvEaZUWkEvYYMKbZKCz71y6pwYWgtNgDdqoLpDLv6K/iCLma/oFsst5Q7bAcMGxtCTgnLxoBm1leUtk5DxUkLOv2yU4hMyLilUdFwlDnDrjsi8MV7dN6f68fX8e1MRSLuISflgZCl5bpNgbaTFnT6ZcmCF4nTkwL0wmtdu40h44uSynyDlKfcshsD/L7mHnJTkIOpq0vIsfyhncyzVjma537ZvhWB8FcWmB3JEHKGXTji7SDne8w3qMGJW3az0Cup7iFnQdvOW3ZZCq4ZlYs4IbfeLzuzv8X64czzsNuklC/KHeYfGd4WUy+KOrymGnYPOV1W7JtbluVgYRkJGDtpQadftnrx/aQE1lmnmwyB9jT8AWYv79vr50Oe1i2sJT3jHCS3d6yTVUfCEWmdZYCwPnfSgk6/bB2QRP9OBuTsaZizBSLjbT2z4O3134vmM68JgeIWcs6OBQGKMuXkwZyYdlpwvT23dvWTmoAmD4Vk0Rn+EGkBP7M2VFfDLfKA7QADYl4XL32TGV/4mZuW4Jzxitf/zb2WJuHBTOOes5CLLkaKeRyQmB/Qth+8Qi0Db+MY1TcxMt9QGYvxs7B036OdFI1ULdtI1SNLSTZIJip0/I1RRdWsW12y/JlxSHs9uU0B4wljTUB5+EXVEx1thFqg1n55qXAZUPGtXXt7SwVBaOuK1fpUELQVpnDKeIXKil4imdFY/CF9GqqkxvPHi0xHDcPfyGjxXNCBHGorwdLjkW2QbQUjWTQEDhM9dPyQzROQwhRmL4w6W6bJ11tF8xSgvZIn2j6qcqNFrjiSJ+rqDR4ZcEeW6cj2OiToXF2+ZK26b6JlqEvGqtu0HaK/0dGJQTopBiT3OnRekX4V6VOZ5pJXs3azWmpEtPX4kr/ow2yr/KOaqoTQnYeO12nflN73E0pNMsHyutCoDgHGFbP4mySn9qPjmqRpxuzfR8fIVsQeukSn+x2g1+vpiKra1TncchDUw2sSPY2sd/eVkJFO5qbK1s7iIMbW0DwIuQocs0+LJB2bCoYnaVWxMj55ckYOtVNSqFm2f/3bsgMGKSHzefAyyrhFyefHqfYsndyLFtmO0PbZ1rE4rhAT+M7Ig5CjbTQFgh2+2O2CwgSCzTvEvwL1yxfNiaIc9KM4ffkau9ujGQyKYK9R1Y6a9tze3h0u12rR/oEyMYsBe80Wj3C2fBDwQ2AOEMS8CiYNVhmVTiKhq4sbJ6nyH4QXVSYQbCBwCZDNZEakTH9yppdImj5eWtOb53UygKP/G+XfM3Snozwdpd9nJw/J5Vrtu0FbFtCOBY7uel4qMIFgIzmOk0YCgUAgEAgEAoFAIBAIBALBZ+MPi2jQvTDed8sAAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "execution_count": 146, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.Image(\"../examples/svg2roughjs.png\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Annexe A : Miscellanées académiques" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Décompositions d'un MCD" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aussi surprenant que cela puisse paraître, tout MCD peut être transformé en un MCD équivalent dont les seules associations sont des dépendances fonctionnelles binaires non porteuses d'attributs, telles que celle ci-dessous :" ] }, { "cell_type": "code", "execution_count": 147, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t?,1\n", "\t \n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tENTITÉ 1\n", "\tat 1 1\n", "\t\n", "\tat 1 2\n", "\tat 1 3\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tENTITÉ 2\n", "\tat 2 1\n", "\t\n", "\tat 2 2\n", "\tat 2 3\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo \n", "ENTITÉ 1_: at 1 1, at 1 2, at 1 3\n", "DF, ?1 ENTITÉ 1_, XX ENTITÉ 2_\n", "ENTITÉ 2_: at 2 1, at 2 2, at 2 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La transformation peut demander jusqu'à trois opérations que Mocodo appelle _drain_, _split_ et _explode_, et que nous allons découvrir dans la suite." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Vider les DF de leurs attributs" ] }, { "cell_type": "code", "execution_count": 148, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tProposer\n", "\t\tdate proposition\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tAttribuer\n", "\t\tdate signature\n", "\t\n", "\t1,1\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tSoutenir\n", "\t\tnote stage\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntreprise\n", "\tnom entreprise\n", "\t\n", "\tadresse\n", "\ttéléphone\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tStage\n", "\tnum. stage\n", "\t\n", "\tsujet\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tÉtudiant\n", "\tnum étudiant\n", "\t\n", "\tnom\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDate\n", "\tdate\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Entreprise** (nom entreprise, adresse, téléphone)\n", "- **Étudiant** (num étudiant, nom, _#num. stage_, date signature, date, note stage)\n", "- **Stage** (num. stage, sujet, _#nom entreprise_, date proposition)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Entreprise: nom entreprise, adresse, téléphone\n", "Proposer, 0N Entreprise, 11 Stage: date proposition\n", "Stage: num. stage, sujet\n", "Attribuer, 11 Étudiant, 01 Stage: date signature\n", "Étudiant: num étudiant, nom\n", "Soutenir, 01 Étudiant, 0N Date: note stage\n", "Date: date" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le MCD ci-dessus comporte trois associations de dépendance fonctionnelle porteuses d'attributs. Certains auteurs considèrent cela comme une anomalie. En tout état de cause, Mocodo peut les déplacer dans les entités distinguées par les cardinalités 11. Nous appelons cette opération un « drainage des dépendances fonctionnelles » (option `-t drain`). Notez bien que ces deux MCD sont strictement équivalents, et produisent le même MLD." ] }, { "cell_type": "code", "execution_count": 149, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tProposer\n", "\t\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tAttribuer\n", "\t\n", "\t1,1\n", "\t0,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tSoutenir\n", "\t\tnote stage\n", "\t\n", "\t0,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEntreprise\n", "\tnom entreprise\n", "\t\n", "\tadresse\n", "\ttéléphone\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tStage\n", "\tnum. stage\n", "\t\n", "\tsujet\n", "\tdate proposition\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tÉtudiant\n", "\tnum étudiant\n", "\t\n", "\tnom\n", "\tdate signature\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDate\n", "\tdate\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Entreprise** (nom entreprise, adresse, téléphone)\n", "- **Étudiant** (num étudiant, nom, date signature, _#num. stage_, date, note stage)\n", "- **Stage** (num. stage, sujet, date proposition, _#nom entreprise_)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --mld -t drain" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La première version peut être préférée pour des raisons de localité sémantique (_date proposition_ dans PROPOSER), ou honnie parce qu'elle complique la définition d'identifiant d'association. Mocodo n'a pas d'opinion sur la question. Par contre, il se refuse à procéder au drainage _via_ les cardinalités 01. Dans le MCD ci-dessus, le placement de _note stage_ dans SOUTENIR indique qu'un étudiant peut ne pas se voir attribuer de note de stage ; si elle était mise dans ÉTUDIANT, la connaissance du fait que ce champ autorise la valeur `NULL` serait perdue." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Décomposer les DF n-aires" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "De même, d'aucuns regardent de travers les dépendances fonctionnelles d'arité $n$ supérieure à 2 :" ] }, { "cell_type": "code", "execution_count": 150, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRécolter\n", "\t\n", "\t0,N\n", "\t0,N\n", "\t1,1\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tBataille\n", "\tnom bataille\n", "\t\n", "\tlieu\n", "\tdate\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tVillageois\n", "\tnom villageois\n", "\t\n", "\tadresse\n", "\tfonction\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTrophée\n", "\tnuméro\n", "\t\n", "\ttype\n", "\tétat\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Bataille** (nom bataille, lieu, date)\n", "- **Trophée** (numéro, type, état, _#nom villageois_, _#nom bataille_)\n", "- **Villageois** (nom villageois, adresse, fonction)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Bataille: nom bataille, lieu, date\n", "Récolter, 0N Villageois, 0N Bataille, 11 Trophée\n", "Villageois: nom villageois, adresse, fonction\n", "\n", "Trophée: numéro, type, état" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Toute DF de ce type peut en effet être « fendue » en $n-1$ DF. L'option `-t split` génère le MCD équivalent :" ] }, { "cell_type": "code", "execution_count": 151, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRécolter\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRécolter\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tBataille\n", "\tnom bataille\n", "\t\n", "\tlieu\n", "\tdate\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTrophée\n", "\tnuméro\n", "\t\n", "\ttype\n", "\tétat\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tVillageois\n", "\tnom villageois\n", "\t\n", "\tadresse\n", "\tfonction\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Bataille** (nom bataille, lieu, date)\n", "- **Trophée** (numéro, type, état, _#nom bataille_, _#nom villageois_)\n", "- **Villageois** (nom villageois, adresse, fonction)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --seed=3 --mld -t split arrange:wide" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Décomposer les associations n-aires non DF" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Toute association non DF d'arité $n$ peut être décomposée en un ensemble de $n$ associations DF : elle-même se voit alors remplacée par une entité liée aux $n$ entités originales. Cette nouvelle entité est appelée « entité-intersection », « entité-associative » ou « Gerund », cf. Song, Il-Yeol & Evans, Mary & Park, Eui Kyun. _A Comparative Analysis of Entity-Relationship Diagrams_. Journal of Computer and Software Engineering. 3 (1995). Dans les cours de [Laurent Audibert](https://laurent-audibert.developpez.com/Cours-BD/?page=conception-des-bases-de-donnees-modele-a#L2-3-3-b) ou de [Patrick Bergougnoux](https://gestion.pumbo.fr/boutique/livre/modelisation-conceptuelle-de-donnees), l'opération est illustrée à l'aide du MCD suivant :" ] }, { "cell_type": "code", "execution_count": 152, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tProjeter\n", "\t\ttarif\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFilm\n", "\tid. film\n", "\t\n", "\ttitre\n", "\tdurée\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSalle\n", "\tnum. salle\n", "\t\n", "\tcapacité\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCréneau\n", "\tnum. créneau\n", "\t\n", "\tdate\n", "\theure début\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Créneau** (num. créneau, date, heure début)\n", "- **Film** (id. film, titre, durée)\n", "- **Projeter** (_#num. salle_, _#num. créneau_, _#id. film_, tarif)\n", "- **Salle** (num. salle, capacité)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Film: id. film, titre, durée\n", "Projeter, 1N Salle, 1N Créneau, 1N Film: tarif\n", "Salle: num. salle, capacité\n", "\n", "Créneau: num. créneau, date, heure début" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La décomposition est invoquée par l'option `-t explode` :" ] }, { "cell_type": "code", "execution_count": 153, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjeter\n", "\tid. projeter\n", "\t\n", "\ttarif\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFilm\n", "\tid. film\n", "\t\n", "\ttitre\n", "\tdurée\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSalle\n", "\tnum. salle\n", "\t\n", "\tcapacité\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCréneau\n", "\tnum. créneau\n", "\t\n", "\tdate\n", "\theure début\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Créneau** (num. créneau, date, heure début)\n", "- **Film** (id. film, titre, durée)\n", "- **Projeter** (id. projeter, tarif, _#id. film_, _#num. salle_, _#num. créneau_)\n", "- **Salle** (num. salle, capacité)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --seed=8 --mld -t explode arrange:balanced" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le nouveau MCD a vocation à être retouché pour :\n", "- se rapprocher du plongement initial ;\n", "- nommer judicieusement les nouveaux identifiant, entité et associations créés." ] }, { "cell_type": "code", "execution_count": 154, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tConcerner\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tAvoir lieu dans\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tAvoir lieu pendant\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tFilm\n", "\tid. film\n", "\t\n", "\ttitre\n", "\tdurée\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjection\n", "\tid. projection\n", "\t\n", "\ttarif\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSalle\n", "\tnum. salle\n", "\t\n", "\tcapacité\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCréneau\n", "\tnum. créneau\n", "\t\n", "\tdate\n", "\theure début\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Créneau** (num. créneau, date, heure début)\n", "- **Film** (id. film, titre, durée)\n", "- **Projection** (id. projection, tarif, _#id. film_, _#num. salle_, _#num. créneau_)\n", "- **Salle** (num. salle, capacité)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Film: id. film, titre, durée\n", "Concerner, 11 Projection, 1N Film\n", "Projection: id. projection, tarif\n", "Avoir lieu dans, 11 Projection, 1N Salle\n", "Salle: num. salle, capacité\n", "\n", "Avoir lieu pendant, 11 Projection, 1N Créneau\n", "\n", "Créneau: num. créneau, date, heure début" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Décomposer les associations binaires non DF" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Considérons le MCD suivant, qui comporte deux associations binaires non DF, l'une porteuse d'attribut, l'autre non :" ] }, { "cell_type": "code", "execution_count": 155, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tLIGNE COM.\n", "\t\tquantité\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tDISPONIBILITÉ\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tnum. commande\n", "\t\n", "\tdate\n", "\tmontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tréf. produit\n", "\t\n", "\tlibellé\n", "\tprix unitaire\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPAYS\n", "\tcode pays\n", "\t\n", "\tnom pays\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **COMMANDE** (num. commande, date, montant)\n", "- **DISPONIBILITÉ** (_#réf. produit_, _#code pays_)\n", "- **LIGNE COM.** (_#num. commande_, _#réf. produit_, quantité)\n", "- **PAYS** (code pays, nom pays)\n", "- **PRODUIT** (réf. produit, libellé, prix unitaire)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "COMMANDE: num. commande, date, montant\n", "LIGNE COM., 1N COMMANDE, 0N PRODUIT: quantité\n", "PRODUIT: réf. produit, libellé, prix unitaire\n", "DISPONIBILITÉ, 1N PRODUIT, 0N PAYS\n", "PAYS: code pays, nom pays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Par souci de concision, quand on parle d'associations $n$-aires dans ce document, c'est par opposition aux associations binaires ; mais bien sûr, la règle de décomposition énoncée à la section précédente est également valable pour $n=2$, même si par défaut elle ne s'applique qu'aux associations d'arité minimale 3. En réduisant la valeur de `arity` à 2, toutes les associations binaires non DF seront également décomposées :" ] }, { "cell_type": "code", "execution_count": 156, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tnum. commande\n", "\t\n", "\tdate\n", "\tmontant\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tLIGNE COM.\n", "\tid. ligne com.\n", "\t\n", "\tquantité\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tréf. produit\n", "\t\n", "\tlibellé\n", "\tprix unitaire\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDISPONIBILITÉ\n", "\tid. disponibilité\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPAYS\n", "\tcode pays\n", "\t\n", "\tnom pays\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **COMMANDE** (num. commande, date, montant)\n", "- **DISPONIBILITÉ** (id. disponibilité, _#réf. produit_, _#code pays_)\n", "- **LIGNE COM.** (id. ligne com., quantité, _#num. commande_, _#réf. produit_)\n", "- **PAYS** (code pays, nom pays)\n", "- **PRODUIT** (réf. produit, libellé, prix unitaire)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i=sandbox --seed=1 --scale=0.9 --mld -t explode:arity=2 arrange:wide=9" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cette décomposition devient un peu extrême ! Vous pouvez conserver certaines associations binaires non DF, à savoir celles qui ne portent aucun attribut, en mettant `arity` à `2.5` (à lire comme « intermédiaire entre `2` et `3` ») :" ] }, { "cell_type": "code", "execution_count": 157, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tDISPONIBILITÉ\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPAYS\n", "\tcode pays\n", "\t\n", "\tnom pays\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tréf. produit\n", "\t\n", "\tlibellé\n", "\tprix unitaire\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tLIGNE COM.\n", "\tid. ligne com.\n", "\t\n", "\tquantité\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tnum. commande\n", "\t\n", "\tdate\n", "\tmontant\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **COMMANDE** (num. commande, date, montant)\n", "- **DISPONIBILITÉ** (_#réf. produit_, _#code pays_)\n", "- **LIGNE COM.** (id. ligne com., quantité, _#réf. produit_, _#num. commande_)\n", "- **PAYS** (code pays, nom pays)\n", "- **PRODUIT** (réf. produit, libellé, prix unitaire)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i=sandbox --mld -t explode:arity=2.5 arrange:wide --seed=1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Décomposer en créant des entités faibles" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ci-dessus, le processus de modification a créé un identifiant _id. ligne com._ qui peut sembler superfétatoire. Si l'on a introduit la notion d'entité faible, on aura ici avantage à utiliser l'option `weak` :" ] }, { "cell_type": "code", "execution_count": 158, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tDISPONIBILITÉ\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPAYS\n", "\tcode pays\n", "\t\n", "\tnom pays\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPRODUIT\n", "\tréf. produit\n", "\t\n", "\tlibellé\n", "\tprix unitaire\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tLIGNE COM.\n", "\tquantité\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCOMMANDE\n", "\tnum. commande\n", "\t\n", "\tdate\n", "\tmontant\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **COMMANDE** (num. commande, date, montant)\n", "- **DISPONIBILITÉ** (_#réf. produit_, _#code pays_)\n", "- **LIGNE COM.** (_#num. commande_, _#réf. produit_, quantité)\n", "- **PAYS** (code pays, nom pays)\n", "- **PRODUIT** (réf. produit, libellé, prix unitaire)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i=sandbox --mld -t explode:arity=2.5,weak arrange:wide --seed=1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Cas des agrégats.** Comme on sait, la notion d'entité faible peut souvent permettre de se passer de celle d'agrégat. Ainsi, le MCD ci-dessous :" ] }, { "cell_type": "code", "execution_count": 159, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRéservation\n", "\t\tdurée\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tChambre\n", "\tnum. chambre\n", "\t\n", "\ttarif\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tréf. client\n", "\t\n", "\tnom client\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDate\n", "\tdate\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Chambre** (num. chambre, tarif)\n", "- **Client** (réf. client, nom client)\n", "- **Réservation** (_#num. chambre_, date, _#réf. client_, durée)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t --colors ocean\n", "Chambre: num. chambre, tarif\n", "Réservation, /1N Client, 1N Chambre, 0N Date: durée\n", "Client: réf. client, nom client\n", "\n", "Date: date" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... se verra décomposé de la même façon que sans agrégat, à ceci près que la patte distinguée par le `\"/\"` produira une cardinalité `11` au lieu d'une cardinalité `_11` :" ] }, { "cell_type": "code", "execution_count": 160, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tChambre\n", "\tnum. chambre\n", "\t\n", "\ttarif\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tRéservation\n", "\tdurée\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tréf. client\n", "\t\n", "\tnom client\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDate\n", "\tdate\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Chambre** (num. chambre, tarif)\n", "- **Client** (réf. client, nom client)\n", "- **Réservation** (date, _#num. chambre_, durée, _#réf. client_)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --mld -t explode:weak arrange:wide=5 --seed 21" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cette décomposition peut quelquefois se simplifier. Ici, on remarque que l'entité DATE est indépendante et réduite à son identifiant. Si l'on anticipe sur le passage au relationnel, on peut donc la supprimer, après avoir pris soin, bien sûr, de déplacer dans RÉSERVATION son unique attribut. Celui-ci renforçant une entité faible, il doit garder son caractère identifiant." ] }, { "cell_type": "code", "execution_count": 161, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tChambre\n", "\tnum. chambre\n", "\t\n", "\ttarif\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tRéservation\n", "\tdate\n", "\t\n", "\tdurée\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tClient\n", "\tréf. client\n", "\t\n", "\tnom client\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Chambre** (num. chambre, tarif)\n", "- **Client** (réf. client, nom client)\n", "- **Réservation** (_#num. chambre_, date, durée, _#réf. client_)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Chambre: num. chambre, tarif\n", "DF, _11 Réservation, 1N Chambre\n", "Réservation: date, durée\n", "DF, 11 Réservation, 1N Client\n", "Client: réf. client, nom client" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conclusion sur ces décompositions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Comme l'indique le nom que nous leur avons malicieusement attribué, certaines de ces décompositions peuvent littéralement faire _exploser_ la taille du MCD. Cependant :\n", "\n", "- elles sont intéressantes d'un point de vue terroriste théorique ;\n", "- elles peuvent permettre la détection d'erreurs de conception ;\n", "- elles peuvent conduire à des améliorations fines du MCD (c'est ce qu'on a fait sur celui des réservations de chambre) ;\n", "- elles décrivent le MCD dans un langage volontairement appauvri, ce qui le rendra plus aisément portable dans une autre notation, comme UML ou _crow's foot_." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Équivalences avec la convention _Look Across_" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La convention _Look Across_ s'oppose à la convention _Look Here_ de Merise. Elle est plus répandue internationalement. \n", "Nous allons l'étudier par le biais du formalisme graphique de Chen, auquel cette section pourra du même coup servir d'introduction pour les personnes familières avec Merise." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Associations binaires en _Look Across_" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Cardinalités minimale et maximale" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Considérons un MCD Merise avec une association de dépendance fonctionnelle :" ] }, { "cell_type": "code", "execution_count": 162, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tTravailler\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEmployé\n", "\temployé\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tDépartement\n", "\tdépartement\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "Employé: employé\n", "Travailler, 11 Département, 1N Employé\n", "Département: département" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans le formalisme graphique de Chen, cela donnera :" ] }, { "cell_type": "code", "execution_count": 163, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t chen:layout=circo --defer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le 1 et le N correspondent aux cardinalités maximales du MCD Merise. Comme on voit, elles sont permutées. Merise suit la convention _Look Here_ (LH) ; la notation de Chen, _Look Across_ (LA). Comme cette dernière est la plus répandue dans le monde anglo-saxon, nous parlerons par commodité d'« ERD » (_Entity-relationship diagram_) pour les modèles conceptuels selon Chen, et toujours de « MCD » pour ceux de Merise." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Considérons maintenant les deux versions d'une association avec des cardinalités minimales distinctes :" ] }, { "cell_type": "code", "execution_count": 164, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tRecevoir\n", "\t\n", "\t0,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tMer\n", "\tmer\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tRivière\n", "\trivière\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "Mer: mer\n", "Recevoir, 01 Rivière, 1N Mer\n", "Rivière: rivière" ] }, { "cell_type": "code", "execution_count": 165, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t chen:layout=circo --defer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les cardinalités maximales sont toujours permutées ; par contre, les cardinalités minimales ne le sont pas. Dans l'ERD, elles sont figurées par un trait simple (0) ou double (1). On les appelle aussi des « participations » : une rivière peut ne participer (directement) à l'alimentation d'aucune mer (ce n'est pas un fleuve) ; toute mer participe à la réception d'une rivière (un fleuve) minimum. Les conventions sont donc les suivantes :\n", "\n", "| Cardinalités | MCD Merise | ERD Chen |\n", "|:--|:--:|:--:|\n", "| minimale | LH | LH |\n", "| maximale | LH | LA |\n", "\n", "La notation Merise a l'avantage de la cohérence ; celle de Chen en a d'autres, que nous verrons un peu plus loin." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Avant cela, voyons un dernier point de détail : lorsque deux cardinalités N se trouvent de part et d'autre d'une association binaire, l'une est notée M :" ] }, { "cell_type": "code", "execution_count": 166, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tREQUÉRIR\n", "\t\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tCOMPOSER\n", "\t\n", "\t0,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPROJET\n", "\tprojet\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tPIÈCE\n", "\tpièce\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "PROJET: projet\n", "REQUÉRIR, 1N PROJET, 0N PIÈCE\n", "PIÈCE: pièce\n", "COMPOSER, 0N PIÈCE, 0N PIÈCE" ] }, { "cell_type": "code", "execution_count": 167, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t chen:layout=circo --defer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Entités faibles et entités associatives" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Maintenant examinons le cas des entités faibles :" ] }, { "cell_type": "code", "execution_count": 168, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t0,N\n", "\t1,1\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tŒuvre\n", "\tœuvre\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tExemplaire\n", "\texemplaire\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo\n", "Œuvre: œuvre\n", "DF, 0N Œuvre, _11 Exemplaire\n", "Exemplaire: exemplaire" ] }, { "cell_type": "code", "execution_count": 169, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t chen:layout=circo --defer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'entité faible, à l'instar de la DF « renforçante », sont entourées d'un double trait." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le vocabulaire de Chen comporte encore un élément graphique, que Merise ne distingue pas spécialement. Nous avons vu que toute association non DF pouvait être décomposée de façon équivalente par l'insertion d'entités faibles :" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "Produit: produit\n", "DF, _11 Ligne de commande, 0N Produit\n", "Ligne de commande: _quantité\n", "DF, _11 Ligne de commande, 1N Commande\n", "Commande: commande" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "LIGNE DE COMMANDE est quelquefois qualifiée d'« entité associative », et Chen la figure logiquement par un losange (association) inscrit dans un rectangle (entité). Graphviz ne prend actuellement [pas](https://stackoverflow.com/questions/48046186/is-it-possible-to-have-nested-node-shapes-on-graphviz) en charge cette représentation. Nous l'approximons donc ainsi :" ] }, { "cell_type": "code", "execution_count": 170, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t chen:layout=circo,mindist=2,scale=0.6 --defer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Associations n-aires en _Look Across_" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Triplet NNN" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Passons maintenant aux choses sérieuses. Jusqu'ici, la différence entre les deux notations était purement cosmétique. Qu'en est-il des associations n-aires, et que signifie _Look Across_ quand _across_ peut désigner plusieurs endroits ?" ] }, { "cell_type": "code", "execution_count": 171, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tAppliquer\n", "\t\n", "\t0,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEmployé\n", "\temployé\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjet\n", "\tprojet\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCompétence\n", "\tcompétence\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Appliquer** (employé, projet, compétence)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t\n", "Employé: employé\n", "Appliquer, 0N Employé, 1N Projet, 1N Compétence\n", "Projet: projet\n", "\n", "Compétence: compétence" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans ce cas (le plus usuel), la question ne se pose pas vraiment, ou du moins n'est pas tranchée de façon évidente :" ] }, { "cell_type": "code", "execution_count": 172, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t chen:layout=circo,mindist=1,scale=0.4 --defer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quels sont les autres cas ? Il ne s'agit pas de ceux où l'association n-aire est entourée d'une ou plusieurs cardinalités maximale 1, puisqu'on pourrait alors la décomposer en DF binaires (cf. argument `split`). En réalité, on touche ici aux limites de la notation _Look Here_, plus intuitive, mais moins puissante que _Look Across_, et que Merise n'a pu rattraper que dans sa version 2.\n", "\n", "Pour répondre progressivement à la question, procédons désormais dans l'ordre inverse, en présentant d'abord l'ERD, et ensuite seulement le MCD équivalent.\n", "\n", "Les exemples qui suivent (ainsi d'ailleurs que le précédent), sont adaptés des pages 28 sqq. et 96 sqq. de l'ouvrage de Toby J. Teorey, Sam S. Lightstone, Tom Nadeau, H.V. Jagadish, _Database Modeling and Design - Logical Design_, 5th Edition - February 10, 2011 (Elsevier)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Triplet 1NN" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le premier ERD apportant véritablement quelque chose de nouveau a cette structure :" ] }, { "cell_type": "code", "execution_count": 173, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t chen:layout=circo,mindist=1,scale=0.45 --defer\n", "Ingénieur: ingénieur\n", "Gérer, /1N Responsable, 1N Ingénieur, 1N Projet\n", "Projet: projet\n", "\n", "Responsable: responsable" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les auteurs explicitent ainsi ses règles de gestion :\n", "\n", "> _Chaque ingénieur travaillant sur un projet particulier a exactement un responsable, mais chaque responsable d'un projet peut gérer plusieurs ingénieurs, et chaque responsable d'un ingénieur peut gérer cet ingénieur sur plusieurs projets._\n", "\n", "Reprenons ces trois assertions, et faisons-les correspondre au trois cardinalités dans le sens anti-horaire à partir du 1 :\n", "\n", "- 1 : pour un couple (ingénieur, projet) donné, un seul responsable possible ;\n", "- N : pour un couple (responsable, projet) donné, plusieurs ingénieurs possibles ;\n", "- N : pour un couple (responsable, ingénieur) donné, plusieurs projets possibles.\n", "\n", "Avec Merise, les assertions seraient plutôt du type : « pour un x donné, _tant_ de couples (y, z) possibles ». Il y a donc bien permutation, mais permutation des références au singleton et au $n-1$-uplet.\n", "\n", "C'est la réponse à notre question initiale, et nous verrons avec plaisir qu'elle reste valable quel que soit le triplet de cardinalités de l'association ternaire.\n", "\n", "Pour l'instant, résumons les règles de gestion par cette **unique** dépendance fonctionnelle :\n", "\n", "- (Ingénieur, Projet) $\\implies$ Responsable.\n", "\n", "Pour exprimer cela en (ou en dépit de) _Look Here_, on doit recourir à un agrégat :" ] }, { "cell_type": "code", "execution_count": 174, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tGérer\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tIngénieur\n", "\tingénieur\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjet\n", "\tprojet\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tResponsable\n", "\tresponsable\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Gérer** (ingénieur, projet, responsable)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --mld --colors pond" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "… ou à une CIF à unicité complète (ici en notation allégée) :" ] }, { "cell_type": "code", "execution_count": 175, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tCIF\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tGérer\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tIngénieur\n", "\tingénieur\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjet\n", "\tprojet\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tResponsable\n", "\tresponsable\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Gérer** (ingénieur, projet, responsable)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --mld -t create:cifs=light arrange:balanced --seed=3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... Ou encore à une entité faible :" ] }, { "cell_type": "code", "execution_count": 176, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tDF\n", "\t\n", "\t1,1\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tIngénieur\n", "\tingénieur\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjet\n", "\tprojet\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tGérer\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tResponsable\n", "\tresponsable\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Gérer** (ingénieur, projet, responsable)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --mld -t explode:weak,arity=2 arrange:balanced --seed=14" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les schémas relationnels produits permettent de se convaincre que ces différentes variantes respectent bien la dépendance fonctionnelle désirée : (Ingénieur, Projet) $\\implies$ Responsable." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Triplet 11N" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Passons à la deuxième combinaison intéressante :" ] }, { "cell_type": "code", "execution_count": 177, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t chen:layout=circo,mindist=1,scale=0.45 --defer\n", "Projet: projet\n", "Affecter, /1N Site, /1N Projet, 0N Employé\n", "Site: site\n", "\n", "Employé: employé" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Règles de gestion :\n", "\n", "> _Chaque employé affecté à un projet travaille sur un seul site pour ce projet, mais peut se trouver sur différents sites pour différents projets. Sur un site donné, un salarié ne travaille que sur un seul projet. Sur un site donné, il peut y avoir plusieurs employés affectés à un projet donné._\n", "\n", "Autrement dit (en partant du bas et dans le sens anti-horaire) :\n", "\n", "1 : pour un couple (projet, employé) donné, un seul site possible ;\n", "1 : pour un couple (employé, site) donné, un seul projet possible ;\n", "N : pour un couple (projet, site) donné, plusieurs employés possibles.\n", "\n", "En résumé, **deux** dépendances fonctionnelles :\n", "\n", "- (Projet, Employé) $\\implies$ Site.\n", "- (Employé, Site) $\\implies$ Projet." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Voici le MCD correspondant, successivement avec des agrégats et des CIFS (plus de variante possible avec entité faible) :" ] }, { "cell_type": "code", "execution_count": 178, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tAffecter\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjet\n", "\tprojet\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSite\n", "\tsite\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEmployé\n", "\temployé\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Affecter** (projet, employé u1, site! u1)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --select all -t mld:c --colors pond" ] }, { "cell_type": "code", "execution_count": 179, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tCIF\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tCIF\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tAffecter\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t0,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjet\n", "\tprojet\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tSite\n", "\tsite\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tEmployé\n", "\temployé\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Affecter** (projet, employé u1, site! u1)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --select mcd mld -t mld:c create:cifs=light arrange:balanced --seed=3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On voit apparaître dans le schéma relationnel un « u1 » en exposant qui signifie : contrainte d'unicité n°1. Il y a en effet deux clés candidates : (projet, employé) et (employé, site). Seule l'une des deux est devenue clé primaire, mais cela ne veut pas dire qu'on a perdu la contrainte de dépendance fonctionnelle induite par l'autre. En SQL :" ] }, { "cell_type": "code", "execution_count": 180, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE Affecter (\n", " PRIMARY KEY (projet, employe),\n", " projet VARCHAR(42) NOT NULL,\n", " employe VARCHAR(42) NOT NULL,\n", " site VARCHAR(42) NOT NULL,\n", " UNIQUE (employe, site)\n", ");\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t sql" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Triplet 111" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La dernière combinaison obéit à la même logique :" ] }, { "cell_type": "code", "execution_count": 181, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_erd_chen.svg\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t chen:layout=circo,mindist=1,scale=0.45 --defer\n", "Technicien: technicien\n", "Utiliser, /1N Technicien, /1N Carnet, /1N Projet\n", "Projet: projet\n", "\n", "Carnet: carnet" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Règles de gestion :\n", "\n", "> _Un technicien utilise exactement un carnet pour chaque projet. Chaque carnet appartient à un technicien pour chaque projet. Notez qu'un technicien peut toujours travailler sur plusieurs projets et gérer différents carnets pour différents projets._\n", "\n", "Autrement dit (en partant du haut et dans le sens horaire) :\n", "\n", "- 1 : pour un couple (technicien, projet) donné, un seul carnet possible ;\n", "- 1 : pour un couple (carnet, projet) donné, un seul technicien possible ;\n", "- 1 : pour un couple (carnet, technicien) donné, un seul projet possible.\n", "\n", "En résumé, **trois** dépendances fonctionnelles :\n", "\n", "- (Technicien, Projet) $\\implies$ Carnet.\n", "- (Carnet, Technicien) $\\implies$ Projet.\n", "- (Carnet, Projet) $\\implies$ Technicien." ] }, { "cell_type": "code", "execution_count": 182, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tUtiliser\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTechnicien\n", "\ttechnicien\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjet\n", "\tprojet\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCarnet\n", "\tcarnet\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Utiliser** (carnet u1, projet u2, technicien! u1 u2)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --select all -t mld:c --colors pond" ] }, { "cell_type": "code", "execution_count": 183, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tCIF\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tCIF\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\tCIF\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\n", "\t\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\tUtiliser\n", "\t\n", "\t1,N\n", "\t1,N\n", "\t1,N\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tTechnicien\n", "\ttechnicien\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tProjet\n", "\tprojet\n", "\t\n", "\n", "\n", "\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "\tCarnet\n", "\tcarnet\n", "\t\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_mld.md\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "- **Utiliser** (carnet u1, projet u2, technicien! u1 u2)\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox --select mcd mld -t mld:c create:cifs=light arrange:balanced flip:d,v --seed=12" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il y a maintenant trois clés candidates, correspondant à deux contraintes d'unicité. En SQL :" ] }, { "cell_type": "code", "execution_count": 184, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE Utiliser (\n", " PRIMARY KEY (carnet, projet),\n", " carnet VARCHAR(42) NOT NULL,\n", " projet VARCHAR(42) NOT NULL,\n", " technicien VARCHAR(42) NOT NULL,\n", " UNIQUE (carnet, technicien),\n", " UNIQUE (projet, technicien)\n", ");\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t sql" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Conclusion sur les agrégats multiples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notre syntaxe `/1N` pour les agrégats permet d'exprimer simplement toutes les combinaisons possibles de cardinalités d'une association n-aire dans la notation de Chen : il suffit de mettre un `/1N` partout où Chen met un `1`. Chaque `/1N` créera une nouvelle dépendance fonctionnelle, autrement dit, une nouvelle clé candidate.\n", "\n", "Plus rigoureusement : supposons une association $n$-aire $A$ exclusivement entourée de 1N (ou 0N). Soient $E_1$, $E_2$, ..., $E_n$ les entités mises en jeu, et $k_1, k_2$, ..., $k_n$ leurs identifiants respectifs. Alors, noter `/1N` (ou `/0N`) la cardinalité de la patte de $E_i$ dénote l'existence de la dépendance fonctionnelle suivante : $(k_1, ..., k_{i-1}, k_{i+1}, ..., k_n) \\implies k_i$, autrement dit : que $(k_1, ..., k_{i-1}, k_{i+1}, ..., k_n)$ est une clé candidate de la table $A$.\n", "\n", "Notez pour finir que Mocodo permet de spécifier à coût zéro quelle clé candidate sera élue clé primaire. Il suffit pour cela de placer en tête de la liste des entités mises en jeu par $A$ celle dont l'identifiant ne doit **pas** entrer dans la clé primaire. Si c'est $E_1$, la clé primaire sera automatiquement $(k_2, ..., k_n)$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Annexe B : La commande `mocodo`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Paramétrage" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `mocodo --help`" ] }, { "cell_type": "code", "execution_count": 185, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "usage: mocodo [--language CODE] [--params_path PATH] [--input PATH] [--help]\n", " [--version] [--restore] [--lib [URL]] [--output_dir PATH]\n", " [--encodings [STR ...]] [--svg_to {png,pdf} [{png,pdf} ...]]\n", " [--print_params] [--reuse_geo] [--uid_suffix INT]\n", " [--select [{all,mcd,rw,source,text,code,mocodo,cv,mld,ddl,sql} ...]]\n", " [--defer [STR ...]] [--mld] [--transform [STR ...]]\n", " [--seed [FLOAT]] [--title STR] [--df STR] [--card_format [STR]]\n", " [--fk_format [STR]] [--strengthen_card [STR]] [--flex FLOAT]\n", " [--colors STEM_OR_PATH] [--shapes STEM_OR_PATH] [--scale RATE]\n", " [--adjust_width RATE] [--detect_overlaps] [--no_assoc_ids]\n", " [--gutters STR [STR ...]]\n", "\n", "NOM :\n", "Mocodo - Un générateur de diagrammes entité-association.\n", "\n", "DESCRIPTION :\n", "Mocodo est un outil libre destiné à l'enseignement des bases de données relationnelles.\n", "Il prend en entrée une description textuelle des entités et associations d'un diagramme\n", "entité-association (MCD). Il produit en sortie un dessin vectoriel en SVG et un schéma\n", "relationnel dans divers formats (SQL, LaTeX, Markdown, etc.).\n", "\n", "NOTE :\n", "Chacune des valeurs suivantes est :\n", "- spécifiée explicitement par l'utilisateur comme option de ligne de commande ;\n", "- sinon, récupérée depuis un fichier de chemin --params_path ;\n", "- sinon, récupérée depuis un fichier nommé « params.json » dans le répertoire d'entrée ;\n", "- sinon, calculée à partir d'une valeur par défaut, éventuellement dépendante de votre système.\n", " \n", "\n", "OPTIONS SUR MOCODO LUI-MÊME :\n", " --language CODE force la localisation des messages avec le code de\n", " langue donné (par exemple, « fr », « en », ...)\n", " (default: fr)\n", " --help affiche ce message d'aide, puis termine\n", " --version affiche le numéro de version, puis termine\n", " --restore recrée une version vierge des fichiers « sandbox.mcd »\n", " et « params.json » dans le répertoire d'entrée, puis\n", " termine (default: False)\n", "\n", "ENTRÉE/SORTIE :\n", " --params_path PATH le chemin du fichier de paramètres. S'il est omis,\n", " utilise « params.json » dans le répertoire d'entrée.\n", " S'il n'existe pas, utilise les paramètres par défaut.\n", " (default: params.json)\n", " --input PATH, -i PATH\n", " le chemin du fichier d'entrée. Par défaut, les\n", " fichiers de sortie seront générés dans le même\n", " répertoire (default: /Users/aristide/Dropbox/Sites/moc\n", " odo/doc/mocodo_notebook/sandbox.mcd)\n", " --lib [URL] remote directory to use as fallback when the input\n", " file is not found locally (default:\n", " https://mocodo.net/web/lib)\n", " --output_dir PATH le répertoire des fichiers de sortie (default: .)\n", " --encodings [STR ...]\n", " un ou plusieurs encodages à essayer successivement\n", " lors de la lecture du fichier d'entrée (default:\n", " ['utf8', 'macroman'])\n", " --svg_to {png,pdf} [{png,pdf} ...]\n", " génère une version PNG ou PDF de la sortie SVG\n", " (requiert CairoSVG) (default: [])\n", " --print_params affiche le contenu du fichier de paramètres, puis\n", " termine (default: False)\n", " --reuse_geo réutilise le fichier de géométrie de l'exécution\n", " précédente (default: False)\n", " --uid_suffix INT ajoute un discriminateur à un SVG interactif (default:\n", " 0)\n", " --select [{all,mcd,rw,source,text,code,mocodo,cv,mld,ddl,sql} ...]\n", " sous Jupyter Notebook, spécifie explicitement les\n", " catégories de résultats à afficher\n", " --defer [STR ...] utilise un service web externe pour convertir les\n", " résultats de la conversion dans les formats graphiques\n", " donnés\n", " --mld alias de compatibilité ascendante pour « -t » (sans\n", " arguments). Équivalent à « -t markdown » mais, sous\n", " Jupyter Notebook, n'empêche pas le rendu du diagramme\n", " conceptuel dans la sortie de la cellule (default:\n", " False)\n", " --transform [STR ...], -t [STR ...]\n", " crée une nouvelle version du MCD en appliquant\n", " séquentiellement les opérations de réécriture données,\n", " et/ou le convertit dans les formats ou langages\n", " donnés. Sous Jupyter Notebook, « -T » remplace\n", " respectivement la cellule courante par le résultat\n", " textuel, ou le copie dans le presse-papier (pip3\n", " install pyperclip)\n", " --seed [FLOAT] valeur initiale pour le générateur de nombres\n", " aléatoires (default: None)\n", " --title STR nom du modèle, utilisé à divers endroits (système de\n", " fichiers, base de données, etc.) (default: MCD)\n", "\n", "ASPECT DE LA SORTIE GRAPHIQUE :\n", " --df STR l'acronyme à entourer dans une dépendance\n", " fonctionnelle (default: DF)\n", " --card_format [STR] chaîne de formatage pour les cardinalités minimales et\n", " maximales (default: {min_card},{max_card})\n", " --fk_format [STR] chaîne de formatage pour les clés étrangères dans le\n", " diagramme relationnel (default: #{label})\n", " --strengthen_card [STR]\n", " chaîne pour les cardinalités relatives (default:\n", " _1,1_)\n", " --flex FLOAT incurve les pattes rectilignes dont les cardinalités\n", " peuvent se chevaucher (default: 0.75)\n", " --colors STEM_OR_PATH\n", " la palette de couleurs à utiliser lors de la\n", " génération du dessin. Nom (sans extension) d'un\n", " fichier situé dans le répertoire « colors », ou chemin\n", " vers un fichier personnel (default: bw)\n", " --shapes STEM_OR_PATH\n", " spécification des polices, dimensions, etc. Nom (sans\n", " extension) d'un fichier situé dans le répertoire\n", " « shapes », ou chemin vers un fichier personnel\n", " (default: copperplate)\n", " --scale RATE applique au diagramme le facteur de mise à l'échelle\n", " donné (default: 1)\n", " --adjust_width RATE applique à tous les textes calculés le facteur de mise\n", " à l'échelle donné (default: 1)\n", " --detect_overlaps lève une erreur quand des pattes horizontales ou\n", " verticales se chevauchent (default: False)\n", " --no_assoc_ids interdit l'utilisation d'identifiants dans les\n", " associations (conformément au standard Merise)\n", " (default: False)\n", " --gutters STR [STR ...]\n", " définit la visibilité et le contenu des gouttières\n", " latérales\n", "\n", "VOIR AUSSI :\n", " Version en ligne https://mocodo.net\n", " Code source https://github.com/laowantong/mocodo\n", " Documentation https://laowantong.github.io/mocodo/doc/fr_refman.html\n", " Aide-mémoire pour -t https://github.com/laowantong/mocodo/blob/master/doc/fr_cheat_sheet.md\n", "\n", "LICENCE : MIT\n", "\n", "CONTACT :\n", " Auteur Aristide Grange\n", " Adresse Université de Lorraine\n", " Laboratoire LCOMS - UFR MIM\n", " 3 rue Augustin Fresnel\n", " 57070 METZ Technopôle\n", " France\n", " Courriel @univ-lorraine.fr\n", " \n" ] } ], "source": [ "%mocodo --help" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Paramétrage à long terme" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour éviter d'avoir à invoquer Mocodo répétitivement avec une longue kyrielle d'options, vous pouvez mettre celles-ci dans un fichier `params.json` situé dans le répertoire de lancement de Mocodo. La commande:\n", "\n", " mocodo --restore\n", "\n", "... le fait pour vous avec un fichier de paramètres vide, _i.e._, un fichier-texte réduit aux deux caractères `{}` (attention, elle rétablit aussi le fichier `sandbox.mcd` à son contenu par défaut).\n", "\n", "Pour que le style de vos MCD soit maintenu à moindre frais à travers tous vos documents, vous êtes encouragés à modifier ce fichier de paramètres selon vos goûts et vos besoins. Mocodo peut même vous aider à le faire en exécutant la cellule suivante:\n", "\n", "```\n", "%mocodo --print_params\n", "```\n", "\n", "Son évaluation remplace son propre contenu par des lignes de code similaires à :" ] }, { "cell_type": "code", "execution_count": 186, "metadata": {}, "outputs": [], "source": [ "# You may edit and run the following lines\n", "import json, pathlib\n", "params = \"\"\"\\\n", "{\n", " \"adjust_width\": 1,\n", " \"card_format\": \"{min_card},{max_card}\",\n", " \"colors\": \"bw\",\n", " \"detect_overlaps\": false,\n", " \"df\": \"DF\",\n", " \"encodings\": [ \"utf8\", \"macroman\" ],\n", " \"fk_format\": \"#{label}\",\n", " \"flex\": 0.75,\n", " \"language\": \"fr\",\n", " \"lib\": \"https://mocodo.net/web/lib\",\n", " \"mld\": false,\n", " \"no_assoc_ids\": false,\n", " \"output_dir\": \".\",\n", " \"restore\": false,\n", " \"scale\": 1,\n", " \"seed\": null,\n", " \"shapes\": \"copperplate\",\n", " \"strengthen_card\": \"_1,1_\",\n", " \"svg_to\": [],\n", " \"title\": \"MCD\",\n", " \"uid_suffix\": 0\n", "}\"\"\"\n", "try:\n", " json.loads(params)\n", "except:\n", " raise RuntimeError(\"Invalid JSON. Check your syntax on https://jsonlint.com.\")\n", "pathlib.Path(\"./params.json\").write_text(params, encoding=\"utf8\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ensuite :\n", "\n", "1. Modifiez la variable `params` à votre gré en respectant la syntaxe [JSON](http://json.org).\n", "2. Exécutez la cellule pour créer un fichier de nom et emplacement adéquats." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En cas de besoin, vous pourrez toujours ponctuellement passer outre ces réglages en en précisant d'autres en ligne de commande. Plus précisément, chaque paramètre est déterminé:\n", "\n", "1. par sa valuation en ligne de commande;\n", "2. à défaut, par sa valuation dans le fichier de paramètres indiqué par `--params_path`;\n", "3. à défaut d'une telle indication, par sa valuation dans le fichier `params.json` du répertoire courant;\n", "4. à défaut, par une valeur défaut éventuellement dépendante de votre système." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dissection de la commande" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'API de Mocodo a été revue de façon radicale pour la version 4.0. Le nombre d'options de premier niveau a été réduit ; en contrepartie, certaines sont devenues de véritables mini-commandes, avec leur propre liste d'arguments, et une syntaxe et un comportement uniformes. Voici leur définition en ABNF :\n", "\n", "![](examples/option_syntax_2.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Introduisons, sur un exemple fictif, un vocabulaire que nous avons essayé d'employer le plus rigoureusement possible tout au long de cette documentation (et dans le code lui-même) :\n", "\n", "```\n", "mocodo --select all --transform drain arrange:wide explode:weak,arity=2.5\n", "```\n", "\n", "- La **commande** `mocodo` est invoquée avec les **options** `--select` et `--transform`.\n", "- On passe à la seconde trois **arguments** de complexité croissante, `drain`, `arrange:wide` et `explode:weak,arity=2.5`. Notez que le séparateur des arguments est l'espace. Par conséquent, un argument ne peut contenir d'espaces qu'entre guillemets.\n", "- Chacun des arguments consiste en un nom de **sous-option** (`drain`, `arrange` ou `explode`), suivi éventuellement d'un deux-points (`:`), suivi d'une liste de **sous-arguments** séparés par des virgules (`,`).\n", "- Chacun des sous-arguments consiste en un nom de **sous-sous-option**, suivi éventuellement d'un signe égal (`=`), suivi d'un **sous-sous-argument** (éventuellement entre guillemets).\n", "\n", "On n'ira pas plus loin et, heureusement, la plupart des commandes réelles n'atteignent pas ce degré d'imbrication. La décomposition suivante fixera peut-être les idées, ou pourra du moins servir de référence en cas de doute :\n", "\n", "$$\n", "\\underbrace{\\texttt{mocodo}}_{\\text{command}}\n", "\\qquad\n", "\\texttt{-}\\,\\texttt{-}\n", "\\underbrace{\\texttt{select}}_{\\text{option}}\n", "\\underbrace{\n", " \\texttt{all}\n", "}_{\\text{argument}}\n", "\\qquad\n", "\\texttt{-}\\,\\texttt{-}\n", "\\underbrace{\\texttt{transform}}_{\\text{option}}\n", "\\qquad\n", "\\underbrace{\n", " \\underbrace{\n", " \\texttt{drain}\n", " }_{\\text{sub-option}}\n", "}_{\\text{argument}}\n", "\\qquad\n", "\\underbrace{\n", " \\underbrace{\n", " \\texttt{arrange}\n", " }_{\\text{sub-option}}\n", " \\texttt{:}\n", " \\underbrace{\n", " \\underbrace{\n", " \\texttt{wide}\n", " }_{\\text{sub-sub-opt}}\n", " }_{\\text{sub-arg}}\n", "}_{\\text{argument}}\n", "\\qquad\n", "\\underbrace{\n", " \\underbrace{\n", " \\texttt{explode}\n", " }_{\\text{sub-option}}\n", " \\texttt{:}\n", " \\underbrace{\n", " \\underbrace{\n", " \\texttt{weak}\n", " }_{\\text{sub-sub-opt}}\n", " }_{\\text{sub-arg}}\n", " \\texttt{,}\n", " \\underbrace{\n", " \\underbrace{\n", " \\texttt{arity}\n", " }_{\\text{sub-sub-opt}}\n", " \\texttt{=}\n", " \\underbrace{\n", " \\texttt{2.5}\n", " }_{\\text{sub-sub-arg}}\n", " }_{\\text{sub-argument}}\n", "}_{\\text{argument}}\n", "$$ \n", "\n", "L'intérêt de cette structuration en arborescence est double. Elle permet :\n", "- de créer des pipelines d'opérations : le résultat de l'application du premier argument est fourni en entrée au second, etc ;\n", "- de regrouper les opérations interdépendantes. Par exemple, avant la version 4.0, l'argument `explode:weak,arity=2.5` aurait dû être mis à plat : `--explode --weak_explosion --explosion_arity=2.5`. Cela donne le même statut à toutes, masquant le fait que certaines n'ont de sens qu'en présence d'une autre." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## L'option `--transform` et ses arguments" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Triage et exécution des transformations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'option à tout faire est `--transform`, que vous pouvez abréger `-t`. Avant de traiter ses arguments, Mocodo commence par les répartir en deux catégories :\n", "\n", "- les **réécritures**, qui transforment le MCD original en un nouveau MCD avec, par exemple, les libellés mis en majuscules, ou les associations n-aires décomposées en DF, ou les boîtes réarrangées ;\n", "- les **conversions**, qui produisent quelque chose qui n'est pas un MCD : ce peut être un dictionnaire des données, un diagramme de classes UML, un ERD dans la notation _crow's foot_, etc.\n", "\n", "L'exécution se déroule alors en deux étapes :\n", "\n", "1. les réécritures sont opérées en pipeline, c'est-à-dire que le MCD produit par la première réécriture est passé en entrée de la seconde, etc. Leur ordre a donc de l'importance ;\n", "2. les conversions sont alors appliquées au résultat de la dernière réécriture. Leur ordre n'a pas d'importance." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Aide-mémoire des transformations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'option `-t` (ou `--transform`) permet d'accéder à un grand nombre d'opérations de conversion ou de réécriture.\n", "\n", "Toutes les opérations de conversion ont déjà été décrites dans ce document :\n", "\n", "- produits dérivés : `data_dict`, `share` ;\n", "- schémas relationnels textuels : `html`, `latex`, `markdown`, `text` ;\n", "- DDL : `sql` (et ses dialectes) ;\n", "- autres sorties relationnelles : `diagram`, `dbml`, `d2`, `dependencies` ;\n", "- conversions dans d'autres notations : `uml`, `crow`, `chen`.\n", "\n", "Certaines opérations de réécriture ont été présentées, d'autres passées sous silence :\n", "\n", "- réorganisation du MCD : `arrange`, `flip` ;\n", "- obfuscation et randomisation d'éléments donnés : `randomize`, `drown` ;\n", "- génération de MCD aléatoires : `grow` ;\n", "- décomposition d'associations : `split`, `drain`, `explode` ;\n", "- inférence d'informations manquantes : `create` ;\n", "- suppression d'éléments donnés : `delete` ;\n", "- traitement de chaînes de caractères : `truncate`, `slice`, `prefix`, `suffix`, `replace` ;\n", "- homogénéisation de la mise en forme : `ascii`, `camel`, `snake`, `pascal`, `lower`, `upper`, etc.\n", "\n", "L'aide-mémoire suivant devrait vous permettre de prendre en main les sous-options de `--transform` qui vous intéressent. Survolez une sous-option pour afficher ses alias éventuels." ] }, { "cell_type": "code", "execution_count": 187, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "### Opérations de conversion\n", "\n", "| Sous-option | Description | Exemples | Explications |\n", "| :-- | :-- | :-- | :-- |\n", "| ast | crée l'arbre de syntaxe abstraite du texte source (pour le débogage) | | |\n", "| chen | convertit le modèle conceptuel dans la notation de Chen | `` chen `` | sans attributs |\n", "| | | `` chen:attrs `` | avec attributs |\n", "| | | `` chen:attrs --defer `` | calcule le rendu graphique via un service web |\n", "| | | `` chen:layout=circo,mindist=2,scale=0.6 `` | ajoute des options arbitraires pour Graphviz |\n", "| crow | convertit le modèle conceptuel dans la notation crow's foot | `` crow `` | format Graphviz |\n", "| | | `` crow --defer `` | calcule le rendu graphique via un service web |\n", "| | | `` crow:mmd `` | format Mermaid |\n", "| | | `` crow:mermaid `` | idem |\n", "| d2 | convertit le modèle conceptuel en un schéma relationnel au format D2 | | |\n", "| data_dict | extrait tous les attributs du MCD dans une table | `` data_dict `` | tableau Markdown, trois colonnes |\n", "| | | `` data_dict:label `` | liste Markdown, une colonne |\n", "| | | `` data_dict:label,type='Description' `` | deux colonnes, un libellé personnalisé |\n", "| | | `` data_dict:label='Attribut',type='Description' `` | deux colonnes, deux libellés personnalisés |\n", "| | | `` data_dict:**box**='Entité ou
association',label,`type`=`'Type de données'` `` | mise en forme de certains libellés |\n", "| | | `` data_dict:tsv `` | tableau TSV, trois colonnes |\n", "| | | `` data_dict:tsv,label `` | liste des attributs séparés par des retours à la ligne |\n", "| dbml | convertit le modèle conceptuel en un schéma relationnel au format DBML | `` dbml `` | version de base |\n", "| | | `` dbml:b `` | avec _boilerplate_ |\n", "| debug | liste des informations internes relatives à la conversion en schéma relationnel | | |\n", "| dependencies | convertit le modèle conceptuel en un graphe de dépendances | | |\n", "| diagram | convertit le modèle conceptuel en un diagramme relationnel au format Mocodo | `` diagram `` | version de base |\n", "| | | `` diagram:c `` | avec contraintes d'unicité et d'optionalité |\n", "| html | convertit le modèle conceptuel en un schéma relationnel au format HTML | `` html `` | version de base |\n", "| | | `` html:b `` | avec _boilerplate_ |\n", "| | | `` html:c `` | avec contraintes d'unicité et d'optionalité |\n", "| | | `` html:e `` | avec explications |\n", "| | | `` html:bce `` | avec _boilerplate_, contraintes et explications |\n", "| latex | convertit le modèle conceptuel en un schéma relationnel au format LaTeX | `` latex `` | version de base |\n", "| | | `` latex:b `` | avec _boilerplate_ |\n", "| | | `` latex:c `` | avec contraintes d'unicité et d'optionalité |\n", "| | | `` latex:e `` | avec explications |\n", "| | | `` latex:bce `` | avec _boilerplate_, contraintes et explications |\n", "| markdown | convertit le modèle conceptuel en un schéma relationnel au format Markdown | `` markdown `` | version de base |\n", "| | | `` markdown:c `` | avec contraintes d'unicité et d'optionalité |\n", "| | | `` markdown:e `` | avec explications |\n", "| | | `` markdown:ce `` | avec contraintes et explications |\n", "| mssql | convertit le modèle conceptuel en un modèle physique pour Microsoft SQL Server | `` mssql `` | version de base |\n", "| | | `` mssql:b `` | avec _boilerplate_ |\n", "| mysql | convertit le modèle conceptuel en un modèle physique pour MySQL | `` mysql `` | version de base |\n", "| | | `` mysql:b `` | avec _boilerplate_ |\n", "| oracle | convertit le modèle conceptuel en un modèle physique pour Oracle DB | `` oracle `` | version de base |\n", "| | | `` oracle:b `` | avec _boilerplate_ |\n", "| postgresql | convertit le modèle conceptuel en un modèle physique pour PostgreSQL | `` postgresql `` | version de base |\n", "| | | `` postgresql:b `` | avec _boilerplate_ |\n", "| prompt | génère un prompt pour demander à une IA de compléter le MCD | `` prompt:cards `` | avec les explications des cardinalités |\n", "| | | `` prompt:types `` | avec les types des attributs |\n", "| relation | convertit le modèle conceptuel en schéma relationnel avec le gabarit donné | `` relation:path/to/my_template.yaml `` | chemin relatif, extension obligatoire |\n", "| share | encode le MCD dans une URL pour Mocodo online | `` qr --defer `` | génère un QR code via un service web |\n", "| sql | convertit le modèle conceptuel en un modèle physique pour SQL | | |\n", "| sqlite | convertit le modèle conceptuel en un modèle physique pour SQLite | `` sqlite `` | version de base |\n", "| | | `` sqlite:b `` | avec _boilerplate_ |\n", "| text | convertit le modèle conceptuel en un schéma relationnel au format texte | `` text `` | version de base |\n", "| | | `` text:c `` | avec contraintes d'unicité et d'optionalité |\n", "| | | `` html:e `` | avec explications |\n", "| | | `` html:ce `` | avec contraintes et explications |\n", "| uml | convertit le modèle conceptuel en diagramme de classes UML | `` uml `` | format PlantUML |\n", "| | | `` uml:plantuml `` | idem |\n", "| | | `` uml --defer `` | calcule le rendu graphique via un service web |\n", "| | | `` uml:plantuml=- `` | supprime les styles par défaut |\n", "| | | `` uml:plantuml='skinparam backgroundColor yellow\\nskinparam classAttributeFontName Arial\\n' `` | ajoute des styles personnalisés |\n", "\n", "### Opérations de réécriture\n", "\n", "| Sous-option | Description | Exemples | Explications |\n", "| :-- | :-- | :-- | :-- |\n", "| arrange | réarrange la disposition, soit par Branch & Bound, soit avec un algorithme génétique | `` arrange `` | B&B sans contraintes |\n", "| | | `` arrange:timeout=60 `` | B&B limité à une minute |\n", "| | | `` arrange:wide `` | B&B privilégiant la largeur |\n", "| | | `` arrange:current `` | B&B sur la grille courante |\n", "| | | `` arrange:balanced=0 `` | B&B sur la plus petite grille équilibrée |\n", "| | | `` arrange:balanced=1 `` | B&B sur la seconde plus petite grille équilibrée |\n", "| | | `` arrange:algo=ga `` | algorithme génétique |\n", "| ascii | réécrit les éléments donnés en ASCII | `` ascii:roles,labels `` | rôles, libellés des boîtes et des attributs en ASCII |\n", "| camel | réécrit les éléments donnés en camelCase | | |\n", "| capitalize | réécrit les éléments donnés en capitalisant la première lettre de chaque mot | | |\n", "| casefold | réécrit les éléments donnés en minuscules, mais plus agressivement que « lower » | | |\n", "| create | essaie d'inférer les types, entités, CIFs ou flèches de DF à partir des éléments existants | `` guess:types `` | deviner les types manquants |\n", "| | | `` create:types= `` | remplacer les types manquants par `[]` |\n", "| | | `` create:types=TODO `` | remplacer les types manquants par `[TODO]` |\n", "| | | `` make:entities `` | réparer l'oubli d'entités référencées dans des associations |\n", "| | | `` create:dfs `` | mettre des DF partout où c'est possible |\n", "| | | `` add:df_arrows `` | ajouter des flèches aux DF 11 |\n", "| | | `` add:cifs `` | ajouter les CIF correspondant aux agrégats |\n", "| | | `` add:cifs=light `` | même chose en visualisation allégée |\n", "| | | `` add:roles `` | mettre comme rôles le nom des associations partout où c'est utile |\n", "| delete | supprime les éléments donnés quand c'est possible | `` empty `` | ne garde que la structure et le nom des boîtes |\n", "| | | `` delete:types,notes,attrs,cards `` | idem |\n", "| | | `` delete:cards `` | remplace les cardinalités par `XX` |\n", "| | | `` delete:card_prefixes `` | supprime les marqueurs d'entités faibles et d'agrégats |\n", "| | | `` delete:dfs `` | supprime les entités indépendantes dont tous les attributs sont identifiants (et les DF qui les relient) |\n", "| drain | déplace tout attribut d'association (1,1) vers l'entité appropriée | | |\n", "| drown | remplace tous les noms d'éléments par un libellé générique numéroté | | |\n", "| echo | réécrit le texte source tel quel | | |\n", "| explode | décompose toute association n-aire (*,N) en n associations binaires | `` explode arrange `` | décomposer les non-DF ternaires et plus, puis réarranger |\n", "| | | `` explode:arity=3 arrange `` | idem |\n", "| | | `` explode:weak arrange `` | idem, avec création d'entités faibles |\n", "| | | `` explode:arity=2.5 arrange `` | étendre aux non-DF binaires porteuses d'attributs |\n", "| | | `` explode:arity=2 arrange `` | étendre à toutes les non-DF binaires |\n", "| fix | essaie de corriger les erreurs courantes dans les éléments donnés | `` fix:cards `` | normaliser les cardinalités en 01, 11, 0N et 1N |\n", "| flip | applique au diagramme une symétrie verticale, horizontale ou diagonale | `` flip:v `` | symétrie verticale |\n", "| | | `` flip:h `` | symétrie horizontale |\n", "| | | `` flip:d `` | symétrie selon la seconde diagonale |\n", "| | | `` flip:vhd `` | symétrie selon la première diagonale |\n", "| | | `` flip:dhv `` | idem (ordre indifférent) |\n", "| grow | ajoute des entités et associations aléatoires (par défaut : 10 nouvelles associations) | `` grow arrange `` | ajouter des éléments avec les paramètres par défaut, puis réarranger |\n", "| | | `` grow:n=10 `` | nombre total d'associations à ajouter (défaut) |\n", "| | | `` grow:arity_1=2 `` | nombre d'associations réflexives (défaut) |\n", "| | | `` grow:arity_3=2 `` | nombre d'associations ternaires (défaut) |\n", "| | | `` grow:arity_4=0 `` | nombre d'associations quaternaires (défaut) |\n", "| | | `` grow:doubles=1 `` | nombre d'associations liant deux mêmes entités (défaut) |\n", "| | | `` grow:composite_ids=1 `` | nombre d'identifiants composites (défaut) |\n", "| | | `` grow:ent_attrs=4 `` | nombre maximal d'attributs par entité (défaut) |\n", "| | | `` grow:assoc_attrs=2 `` | nombre maximal d'attributs par association (défaut) |\n", "| | | `` grow:'*1-*N'=3 `` | nombre d'associations `*1-*N` (défaut) |\n", "| | | `` grow:'01-11'=1 `` | nombre d'associations `01-11` (défaut) |\n", "| | | `` grow:'_11-*N'=1 `` | une entité faible (zéro par défaut) |\n", "| | | `` grow:'/1N-*N'=1 `` | un agrégat (zéro par défaut) |\n", "| | | `` grow:from_scratch arrange `` | à partir d'un MCD vide |\n", "| | | `` grow:grow:n=9,from_scratch,ent_attrs=3 obfuscate:labels=en4 create:roles lower:roles arrange `` | créer un MCD d'entraînement à la conversion en relationnel |\n", "| lower | réécrit les éléments donnés en minuscules | `` lower:attrs,roles `` | attributs et rôles en minuscules |\n", "| pascal | réécrit les élements donnés en PascalCase | | |\n", "| prefix | préfixe les éléments donnés avec la chaîne donnée | `` prefix:roles='-' `` | force les rôles à remplacer le nom des clés étrangères lors du passage au relationnel |\n", "| randomize | garde la structure, mais randomise les éléments donnés quand c'est possible | `` obfuscate `` | libellés remplacés par du Lorem Ipsum |\n", "| | | `` obfuscate:labels=lorem `` | idem |\n", "| | | `` obfuscate:labels=disparition `` | idem, lexique du roman de Perec |\n", "| | | `` obfuscate:labels=en4 `` | idem, mots anglais de 4 lettres (SFW) |\n", "| | | `` obfuscate:attrs=fr,boxes=fr5 `` | idem, mots français de longueur quelconque pour les attributs, de 5 lettres pour les boîtes |\n", "| | | `` randomize:types `` | types randomisés avec les fréquences de `default_datatypes_fr.tsv`. |\n", "| replace | réécrit les éléments donnés en appliquant le motif « recherche/remplacement » donné | `` replace:boxes='DIRIGER/RÉPONDRE DE' `` | renomme une boîte |\n", "| | | `` replace:texts='personel/personnel' `` | corrige une faute d'orthographe |\n", "| | | `` replace:replace:texts='_/ ' `` | remplace les tirets bas par des espaces |\n", "| | | `` replace:types='VARCHAR/VARCHAR2' `` | modifie un nom de type |\n", "| | | `` replace:cards=0N/1N `` | remplace toutes les cardinalités 0N par 1N |\n", "| | | `` replace:cards=1N//1N `` | crée des agrégats un peu partout |\n", "| | | `` replace:cards='0/X' replace:cards='11/X1' replace:cards='1N/XN' `` | masque les cardinalités minimales |\n", "| | | `` delete:card_prefixes replace:cards=11/_11 `` | ajoute des marqueurs d'entités faibles |\n", "| slice | réécrit les éléments donnés en n'en gardant qu'une tranche donnée | `` slice:boxes=5:10 `` | de l'indice 5 (inclus) à l'indice 10 (exclu) |\n", "| | | `` slice:boxes=5: `` | supprime les 5 premiers caractères |\n", "| | | `` slice:boxes=:5 `` | ne garde que les 5 premiers caractères |\n", "| | | `` slice:boxes=:-5 `` | supprime les 5 derniers caractères |\n", "| | | `` slice:boxes=: `` | équivalent de `echo` |\n", "| | | `` slice:boxes= `` | idem |\n", "| | | `` slice:boxes `` | idem |\n", "| snake | réécrit les éléments donnés en snake_case | | |\n", "| split | décompose toute association n-aire (*,1) en n-1 associations binaires | `` split arrange `` | décomposer, puis réarranger |\n", "| suffix | suffixe les éléments donnés avec la chaîne donnée | `` suffix:boxes=1 `` | Ajoute un suffixe numérique au nom des boîtes en vue de mettre un MCD et sa copie sur le même diagramme. |\n", "| swapcase | réécrit les éléments donnés en inversant la casse de chaque lettre | | |\n", "| title | réécrit les éléments donnés en mettant la première lettre de chaque mot en majuscule | | |\n", "| truncate | tronque les éléments donnés à la longueur donnée (par défaut : 64) | `` truncate:boxes=10 `` | tronque les noms des boîtes à 10 caractères |\n", "| upper | réécrit les éléments donnés en majuscules | `` upper:boxes `` | noms des boîtes en majuscules |" ], "text/plain": [ "" ] }, "execution_count": 187, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.Markdown(\"../fr_cheat_sheet.md\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Réécriture d'éléments donnés" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans l'aide-mémoire, de nombreuses opérations de réécriture sont décrites comme s'appliquant à des « éléments donnés ». Il s'agit de certains ensembles de _tokens_ produits par l'analyse syntaxique. Plus précisément :\n", "\n", "- `arrows` : flèches de pattes (suffixes `<` ou `>` des cardinalités).\n", "- `attrs` : libellés des attributs d'entités ou d'associations.\n", "- `boxes` : libellés des entités et associations.\n", "- `cards` : cardinalités (`11`, `1N`, etc., sans préfixe ni suffixe).\n", "- `card_prefixes` : préfixes de cardinalités (`_` pour les entités faibles, `/` pour les agrégats).\n", "- `constraint_notes` : messages affichés au survol d'une contrainte.\n", "- `labels` : libellés des entités, des associations et des attributs.\n", "- `leg_notes` : notes de pattes (rôles ou messages affichés au survol).\n", "- `notes` : équivalent de `roles` + `constraint_notes`.\n", "- `roles` : alias de `leg_notes`.\n", "- `texts` : équivalent de `labels` + `notes`.\n", "- `types` : types de données." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fonctionnement de la commande magique" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il peut être utile de comprendre ce qui se passe en coulisses lorsque l'on invoque la commande magique sur une cellule d'un notebook Jupyter :\n", "\n", "- dans le répertoire courant est créé au besoin un sous-répertoire `mocodo_notebook` ;\n", "- le texte d'entrée y est sauvegardé sous le nom de `sandbox.mcd` ;\n", "- la commande est lancée sur ce fichier avec tous les paramètres donnés par l'utilisateur. Cela signifie que tous les fichiers générés le seront au même endroit sous les noms de `sandbox.svg`, `sandbox.html`, etc. (relisez-les avec la commande magique `%load`) ;\n", "- certains des fichiers générés sont affichés dans la sortie de la cellule, d'autres pas, selon les règles suivantes (`rw` et `cv` dénotent respectivement une opération de réécriture et de conversion) :" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "| commande | tracé
du MCD | résultat des
réécritures | résultats des
conversions |\n", "|:---|:---:|:---:|:---:|\n", "| `mocodo` | initial | - | - |\n", "| `mocodo -t` ou `mocodo --mld` | initial | - | MLD en
Markdown |\n", "| `mocodo -t rw1 rw2 ...` | final | final | - |\n", "| `mocodo -t cv1 cv2 ...` | - | - | tous |\n", "| `mocodo -t rw1 rw2 ... cv1 cv2 ...` | final | - | tous |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ces règles d'affichage par défaut devraient répondre aux besoins les plus courants, mais l'option `--select` peut prendre le pas :\n", "\n", "- sans arguments, rien n'est affiché ;\n", "- l'argument `mcd` affiche le diagramme conceptuel ;\n", "- l'argument `rw` (ou un alias de commodité parmi `source`, `text`, `code`, `mocodo`) affiche le résultat de la _dernière_ réécriture ;\n", "- l'argument `cv` (ou un alias de commodité parmi `mld`, `ddl`, `sql`) affiche les résultats des conversions ;\n", "- ces arguments peuvent être librement combinés et ordonnés ;\n", "- l'argument `all` affiche tout ce qui est disponible, dans l'ordre `mcd`, `rw` et `cv` ;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les conversions génèrent par défaut un fichier-texte. Un dessin s'affichera à la place :\n", "- pour l'argument `diagram` ;\n", "- avec l'option `--defer`, pour autant qu'un web-service de rendu soit défini pour le fichier en question." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il est possible de mettre `-t` en majuscule. Dans ce cas :\n", "\n", "- s'il y a une ou plusieurs opérations de réécriture, le résultat de la dernière réécriture remplace le contenu de la cellule et la sortie est effacée ;\n", "- sinon, s'il y a une ou plusieurs opérations de conversion et que [Pyperclip](https://github.com/asweigart/pyperclip) est installé, le résultat de la dernière conversion est copié dans le presse-papier." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Gabarits de passage au relationnel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'algorithme convertissant la représentation interne d'un schéma relationnel en une représentation externe dans tel ou tel format est totalement découplé dudit format. Cela signifie que vous pouvez modifier ou créer un format sans écrire une seule ligne de code, mais en remplissant une espèce de formulaire, ou gabarit (_template_ en anglais).\n", "\n", "Si vous n'avez aucune velléité de le faire ou que vous n'êtes pas ami avec les expressions régulières, vous pouvez sauter cette section. Sinon, accrochez-vous, ça va devenir un peu technique." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Format des gabarits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les gabarits de Mocodo étaient à l'origine écrits en JSON. Par conséquent, un grand nombre des caractères utilisés dans les expressions régulières devaient être échappés, ce qui rendait celles-ci (encore) plus difficiles à écrire, à relire et à tester. La version 4 passe à YAML, qui n'a pas cet inconvénient. Voici par exemple un extrait de gabarit dans l'ancienne syntaxe :\n", "\n", "```json\n", " {\n", " \"order\": 400,\n", " \"comment\": \"Move all foreign keys constraints at the end of the document\",\n", " \"search\": \"(?sm)(CREATE TABLE ([^\\n]+) \\\\(\\n(?:[^\\n]+\\n)*) (FOREIGN KEY[^\\n]+),\\n(.*)\",\n", " \"replace\": \"\\\\1\\\\4\\nALTER TABLE \\\\2 ADD \\\\3;\",\n", " \"iterated\": true\n", " },\n", "```\n", "\n", "... et le même dans la nouvelle :\n", "\n", "```yaml\n", " - order: 400\n", " comment: 'Move all foreign keys constraints at the end of the document'\n", " search: '(?sm)(CREATE TABLE ([^\\n]+) \\(\\n(?:[^\\n]+\\n)*) (FOREIGN KEY[^\\n]+),\\n(.*)'\n", " replace: '\\1\\4\\nALTER TABLE \\2 ADD \\3;'\n", " iterated: true\n", "```\n", "\n", "Si vous avez écrit vos propres gabarits JSON, vous pouvez les convertir automatiquement en YAML à l'aide du script `json_to_yaml_templates.py` fourni dans le répertoire `dev`.\n", "\n", "**Remarque.** Si les gabarits de Mocodo constituent des fichiers YAML valides, l'inverse n'est pas forcément vrai. Mocodo utilise un sous-ensemble sain de YAML adapté à ses besoins. Il a son propre _parser_ avec les particularités suivantes :\n", "\n", "- Un gabarit est un dictionnaire dont les valeurs sont :\n", " - soit des types scalaires (chaînes, nombres, booléens, `null`) ;\n", " - soit des listes de dictionnaires à valeurs scalaires.\n", "- Toute _valeur_ chaîne doit être délimitée par des simples quotes (`'`). Les quotes internes sont échappées en les doublant (`''`).\n", "- Les expressions régulières de recherche et de remplacement suivent les mêmes règles d'échappement que les r-strings de Python.\n", "- Les autres chaînes (notamment les chaînes de composition) ne demandent pas à échapper les anti-slashes, mais à doubler les accolades (règles des fr-strings). Exception : pour les distinguer du retour-chariot et de la tabulation, il faudra échapper les `\\n` et les `\\t` si on veut les laisser littéralement. Vous en avez un exemple pour `\\newcommand` et `\\title` dans le [_boilerplate_ du gabarit pour $\\LaTeX$](https://github.com/laowantong/mocodo/blob/53ba0fce41aaf7b8156821a50dd3d3f1051f8af0/mocodo/resources/relation_templates/latex-bce.yaml#L2)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dérivation de gabarits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Depuis la version 3.1, il est possible de définir un gabarit dérivé d'un autre (qui lui-même peut dériver d'un autre, etc.). Dans le but de réduire les redondances et de faciliter les tests et la maintenance, la version 4.0 fait un usage intensif de cette fonctionnalité :" ] }, { "cell_type": "code", "execution_count": 188, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "G\n", "\n", "\n", "\n", "d2\n", "\n", "d2\n", "\n", "\n", "\n", "dbml\n", "\n", "dbml\n", "\n", "\n", "\n", "dbml-b\n", "\n", "dbml-b\n", "\n", "\n", "\n", "dbml->dbml-b\n", "\n", "\n", "\n", "\n", "\n", "sql\n", "\n", "sql\n", "\n", "\n", "\n", "ddl\n", "\n", "ddl\n", "\n", "\n", "\n", "sql->ddl\n", "\n", "\n", "\n", "\n", "\n", "mssql\n", "\n", "mssql\n", "\n", "\n", "\n", "sql->mssql\n", "\n", "\n", "\n", "\n", "\n", "mysql\n", "\n", "mysql\n", "\n", "\n", "\n", "sql->mysql\n", "\n", "\n", "\n", "\n", "\n", "oracle\n", "\n", "oracle\n", "\n", "\n", "\n", "sql->oracle\n", "\n", "\n", "\n", "\n", "\n", "postgresql\n", "\n", "postgresql\n", "\n", "\n", "\n", "sql->postgresql\n", "\n", "\n", "\n", "\n", "\n", "sqlite\n", "\n", "sqlite\n", "\n", "\n", "\n", "sql->sqlite\n", "\n", "\n", "\n", "\n", "\n", "debug\n", "\n", "debug\n", "\n", "\n", "\n", "dependencies\n", "\n", "dependencies\n", "\n", "\n", "\n", "diagram-c\n", "\n", "diagram-c\n", "\n", "\n", "\n", "diagram\n", "\n", "diagram\n", "\n", "\n", "\n", "diagram-c->diagram\n", "\n", "\n", "\n", "\n", "\n", "html-bc\n", "\n", "html-bc\n", "\n", "\n", "\n", "html-b\n", "\n", "html-b\n", "\n", "\n", "\n", "html-bc->html-b\n", "\n", "\n", "\n", "\n", "\n", "html-c\n", "\n", "html-c\n", "\n", "\n", "\n", "html-c->html-bc\n", "\n", "\n", "\n", "\n", "\n", "html-ce\n", "\n", "html-ce\n", "\n", "\n", "\n", "html-c->html-ce\n", "\n", "\n", "\n", "\n", "\n", "html\n", "\n", "html\n", "\n", "\n", "\n", "html-c->html\n", "\n", "\n", "\n", "\n", "\n", "latex-c\n", "\n", "latex-c\n", "\n", "\n", "\n", "html-c->latex-c\n", "\n", "\n", "\n", "\n", "\n", "markdown-c\n", "\n", "markdown-c\n", "\n", "\n", "\n", "html-c->markdown-c\n", "\n", "\n", "\n", "\n", "\n", "text-c\n", "\n", "text-c\n", "\n", "\n", "\n", "html-c->text-c\n", "\n", "\n", "\n", "\n", "\n", "html-bce\n", "\n", "html-bce\n", "\n", "\n", "\n", "html-ce->html-bce\n", "\n", "\n", "\n", "\n", "\n", "html-e\n", "\n", "html-e\n", "\n", "\n", "\n", "html-ce->html-e\n", "\n", "\n", "\n", "\n", "\n", "latex-ce\n", "\n", "latex-ce\n", "\n", "\n", "\n", "html-ce->latex-ce\n", "\n", "\n", "\n", "\n", "\n", "markdown-ce\n", "\n", "markdown-ce\n", "\n", "\n", "\n", "html-ce->markdown-ce\n", "\n", "\n", "\n", "\n", "\n", "text-ce\n", "\n", "text-ce\n", "\n", "\n", "\n", "html-ce->text-ce\n", "\n", "\n", "\n", "\n", "\n", "html-be\n", "\n", "html-be\n", "\n", "\n", "\n", "html-bce->html-be\n", "\n", "\n", "\n", "\n", "\n", "latex-bc\n", "\n", "latex-bc\n", "\n", "\n", "\n", "latex-b\n", "\n", "latex-b\n", "\n", "\n", "\n", "latex-bc->latex-b\n", "\n", "\n", "\n", "\n", "\n", "tex-bc\n", "\n", "tex-bc\n", "\n", "\n", "\n", "latex-bc->tex-bc\n", "\n", "\n", "\n", "\n", "\n", "tex-b\n", "\n", "tex-b\n", "\n", "\n", "\n", "latex-b->tex-b\n", "\n", "\n", "\n", "\n", "\n", "latex-c->latex-bc\n", "\n", "\n", "\n", "\n", "\n", "latex\n", "\n", "latex\n", "\n", "\n", "\n", "latex-c->latex\n", "\n", "\n", "\n", "\n", "\n", "tex-c\n", "\n", "tex-c\n", "\n", "\n", "\n", "latex-c->tex-c\n", "\n", "\n", "\n", "\n", "\n", "latex-bce\n", "\n", "latex-bce\n", "\n", "\n", "\n", "latex-ce->latex-bce\n", "\n", "\n", "\n", "\n", "\n", "latex-e\n", "\n", "latex-e\n", "\n", "\n", "\n", "latex-ce->latex-e\n", "\n", "\n", "\n", "\n", "\n", "tex-ce\n", "\n", "tex-ce\n", "\n", "\n", "\n", "latex-ce->tex-ce\n", "\n", "\n", "\n", "\n", "\n", "latex-be\n", "\n", "latex-be\n", "\n", "\n", "\n", "latex-bce->latex-be\n", "\n", "\n", "\n", "\n", "\n", "tex-bce\n", "\n", "tex-bce\n", "\n", "\n", "\n", "latex-bce->tex-bce\n", "\n", "\n", "\n", "\n", "\n", "tex-be\n", "\n", "tex-be\n", "\n", "\n", "\n", "latex-be->tex-be\n", "\n", "\n", "\n", "\n", "\n", "tex-e\n", "\n", "tex-e\n", "\n", "\n", "\n", "latex-e->tex-e\n", "\n", "\n", "\n", "\n", "\n", "tex\n", "\n", "tex\n", "\n", "\n", "\n", "latex->tex\n", "\n", "\n", "\n", "\n", "\n", "markdown-bc\n", "\n", "markdown-bc\n", "\n", "\n", "\n", "markdown-b\n", "\n", "markdown-b\n", "\n", "\n", "\n", "markdown-bc->markdown-b\n", "\n", "\n", "\n", "\n", "\n", "md-bc\n", "\n", "md-bc\n", "\n", "\n", "\n", "markdown-bc->md-bc\n", "\n", "\n", "\n", "\n", "\n", "mld-bc\n", "\n", "mld-bc\n", "\n", "\n", "\n", "markdown-bc->mld-bc\n", "\n", "\n", "\n", "\n", "\n", "md-b\n", "\n", "md-b\n", "\n", "\n", "\n", "markdown-b->md-b\n", "\n", "\n", "\n", "\n", "\n", "mld-b\n", "\n", "mld-b\n", "\n", "\n", "\n", "markdown-b->mld-b\n", "\n", "\n", "\n", "\n", "\n", "markdown-c->markdown-bc\n", "\n", "\n", "\n", "\n", "\n", "markdown\n", "\n", "markdown\n", "\n", "\n", "\n", "markdown-c->markdown\n", "\n", "\n", "\n", "\n", "\n", "md-c\n", "\n", "md-c\n", "\n", "\n", "\n", "markdown-c->md-c\n", "\n", "\n", "\n", "\n", "\n", "mld-c\n", "\n", "mld-c\n", "\n", "\n", "\n", "markdown-c->mld-c\n", "\n", "\n", "\n", "\n", "\n", "markdown-bce\n", "\n", "markdown-bce\n", "\n", "\n", "\n", "markdown-ce->markdown-bce\n", "\n", "\n", "\n", "\n", "\n", "markdown-e\n", "\n", "markdown-e\n", "\n", "\n", "\n", "markdown-ce->markdown-e\n", "\n", "\n", "\n", "\n", "\n", "md-ce\n", "\n", "md-ce\n", "\n", "\n", "\n", "markdown-ce->md-ce\n", "\n", "\n", "\n", "\n", "\n", "mld-ce\n", "\n", "mld-ce\n", "\n", "\n", "\n", "markdown-ce->mld-ce\n", "\n", "\n", "\n", "\n", "\n", "markdown-be\n", "\n", "markdown-be\n", "\n", "\n", "\n", "markdown-bce->markdown-be\n", "\n", "\n", "\n", "\n", "\n", "md-bce\n", "\n", "md-bce\n", "\n", "\n", "\n", "markdown-bce->md-bce\n", "\n", "\n", "\n", "\n", "\n", "mld-bce\n", "\n", "mld-bce\n", "\n", "\n", "\n", "markdown-bce->mld-bce\n", "\n", "\n", "\n", "\n", "\n", "md-be\n", "\n", "md-be\n", "\n", "\n", "\n", "markdown-be->md-be\n", "\n", "\n", "\n", "\n", "\n", "mld-be\n", "\n", "mld-be\n", "\n", "\n", "\n", "markdown-be->mld-be\n", "\n", "\n", "\n", "\n", "\n", "md-e\n", "\n", "md-e\n", "\n", "\n", "\n", "markdown-e->md-e\n", "\n", "\n", "\n", "\n", "\n", "mld-e\n", "\n", "mld-e\n", "\n", "\n", "\n", "markdown-e->mld-e\n", "\n", "\n", "\n", "\n", "\n", "md\n", "\n", "md\n", "\n", "\n", "\n", "markdown->md\n", "\n", "\n", "\n", "\n", "\n", "mld\n", "\n", "mld\n", "\n", "\n", "\n", "markdown->mld\n", "\n", "\n", "\n", "\n", "\n", "mssql-b\n", "\n", "mssql-b\n", "\n", "\n", "\n", "ms_sql-b\n", "\n", "ms_sql-b\n", "\n", "\n", "\n", "mssql-b->ms_sql-b\n", "\n", "\n", "\n", "\n", "\n", "sql_server-b\n", "\n", "sql_server-b\n", "\n", "\n", "\n", "mssql-b->sql_server-b\n", "\n", "\n", "\n", "\n", "\n", "sqlserver-b\n", "\n", "sqlserver-b\n", "\n", "\n", "\n", "mssql-b->sqlserver-b\n", "\n", "\n", "\n", "\n", "\n", "mssql->mssql-b\n", "\n", "\n", "\n", "\n", "\n", "ms_sql\n", "\n", "ms_sql\n", "\n", "\n", "\n", "mssql->ms_sql\n", "\n", "\n", "\n", "\n", "\n", "sql_server\n", "\n", "sql_server\n", "\n", "\n", "\n", "mssql->sql_server\n", "\n", "\n", "\n", "\n", "\n", "sqlserver\n", "\n", "sqlserver\n", "\n", "\n", "\n", "mssql->sqlserver\n", "\n", "\n", "\n", "\n", "\n", "mysql-b\n", "\n", "mysql-b\n", "\n", "\n", "\n", "mysql->mysql-b\n", "\n", "\n", "\n", "\n", "\n", "oracle-b\n", "\n", "oracle-b\n", "\n", "\n", "\n", "oracle->oracle-b\n", "\n", "\n", "\n", "\n", "\n", "oracle_db\n", "\n", "oracle_db\n", "\n", "\n", "\n", "oracle->oracle_db\n", "\n", "\n", "\n", "\n", "\n", "oracle_db-b\n", "\n", "oracle_db-b\n", "\n", "\n", "\n", "oracle-b->oracle_db-b\n", "\n", "\n", "\n", "\n", "\n", "postgresql-b\n", "\n", "postgresql-b\n", "\n", "\n", "\n", "postgres-b\n", "\n", "postgres-b\n", "\n", "\n", "\n", "postgresql-b->postgres-b\n", "\n", "\n", "\n", "\n", "\n", "postgresql->postgresql-b\n", "\n", "\n", "\n", "\n", "\n", "postgres\n", "\n", "postgres\n", "\n", "\n", "\n", "postgresql->postgres\n", "\n", "\n", "\n", "\n", "\n", "sqlite-b\n", "\n", "sqlite-b\n", "\n", "\n", "\n", "sqlite->sqlite-b\n", "\n", "\n", "\n", "\n", "\n", "text-b\n", "\n", "text-b\n", "\n", "\n", "\n", "text-c->text-b\n", "\n", "\n", "\n", "\n", "\n", "text-bc\n", "\n", "text-bc\n", "\n", "\n", "\n", "text-c->text-bc\n", "\n", "\n", "\n", "\n", "\n", "text\n", "\n", "text\n", "\n", "\n", "\n", "text-c->text\n", "\n", "\n", "\n", "\n", "\n", "txt-c\n", "\n", "txt-c\n", "\n", "\n", "\n", "text-c->txt-c\n", "\n", "\n", "\n", "\n", "\n", "txt-b\n", "\n", "txt-b\n", "\n", "\n", "\n", "text-b->txt-b\n", "\n", "\n", "\n", "\n", "\n", "txt-bc\n", "\n", "txt-bc\n", "\n", "\n", "\n", "text-bc->txt-bc\n", "\n", "\n", "\n", "\n", "\n", "text-bce\n", "\n", "text-bce\n", "\n", "\n", "\n", "text-ce->text-bce\n", "\n", "\n", "\n", "\n", "\n", "text-e\n", "\n", "text-e\n", "\n", "\n", "\n", "text-ce->text-e\n", "\n", "\n", "\n", "\n", "\n", "txt-ce\n", "\n", "txt-ce\n", "\n", "\n", "\n", "text-ce->txt-ce\n", "\n", "\n", "\n", "\n", "\n", "text-be\n", "\n", "text-be\n", "\n", "\n", "\n", "text-bce->text-be\n", "\n", "\n", "\n", "\n", "\n", "txt-bce\n", "\n", "txt-bce\n", "\n", "\n", "\n", "text-bce->txt-bce\n", "\n", "\n", "\n", "\n", "\n", "txt-be\n", "\n", "txt-be\n", "\n", "\n", "\n", "text-be->txt-be\n", "\n", "\n", "\n", "\n", "\n", "txt-e\n", "\n", "txt-e\n", "\n", "\n", "\n", "text-e->txt-e\n", "\n", "\n", "\n", "\n", "\n", "txt\n", "\n", "txt\n", "\n", "\n", "\n", "text->txt\n", "\n", "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "execution_count": 188, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.SVG(\"../../mocodo/resources/relation_templates/_graph.svg\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les teintes les plus foncées correspondent approximativement aux gabarits les plus complexes. Les autres ne sont souvent que des alias, par exemple, `tex.yaml` renvoie directement à `latex.yaml` :" ] }, { "cell_type": "code", "execution_count": 189, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
parent: 'latex'\n",
       "
\n" ], "text/latex": [ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", "\\PY{n+nt}{parent}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{latex}\\PY{l+s}{\\PYZsq{}}\n", "\\end{Verbatim}\n" ], "text/plain": [ "parent: 'latex'" ] }, "execution_count": 189, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.Code(\"../../mocodo/resources/relation_templates/tex.yaml\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La clé `\"parent\"` donne le chemin ou le nom du gabarit dont on souhaite créer une version dérivée :\n", "\n", "- une chaîne terminée par `.yaml` est interprétée comme un chemin ;\n", "- sinon, comme le nom d'un gabarit de la distribution." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tous les gabarits textuels (`text.yaml`, `latex.yaml`, `markdown.yaml`, `html.yaml` et leurs alias) dérivent des gabarits `html-c.yaml` (version avec affichage des contraintes) et `html-ce.yaml` (version avec affichage des contraintes et des explications).\n", "\n", "Par exemple, le gabarit `latex` se borne à ajouter un message d'aide pour la ligne de commande et à empêcher l'ajout des contraintes d'unicité et d'optionalité :" ] }, { "cell_type": "code", "execution_count": 190, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
help_en: 'convert the conceptual model into a relational schema in LaTeX format'\n",
       "help_fr: 'convertit le modèle conceptuel en un schéma relationnel au format LaTeX'\n",
       "help_zh: '将概念模型转换为 LaTeX 格式的关系模式'\n",
       "fr_examples:\n",
       "  - order: 1\n",
       "    example: 'latex'\n",
       "    explanation: 'version de base'\n",
       "  - order: 2\n",
       "    example: 'latex:b'\n",
       "    explanation: 'avec _boilerplate_'\n",
       "  - order: 3\n",
       "    example: 'latex:c'\n",
       "    explanation: 'avec contraintes d''unicité et d''optionalité'\n",
       "  - order: 4\n",
       "    example: 'latex:e'\n",
       "    explanation: 'avec explications'\n",
       "  - order: 5\n",
       "    example: 'latex:bce'\n",
       "    explanation: 'avec _boilerplate_, contraintes et explications'\n",
       "parent: 'latex-c'\n",
       "add_unicity_constraints:\n",
       "add_optionality_constraints:\n",
       "
\n" ], "text/latex": [ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", "\\PY{n+nt}{help\\PYZus{}en}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{convert}\\PY{n+nv}{ }\\PY{l+s}{the}\\PY{n+nv}{ }\\PY{l+s}{conceptual}\\PY{n+nv}{ }\\PY{l+s}{model}\\PY{n+nv}{ }\\PY{l+s}{into}\\PY{n+nv}{ }\\PY{l+s}{a}\\PY{n+nv}{ }\\PY{l+s}{relational}\\PY{n+nv}{ }\\PY{l+s}{schema}\\PY{n+nv}{ }\\PY{l+s}{in}\\PY{n+nv}{ }\\PY{l+s}{LaTeX}\\PY{n+nv}{ }\\PY{l+s}{format}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{help\\PYZus{}fr}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{convertit}\\PY{n+nv}{ }\\PY{l+s}{le}\\PY{n+nv}{ }\\PY{l+s}{modèle}\\PY{n+nv}{ }\\PY{l+s}{conceptuel}\\PY{n+nv}{ }\\PY{l+s}{en}\\PY{n+nv}{ }\\PY{l+s}{un}\\PY{n+nv}{ }\\PY{l+s}{schéma}\\PY{n+nv}{ }\\PY{l+s}{relationnel}\\PY{n+nv}{ }\\PY{l+s}{au}\\PY{n+nv}{ }\\PY{l+s}{format}\\PY{n+nv}{ }\\PY{l+s}{LaTeX}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{help\\PYZus{}zh}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{将概念模型转换为}\\PY{n+nv}{ }\\PY{l+s}{LaTeX}\\PY{n+nv}{ }\\PY{l+s}{格式的关系模式}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{fr\\PYZus{}examples}\\PY{p}{:}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{1}\n", "\\PY{+w}{ }\\PY{n+nt}{example}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{latex}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{explanation}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{version}\\PY{n+nv}{ }\\PY{l+s}{de}\\PY{n+nv}{ }\\PY{l+s}{base}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{2}\n", "\\PY{+w}{ }\\PY{n+nt}{example}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{latex:b}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{explanation}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{avec}\\PY{n+nv}{ }\\PY{l+s}{\\PYZus{}boilerplate\\PYZus{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{3}\n", "\\PY{+w}{ }\\PY{n+nt}{example}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{latex:c}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{explanation}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{avec}\\PY{n+nv}{ }\\PY{l+s}{contraintes}\\PY{n+nv}{ }\\PY{l+s}{d}\\PY{l+s+se}{\\PYZsq{}\\PYZsq{}}\\PY{l+s}{unicité}\\PY{n+nv}{ }\\PY{l+s}{et}\\PY{n+nv}{ }\\PY{l+s}{d}\\PY{l+s+se}{\\PYZsq{}\\PYZsq{}}\\PY{l+s}{optionalité}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{4}\n", "\\PY{+w}{ }\\PY{n+nt}{example}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{latex:e}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{explanation}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{avec}\\PY{n+nv}{ }\\PY{l+s}{explications}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{5}\n", "\\PY{+w}{ }\\PY{n+nt}{example}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{latex:bce}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{explanation}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{avec}\\PY{n+nv}{ }\\PY{l+s}{\\PYZus{}boilerplate\\PYZus{},}\\PY{n+nv}{ }\\PY{l+s}{contraintes}\\PY{n+nv}{ }\\PY{l+s}{et}\\PY{n+nv}{ }\\PY{l+s}{explications}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{parent}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{latex\\PYZhy{}c}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{add\\PYZus{}unicity\\PYZus{}constraints}\\PY{p}{:}\n", "\\PY{n+nt}{add\\PYZus{}optionality\\PYZus{}constraints}\\PY{p}{:}\n", "\\end{Verbatim}\n" ], "text/plain": [ "help_en: 'convert the conceptual model into a relational schema in LaTeX format'\n", "help_fr: 'convertit le modèle conceptuel en un schéma relationnel au format LaTeX'\n", "help_zh: '将概念模型转换为 LaTeX 格式的关系模式'\n", "fr_examples:\n", " - order: 1\n", " example: 'latex'\n", " explanation: 'version de base'\n", " - order: 2\n", " example: 'latex:b'\n", " explanation: 'avec _boilerplate_'\n", " - order: 3\n", " example: 'latex:c'\n", " explanation: 'avec contraintes d''unicité et d''optionalité'\n", " - order: 4\n", " example: 'latex:e'\n", " explanation: 'avec explications'\n", " - order: 5\n", " example: 'latex:bce'\n", " explanation: 'avec _boilerplate_, contraintes et explications'\n", "parent: 'latex-c'\n", "add_unicity_constraints:\n", "add_optionality_constraints:" ] }, "execution_count": 190, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.Code(\"../../mocodo/resources/relation_templates/latex.yaml\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si maintenant on regarde le contenu de son parent `latex-c` :" ] }, { "cell_type": "code", "execution_count": 191, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
parent: 'html-c'\n",
       "extension: 'tex'\n",
       "highlight: 'latex'\n",
       "column_separator: ', '\n",
       "compose_relation: '  \\item \\relat{{{this_relation_name}}} ({columns})'\n",
       "compose_relational_schema: '% Generated by Mocodo {version}\\n\\n\\begin{{itemize}}\\n{relations}\\n\\end{{itemize}}\\n'\n",
       "transform_relation:\n",
       "  - order: 1000\n",
       "    comment: 'Compose normal attributes'\n",
       "    search: '<span class=''normal''>(.+?)</span>'\n",
       "    replace: '\\\\attr{\\1}'\n",
       "  - order: 1100\n",
       "    comment: 'Compose primary keys'\n",
       "    search: '<span class=''primary''>(.+?)</span>'\n",
       "    replace: '\\\\prim{\\1}'\n",
       "  - order: 1200\n",
       "    comment: 'Compose foreign primary keys'\n",
       "    search: '<span class=''foreign primary''>#(.+?)</span>'\n",
       "    replace: '\\\\foreign{\\\\prim{\\1}}'\n",
       "  - order: 1300\n",
       "    comment: 'Compose foreign attributes'\n",
       "    search: '<span class=''foreign''>#(.+?)</span>'\n",
       "    replace: '\\\\foreign{\\1}'\n",
       "  - order: 1400\n",
       "    comment: 'Exponents'\n",
       "    search: '(<sup>.*u)(\\d)(.*</sup>)'\n",
       "    replace: '\\1_\\2\\3'\n",
       "    iterated: true\n",
       "  - order: 1500\n",
       "    comment: 'Exponents'\n",
       "    search: ' <sup>(.+?)</sup>'\n",
       "    replace: '$^{\\1}$'\n",
       "  - order: 1600\n",
       "    comment: 'Escape underlines'\n",
       "    search: '_'\n",
       "    replace: '\\_'\n",
       "
\n" ], "text/latex": [ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", "\\PY{n+nt}{parent}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{html\\PYZhy{}c}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{extension}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{tex}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{highlight}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{latex}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{column\\PYZus{}separator}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{,}\\PY{n+nv}{ }\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{compose\\PYZus{}relation}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{n+nv}{ }\\PY{l+s}{\\PYZbs{}item}\\PY{n+nv}{ }\\PY{l+s}{\\PYZbs{}relat\\PYZob{}\\PYZob{}\\PYZob{}this\\PYZus{}relation\\PYZus{}name\\PYZcb{}\\PYZcb{}\\PYZcb{}}\\PY{n+nv}{ }\\PY{l+s}{(\\PYZob{}columns\\PYZcb{})}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{compose\\PYZus{}relational\\PYZus{}schema}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZpc{}}\\PY{n+nv}{ }\\PY{l+s}{Generated}\\PY{n+nv}{ }\\PY{l+s}{by}\\PY{n+nv}{ }\\PY{l+s}{Mocodo}\\PY{n+nv}{ }\\PY{l+s}{\\PYZob{}version\\PYZcb{}\\PYZbs{}n\\PYZbs{}n\\PYZbs{}begin\\PYZob{}\\PYZob{}itemize\\PYZcb{}\\PYZcb{}\\PYZbs{}n\\PYZob{}relations\\PYZcb{}\\PYZbs{}n\\PYZbs{}end\\PYZob{}\\PYZob{}itemize\\PYZcb{}\\PYZcb{}\\PYZbs{}n}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{n+nt}{transform\\PYZus{}relation}\\PY{p}{:}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{1000}\n", "\\PY{+w}{ }\\PY{n+nt}{comment}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{Compose}\\PY{n+nv}{ }\\PY{l+s}{normal}\\PY{n+nv}{ }\\PY{l+s}{attributes}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{search}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZlt{}span}\\PY{n+nv}{ }\\PY{l+s}{class=}\\PY{l+s+se}{\\PYZsq{}\\PYZsq{}}\\PY{l+s}{normal}\\PY{l+s+se}{\\PYZsq{}\\PYZsq{}}\\PY{l+s}{\\PYZgt{}(.+?)\\PYZlt{}/span\\PYZgt{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{replace}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZbs{}\\PYZbs{}attr\\PYZob{}\\PYZbs{}1\\PYZcb{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{1100}\n", "\\PY{+w}{ }\\PY{n+nt}{comment}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{Compose}\\PY{n+nv}{ }\\PY{l+s}{primary}\\PY{n+nv}{ }\\PY{l+s}{keys}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{search}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZlt{}span}\\PY{n+nv}{ }\\PY{l+s}{class=}\\PY{l+s+se}{\\PYZsq{}\\PYZsq{}}\\PY{l+s}{primary}\\PY{l+s+se}{\\PYZsq{}\\PYZsq{}}\\PY{l+s}{\\PYZgt{}(.+?)\\PYZlt{}/span\\PYZgt{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{replace}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZbs{}\\PYZbs{}prim\\PYZob{}\\PYZbs{}1\\PYZcb{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{1200}\n", "\\PY{+w}{ }\\PY{n+nt}{comment}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{Compose}\\PY{n+nv}{ }\\PY{l+s}{foreign}\\PY{n+nv}{ }\\PY{l+s}{primary}\\PY{n+nv}{ }\\PY{l+s}{keys}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{search}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZlt{}span}\\PY{n+nv}{ }\\PY{l+s}{class=}\\PY{l+s+se}{\\PYZsq{}\\PYZsq{}}\\PY{l+s}{foreign}\\PY{n+nv}{ }\\PY{l+s}{primary}\\PY{l+s+se}{\\PYZsq{}\\PYZsq{}}\\PY{l+s}{\\PYZgt{}\\PYZsh{}(.+?)\\PYZlt{}/span\\PYZgt{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{replace}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZbs{}\\PYZbs{}foreign\\PYZob{}\\PYZbs{}\\PYZbs{}prim\\PYZob{}\\PYZbs{}1\\PYZcb{}\\PYZcb{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{1300}\n", "\\PY{+w}{ }\\PY{n+nt}{comment}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{Compose}\\PY{n+nv}{ }\\PY{l+s}{foreign}\\PY{n+nv}{ }\\PY{l+s}{attributes}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{search}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZlt{}span}\\PY{n+nv}{ }\\PY{l+s}{class=}\\PY{l+s+se}{\\PYZsq{}\\PYZsq{}}\\PY{l+s}{foreign}\\PY{l+s+se}{\\PYZsq{}\\PYZsq{}}\\PY{l+s}{\\PYZgt{}\\PYZsh{}(.+?)\\PYZlt{}/span\\PYZgt{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{replace}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZbs{}\\PYZbs{}foreign\\PYZob{}\\PYZbs{}1\\PYZcb{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{1400}\n", "\\PY{+w}{ }\\PY{n+nt}{comment}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{Exponents}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{search}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{(\\PYZlt{}sup\\PYZgt{}.*u)(\\PYZbs{}d)(.*\\PYZlt{}/sup\\PYZgt{})}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{replace}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZbs{}1\\PYZus{}\\PYZbs{}2\\PYZbs{}3}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{iterated}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{true}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{1500}\n", "\\PY{+w}{ }\\PY{n+nt}{comment}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{Exponents}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{search}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{n+nv}{ }\\PY{l+s}{\\PYZlt{}sup\\PYZgt{}(.+?)\\PYZlt{}/sup\\PYZgt{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{replace}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZdl{}\\PYZca{}\\PYZob{}\\PYZbs{}1\\PYZcb{}\\PYZdl{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{p+pIndicator}{\\PYZhy{}}\\PY{+w}{ }\\PY{n+nt}{order}\\PY{p}{:}\\PY{+w}{ }\\PY{l+lScalar+lScalarPlain}{1600}\n", "\\PY{+w}{ }\\PY{n+nt}{comment}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{Escape}\\PY{n+nv}{ }\\PY{l+s}{underlines}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{search}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZus{}}\\PY{l+s}{\\PYZsq{}}\n", "\\PY{+w}{ }\\PY{n+nt}{replace}\\PY{p}{:}\\PY{+w}{ }\\PY{l+s}{\\PYZsq{}}\\PY{l+s}{\\PYZbs{}\\PYZus{}}\\PY{l+s}{\\PYZsq{}}\n", "\\end{Verbatim}\n" ], "text/plain": [ "parent: 'html-c'\n", "extension: 'tex'\n", "highlight: 'latex'\n", "column_separator: ', '\n", "compose_relation: ' \\item \\relat{{{this_relation_name}}} ({columns})'\n", "compose_relational_schema: '% Generated by Mocodo {version}\\n\\n\\begin{{itemize}}\\n{relations}\\n\\end{{itemize}}\\n'\n", "transform_relation:\n", " - order: 1000\n", " comment: 'Compose normal attributes'\n", " search: '(.+?)'\n", " replace: '\\\\attr{\\1}'\n", " - order: 1100\n", " comment: 'Compose primary keys'\n", " search: '(.+?)'\n", " replace: '\\\\prim{\\1}'\n", " - order: 1200\n", " comment: 'Compose foreign primary keys'\n", " search: '#(.+?)'\n", " replace: '\\\\foreign{\\\\prim{\\1}}'\n", " - order: 1300\n", " comment: 'Compose foreign attributes'\n", " search: '#(.+?)'\n", " replace: '\\\\foreign{\\1}'\n", " - order: 1400\n", " comment: 'Exponents'\n", " search: '(.*u)(\\d)(.*)'\n", " replace: '\\1_\\2\\3'\n", " iterated: true\n", " - order: 1500\n", " comment: 'Exponents'\n", " search: ' (.+?)'\n", " replace: '$^{\\1}$'\n", " - order: 1600\n", " comment: 'Escape underlines'\n", " search: '_'\n", " replace: '\\_'" ] }, "execution_count": 191, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display.Code(\"../../mocodo/resources/relation_templates/latex-c.yaml\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... on voit que les opérations consistent simplement à modifier le séparateur de colonne, à composer les tables et le schéma relationnel complet avec un _boilerplate_, et enfin à remplacer les balises HTML par leur équivalent en $\\LaTeX$. Toute la logique complexe est traitée dans le gabarit `html-c`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exemples de création de gabarit dérivé " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le gabarit SQL par défaut rend explicite la contrainte `NOT NULL` des clés primaires :" ] }, { "cell_type": "code", "execution_count": 192, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE PERSONNE (\n", " PRIMARY KEY (id_personne),\n", " id_personne VARCHAR(8) NOT NULL,\n", " nom VARCHAR(255)\n", ");\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mocodo -t sql\n", "PERSONNE: id. personne [VARCHAR(8)], nom [VARCHAR(255)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Supposons que cette redondance nous défrise, et voyons comment l'éliminer. Pour cela, examinons le contenu du gabarit `sql.yaml` :\n", "\n", "![](examples/sql_template_1.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La contrainte d'optionalité `{optionality}` apparaît à côté de `PRIMARY KEY` en lignes 28 et 31. Nous pouvons donc simplement remplacer les valeurs associées à `compose_primary_key` et `compose_primary_foreign_key` par des copies supprimant cette mention :" ] }, { "cell_type": "code", "execution_count": 193, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting new_sql.yaml\n" ] } ], "source": [ "%%file new_sql.yaml\n", "parent: 'sql'\n", "compose_primary_key: '{label} {datatype}\\nPRIMARY KEY ({label})'\n", "compose_primary_foreign_key: '{label} {datatype}\\nPRIMARY KEY ({label})\\nFOREIGN KEY ({label}) REFERENCES {outer_source} ({non_disambiguated_label})'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour tester ce nouveau gabarit, nous devons ajouter la sous-option `-t relation` avec en sous-argument le chemin complet du fichier créé :" ] }, { "cell_type": "code", "execution_count": 194, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE PERSONNE (\n", " PRIMARY KEY (id_personne),\n", " id_personne VARCHAR(8),\n", " nom VARCHAR(255)\n", ");\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t relation:new_sql.yaml" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mission accomplie !" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous aurions pu procéder autrement. Reprenons le gabarit original :\n", " \n", "![](examples/sql_template_2.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le dictionnaire `transform_relation` spécifie une séquence de réécritures à appliquer à chacune des clauses `CREATE TABLE` (composées ligne 37). La première accumule toutes les lignes contenant `PRIMARY KEY` en fin de table (où elles seront concaténées avant d'être ramenées au début). Nous devons donc intervenir _avant_ que `NOT NULL` ne soit séparé de `PRIMARY KEY`. Pour cela, définissons un gabarit qui se borne à **insérer** avant cette transformation (de numéro d'ordre 100) une suppression des `NOT NULL` incriminés :" ] }, { "cell_type": "code", "execution_count": 195, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting new_sql.yaml\n" ] } ], "source": [ "%%file new_sql.yaml\n", "parent: 'sql'\n", "transform_relation:\n", " - order: 50\n", " comment: 'Suppress PK NN redundancy.'\n", " search: ' NOT NULL\\nPRIMARY KEY'\n", " replace: '\\nPRIMARY KEY'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le résultat est le même :" ] }, { "cell_type": "code", "execution_count": 196, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "\n", "
\n", "
\n", "\n", "\n", "sandbox_ddl.sql\n", "\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```sql\n", "CREATE TABLE PERSONNE (\n", " PRIMARY KEY (id_personne),\n", " id_personne VARCHAR(8),\n", " nom VARCHAR(255)\n", ");\n", "\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%mocodo -i sandbox -t relation:new_sql.yaml" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**NB.** Les gabarits définis par l'utilisateur n'ont **pas** vocation à être placés dans le répertoire `relation_templates` de la distribution. Votre nouveau gabarit `new_sql` ne peut donc pas être invoqué aussi simplement que les gabarits officiels (i.e., avec `-t new_sql`). Si vous faites une modification ou un ajout que vous voulez voir inclus dans la distribution, n'hésitez pas à faire une PR." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Mécanisme de mise à jour" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le mécanisme de mise à jour des clés dépend de la scalarité de leur valeur associée.\n", "\n", "**Cas scalaire.**\n", "\n", "Pour les valeurs scalaires (chaînes, nombres, booléens, `null`), selon la présence d'une entrée donnée dans le gabarit enfant et/ou le gabarit parent, l'association clé-valeur correspondante sera créée, mise à jour ou conservée :\n", "\n", "| enfant | parent | résultat |\n", "|--------|--------|--------------|\n", "| oui | non | création |\n", "| oui | oui | mise à jour |\n", "| non | oui | conservation |\n", "\n", "Il n'y a pas de moyen de supprimer une association clé-valeur, mais étant donné que toutes celles qui sont absentes se voient substituer une association par défaut, il est équivalent de spécifier explicitement la valeur-défaut.\n", "\n", "**Cas non scalaire.**\n", "\n", "Dans les gabarits de relations de Mocodo, une seule catégorie de valeur non scalaire est possible : la liste de dictionnaires. Dans l'exemple précédent, c'est le cas de `transform_relation`.\n", "\n", "Une clé `\"order\"` est systématiquement présente dans chacun des dictionnaires de la liste. Selon la présence d'un dictionnaire de numéro d'ordre donné dans le gabarit enfant et/ou le gabarit parent, ce dictionnaire sera inséré, supprimé, mis à jour ou conservé :\n", "\n", "| enfant | parent | résultat |\n", "|--------|--------|--------------|\n", "| oui | non | insertion |\n", "| oui | oui | si les clés sont parmi {`\"order\"`, `\"comment\"`} : suppression
s'il y a plusieurs clés : [mise à jour](https://docs.python.org/3/library/stdtypes.html#dict.update) |\n", "| non | oui | conservation |\n", "\n", "**Nouveauté de la version 4.0.** Si une clé d'un gabarit enfant est associée à une liste vide, cette association remplace l'éventuelle association existante.\n", "\n", "**Remarque.** Les numéros d'ordre utilisés dans les dictionnaires sont des multiples de 100. Si vous voulez créer des gabarits dérivés, évitez par précaution les multiples de 10 : cela augmentera les chances que votre gabarit survive à une mise à jour de l'un de ses ancêtres. Vous pouvez aussi partir d'une copie de ceux-ci, sachant que vous renoncez alors aux bénéfices de leurs éventuelles mises à jour." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Annexe C : Crédits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- **Auteur**\n", " - Aristide Grange, Université de Lorraine, Metz, [LCOMS](http://lcoms.univ-lorraine.fr).\n", "- **Administration système**\n", " - Cyril Minette.\n", "- **Outils**\n", " - Aspect visuel\n", " - [Color Brewer](http://colorbrewer2.org) (Cynthia Brewer).\n", " - [Real Favicon Generator](http://realfavicongenerator.net) (Philippe Bernard).\n", " - [reset.css](http://meyerweb.com/eric/tools/css/reset/) (Eric Meyer).\n", " - JavaScript\n", " - [JQuery](http://jquery.com/) (John Resig).\n", " - [JS-Cookie](https://github.com/js-cookie/js-cookie) (Klaus Hartl).\n", " - [Ace Editor](https://ace.c9.io) ([Cloud9 IDE](https://c9.io/) et [Mozilla](https://mozilla.org/)).\n", " - Python\n", " - [Distance de Damerau-Levenshtein](http://mwh.geek.nz/2009/04/26/python-damerau-levenshtein-distance/) (Michael Homer).\n", " - [Lark Parser](https://github.com/lark-parser/lark) (Erez Shinan).\n", " - [CairoSVG](https://cairosvg.org) ([CourtBouillon](https://www.courtbouillon.org)).\n", " - [Pyperclip](https://github.com/asweigart/pyperclip) (Al Sweigart).\n", " - Services de rendu\n", " - [Kroki.io](https://kroki.io) (Guillaume Grossetie).\n", " - [QR Code Generator](https://goqr.me/api/) (Foundata GmbH).\n", "- **Divers**\n", " - [Modern SQL](https://modern-sql.com) (Markus Winand).\n", " - [DBML](https://dbml.dbdiagram.io/home/) ([Holistics](https://www.holistics.io)).\n", " - [D2](https://d2lang.com) ([Terrastruct](https://terrastruct.com)).\n", " - [Basthon](https://basthon.fr) (Romain Casati)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.13" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Mocodo", "title_sidebar": "Mocodo", "toc_cell": true, "toc_position": { "height": "499.575px", "left": "40px", "top": "132px", "width": "260.568px" }, "toc_section_display": true, "toc_window_display": true }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: doc/readme/ccp.mcd ================================================ %%mocodo --colors ocean CLIENT: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)] PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)] INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité [INTEGER] PRODUIT: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)] ================================================ FILE: doc/tutorial.ipynb ================================================ { "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "init_cell": true }, "outputs": [], "source": [ "%reload_ext mocodo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Basic" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Tutoriel interactif (1/2)\n", "% Un petit zoo de la syntaxe de Mocodo, avec quelques possibilités de base :\n", "% entité, association, attribut, identifiant, cardinalité, patte, rôle.\n", "% \n", "% MCD adapté de : The entity-relationship model—toward a unified view of data\n", "% (Chen, 1976).\n", "\n", "AYANT-DROIT: num. ayant-droit, nom ayant-droit, lien\n", "DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET\n", "REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise\n", "PIÈCE: réf. pièce, libellé pièce\n", "COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité\n", "\n", "DF, 11 AYANT-DROIT, 0N EMPLOYÉ\n", "EMPLOYÉ: matricule, nom employé\n", "PROJET: num. projet, nom projet\n", "FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie\n", "\n", "DÉPARTEMENT: num. département, nom département\n", "EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT\n", "TRAVAILLER, 0N EMPLOYÉ, 1N PROJET\n", "SOCIÉTÉ: num. société, raison sociale\n", "CONTRÔLER, 0N [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Entité\n", "% Pour créer une entité E, écrivez sur une ligne « E: ».\n", "% Bon à savoir : les lignes commençant par « % » sont des commentaires.\n", "\n", "Produit:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Identifiant et attributs d'entité\n", "% Donnez-lui des attributs en écrivant « E: attr_1, attr_2, ... ».\n", "% L'usage des espaces, accents et signes de ponctuation corrects est conseillé :\n", "% « Réf. produit » est plus lisible que « refproduit » et pourra toujours être\n", "% « appauvri » ultérieurement (bouton Éditer).\n", "%\n", "% Astuce : pour passer à l'exemple suivant, tapez [↓], [↓], [⏎] (ou seulement\n", "% [→] sous Firefox).\n", "\n", "Produit: Réf. produit, Libellé, Prix unitaire" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Identifiant composite\n", "% Vous avez remarqué que, par défaut, le premier attribut est considéré comme\n", "% un identifiant, et donc souligné. Pour souligner d'autres attributs, il faut\n", "% les préfixer d'un tiret bas.\n", "\n", "Gratte-ciel: latitude, _longitude, nom, hauteur, année de construction" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Association\n", "% Pour une association A entre plusieurs entités E1, E2, ..., écrivez sur une\n", "% ligne « A, XX E1, XX E2, ... ».\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, XX Commande, XX Produit\n", "Commande: num. commande, date, montant" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Cardinalités\n", "% Si vous connaissez le couple de cardinalités d'une patte, mettez 01, 11, 0N\n", "% ou 1N à la place du XX de l'entité distinguée par cette patte.\n", "%\n", "% NB. Cliquez sur le bouton Éditer et sélectionnez « Correction des fautes de\n", "% frappe dans le cardinalités » pour transformer « ON » en « 0N » ci-dessous.\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, ON Produit\n", "Commande: num. commande, date, montant" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Attribut d'association\n", "% Pour ajouter des attributs à une association, faites comme pour une entité :\n", "% mettez un deux-points au bout de la ligne, puis listez les attributs en les\n", "% séparant par des virgules.\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Association de dépendance fonctionnelle\n", "% Si votre association a une cardinalité 11, mais aucun attribut, vous pouvez\n", "% la nommer « DF » : Mocodo la représentera par un cercle.\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client, nom, prénom, adresse" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Association réflexive\n", "% En répétant le nom d'une entité dans la définition d'une association, vous\n", "% rendez celle-ci réflexive.\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client, nom, prénom, adresse\n", "Parrainer, 01 Client, 0N Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo --mld\n", "% Schéma relationnel\n", "% Allez sous l'onglet Options, cochez « Schéma relationnel expliqué », puis\n", "% rafraîchissez la figure : une liste des tables apparaît au-dessous. Cliquez\n", "% sur n'importe quelle ligne pour mieux comprendre comment elle a été obtenue.\n", "%\n", "% Dans la table « Client », remarquez les colonnes « Réf. client » et « Réf.\n", "% client 2 » : ce n'est pas très satisfaisant...\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client, nom, prénom, adresse\n", "Parrainer, 01 Client, 0N Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo -t mld\n", "% Rôles\n", "% Dans ce MCD, on a ajouté la chaîne « parrain » sur la patte 0,N de PARRAINER.\n", "% Cette technique permet de préserver la sémantique des associations disparues.\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client, nom, prénom, adresse\n", "Parrainer, 01 Client, 0N [parrain] Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo -t diagram\n", "% Diagramme relationnel (1)\n", "% Cochez « Diagramme relationnel en Mocodo », rafraîchissez la figure et allez\n", "% sous l'onglet Autres sorties. Vous y trouvez un texte-source que vous pouvez\n", "% copier-coller (⧉) ici pour voir apparaître un autre type de schéma.\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client, nom, prénom, adresse\n", "Parrainer, 01 Client, 0N [parrain] Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Diagramme relationnel (2)\n", "% Voici le résultat de la manipulation précédente. Les associations ont cédé la\n", "% place à des flèches entre les clés étrangères et les clés primaires d'origine.\n", "\n", ":\n", "Produit: réf. produit, libellé, prix unitaire\n", ":\n", "Inclure: #num. commande > Commande > num. commande, _#réf. produit > Produit > réf. produit, quantité\n", ":\n", "Commande: num. commande, date, montant, #réf. client > Client > réf. client\n", ":\n", "Client: réf. client, nom, prénom, adresse, #réf. client parrain > Client > réf. client, date parrainage\n", ":" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo -t guess:types\n", "% Inférence de types\n", "% Cliquez sur le lapin magique, puis sélectionnez : « deviner les types à\n", "% partir du nom des attributs » (en français ou en anglais). Bien sûr, vous\n", "% pourrez toujours rectifier ceux-ci au besoin.\n", "% Notez que l'inférence donne de meilleurs résultats si les libellés sont écrits\n", "% de façon standard : Mocodo proposera un type pour « réf. produit », mais pas\n", "% pour « refproduit ».\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client, nom, prénom, adresse\n", "Parrainer, 01 Client, 0N [parrain] Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo -t sql\n", "% Génération du DDL\n", "% Maintenant que le MCD est typé, cochez « Script SQL de création des tables »,\n", "% rafraîchissez et allez voir le résultat sous l'onglet Autres sorties. Notez en\n", "% particulier les contraintes de clés étrangères à la fin. Notez aussi que les\n", "% les libellés originaux ont été automatiquement « appauvris » : ainsi, « réf.\n", "% produit » est devenu « ref_produit », etc. Cela rend le code SQL plus portable\n", "% et simplifie les références lors de l'utilisation de la base.\n", "\n", "Produit: réf. produit [VARCHAR(8)], libellé [VARCHAR(50)], prix unitaire [DECIMAL(10,2)]\n", "Inclure, 1N Commande, 0N Produit: quantité [INTEGER]\n", "Commande: num. commande [VARCHAR(8)], date [DATE], montant [DECIMAL(10,2)]\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client [VARCHAR(8)], nom [VARCHAR(255)], prénom [VARCHAR(255)], adresse [VARCHAR(30)]\n", "Parrainer, 01 Client, 0N [parrain] Client : date parrainage [DATE]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Schéma sur plusieurs rangées\n", "% Pour finir la première partie du tutoriel, quelques notions de mise en page.\n", "% Les boîtes (entités et association) définies sur des lignes consécutives sont\n", "% alignées horizontalement sur la même rangée d'une grille invisible. Pour créer\n", "% une nouvelle rangée, il suffit de sauter une ligne entre deux clauses.\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant\n", "\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client, nom, prénom, adresse\n", "Parrainer, 01 Client, 0N Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Réarrangement automatique\n", "% Quand le schéma devient confus, faites un double clic sur la tablette de\n", "% chocolat pour le réarranger aléatoirement. L'algorithme veille à éviter\n", "% les croisements et à minimiser la longueur des pattes. Essayez !\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant\n", "\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client, nom, prénom, adresse\n", "Parrainer, 01 Client, 0N Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "%%mocodo\n", "% Réarrangement automatique avec contraintes\n", "% Cliquez sur la tablette de chocolat pour faire apparaître d'autres options de\n", "% réorganisation. Celles-ci tentent de plonger le MCD sur la grille actuelle, ou\n", "% en largeur d'abord (pour un document paginé), ou en équilibrant les dimensions\n", "% (pour un diaporama).\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant\n", "\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client, nom, prénom, adresse\n", "Parrainer, 01 Client, 0N Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Entraînement au passage au relationnel\n", "% Cochez l'option « Masquage et génération aléatoire », cliquez le bouton qui\n", "% vient d'apparaître et sélectionnez « Créer un MCD d'entraînement à la\n", "% conversion en relationnel ». Apparaît un MCD avec des libellés aléatoires,\n", "% et dont la structure met en œuvre une certaine diversité de cas. Essayez de\n", "% le convertir en relationnel, et comparez votre solution à celle de Mocodo.\n", "\n", "LOREM: ipsum, dolor, amet" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Pour aller plus loin...\n", "% Si vous débutez dans les bases de données, les exemples précédents devraient\n", "% suffire à votre bonheur. Dans le cas contraire, cochez l'option « Tutoriel\n", "% interactif (2/2) » et revenez ici.\n", "\n", "À suivre...: ou pas!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Advanced" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo --mld\n", "% Tutoriel interactif (2/2)\n", "% Une variation du MCD de Chen avec deux notions plus avancées : entité faible\n", "% et contrainte sur associations ; ainsi que trois nouvelles fonctionnalités :\n", "% flèches, calques, messages au survol.\n", "\n", "AYANT-DROIT: nom ayant-droit, lien\n", "DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET\n", "REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise\n", "PIÈCE: réf. pièce, libellé pièce\n", "COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité\n", "\n", "DF, _11 AYANT-DROIT, 0N EMPLOYÉ\n", "EMPLOYÉ: matricule, nom employé\n", "PROJET: num. projet, nom projet\n", "FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie\n", "\n", "DÉPARTEMENT: num. département, nom département\n", "EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT\n", "TRAVAILLER, 0N EMPLOYÉ, 1N PROJET\n", "SOCIÉTÉ: num. société, raison sociale\n", "CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ\n", "\n", " (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Entité faible (ou identification relative)\n", "% Préfixez d'un tiret bas une cardinalité 11 pour « affaiblir » l'entité qu'elle\n", "% distingue, et dont l'identifiant sera alors souligné en pointillés.\n", "%\n", "% NB. De façon non conventionnelle, Mocodo souligne le 11 pour indiquer que le\n", "% « renforcement » de l'identifiant va passer par là. Voyez la documentation\n", "% pour des options alternatives.\n", "\n", "Œuvre: cote œuvre, titre, date parution\n", "DF, 1N Œuvre, _11 Exemplaire\n", "Exemplaire: num. exemplaire, état du livre, date d'achat" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Entité faible sans identifiant\n", "% Préfixez d'un tiret bas le premier attribut pour l'exclure de l'identifiant,\n", "% p. ex. pour une entité faible sans discriminateur.\n", "\n", "Pavillon: id. pavillon, surface, nombre d'étages\n", "Posséder, 01 Pavillon, _11 Piscine\n", "Piscine: _surface, volume" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Identifiants candidats\n", "% Préfixez d'un « i_ » les attributs formant le ième identifiant alternatif.\n", "% Ce numéro apparaîtra dans une gouttière latérale, ainsi qu'un symbole ID en\n", "% face des attributs que vous aurez retenus pour l'identifiant. Le code SQL\n", "% généré inclura une contrainte d'unicité pour chacun des groupes concernés.\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N Produit: quantité\n", "Commande: num. commande, date, montant\n", "DF, 0N Client, 11 Commande\n", "Client: réf. client, 1_nom, 1_prénom, adresse, 2_mail\n", "Parrainer, 01 Client, 0N [parrain] Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "%%mocodo -t mld sql\n", "% Héritage (ou spécialisation)\n", "% Pour spécialiser une entité E en une ou plusieurs autres entités E1, E2, ...\n", "% écrivez « /\\ E <- E1, E2, ... ».\n", "%\n", "% NB. Vous pouvez insérer X (exclusion) et/ou T (totalité) entre les barres\n", "% obliques, ajouter une liste d'attributs ou modifier la sémantique et/ou la\n", "% représentation graphique de l'héritage en remplaçant la flèche par <=, ->,\n", "% =>, <<=, <<-, ->> =>>. Voir la documentation.\n", "\n", "Client: réf. client, nom, prénom, adresse\n", "\n", "/X\\ Client <- Particulier, Employé, Administratif\n", "\n", "Particulier:\n", "Employé: siret entreprise\n", "Administratif: nom ministère" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo -t mld:c --select mcd mld\n", "% Agrégation (ou pseudo-entité)\n", "% Préfixez de « / » une cardinalité pour entourer d'une enveloppe pointillée\n", "% l'association et les autres entités participantes.\n", "% Ici, la dépendance fonctionnelle (date, numéro) => id. client se traduit au\n", "% niveau relationnel par un identifiant réduit pour Réserver, ainsi qu'une\n", "% contrainte de non-optionalité pour id. client. Les contraintes apparaissent\n", "% dans les sorties SQL, mais vous pouvez aussi les visualiser au niveau du MLD\n", "% en cochant l'option « Contraintes d'unicité et d'optionalité ».\n", "%\n", "% NB. La représentation usuelle (non prise en charge par Mocodo) insérerait\n", "% une association entre Client et Réserver.\n", "\n", "Date: date\n", "Réserver, /1N Client, 1N Chambre, 0N Date: durée\n", "Chambre: numéro, prix\n", "\n", "Client: id. client" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo -t mld:c sql --select mcd mld\n", "% Agrégation et contraintes d'unicité\n", "% La conversion en relationnel d'un agrégat avec une cardinalité maximale 1\n", "% ajoute une contrainte d'unicité sur la clé étrangère.\n", "\n", "Voilier: num voilier, longueur\n", "Offrir, 0N Voilier, 0N Semaine, /11 Réservation: tarif\n", "Semaine: num semaine, 1_date début\n", "\n", "Réservation: num résa, arrhes, date résa" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Agrégation multiple\n", "% Mocodo gère correctement les cas difficiles à modéliser dans la notation Look\n", "% Here de Merise. Par exemple, deux dépendances fonctionnelles simultanées :\n", "% - (Projet, Employé) => Site.\n", "% - (Employé, Site) => Projet.\n", "\n", "Projet: projet\n", "Affecter, /1N Site, /1N Projet, 0N Employé\n", "Site: site\n", "\n", "Employé: employé" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Contrainte d'intégrité fonctionnelle (CIF)\n", "% Les mêmes cas de figure peuvent aussi être rendus visuellement par des CIF à\n", "% unicité complète (le seul type de CIF pris en charge par Mocodo).\n", "\n", "Date: date\n", "Réserver, /1N Client, 1N Chambre, 0N Date: durée\n", "Chambre: numéro, prix\n", " \n", "Client: id. client\n", " \n", "(CIF) ->Client, --Chambre, --Date, ..Réserver: Date, Client" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Autres contraintes sur associations\n", "% Ajoutez à votre MCD diverses contraintes sous forme de lettres parenthésées\n", "% et liées aux boîtes par des traits fléchés ou non, pleins ou pointillés, ou\n", "% même invisibles. Ces contraintes sont purement décoratives, et ignorées lors\n", "% du passage au relationnel.\n", "%\n", "% NB : par défaut, le centre de la contrainte coïncide avec le barycentre des\n", "% boîtes qu'elle met en jeu.\n", "%\n", "% Source du MCD : Merise, deuxième génération (Nanci et Espinasse, 2001).\n", "\n", ":::\n", "Dépôt: num dépôt, surface\n", "\n", ":\n", "Louer, 11 Commande, 0N Dépôt\n", ":\n", "Stocker, 1N Dépôt, 1N Article: quantité\n", "\n", "Commande: num. commande, date\n", "Composer, 1N Commande, 0N Article\n", ":\n", "Article: réf. article, prix\n", "\n", "(I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer, Commande, Stocker" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Explication interactive d'une contrainte\n", "% Le survol d'une contrainte suivie d'un texte entre crochets affiche celui-ci\n", "% dans un bandeau. Essayez !\n", "\n", "Projet: num. projet, nom projet\n", ":\n", "Fournir, 1N Projet, 1N Pièce, 1N Société: quantité\n", "Société: num. société, raison sociale\n", "\n", "Requérir, 1N Projet, 0N Pièce: quantité\n", ":\n", "Pièce: réf. pièce, libellé pièce\n", "\n", "(I) [Toute pièce fournie doit avoir été requise.] ..Pièce, ->Requérir, --Fournir, Projet" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Explication interactive des cardinalités\n", "% Même fonctionnalité pour les cardinalités. Si l'élément survolé est en bas\n", "% du diagramme, le bandeau apparaît en haut, et vice versa.\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N [Une commande inclut au moins un produit.] Commande, 0N [Un produit peut être commandé un nombre quelconque de fois.] Produit: quantité\n", "Commande: num. commande, date, montant\n", "DF, 0N [Un client peut passer zéro (prospect) ou plusieurs commandes.] Client, 11 [Une commande est passée par un et un seul client.] Commande\n", "Client: réf. client, nom, adresse\n", "Parrainer, 01 [Un client peut avoir été parrainé ou non.] Client, 0N [Un client peut parrainer d'autres clients.] Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Flèche sur une patte\n", "% En suffixant de « < » ou « > » des cardinalités, vous créez une flèche dont\n", "% la direction se lit de l'association à l'entité.\n", "\n", "Produit: réf. produit, libellé, prix unitaire\n", "Inclure, 1N Commande, 0N> Produit: quantité\n", "Commande: num. commande, date, montant\n", "DF, 0N Client, 11> Commande\n", "Client: réf. client, nom, prénom, adresse\n", "Parrainer, 01> Client, 0N< Client : date parrainage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Dévoilement progressif du schéma\n", "% Indentez (i.e., insérez des espaces au début de) certaines clauses pour créer\n", "% des calques. Les boîtes définies avec la même indentation sont tracées sur le\n", "% même calque. Les calques sont ordonnés par indentation croissante. Sous Mocodo\n", "% online, pour faciliter la mise au point, le schéma est directement dévoilé.\n", "\n", " Parrainer, 01 Client, 0N Client : date parrainage\n", "Produit: réf. produit, libellé, prix unitaire\n", " Inclure, 1N Commande, 0N Produit: quantité\n", " \n", " Client: réf. client, nom, prénom, adresse\n", " DF, 0N Client, 11 Commande\n", " Commande: num. commande, date, montant" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo \n", "% Boîtes homonymes\n", "% Le dernier chiffre ou tiret bas d'un nom d'entité ou d'association n'est pas\n", "% affiché. C'est bon à savoir, p. ex. pour monnayer une entité « fictive » trop\n", "% pattue : ici, on évite les croisements en créant deux entités Date.\n", "\n", "Association 9_, XX Entité 3_, XX Date1\n", "Date1: date\n", "Association 10_, XX Entité 1_, XX Date1\n", "Association 11_, XX Entité 1_, XX Entité 2_, XX Date2\n", ":\n", "\n", "Entité 3_:\n", "Association 8_, XX Entité 3_, XX Entité 1_, XX Date1\n", "Entité 1_:\n", "Entité 2_:\n", "Date2: date\n", "\n", "Association 5_, XX Entité 4_, XX Entité 3_\n", "Entité 4_:\n", "Association 6_, XX Entité 4_, XX Entité 1_\n", "Association 7_, XX Entité 1_, XX Entité 2_, XX Date2\n", ":" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Vue en extension\n", "% La même fonctionnalité permet de créer une vue en extension (à droite du MCD).\n", "% Notez qu'il n'y a pas besoin de suffixe pour créer plusieurs associations DF.\n", "% Notez également les lignes réduites à des deux-points : ceux-ci sont traités\n", "% comme des boîtes « fantômes », et jouent le rôle d'espaces horizontaux.\n", "\n", "ŒUVRE1: cote, titre, date de publication\n", ":::\n", "ŒUVRE2: 612.NAT.34, J'apprends à lire à mes souris blanches, mai 1975\n", ":\n", " \n", "DF, 1N ŒUVRE1, _11 EXEMPLAIRE1\n", "::\n", "DF, XX ŒUVRE2, XX EXEMPLAIRE2\n", "DF, XX ŒUVRE2, XX EXEMPLAIRE3\n", "DF, XX ŒUVRE2, XX EXEMPLAIRE4\n", "\n", "EXEMPLAIRE1: numéro d'exemplaire, état, date d'achat\n", "::\n", "EXEMPLAIRE2: 1, bon état, 12/6/1975\n", "EXEMPLAIRE3: 2, bon état, 1/8/1977\n", "EXEMPLAIRE4: 3, reliure rongée, 3/4/2005" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "%%mocodo -t explode arrange sql --seed=1 --select mcd sql\n", "% Décomposition des associations ternaires (1)\n", "% Cochez l'option « Décomposition d'associations » et double-cliquez le bouton\n", "% qui vient d'apparaître : l'association Fournir est remplacée par une entité\n", "% de même nom pourvue d'un identifiant générique et assortie de trois DF.\n", "\n", "AYANT-DROIT: nom ayant-droit, lien\n", "DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET\n", "REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise\n", "PIÈCE: réf. pièce, libellé pièce\n", "COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité\n", "\n", "DF, _11 AYANT-DROIT, 0N EMPLOYÉ\n", "EMPLOYÉ: matricule, nom employé\n", "PROJET: num. projet, nom projet\n", "FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie\n", "\n", "DÉPARTEMENT: num. département, nom département\n", "EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT\n", "TRAVAILLER, 0N EMPLOYÉ, 1N PROJET\n", "SOCIÉTÉ: num. société, raison sociale\n", "CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [], "source": [ "%%mocodo -t explode:weak arrange sql --seed=1 --select mcd sql\n", "% Décomposition des associations ternaires (2)\n", "% Cochez maintenant l'option « Entité faible » : l'entité Fournir devient une\n", "% entité faible sans identifiant. Vous pouvez vérifier que les schémas sont\n", "% conceptuellement équivalents, et produisent les mêmes sorties relationnelles\n", "% (MLD) et physiques (MPD).\n", "\n", "AYANT-DROIT: nom ayant-droit, lien\n", "DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET\n", "REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise\n", "PIÈCE: réf. pièce, libellé pièce\n", "COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité\n", "\n", "DF, _11 AYANT-DROIT, 0N EMPLOYÉ\n", "EMPLOYÉ: matricule, nom employé\n", "PROJET: num. projet, nom projet\n", "FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie\n", "\n", "DÉPARTEMENT: num. département, nom département\n", "EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT\n", "TRAVAILLER, 0N EMPLOYÉ, 1N PROJET\n", "SOCIÉTÉ: num. société, raison sociale\n", "CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%mocodo\n", "% Pour aller plus loin...\n", "% Chacune des opérations que Mocodo online présente sous la forme de bouton ou\n", "% de case à cocher consiste en un simple appel à la commande « mocodo » avec\n", "% une séquence fixe d'options. En installant Mocodo, vous pourrez composer ces\n", "% séquences librement, ce qui vous ouvrira d'autres perspectives : création de\n", "% jeux de tests, génération d'examens avec un sujet distinct par étudiant,\n", "% automatisation des corrections, etc. Pour l'heure, l'étape suivante semble\n", "% être la lecture de la documentation (lien en bas de page), qui vous donnera\n", "% un éventail complet des possibilités, et peut-être d'autres idées !\n", "\n", "À suivre...: ou pas!" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "init_cell": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0. Tutoriel interactif (1/2)\n", "1. Entité\n", "2. Identifiant et attributs d'entité\n", "3. Identifiant composite\n", "4. Association\n", "5. Cardinalités\n", "6. Attribut d'association\n", "7. Association de dépendance fonctionnelle\n", "8. Association réflexive\n", "9. Schéma relationnel\n", "10. Rôles\n", "11. Diagramme relationnel (1)\n", "12. Diagramme relationnel (2)\n", "13. Inférence de types\n", "14. Génération du DDL\n", "15. Schéma sur plusieurs rangées\n", "16. Réarrangement automatique\n", "17. Réarrangement automatique avec contraintes\n", "18. Entraînement au passage au relationnel\n", "19. Pour aller plus loin...\n", "20. Tutoriel interactif (2/2)\n", "21. Entité faible (ou identification relative)\n", "22. Entité faible sans identifiant\n", "23. Identifiants candidats\n", "24. Héritage (ou spécialisation)\n", "25. Agrégation (ou pseudo-entité)\n", "26. Agrégation et contraintes d'unicité\n", "27. Agrégation multiple\n", "28. Contrainte d'intégrité fonctionnelle (CIF)\n", "29. Autres contraintes sur associations\n", "30. Explication interactive d'une contrainte\n", "31. Explication interactive des cardinalités\n", "32. Flèche sur une patte\n", "33. Dévoilement progressif du schéma\n", "34. Boîtes homonymes\n", "35. Vue en extension\n", "36. Décomposition des associations ternaires (1)\n", "37. Décomposition des associations ternaires (2)\n", "38. Pour aller plus loin...\n", "Updated 39 files in tutorial_lib folder.\n", "Updated mocodo.js.\n" ] } ], "source": [ "from pathlib import Path\n", "import os\n", "import re\n", "import json\n", "\n", "def boxed_text(lines):\n", " width = len(max(lines, key=len))\n", " lines[0] = \" \" * max(0, (width - len(lines[0])) // 2) + lines[0].upper()\n", " lines[0:0] = [\"\"]\n", " lines[2:2] = [\"\"]\n", " lines.append(\"\")\n", " result = []\n", " result.append(\"%\" * (width + 6))\n", " for line in lines:\n", " spaces = \" \" * (width - len(line))\n", " result.append(\"% \" + line + spaces + \" %\")\n", " result.append(\"%\" * (width + 6))\n", " result.append(\"\")\n", " return \"\\n\".join(result)\n", "\n", "\n", "os.system(\"rm tutorial_lib/*.mcd\")\n", "path = Path(\"tutorial.ipynb\")\n", "doc = json.loads(path.read_text())\n", "cells = []\n", "i = 0\n", "titles = []\n", "for cell in doc[\"cells\"]:\n", " if cell[\"cell_type\"] == \"code\":\n", " source = cell[\"source\"]\n", " if source and source[0].startswith(\"%%mocodo\"):\n", " title = source[1][1:].strip()\n", " print(f\"{i}. {title}\")\n", " titles.append(f'\"{title}\"')\n", " lines = []\n", " for (j, line) in enumerate(source[1:]):\n", " if not line.startswith(\"%\"):\n", " text = boxed_text(lines) + \"\".join(source[j+1:])\n", " break\n", " lines.append(line[2:-1])\n", " Path(f\"tutorial_lib/tuto-{i:04d}.mcd\").write_text(text)\n", " Path(f\"../web/box/tuto-{i:04d}.mcd\").write_text(text)\n", " i += 1\n", " elif cell[\"source\"] and cell[\"source\"][0].startswith(\"# Advanced\"):\n", " basic_tutorial_limit = str(i)\n", "print(f\"Updated {i} files in tutorial_lib folder.\")\n", "js_path = Path(\"../web/mocodo.js\")\n", "code = js_path.read_text()\n", "(code, n) = re.subn(r'(?<=var tutorialOptions = )\\[.+?\\]', f\"[{', '.join(titles)}]\", code)\n", "if n == 0:\n", " raise RuntimeError(\"Unable to update tutorialOptions!\")\n", "(code, n) = re.subn(r'(?<=var basicTutorialLimit = )\\d+', basic_tutorial_limit, code)\n", "if n == 0:\n", " raise RuntimeError(\"Unable to update basicTutorialLimit!\")\n", "js_path.write_text(code)\n", "print(\"Updated mocodo.js.\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "celltoolbar": "Initialization Cell", "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.13" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": false, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": "block", "toc_window_display": true }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 1 } ================================================ FILE: doc/tutorial_lib/tuto-0000.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % TUTORIEL INTERACTIF (1/2) % % % % Un petit zoo de la syntaxe de Mocodo, avec quelques possibilités de base : % % entité, association, attribut, identifiant, cardinalité, patte, rôle. % % % % MCD adapté de : The entity-relationship model—toward a unified view of data % % (Chen, 1976). % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% AYANT-DROIT: num. ayant-droit, nom ayant-droit, lien DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise PIÈCE: réf. pièce, libellé pièce COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, 11 AYANT-DROIT, 0N EMPLOYÉ EMPLOYÉ: matricule, nom employé PROJET: num. projet, nom projet FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT TRAVAILLER, 0N EMPLOYÉ, 1N PROJET SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ ================================================ FILE: doc/tutorial_lib/tuto-0001.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % ENTITÉ % % % % Pour créer une entité E, écrivez sur une ligne « E: ». % % Bon à savoir : les lignes commençant par « % » sont des commentaires. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: ================================================ FILE: doc/tutorial_lib/tuto-0002.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % IDENTIFIANT ET ATTRIBUTS D'ENTITÉ % % % % Donnez-lui des attributs en écrivant « E: attr_1, attr_2, ... ». % % L'usage des espaces, accents et signes de ponctuation corrects est conseillé : % % « Réf. produit » est plus lisible que « refproduit » et pourra toujours être % % « appauvri » ultérieurement (bouton Éditer). % % % % Astuce : pour passer à l'exemple suivant, tapez [↓], [↓], [⏎] (ou seulement % % [→] sous Firefox). % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: Réf. produit, Libellé, Prix unitaire ================================================ FILE: doc/tutorial_lib/tuto-0003.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % IDENTIFIANT COMPOSITE % % % % Vous avez remarqué que, par défaut, le premier attribut est considéré comme % % un identifiant, et donc souligné. Pour souligner d'autres attributs, il faut % % les préfixer d'un tiret bas. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Gratte-ciel: latitude, _longitude, nom, hauteur, année de construction ================================================ FILE: doc/tutorial_lib/tuto-0004.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % ASSOCIATION % % % % Pour une association A entre plusieurs entités E1, E2, ..., écrivez sur une % % ligne « A, XX E1, XX E2, ... ». % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, XX Commande, XX Produit Commande: num. commande, date, montant ================================================ FILE: doc/tutorial_lib/tuto-0005.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % CARDINALITÉS % % % % Si vous connaissez le couple de cardinalités d'une patte, mettez 01, 11, 0N % % ou 1N à la place du XX de l'entité distinguée par cette patte. % % % % NB. Cliquez sur le bouton Éditer et sélectionnez « Correction des fautes de % % frappe dans le cardinalités » pour transformer « ON » en « 0N » ci-dessous. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, ON Produit Commande: num. commande, date, montant ================================================ FILE: doc/tutorial_lib/tuto-0006.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % ATTRIBUT D'ASSOCIATION % % % % Pour ajouter des attributs à une association, faites comme pour une entité : % % mettez un deux-points au bout de la ligne, puis listez les attributs en les % % séparant par des virgules. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant ================================================ FILE: doc/tutorial_lib/tuto-0007.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % ASSOCIATION DE DÉPENDANCE FONCTIONNELLE % % % % Si votre association a une cardinalité 11, mais aucun attribut, vous pouvez % % la nommer « DF » : Mocodo la représentera par un cercle. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11 Commande Client: réf. client, nom, prénom, adresse ================================================ FILE: doc/tutorial_lib/tuto-0008.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % ASSOCIATION RÉFLEXIVE % % % % En répétant le nom d'une entité dans la définition d'une association, vous % % rendez celle-ci réflexive. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11 Commande Client: réf. client, nom, prénom, adresse Parrainer, 01 Client, 0N Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0009.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % SCHÉMA RELATIONNEL % % % % Allez sous l'onglet Options, cochez « Schéma relationnel expliqué », puis % % rafraîchissez la figure : une liste des tables apparaît au-dessous. Cliquez % % sur n'importe quelle ligne pour mieux comprendre comment elle a été obtenue. % % % % Dans la table « Client », remarquez les colonnes « Réf. client » et « Réf. % % client 2 » : ce n'est pas très satisfaisant... % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11 Commande Client: réf. client, nom, prénom, adresse Parrainer, 01 Client, 0N Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0010.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % RÔLES % % % % Dans ce MCD, on a ajouté la chaîne « parrain » sur la patte 0,N de PARRAINER. % % Cette technique permet de préserver la sémantique des associations disparues. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11 Commande Client: réf. client, nom, prénom, adresse Parrainer, 01 Client, 0N [parrain] Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0011.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % DIAGRAMME RELATIONNEL (1) % % % % Cochez « Diagramme relationnel en Mocodo », rafraîchissez la figure et allez % % sous l'onglet Autres sorties. Vous y trouvez un texte-source que vous pouvez % % copier-coller (⧉) ici pour voir apparaître un autre type de schéma. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11 Commande Client: réf. client, nom, prénom, adresse Parrainer, 01 Client, 0N [parrain] Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0012.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % DIAGRAMME RELATIONNEL (2) % % % % Voici le résultat de la manipulation précédente. Les associations ont cédé la % % place à des flèches entre les clés étrangères et les clés primaires d'origine. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% : Produit: réf. produit, libellé, prix unitaire : Inclure: #num. commande > Commande > num. commande, _#réf. produit > Produit > réf. produit, quantité : Commande: num. commande, date, montant, #réf. client > Client > réf. client : Client: réf. client, nom, prénom, adresse, #réf. client parrain > Client > réf. client, date parrainage : ================================================ FILE: doc/tutorial_lib/tuto-0013.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % INFÉRENCE DE TYPES % % % % Cliquez sur le lapin magique, puis sélectionnez : « deviner les types à % % partir du nom des attributs » (en français ou en anglais). Bien sûr, vous % % pourrez toujours rectifier ceux-ci au besoin. % % Notez que l'inférence donne de meilleurs résultats si les libellés sont écrits % % de façon standard : Mocodo proposera un type pour « réf. produit », mais pas % % pour « refproduit ». % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11 Commande Client: réf. client, nom, prénom, adresse Parrainer, 01 Client, 0N [parrain] Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0014.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % GÉNÉRATION DU DDL % % % % Maintenant que le MCD est typé, cochez « Script SQL de création des tables », % % rafraîchissez et allez voir le résultat sous l'onglet Autres sorties. Notez en % % particulier les contraintes de clés étrangères à la fin. Notez aussi que les % % les libellés originaux ont été automatiquement « appauvris » : ainsi, « réf. % % produit » est devenu « ref_produit », etc. Cela rend le code SQL plus portable % % et simplifie les références lors de l'utilisation de la base. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit [VARCHAR(8)], libellé [VARCHAR(50)], prix unitaire [DECIMAL(10,2)] Inclure, 1N Commande, 0N Produit: quantité [INTEGER] Commande: num. commande [VARCHAR(8)], date [DATE], montant [DECIMAL(10,2)] DF, 0N Client, 11 Commande Client: réf. client [VARCHAR(8)], nom [VARCHAR(255)], prénom [VARCHAR(255)], adresse [VARCHAR(30)] Parrainer, 01 Client, 0N [parrain] Client : date parrainage [DATE] ================================================ FILE: doc/tutorial_lib/tuto-0015.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % SCHÉMA SUR PLUSIEURS RANGÉES % % % % Pour finir la première partie du tutoriel, quelques notions de mise en page. % % Les boîtes (entités et association) définies sur des lignes consécutives sont % % alignées horizontalement sur la même rangée d'une grille invisible. Pour créer % % une nouvelle rangée, il suffit de sauter une ligne entre deux clauses. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11 Commande Client: réf. client, nom, prénom, adresse Parrainer, 01 Client, 0N Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0016.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % RÉARRANGEMENT AUTOMATIQUE % % % % Quand le schéma devient confus, faites un double clic sur la tablette de % % chocolat pour le réarranger aléatoirement. L'algorithme veille à éviter % % les croisements et à minimiser la longueur des pattes. Essayez ! % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11 Commande Client: réf. client, nom, prénom, adresse Parrainer, 01 Client, 0N Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0017.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % RÉARRANGEMENT AUTOMATIQUE AVEC CONTRAINTES % % % % Cliquez sur la tablette de chocolat pour faire apparaître d'autres options de % % réorganisation. Celles-ci tentent de plonger le MCD sur la grille actuelle, ou % % en largeur d'abord (pour un document paginé), ou en équilibrant les dimensions % % (pour un diaporama). % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11 Commande Client: réf. client, nom, prénom, adresse Parrainer, 01 Client, 0N Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0018.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % ENTRAÎNEMENT AU PASSAGE AU RELATIONNEL % % % % Cochez l'option « Masquage et génération aléatoire », cliquez le bouton qui % % vient d'apparaître et sélectionnez « Créer un MCD d'entraînement à la % % conversion en relationnel ». Apparaît un MCD avec des libellés aléatoires, % % et dont la structure met en œuvre une certaine diversité de cas. Essayez de % % le convertir en relationnel, et comparez votre solution à celle de Mocodo. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LOREM: ipsum, dolor, amet ================================================ FILE: doc/tutorial_lib/tuto-0019.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % POUR ALLER PLUS LOIN... % % % % Si vous débutez dans les bases de données, les exemples précédents devraient % % suffire à votre bonheur. Dans le cas contraire, cochez l'option « Tutoriel % % interactif (2/2) » et revenez ici. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% À suivre...: ou pas! ================================================ FILE: doc/tutorial_lib/tuto-0020.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % TUTORIEL INTERACTIF (2/2) % % % % Une variation du MCD de Chen avec deux notions plus avancées : entité faible % % et contrainte sur associations ; ainsi que trois nouvelles fonctionnalités : % % flèches, calques, messages au survol. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise PIÈCE: réf. pièce, libellé pièce COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, _11 AYANT-DROIT, 0N EMPLOYÉ EMPLOYÉ: matricule, nom employé PROJET: num. projet, nom projet FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT TRAVAILLER, 0N EMPLOYÉ, 1N PROJET SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET ================================================ FILE: doc/tutorial_lib/tuto-0021.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % ENTITÉ FAIBLE (OU IDENTIFICATION RELATIVE) % % % % Préfixez d'un tiret bas une cardinalité 11 pour « affaiblir » l'entité qu'elle % % distingue, et dont l'identifiant sera alors souligné en pointillés. % % % % NB. De façon non conventionnelle, Mocodo souligne le 11 pour indiquer que le % % « renforcement » de l'identifiant va passer par là. Voyez la documentation % % pour des options alternatives. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Œuvre: cote œuvre, titre, date parution DF, 1N Œuvre, _11 Exemplaire Exemplaire: num. exemplaire, état du livre, date d'achat ================================================ FILE: doc/tutorial_lib/tuto-0022.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % ENTITÉ FAIBLE SANS IDENTIFIANT % % % % Préfixez d'un tiret bas le premier attribut pour l'exclure de l'identifiant, % % p. ex. pour une entité faible sans discriminateur. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Pavillon: id. pavillon, surface, nombre d'étages Posséder, 01 Pavillon, _11 Piscine Piscine: _surface, volume ================================================ FILE: doc/tutorial_lib/tuto-0023.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % IDENTIFIANTS CANDIDATS % % % % Préfixez d'un « i_ » les attributs formant le ième identifiant alternatif. % % Ce numéro apparaîtra dans une gouttière latérale, ainsi qu'un symbole ID en % % face des attributs que vous aurez retenus pour l'identifiant. Le code SQL % % généré inclura une contrainte d'unicité pour chacun des groupes concernés. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11 Commande Client: réf. client, 1_nom, 1_prénom, adresse, 2_mail Parrainer, 01 Client, 0N [parrain] Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0024.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % HÉRITAGE (OU SPÉCIALISATION) % % % % Pour spécialiser une entité E en une ou plusieurs autres entités E1, E2, ... % % écrivez « /\ E <- E1, E2, ... ». % % % % NB. Vous pouvez insérer X (exclusion) et/ou T (totalité) entre les barres % % obliques, ajouter une liste d'attributs ou modifier la sémantique et/ou la % % représentation graphique de l'héritage en remplaçant la flèche par <=, ->, % % =>, <<=, <<-, ->> =>>. Voir la documentation. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Client: réf. client, nom, prénom, adresse /X\ Client <- Particulier, Employé, Administratif Particulier: Employé: siret entreprise Administratif: nom ministère ================================================ FILE: doc/tutorial_lib/tuto-0025.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % AGRÉGATION (OU PSEUDO-ENTITÉ) % % % % Préfixez de « / » une cardinalité pour entourer d'une enveloppe pointillée % % l'association et les autres entités participantes. % % Ici, la dépendance fonctionnelle (date, numéro) => id. client se traduit au % % niveau relationnel par un identifiant réduit pour Réserver, ainsi qu'une % % contrainte de non-optionalité pour id. client. Les contraintes apparaissent % % dans les sorties SQL, mais vous pouvez aussi les visualiser au niveau du MLD % % en cochant l'option « Contraintes d'unicité et d'optionalité ». % % % % NB. La représentation usuelle (non prise en charge par Mocodo) insérerait % % une association entre Client et Réserver. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Date: date Réserver, /1N Client, 1N Chambre, 0N Date: durée Chambre: numéro, prix Client: id. client ================================================ FILE: doc/tutorial_lib/tuto-0026.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % AGRÉGATION ET CONTRAINTES D'UNICITÉ % % % % La conversion en relationnel d'un agrégat avec une cardinalité maximale 1 % % ajoute une contrainte d'unicité sur la clé étrangère. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Voilier: num voilier, longueur Offrir, 0N Voilier, 0N Semaine, /11 Réservation: tarif Semaine: num semaine, 1_date début Réservation: num résa, arrhes, date résa ================================================ FILE: doc/tutorial_lib/tuto-0027.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % AGRÉGATION MULTIPLE % % % % Mocodo gère correctement les cas difficiles à modéliser dans la notation Look % % Here de Merise. Par exemple, deux dépendances fonctionnelles simultanées : % % - (Projet, Employé) => Site. % % - (Employé, Site) => Projet. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Projet: projet Affecter, /1N Site, /1N Projet, 0N Employé Site: site Employé: employé ================================================ FILE: doc/tutorial_lib/tuto-0028.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % CONTRAINTE D'INTÉGRITÉ FONCTIONNELLE (CIF) % % % % Les mêmes cas de figure peuvent aussi être rendus visuellement par des CIF à % % unicité complète (le seul type de CIF pris en charge par Mocodo). % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Date: date Réserver, /1N Client, 1N Chambre, 0N Date: durée Chambre: numéro, prix Client: id. client (CIF) ->Client, --Chambre, --Date, ..Réserver: Date, Client ================================================ FILE: doc/tutorial_lib/tuto-0029.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % AUTRES CONTRAINTES SUR ASSOCIATIONS % % % % Ajoutez à votre MCD diverses contraintes sous forme de lettres parenthésées % % et liées aux boîtes par des traits fléchés ou non, pleins ou pointillés, ou % % même invisibles. Ces contraintes sont purement décoratives, et ignorées lors % % du passage au relationnel. % % % % NB : par défaut, le centre de la contrainte coïncide avec le barycentre des % % boîtes qu'elle met en jeu. % % % % Source du MCD : Merise, deuxième génération (Nanci et Espinasse, 2001). % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ::: Dépôt: num dépôt, surface : Louer, 11 Commande, 0N Dépôt : Stocker, 1N Dépôt, 1N Article: quantité Commande: num. commande, date Composer, 1N Commande, 0N Article : Article: réf. article, prix (I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer, Commande, Stocker ================================================ FILE: doc/tutorial_lib/tuto-0030.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % EXPLICATION INTERACTIVE D'UNE CONTRAINTE % % % % Le survol d'une contrainte suivie d'un texte entre crochets affiche celui-ci % % dans un bandeau. Essayez ! % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Projet: num. projet, nom projet : Fournir, 1N Projet, 1N Pièce, 1N Société: quantité Société: num. société, raison sociale Requérir, 1N Projet, 0N Pièce: quantité : Pièce: réf. pièce, libellé pièce (I) [Toute pièce fournie doit avoir été requise.] ..Pièce, ->Requérir, --Fournir, Projet ================================================ FILE: doc/tutorial_lib/tuto-0031.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % EXPLICATION INTERACTIVE DES CARDINALITÉS % % % % Même fonctionnalité pour les cardinalités. Si l'élément survolé est en bas % % du diagramme, le bandeau apparaît en haut, et vice versa. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N [Une commande inclut au moins un produit.] Commande, 0N [Un produit peut être commandé un nombre quelconque de fois.] Produit: quantité Commande: num. commande, date, montant DF, 0N [Un client peut passer zéro (prospect) ou plusieurs commandes.] Client, 11 [Une commande est passée par un et un seul client.] Commande Client: réf. client, nom, adresse Parrainer, 01 [Un client peut avoir été parrainé ou non.] Client, 0N [Un client peut parrainer d'autres clients.] Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0032.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % FLÈCHE SUR UNE PATTE % % % % En suffixant de « < » ou « > » des cardinalités, vous créez une flèche dont % % la direction se lit de l'association à l'entité. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N> Produit: quantité Commande: num. commande, date, montant DF, 0N Client, 11> Commande Client: réf. client, nom, prénom, adresse Parrainer, 01> Client, 0N< Client : date parrainage ================================================ FILE: doc/tutorial_lib/tuto-0033.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % DÉVOILEMENT PROGRESSIF DU SCHÉMA % % % % Indentez (i.e., insérez des espaces au début de) certaines clauses pour créer % % des calques. Les boîtes définies avec la même indentation sont tracées sur le % % même calque. Les calques sont ordonnés par indentation croissante. Sous Mocodo % % online, pour faciliter la mise au point, le schéma est directement dévoilé. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Parrainer, 01 Client, 0N Client : date parrainage Produit: réf. produit, libellé, prix unitaire Inclure, 1N Commande, 0N Produit: quantité Client: réf. client, nom, prénom, adresse DF, 0N Client, 11 Commande Commande: num. commande, date, montant ================================================ FILE: doc/tutorial_lib/tuto-0034.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % BOÎTES HOMONYMES % % % % Le dernier chiffre ou tiret bas d'un nom d'entité ou d'association n'est pas % % affiché. C'est bon à savoir, p. ex. pour monnayer une entité « fictive » trop % % pattue : ici, on évite les croisements en créant deux entités Date. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Association 9_, XX Entité 3_, XX Date1 Date1: date Association 10_, XX Entité 1_, XX Date1 Association 11_, XX Entité 1_, XX Entité 2_, XX Date2 : Entité 3_: Association 8_, XX Entité 3_, XX Entité 1_, XX Date1 Entité 1_: Entité 2_: Date2: date Association 5_, XX Entité 4_, XX Entité 3_ Entité 4_: Association 6_, XX Entité 4_, XX Entité 1_ Association 7_, XX Entité 1_, XX Entité 2_, XX Date2 : ================================================ FILE: doc/tutorial_lib/tuto-0035.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % VUE EN EXTENSION % % % % La même fonctionnalité permet de créer une vue en extension (à droite du MCD). % % Notez qu'il n'y a pas besoin de suffixe pour créer plusieurs associations DF. % % Notez également les lignes réduites à des deux-points : ceux-ci sont traités % % comme des boîtes « fantômes », et jouent le rôle d'espaces horizontaux. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ŒUVRE1: cote, titre, date de publication ::: ŒUVRE2: 612.NAT.34, J'apprends à lire à mes souris blanches, mai 1975 : DF, 1N ŒUVRE1, _11 EXEMPLAIRE1 :: DF, XX ŒUVRE2, XX EXEMPLAIRE2 DF, XX ŒUVRE2, XX EXEMPLAIRE3 DF, XX ŒUVRE2, XX EXEMPLAIRE4 EXEMPLAIRE1: numéro d'exemplaire, état, date d'achat :: EXEMPLAIRE2: 1, bon état, 12/6/1975 EXEMPLAIRE3: 2, bon état, 1/8/1977 EXEMPLAIRE4: 3, reliure rongée, 3/4/2005 ================================================ FILE: doc/tutorial_lib/tuto-0036.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % DÉCOMPOSITION DES ASSOCIATIONS TERNAIRES (1) % % % % Cochez l'option « Décomposition d'associations » et double-cliquez le bouton % % qui vient d'apparaître : l'association Fournir est remplacée par une entité % % de même nom pourvue d'un identifiant générique et assortie de trois DF. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise PIÈCE: réf. pièce, libellé pièce COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, _11 AYANT-DROIT, 0N EMPLOYÉ EMPLOYÉ: matricule, nom employé PROJET: num. projet, nom projet FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT TRAVAILLER, 0N EMPLOYÉ, 1N PROJET SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ ================================================ FILE: doc/tutorial_lib/tuto-0037.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % DÉCOMPOSITION DES ASSOCIATIONS TERNAIRES (2) % % % % Cochez maintenant l'option « Entité faible » : l'entité Fournir devient une % % entité faible sans identifiant. Vous pouvez vérifier que les schémas sont % % conceptuellement équivalents, et produisent les mêmes sorties relationnelles % % (MLD) et physiques (MPD). % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise PIÈCE: réf. pièce, libellé pièce COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, _11 AYANT-DROIT, 0N EMPLOYÉ EMPLOYÉ: matricule, nom employé PROJET: num. projet, nom projet FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT TRAVAILLER, 0N EMPLOYÉ, 1N PROJET SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ ================================================ FILE: doc/tutorial_lib/tuto-0038.mcd ================================================ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % POUR ALLER PLUS LOIN... % % % % Chacune des opérations que Mocodo online présente sous la forme de bouton ou % % de case à cocher consiste en un simple appel à la commande « mocodo » avec % % une séquence fixe d'options. En installant Mocodo, vous pourrez composer ces % % séquences librement, ce qui vous ouvrira d'autres perspectives : création de % % jeux de tests, génération d'examens avec un sujet distinct par étudiant, % % automatisation des corrections, etc. Pour l'heure, l'étape suivante semble % % être la lecture de la documentation (lien en bas de page), qui vous donnera % % un éventail complet des possibilités, et peut-être d'autres idées ! % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% À suivre...: ou pas! ================================================ FILE: index.php ================================================ Mocodo online $(document).ready(function() { $('#lib').val('{$lib}'); });"; } ?>
Modélisation Conceptuelle de Données. Nickel. Ni souris.
Réarranger
Inverser
Éditer
Révéler
Masquer

Star

Mocodo est un logiciel d'aide à l'enseignement et à l'apprentissage des bases de données relationnelles.

  • En entrée, il prend un MCD (modèle conceptuel de données) décrit dans un langage dédié minimaliste.
  • En sortie, il produit un diagramme entité-association et, à la demande, un MLD (schéma relationnel, sous forme graphique ou textuelle), un DDL (script SQL de création de la base), un diagramme de classes UML, etc.
  • En bonus, il est capable de réarranger automatiquement votre MCD de façon esthétique, et de lui appliquer des opérations de réécriture qui vont du mondain (typographie) à l'académique (décomposition d'associations), en passant par le merveilleux (inférence de types, génération d'exercices et d'exemples).

Ce site est prévu pour une utilisation basique et occasionnelle, typiquement en salle de classe. Si vous travaillez sur des données confidentielles¹, ou souhaitez avoir accès à toutes les fonctionnalités de Mocodo, vous pouvez, soit l'importer sous Basthon, soit l'installer sur votre ordinateur :


> pip install mocodo

Sous cette dernière forme, Mocodo est un puissant logiciel en ligne de commande, multiplateforme, open-source, libre et gratuit. Il s'intègre particulièrement bien à l'environnement Jupyter Notebook.


Pour en savoir plus, suivez nos tutoriels interactifs (onglet Entrée), puis plongez-vous dans la documentation.


Aristide Grange, Université de Lorraine, Metz (France)


¹ Ce site stocke dans votre navigateur un cookie non soumis à obligation de consentement préalable qui lui permet de retrouver vos réglages de l'onglet Options. Côté serveur, il stocke le dernier état de votre travail afin de le mettre à votre disposition sous forme d'archive téléchargeable. Ce dossier est détruit au bout de 24 heures. Pour le détruire dès la fin d'une session, effacez le texte d'entrée et pressez le bouton de rafraîchissement. Sous Basthon ou en local, rien ne quitte votre ordinateur.

    Autres options de conversions…
Découvrir…
Coordonnées du centre des boîtes
Décalage des cardinalités
Zone de rognage
Position des flèches des associations
================================================ FILE: laowantong.vscode-mocodo/.vscode/launch.json ================================================ // A launch configuration that launches the extension inside a new window // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 { "version": "0.2.0", "configurations": [ { "name": "Extension", "type": "extensionHost", "request": "launch", "args": [ "--extensionDevelopmentPath=${workspaceFolder}" ] } ] } ================================================ FILE: laowantong.vscode-mocodo/.vscodeignore ================================================ .vscode/** .vscode-test/** .gitignore vsc-extension-quickstart.md *.vsix ================================================ FILE: laowantong.vscode-mocodo/LICENSE ================================================ MIT License Copyright (c) 2017- Aristide Grange Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: laowantong.vscode-mocodo/README.md ================================================ # Mocodo Syntax Syntax highlighting support for Mocodo. ![Mocodo text source](doc/syntax_screenshot.png) Mocodo is a tool for designing and teaching relational databases. It is based on the French-flavored Entity-Relationship model (aka Merise). ![Mocodo text source](doc/readme_1.png) ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/laowantong/mocodo/. ## License MIT. ## Release Notes ### 1.0.5 -> 1.0.6 Update for Mocodo 4.0.5. ### 1.0.2 Add support for Mocodo 4.0's invisible boxes. ### 1.0.1 Don't regard a mix of empty and non-empty entity attributes as an error. ### 1.0.0 Improve color consistency between [Mocodo online](https://mocodo.net) and VS-Code's "Light+" or "Light Modern" themes. ### 0.0.2 -> 0.0.5 Fix packaging problems. ### 0.0.1 Initial release. ## For more information * [Mocodo online](https://mocodo.net) * [Github](https://github.com/laowantong/mocodo/) * [Documentation](https://laowantong.github.io/mocodo/doc/fr_refman.html) ================================================ FILE: laowantong.vscode-mocodo/highlighting_zoo.mcd ================================================ %%mocodo : ::: : : : : : : % commented % commented % % foo\\ , % foo /TX\\ AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, _11 AYANT-DROIT, 0N EMPLOYÉ FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie CONTRÔLER, 0N< [filiale] SOCIÉTÉ, 01 [mère] SOCIÉTÉ DF, 0N> CLIENT, 11 COMMANDE GRATTE-CIEL: latitude, _longitude, nom, hauteur, année de construction Peut recevoir, 1N> Groupe sanguin, 1N< Groupe sanguin Engendre, 0N< Personne, 22> Personne Agent 0070: bar DF4, 11 Agent 0070, 1N Agent1 Agent1: bar DF, _11 ŒUVRE, _11 EXEMPLAIRE Réserver, /1N Client, 1N Chambre, 0N Date: Durée LIGULA, 0N LACUS, /1N EROS, 0N TELLUS, 0N CONSEQUAT: metus Réserver: _Durée +LIGULA, 01 LACUS, 1N EROS: metus PASSER, 0N [Un client peut passer un nombre quelconque de commandes.] CLIENT, 11 [Toute commande est passée par un en un seul client.] COMMANDE INCLURE, 1N [Une commande peut inclure plusieurs produits distincts, et en inclut au moins un.] COMMANDE, 0N [Certains produits ne sont jamais commandés, d'autres le sont plusieurs fois.] PRODUIT: Quantité CLIENT: CLIENT: , ,, COMMANDE: , , PRODUIT: , , Unit, 1N Draw, 11 Folk: Peer, Tour, PASSER, XX CLIENT, XX COMMANDE INCLURE, XX COMMANDE, XX PRODUIT: ŒUVRE: 612.NAT.34, J'apprends à lire à mes souris blanches, mai 1975 EXEMPLAIRE2: 1, bon état, 12/6/1975 EXEMPLAIRE3: 2, bon état, 1/8/1977 EXEMPLAIRE4: 3, reliure rongée, 3/4/2005 DF, -1N ŒUVRE, -_11 EXEMPLAIRE1 +Prof: Num. prof, Nom prof Enseignant: num. ens. [numéro identifiant un enseignant], nom ens. [nom enseignant], tél. ens. [téléphone enseignant] COMMANDE: Num commande, Date, Montant, #Réf. client>CLIENT>Réf. client COMMANDE: Num commande, Date, Montant, #Réf. client > CLIENT > Réf. client INCLURE: #Num commande>COMMANDE>Num commande, _#Réf. produit>PRODUIT>Réf. produit, Quantité INCLURE: #Num commande > COMMANDE > Num commande, _#Réf. produit > PRODUIT > Réf. produit, Quantité CLIENT: Réf. client [varchar(8)], Nom [varchar(20)], Adresse [varchar(40)] INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité [tinyint(4)] PARTICIPANT: numero [], nom, adresse [type3] L33T, 0N> H4X0R, 0N< H4X0R L33T123, 0N> H4X0R12, 0N< H4X0R0 AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise DIRIGER, EMPLOYÉ, PROJET A, B, C Foo, Bar DIRIGER, EMPLOYÉ, PROJET: biz, buz A, B, C: biz, buz Foo, Bar: biz, buz AYANT-DROIT : nom ayant-droit , lien DIRIGER , 0N EMPLOYÉ , 01 PROJET : fizz, buzz AYANT-DROIT  :  nom ayant-droit  ,  lien  DIRIGER  ,  0N  EMPLOYÉ  ,  01  PROJET  : fizz, buzz  AYANT-DROIT : nom ayant-droit , lien DIRIGER , 0N EMPLOYÉ , 01 PROJET : fizz, buzz () [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET (II) ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET (III) [bla bla.] (IV) (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, 30 (II) ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, 30 (III) [bla bla.]: 12.5, 30 (IV) : 12.5, 30 (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: FOO, 30 (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, BAR (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: FOO, BAR (IV) : 12.5, 30 (A) --Lorem, .....Ipsum, -Dolor: 30, 10 (B) ->Dolor, <-->Sit, -->Amet: 69, 10 (XX) ->foo, -->foo, -> foo, --> foo (I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer /XT\ Personne ==> Homme, Femme: sexe /XT1\\ Personne <= Homme, Femme: sexe /XT\\ Personne => Homme, Femme: sexe /T\\ Personne <= Homme, Femme: sexe /T\\ Personne => Homme, Femme: sexe /X\\ Personne <= Homme, Femme: sexe /X\\ Personne => Homme, Femme: sexe /\\ Personne <= Homme, Femme: sexe /\\ Personne => Homme, Femme: sexe /1\\ FOO => BAR /T\\foo==>foo11 Étudiant: num, 1_nom, 1_prénom, adresse, 2_mail Étudiant: 0_num, 1_nom, 1_prénom, adresse, 2_mail Position: 0_latitude, 0_longitude, altitude Foo: bar, 1_baz, 21_qux, 123_quux % Invalid : foobar : foobar FOO, 0N Bar, 1N Biz [bla] FOO, 0N Bar, 1N Biz [bla] FOO, 0N Bar, 1N Biz [bla], 0N Buz foo /TX\\# ] #/ BACK\\SLASH: foo_11<11_ ]<-->]foo FOOBAR FOOBAR> FOOBAR > FOOBAR+ /T\\, foobar DIRIGER, 0 EMPLOYÉ, 01 PROJET DIRIGER, 0 EMPLOYÉ, 01 PROJET: biz, buz DIRIGER, 0NV /ANYTHING\\ Personne => Homme, Femme: sexe /X12\\ Personne => Homme, Femme: sexe /1N\\ (I) : 1, 2, 3 DIRIGER, DIRIGER, (I) : (I) ->Foo ..Bar : (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: FOOBAR, (A) : Ipsum, --Lorem: 30, 90 + +# /T\\ \\.. ,\\foo +..==> /TX\\ foo -> .bar PARTICIPANT: numero [, nom, adresse (]) (+) (/) (IIII) ( ) (    ) (A) --Lorem, >Ipsum: 30, 90 (A) --Lorem, : Ipsum: 30, 90 (A) --Lorem 30, 90 (A) >Ipsum, --Lorem: 30, 90 /TX\\ foo bar COMMANDE: Num commande, Date, Montant, #Réf. client->CLIENT->Réf. client INCLURE: #Num commande->COMMANDE->Num commande, _#Réf. produit->PRODUIT->Réf. produit, Quantité (A) ->Ipsum ->Lorem: 30, 90 FOOBAR: foo, /bar FOO, 1N Bar: -->Amet FOOBAR: foo>bar, biz FOO: #bar, biz FOO: #bar FOO: biz, #bar FOO: #bar>buzz, biz FOO: #bar>buzz FOO: biz, #bar>buzz FOO: #bar>buzz>, biz FOO: #bar>buzz> FOO: biz, #bar>buzz> DIRIGER, 0N EMPLOYÉ > PRODUIT, Quantité (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5 (A) ->Ipsum, ->Lorem: 30, 9N (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 3,14 3,14 ================================================ FILE: laowantong.vscode-mocodo/language-configuration.json ================================================ { "comments": { "lineComment": "%", }, "autoClosingPairs": [ ["[", "]"], ["(", ")"] ], "surroundingPairs": [ ["[", "]"], ["(", ")"] ] } ================================================ FILE: laowantong.vscode-mocodo/package.json ================================================ { "name": "vscode-mocodo", "version": "1.0.6", "publisher": "laowantong", "engines": { "vscode": "^1.2.0" }, "icon": "images/android-chrome-512x512.png", "license": "MIT", "displayName": "Mocodo syntax highlighter", "description": "A syntax highlighter for Mocodo E/R diagrams", "categories": [ "Programming Languages" ], "homepage": "https://marketplace.visualstudio.com/items?itemName=laowantong.vscode-mocodo", "repository": { "type": "git", "url": "https://github.com/laowantong/mocodo" }, "bugs": { "url": "https://github.com/laowantong/mocodo/issues" }, "contributes": { "languages": [{ "id": "mocodo", "aliases": ["Mocodo", "mocodo"], "extensions": [".mcd", ".mld"], "configuration": "./language-configuration.json" }], "grammars": [{ "language": "mocodo", "scopeName": "source.mocodo", "path": "./syntaxes/mocodo.tmLanguage.json" }] } } ================================================ FILE: laowantong.vscode-mocodo/syntaxes/mocodo.tmLanguage.json ================================================ { "name": "Mocodo", "scopeName": "source.mocodo", "uuid": "2AD24A5A-63C9-40B3-B6AA-E240B7B54480", "patterns": [ { "include": "#indent" }, { "include": "#comment" }, { "include": "#phantoms" }, { "include": "#constraint_clause" }, { "include": "#inheritance_clause" }, { "include": "#association_clause" }, { "include": "#entity_clause" }, { "include": "#invalid" } ], "repository": { "indent": { "match": "^\\s+" }, "comment": { "patterns": [ { "match": "%%mocodo\\b.*$", "name": "markup.bold comment.line.magic.mocodo" }, { "match": "%.*$", "name": "comment.line.normal.mocodo" } ] }, "phantoms": { "description": "A line containing at least one colon, ane nothing else but spaces and colons", "match": ":[:\\s]*$", "name": "markup.italic punctuation.separator.phantom.mocodo" }, "association_clause": { "name": "association.mocodo", "begin": "(\\+|-)?((?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF])[^=<>,:\\\\]*[^-\\[\\]\\\\=<>,:\\s\\\\])(?=\\s*,\\s*)", "end": "$|^", "beginCaptures": { "1": { "name": "keyword.control.mocodo" }, "2": { "name": "markup.bold support.function.association.mocodo" } }, "patterns": [ { "include": "#association_legs" } ] }, "association_legs": { "name": "legs.mocodo", "begin": "", "end": "$|^", "patterns": [ { "match": "\\s*,\\s*(?:(-)?([_/])?((?![-_\\/])(?:[\\w?]{2})(?=[\\s]*[^\\w,\\r\\n:])))?([<>])?\\s*(\\[.*?\\])?\\s*((?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF])[^=<>,:\\\\]*[^-\\[\\]\\\\=<>,:\\s\\\\])\\s*", "captures": { "1": { "name": "keyword.control.card_hidden.mocodo" }, "2": { "name": "keyword.control.card_prefix.mocodo" }, "3": { "name": "support.function.cardinality.mocodo" }, "4": { "name": "keyword.control.arrow.mocodo" }, "5": { "name": "string.regexp.note.mocodo" }, "6": { "name": "token.info-token.entity.mocodo" } } }, { "include": "#association_attrs" }, { "include": "#invalid" } ] }, "association_attrs": { "name": "attrs.mocodo", "begin": "\\s*:\\s*", "end": "$|^", "patterns": [ { "include": "#typed_attr" }, { "match": "\\s*,\\s*", "name": "punctuation.separator" } ] }, "typed_attr": { "name": "typed_attribute.mocodo", "match": "((?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s])\\s*(\\[.*?\\])?\\s*", "captures": { "1": { "name": "support.type.attribute.mocodo" }, "2": { "name": "markup.italic token.warn-token.datatype.mocodo" } } }, "entity_clause": { "name": "entity.mocodo", "begin": "(\\+|-)?((?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF])[^=<>,:\\\\]*[^-\\[\\]\\\\=<>,:\\s\\\\])\\s*:\\s*", "end": "$|^", "beginCaptures": { "1": { "name": "keyword.control.mocodo" }, "2": { "name": "markup.bold token.info-token.entity.mocodo" } }, "patterns": [ { "include": "#entity_first_attr" } ] }, "entity_first_attr": { "begin": "(?:((?:\\d*0\\d*_|_))((?:(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]|#(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]\\s*>\\s*(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]\\s*>\\s*(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]))|([1-9]+_)?((?:(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]|#(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]\\s*>\\s*(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]\\s*>\\s*(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]))|)\\s*(\\[.*?\\])?", "end": "$|^", "beginCaptures": { "1": { "name": "string.regexp.id_symbols.mocodo" }, "2": { "name": "support.type.attribute.mocodo" }, "3": { "name": "string.regexp.alt_id_symbols.mocodo" }, "4": { "name": "markup.underline support.type.attribute.mocodo" }, "5": { "name": "markup.italic token.warn-token.datatype.mocodo" } }, "patterns": [ { "include": "#entity_next_attrs" }, { "include": "#invalid" } ] }, "entity_next_attrs": { "patterns": [ { "match": "\\s*,\\s*(?:((?:\\d*0\\d*_|_))((?:(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]|#(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]\\s*>\\s*(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]\\s*>\\s*(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]))|([1-9]+_)?((?:(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]|#(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]\\s*>\\s*(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]\\s*>\\s*(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]))|)\\s*(\\[.*?\\])?", "captures": { "1": { "name": "string.regexp.id_symbols.mocodo" }, "2": { "name": "markup.underline support.type.attribute.mocodo" }, "3": { "name": "string.regexp.alt_id_symbols.mocodo" }, "4": { "name": "support.type.attribute.mocodo" }, "5": { "name": "markup.italic token.warn-token.datatype.mocodo" } } }, { "include": "#invalid" } ] }, "constraint_clause": { "name": "constraint.mocodo", "begin": "(\\([a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF\\d_\\s]{0,3}\\))\\s*(\\[.*?\\])?\\s*", "end": "$|^", "beginCaptures": { "1": { "name": "markup.bold variable.constraint_name.mocodo" }, "2": { "name": "string.regexp.note.mocodo" } }, "patterns": [ { "include": "#constraint_targets" } ] }, "constraint_targets": { "name": "targets.mocodo", "begin": "", "end": "$|^", "patterns": [ { "match": "(?)?\\s*((?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF])[^=<>,:\\\\]*[^-\\[\\]\\\\=<>,:\\s\\\\])", "captures": { "1": { "name": "string.regexp.constraint_leg.mocodo" }, "2": { "name": "variable.other.constant.box.mocodo" } } }, { "match": "\\s*,\\s*" }, { "include": "#constraint_coords" }, { "include": "#invalid" } ] }, "constraint_coords": { "name": "coords.mocodo", "match": "\\s*:\\s*((?:-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?|(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF])[^=<>,:\\\\]*[^-\\[\\]\\\\=<>,:\\s\\\\]))\\s*,\\s*((?:-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?|(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF])[^=<>,:\\\\]*[^-\\[\\]\\\\=<>,:\\s\\\\]))$", "captures": { "1": { "name": "markup.italic string.regexp.constraint_coords.mocodo" }, "2": { "name": "markup.italic string.regexp.constraint_coords.mocodo" } } }, "inheritance_clause": { "name": "inheritance.mocodo", "begin": "(/(?:XT\\d?|TX\\d?|X\\d?|T\\d?|\\d?)\\\\{1,2})\\s*((?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF])[^=<>,:\\\\]*[^-\\[\\]\\\\=<>,:\\s\\\\])?\\s*((?:<==?|<--?|--?>|==?>))\\s*", "end": "$|^", "beginCaptures": { "1": { "name": "markup.bold support.function.inheritance.mocodo markup.underline" }, "2": { "name": "token.info-token.parent.mocodo" }, "3": { "name": "keyword.control.arrow.mocodo" } }, "patterns": [ { "include": "#inheritance_children" } ] }, "inheritance_children": { "name": "children.mocodo", "begin": "", "end": "$|^", "patterns": [ { "match": "((?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF])[^=<>,:\\\\]*[^-\\[\\]\\\\=<>,:\\s\\\\])", "name": "token.info-token.child.mocodo" }, { "match": "\\s*,\\s*" }, { "include": "#inheritance_attrs" }, { "include": "#invalid" } ] }, "inheritance_attrs": { "name": "attrs.mocodo", "begin": "\\s*:\\s*", "end": "$|^", "patterns": [ { "match": "(?=[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF0-9])[^\\[\\]>,]*[^\\[\\]>,\\s]", "name": "markup.italic string.regexp.inheritance_attribute.mocodo" }, { "match": "\\s*,\\s*" }, { "include": "#invalid" } ] }, "invalid": { "match": ".+", "name": "invalid.illegal.mocodo" } } } ================================================ FILE: laowantong.vscode-mocodo/syntaxes/mocodo.tmLanguage.yaml ================================================ --- name: Mocodo scopeName: source.mocodo uuid: 2AD24A5A-63C9-40B3-B6AA-E240B7B54480 # Warning: yaml_to_json_tmlanguage.py will suppress all " " symbols from the regexes. variables: latin: 'a-zA-Z\u00C0-\u024F\u1E00-\u1EFF' attr: '(?=[{{latin}}0-9]) [^\[\]>,]*[^\[\]>,\s]' box_def_prefix: '\+|-' constraint_name: '\([{{latin}}\d_\s]{0,3}\)' card_hidden: '-' card_prefix: '[_/]' colon: '\s*:\s*' comma: '\s*,\s*' constraint_leg: '?' id_symbols: '(?: \d*0\d*_|_)' alt_id_symbols: '[1-9]+_' inheritance_name: '/ (?: XT\d?|TX\d?|X\d?|T\d?|\d? ) \\{1,2}' inheritance_arrow: '(?: <==?|<--?|--?>|==?> )' leg_arrow: '[<>]' morethan: '\s*>\s*' note: \[.*?\] number: '-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?' box_name: '(?=[{{latin}}]) [^=<>,:\\]* [^-\[\]\\=<>,:\s\\]' card: '(?![-_\/]) (?:[\w?]{2}) (?=[\s]*[^\w,\r\n:])' constraint_coord: '(?: {{number}} | {{box_name}} )' foreign_reference: '#{{attr}} {{morethan}} {{attr}} {{morethan}} {{attr}}' entity_or_table_attr: '(?: {{attr}} | {{foreign_reference}} )' patterns: - include: "#indent" - include: "#comment" - include: "#phantoms" - include: "#constraint_clause" - include: "#inheritance_clause" - include: "#association_clause" - include: "#entity_clause" - include: "#invalid" repository: indent: match: ^\s+ comment: patterns: - # Jupyter cell magic command, interpreted as a comment match: '%%mocodo\b.*$' name: "markup.bold comment.line.magic.mocodo" - # Normal comment line match: '%.*$' name: comment.line.normal.mocodo phantoms: description: A line containing at least one colon, ane nothing else but spaces and colons match: ':[:\s]*$' name: "markup.italic punctuation.separator.phantom.mocodo" association_clause: name: association.mocodo begin: '({{box_def_prefix}})? ({{box_name}}) (?={{comma}})' end: '$|^' beginCaptures: '1': { name: keyword.control.mocodo } '2': { name: "markup.bold support.function.association.mocodo" } patterns: - include: "#association_legs" association_legs: name: legs.mocodo begin: '' end: '$|^' patterns: - match: >- # Multiline string without a linebreak appended at the end. {{comma}} (?: ({{card_hidden}})? ({{card_prefix}})? ({{card}}))? ({{leg_arrow}})? \s* ({{note}})? \s* ({{box_name}}) \s* captures: '1': { name: keyword.control.card_hidden.mocodo } '2': { name: keyword.control.card_prefix.mocodo } '3': { name: support.function.cardinality.mocodo } '4': { name: keyword.control.arrow.mocodo } '5': { name: string.regexp.note.mocodo } '6': { name: token.info-token.entity.mocodo } - include: "#association_attrs" - include: "#invalid" association_attrs: name: attrs.mocodo begin: "{{colon}}" end: '$|^' patterns: - include: "#typed_attr" - match: '{{comma}}' name: punctuation.separator typed_attr: name: typed_attribute.mocodo match: '({{attr}}) \s* ({{note}})? \s*' captures: '1': { name: support.type.attribute.mocodo } '2': { name: "markup.italic token.warn-token.datatype.mocodo" } entity_clause: name: entity.mocodo begin: '( {{box_def_prefix}} )? ( {{box_name}} ) {{colon}}' end: '$|^' beginCaptures: '1': { name: keyword.control.mocodo } '2': { name: "markup.bold token.info-token.entity.mocodo" } patterns: - include: "#entity_first_attr" entity_first_attr: # After the entity's name, process the first attribute # Underline it iff it is **not** prefixed by id_symbols. begin: '(?: ({{id_symbols}}) ({{entity_or_table_attr}}) | ({{alt_id_symbols}})? ({{entity_or_table_attr}}) | ) \s* ({{note}})?' end: '$|^' beginCaptures: '1': { name: string.regexp.id_symbols.mocodo } '2': { name: support.type.attribute.mocodo} '3': { name: string.regexp.alt_id_symbols.mocodo } '4': { name: "markup.underline support.type.attribute.mocodo" } '5': { name: "markup.italic token.warn-token.datatype.mocodo" } patterns: - include: "#entity_next_attrs" - include: "#invalid" entity_next_attrs: # After the first attribute, process the remaining ones. # Underline them iff they are prefixed by a "_" symbol. patterns: - match: '{{comma}} (?: ({{id_symbols}}) ({{entity_or_table_attr}}) | ({{alt_id_symbols}})? ({{entity_or_table_attr}}) | ) \s* ({{note}})?' captures: '1': { name: string.regexp.id_symbols.mocodo } '2': { name: "markup.underline support.type.attribute.mocodo" } '3': { name: string.regexp.alt_id_symbols.mocodo } '4': { name: support.type.attribute.mocodo} '5': { name: "markup.italic token.warn-token.datatype.mocodo" } - include: '#invalid' constraint_clause: name: constraint.mocodo begin: '({{constraint_name}}) \s* ({{note}})? \s*' end: '$|^' beginCaptures: '1': { name: markup.bold variable.constraint_name.mocodo } '2': { name: string.regexp.note.mocodo } patterns: - include: "#constraint_targets" constraint_targets: name: targets.mocodo begin: '' end: '$|^' patterns: - match: '({{constraint_leg}})? \s* ({{box_name}})' captures: '1': { name: string.regexp.constraint_leg.mocodo } '2': { name: variable.other.constant.box.mocodo } - match: '{{comma}}' - include: "#constraint_coords" - include: "#invalid" constraint_coords: name: coords.mocodo match: '{{colon}} ({{constraint_coord}}) {{comma}} ({{constraint_coord}})$' captures: '1': { name: "markup.italic string.regexp.constraint_coords.mocodo" } '2': { name: "markup.italic string.regexp.constraint_coords.mocodo" } inheritance_clause: name: inheritance.mocodo begin: '({{inheritance_name}}) \s* ({{box_name}})? \s* ({{inheritance_arrow}}) \s*' end: '$|^' beginCaptures: '1': { name: "markup.bold support.function.inheritance.mocodo markup.underline" } '2': { name: token.info-token.parent.mocodo } '3': { name: keyword.control.arrow.mocodo } patterns: - include: "#inheritance_children" inheritance_children: name: children.mocodo begin: '' end: '$|^' patterns: - match: '({{box_name}})' name: token.info-token.child.mocodo - match: '{{comma}}' - include: "#inheritance_attrs" - include: "#invalid" inheritance_attrs: name: attrs.mocodo begin: '{{colon}}' end: '$|^' patterns: - match: '{{attr}}' name: "markup.italic string.regexp.inheritance_attribute.mocodo" - match: '{{comma}}' - include: "#invalid" invalid: match: '.+' name: invalid.illegal.mocodo ================================================ FILE: laowantong.vscode-mocodo/vsc-extension-quickstart.md ================================================ # Welcome to your VS Code Extension ## What's in the folder * This folder contains all of the files necessary for your extension. * `package.json` - this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension. * `syntaxes/mocodo.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization. * `language-configuration.json` - this is the language configuration, defining the tokens that are used for comments and brackets. ## Get up and running straight away * Make sure the language configuration settings in `language-configuration.json` are accurate. * Press `F5` to open a new window with your extension loaded. * Create a new file with a file name suffix matching your language. * Verify that syntax highlighting works and that the language configuration settings are working. ## Make changes * You can relaunch the extension from the debug toolbar after making changes to the files listed above. * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. ## Add more language features * To add features such as IntelliSense, hovers and validators check out the VS Code extenders documentation at https://code.visualstudio.com/docs ## Install your extension * To start using your extension with Visual Studio Code copy it into the `/.vscode/extensions` folder and restart Code. * To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension. ================================================ FILE: mocodo/__init__.py ================================================ from pathlib import Path from importlib import import_module __version__ = "4.3.3" SCRIPT_DIRECTORY = Path(__file__).resolve().parent def load_ipython_extension(ipython): # This function is called when the extension is loaded in a notebook # with %load_ext mocodo or %reload_ext mocodo. mocodo = import_module("mocodo.magic").mocodo ipython.register_magic_function(mocodo, 'line_cell', 'mocodo') print(f"Mocodo {__version__} loaded.") ================================================ FILE: mocodo/__main__.py ================================================ import sys if sys.version_info < (3, 6): print(f"Mocodo requires Python 3.6 or later to run.\nThis version is {sys.version}.") sys.exit() import contextlib import importlib import json from pathlib import Path from .argument_parser import parsed_arguments from .common import Common, Printer from .convert.read_template import read_template from .convert.relations import Relations from .font_metrics import font_metrics_factory from .guess_title import may_update_params_with_guessed_title from .mcd import Mcd from .mcd_to_svg import main as dump_mcd_to_svg from .mocodo_error import MocodoError, subopt_error from .rewrite import op_tk, guess_entities from .tools.graphviz_tools import minify_graphviz from .tools.string_tools import urlsafe_encoding from .tools.various import invert_dict SHOW_ARGS = invert_dict({ "mcd": ["mcd"], "rw": ["rw", "source", "text", "code", "mocodo"], "cv": ["cv", "mld", "ddl", "sql"], }) class ResponseLogger: def __init__(self, params): if not params["is_magic"]: self.may_log = self.log_nothing return self.response = { "mld": params["mld"], "args_to_delete": params["args_to_delete"], "opt_to_restore": params["opt_to_restore"], "redirect_output": params["redirect_output"], "select": params["select"], } self.may_log = self.log_for_magic self.path = Path(f"{params['output_name']}_response_for_magic_command.json") self.dump() def log_nothing(self, *args, **kwargs): pass def log_for_magic(self, key, value): self.response[key] = value self.dump() def dump(self): self.path.write_text(json.dumps(self.response, ensure_ascii=False), encoding="utf8") def flip(source, subargs): for subsubopt in "".join(subargs.keys()): mcd = Mcd(source) if subsubopt == "v": source = mcd.get_vertically_flipped_clauses() elif subsubopt == "h": source = mcd.get_horizontally_flipped_clauses() elif subsubopt == "d": source = mcd.get_diagonally_flipped_clauses() else: subopt_error("flip", subsubopt) return source class Runner: def __init__(self, args, printer): self.printer = printer try: self.params = parsed_arguments(args) self.parsing_error = None self.common = Common(self.params) self.get_font_metrics = font_metrics_factory(self.params) except MocodoError as e: # Raising a parsing error must be delayed, otherwise it will not be displayed as a MocodoError self.parsing_error = e def __call__(self): if self.parsing_error: raise self.parsing_error source = self.common.load_input_file() if self.params["restore"]: shutil = importlib.import_module("shutil") path = Path(self.params["script_directory"], "resources", "pristine_sandbox.mcd") shutil.copyfile(path, "sandbox.mcd") Path("params.json").write_text("{}\n", encoding="utf8") return if self.params["print_params"]: for added_key in self.params["keys_to_hide"][:]: self.params.pop(added_key, None) with contextlib.suppress(ValueError): # raised when called as a function (from mocodo import mocodo) self.params["output_dir"] = str(Path(self.params["output_dir"]).resolve().relative_to(Path.cwd())) text = json.dumps(self.params, ensure_ascii=False, indent=2, sort_keys=True) text = text.replace("\n ", " ") text = text.replace("\n ]", " ]") self.printer.write(text.strip()) return "\n".join(self.printer.accumulator) self.add_gutter_params(self.params) if self.params["mld"] or ("transform" in self.params and self.params["transform"] == []): # In case there is an option `--mld` or an option `--transform` without arguments, # inject manually the equivalent --convert sub-option self.params["mld"] = True self.params["convert"].insert(0, ("markdown", {})) if "select" in self.params: # the user wants to override the default display policy under Jupyter if "all" in self.params["select"]: self.params["select"] = ["mcd", "rw", "cv"] if self.params["rewrite"] else ["mcd", "cv"] normalized_user_choices = [] for k in self.params["select"]: if k.lower() not in SHOW_ARGS: raise MocodoError(28, _('Unknown argument "{k}" for option --select.').format(k=k)) # fmt: skip normalized_user_choices.append(SHOW_ARGS[k.lower()]) self.params["select"] = normalized_user_choices else: if self.params["rewrite"] and self.params["convert"]: self.params["select"] = ["mcd", "cv"] elif self.params["rewrite"]: self.params["select"] = ["mcd", "rw"] elif self.params["convert"] and self.params["mld"]: self.params["select"] = ["mcd", "cv"] elif self.params["convert"]: self.params["select"] = ["cv"] else: self.params["select"] = ["mcd"] response = ResponseLogger(self.params) if self.params["rewrite"]: for (subopt, subargs) in self.params["rewrite"]: if subopt == "echo": pass elif subopt == "flip": source = flip(source, subargs) elif subopt == "create" and "entities" in subargs: source = guess_entities.run(source, subargs["entities"]) elif subopt == "delete" and "dfs" in subargs: module = importlib.import_module(f".rewrite._delete_dfs", package="mocodo") source = module.run(source, self.params) elif subopt in self.params["op_tk_rewritings"]: # ex.: create, delete, ascii, etc. source = op_tk.run(source, op_name=subopt, subargs=subargs, params=self.params).rstrip() else: # An unspecified rewrite operation, dynamically loaded try: module = importlib.import_module(f".rewrite._{subopt}", package="mocodo") except ModuleNotFoundError: raise subopt_error("rewrite", subopt) source = module.run(source, subopt=subopt, subargs=subargs, params=self.params).rstrip() response.may_log("rewritten_source", source) if not self.params["is_magic"]: self.printer.write(self.common.update_source(source)) may_update_params_with_guessed_title(source, self.params) converted_file_paths = [] # list of files to be displayed in the notebook raw_relations = None sql_relations = None if self.params["convert"]: response.may_log("has_explicit_conversion", True) results = [] for (subopt, subargs) in self.params["convert"]: # Try to interpret subopt as the stem or path of a relation template template = None official_template_dir = Path(self.params["script_directory"], "resources", "relation_templates") if subopt == "relation": stem_or_path = next(iter(subargs.keys()), "") # ignore all sub-arguments after the first one template = read_template(stem_or_path, official_template_dir) elif Path(official_template_dir, f"{subopt}.yaml").is_file(): template_suffix = next(iter(subargs.keys()), "") # ignore all sub-arguments after the first one if template_suffix and set("bce").issuperset(template_suffix): stem = f"{subopt}-{''.join(sorted(template_suffix))}" else: stem = subopt template = read_template(stem, official_template_dir) # If the subopt was a relation template, use it to convert the source if template: if template["extension"] == "sql": if not sql_relations: sql_source = source sql_source = op_tk.run(sql_source, "ascii", {"labels": 1, "roles": 1}, self.params) sql_mcd = Mcd(sql_source, self.get_font_metrics, **self.params) sql_relations = Relations(sql_mcd, self.params) text = sql_relations.get_text(template) else: if not raw_relations: mcd = Mcd(source, self.get_font_metrics, **self.params) raw_relations = Relations(mcd, self.params) text = raw_relations.get_text(template) result = { "stem_suffix": template["stem_suffix"], "text": text, "extension": template["extension"], "to_defer": template.get("to_defer", False), "highlight": template.get("highlight", "plain"), } # Fall back to a dynamically loaded conversion operation else: try: module = importlib.import_module(f".convert._{subopt}", package="mocodo") except ModuleNotFoundError: raise subopt_error("convert", subopt) result = module.run(source, subargs, self.common) result["text_path"] = Path(f"{self.params['output_name']}{result['stem_suffix']}.{result['extension']}") result["text_path"].write_text(f"{result['text'].rstrip()}\n", encoding="utf8") self.printer.write(self.common.output_success_message(result["text_path"])) results.append(result) for result in results: converted_file_paths.extend(self.get_converted_file_paths(result)) response.may_log("converted_file_paths", converted_file_paths) if not raw_relations: # if the MCD has not be calculated during the conversions mcd = Mcd(source, self.get_font_metrics, **self.params) self.control_for_overlaps(mcd) resulting_paths = dump_mcd_to_svg(mcd, self.common) # potential side-effect: update *_geo.json for path in resulting_paths: self.printer.write(self.common.output_success_message(path)) def get_rendering_service(self, extension): path = Path(self.params["script_directory"], "resources", "rendering_services.json") try: rendering_services = json.loads(path.read_text(encoding="utf8")) except FileNotFoundError: raise MocodoError(46, _('The file "{path}" is missing.').format(path=path)) # fmt: skip except json.decoder.JSONDecodeError: raise MocodoError(47, _('The file "{path}" is not a valid JSON file.').format(path=path)) # fmt: skip except Exception as err: raise MocodoError(48, _('The file "{path}" could not be read:\n{err}').format(path=path, err=err)) # fmt: skip try: return rendering_services[extension] except KeyError: raise MocodoError(49, _('No third-party rendering service for extension "{extension}". You may want to add one in "{path}".').format(extension=extension, path=path)) def get_converted_file_paths(self, result): if result.get("to_defer") and "defer" in self.params: # This text must be rendered by a third-party service urllib = importlib.import_module("urllib") requests = importlib.import_module("requests") mimetypes = importlib.import_module("mimetypes") service = self.get_rendering_service(result["extension"]) data = result["text"] for preprocessing in service.get("preprocessing", []): if preprocessing == "minify_graphviz": # Spare some bandwidth data = minify_graphviz(data) elif preprocessing == "urlsafe_encoding": data = urlsafe_encoding(data) elif preprocessing == "encode_prefix": # Sole use case (so far): "https://mocodo.net/?mcd="" becomes "https%3A//mocodo.net/%3Fmcd%3D" index = data.find("=") + 1 data = urllib.parse.quote(data[:index]) + data[index:] url = service["url"].format(data=data) response = requests.get(url) if not response.ok: raise MocodoError(23, _("The HTTP status code {code} was returned by:\n{url}").format(code=response.status_code, url=url)) # fmt: skip content_type = response.headers['content-type'] extension = mimetypes.guess_extension(content_type) resp_path = result["text_path"].with_suffix(extension) if content_type.startswith("text/"): resp_path.write_text(response.text, encoding="utf8") else: resp_path.write_bytes(response.content) yield str(resp_path) elif result.get("stem_suffix") == "_mld" and result.get("extension") == "mcd" and self.params["is_magic"]: # relational diagram mld = Mcd(result["text"], self.get_font_metrics, **self.params) backup_input = self.params["input"] backup_output_name = self.params["output_name"] self.params["input"] = result["text_path"] self.params["output_name"] = f"{self.params['output_name']}_mld" dump_mcd_to_svg(mld, self.common) self.params["input"] = backup_input self.params["output_name"] = backup_output_name yield str(result['text_path'].with_suffix(".svg")) else: # This text doesn't need further processing yield str(result["text_path"]) def control_for_overlaps(self, mcd): if self.params["detect_overlaps"]: overlaps = mcd.get_overlaps() if overlaps: acc = [] for (b1, b2, b3, b4) in overlaps: if b3 == b4: acc.append(_(' - Leg "{b1} — {b2}" overlaps "{b3}".').format(b1=b1, b2=b2, b3=b3)) # fmt: skip else: acc.append(_(' - Legs "{b1} — {b2}" and "{b3} — {b4}" overlap.').format(b1=b1, b2=b2, b3=b3, b4=b4)) # fmt: skip details = "\n".join(sorted(acc)) raise MocodoError(29, _('Bad layout of boxes:\n{details}\nTo fix the problem, reorder and/or skip lines in the source text, either manually, or with the option -t arrange (chocolate bar under Mocodo online).').format(details=details)) # fmt: skip def add_gutter_params(self, params): gutters = dict(params.get("gutters", [])) # Ensure that the --gutter sub-options are a subset of {"ids", "types"} for subopt in gutters: if subopt not in ("ids", "types"): raise subopt_error("gutters", subopt) # Create the sub-option "ids" if needed, and initialize id_gutter params if "ids" not in gutters: gutters["ids"] = {} # created at the end of the (ordered) dict params["id_gutter_visibility"] = gutters["ids"].get("visibility", "auto") params["id_gutter_weak_string"] = gutters["ids"].get("weak", "id") params["id_gutter_strong_string"] = gutters["ids"].get("strong", "ID") s = gutters["ids"].get("alts", "123456789") params["id_gutter_alts"] = dict(zip("123456789", s + "123456789"[len(s):])) # Create the sub-option "types" if needed, and initialize type_gutter params # NB: the gutter for types is not implemented yet, but all needed params are already there if "types" not in gutters: gutters["types"] = {} params["type_gutter_visibility"] = gutters["types"].get("visibility", "auto") # We are sure that gutters is a dict with keys "ids" and "types" only. Order matters. (params["left_gutter"], params["right_gutter"]) = tuple(gutters.keys()) def main(): printer = Printer(quiet=True) run = Runner(sys.argv[1:], printer) try: run() except MocodoError as err: print(str(err), file=sys.stderr) sys.exit(err.errno) if __name__ == "__main__": # to be run with `python -m mocodo` main() ================================================ FILE: mocodo/api.py ================================================ import argparse import os from pathlib import Path import shlex from . import __version__, SCRIPT_DIRECTORY from .__main__ import Printer, Runner def mocodo(arg_string="", quiet=True): """ Simulate the command line `mocodo` as a function, with the same arguments provided as a string. With `quiet=True`, no success messages are printed. In either case, return the printed messages accumulated in a "\n" separated string. """ parser = argparse.ArgumentParser(add_help=False) parser.add_argument("--input", "-i") parser.add_argument("--output_dir") (args, remaining_args) = parser.parse_known_args(shlex.split(arg_string)) input_path = args.input if not args.input: # No path is provided for the source of the MCD. Fall back to the pristine sandbox. input_path = str(Path(SCRIPT_DIRECTORY, "resources", "pristine_sandbox.mcd")) output_dir = args.output_dir if not args.output_dir: output_dir = os.getcwd() remaining_args.extend([ "--input", input_path, "--output_dir", output_dir, ]) # may override user's provided options printer = Printer(quiet=quiet) try: run = Runner(remaining_args, printer) except SystemExit: # raised by argparse with certain arguments: --help, --version if "--version" in remaining_args: return __version__ return run() return "".join(printer.accumulator) ================================================ FILE: mocodo/argument_parser.py ================================================ import argparse import contextlib import gettext import json import locale import os import random import re import importlib import sys from pathlib import Path from mocodo.tools.string_tools import strip_surrounds from mocodo.tools.various import invert_dict from . import __version__, SCRIPT_DIRECTORY from .mocodo_error import MocodoError class ArgumentDefaultsRawDescriptionHelpFormatter( argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter, ): pass def init_localization(language): if not language: if ( sys.platform.lower().startswith("darwin") and os.system("defaults read -g AppleLanguages > /tmp/languages.txt") == 0 ): language = re.search(r"\W*(\w+)", Path("/tmp/languages.txt").read_text(encoding="utf8")).group(1) else: try: language = locale.getdefaultlocale()[0][:2] except: language = "en" try: path = Path(SCRIPT_DIRECTORY, "resources", "i18n", f"messages_{language}.mo") with path.open("rb") as mo_contents: trans = gettext.GNUTranslations(mo_contents) except IOError: trans = gettext.NullTranslations() # Beware that the following line sets the global variable `_`. Using a single underscore # would clash with the gettext alias, EVEN IF THE CODE IS NOT REACHED: the compiler would # consider that _ is a local variable, and shadow the global one, resulting in: # UnboundLocalError: local variable '_' referenced before assignment # Solution : use `__` instead of `_` in any function that uses the gettext alias. trans.install() return language class Transformations: def __init__(self, language): metadata_path = Path(SCRIPT_DIRECTORY, "resources", "transformations.json") self.metadata = json.loads(metadata_path.read_text(encoding="utf8")) index_path = Path(SCRIPT_DIRECTORY, "resources", "relation_templates", "_index.json") template_data = json.loads(index_path.read_text(encoding="utf8")) for (stem, d) in template_data.items(): if f"help_{language}" in d: d["help"] = d.pop(f"help_{language}") continue try: d["help"] = d[f"help_en"] except KeyError: raise MocodoError(22, _("The template '{stem}' doesn't have a help message in language of code '{language}' or 'en'.").format(stem=stem, language=language)) # fmt: skip self.metadata.update(template_data) self.normalize = invert_dict({k: v["aliases"] for (k, v) in self.metadata.items()}) self.normalize.update((k, k) for k in self.metadata) # recreate the dictionary to have it in alphabetical order self.metadata = {k: self.metadata[k] for k in sorted(self.metadata.keys(), key=str.casefold)} self.operations = {"rw": [], "cv": []} self.op_tk_rewritings = set(k for (k, v) in self.metadata.items() if v.get("op_tk")) self.args_to_delete = ["-t", "-T", "--transform"] self.opt_to_restore = " " def extract_transformation_subargs(self, arg): (subopt, __, tail) = arg.partition(":") try: subopt = self.normalize[subopt.lower()] except: valid = ", ".join(sorted(self.normalize.keys())) raise MocodoError(45, _("The transformation '{subopt}' is not among the possible ones:\n{valid}.").format(subopt=subopt, valid=valid)) # fmt: skip result = extract_subargs(f"{subopt}:{tail}") category = self.metadata[subopt]["category"] self.operations[category].append(result) if category == "rw": self.args_to_delete.append(arg) else: self.opt_to_restore = " -t " return result def extract_subargs(arg): (subopt, _, tail) = arg.partition(":") subargs = {} for string in filter(None, tail.split(",")): subsubopt, equal, subsubarg = string.partition("=") if equal: # subopt:subsubopt= / subopt:subsubopt=subsubarg subsubarg = strip_surrounds(subsubarg, "''") subsubarg = strip_surrounds(subsubarg, '""') else: # subopt:subsubopt subsubarg = None subargs[subsubopt] = subsubarg return (subopt, subargs) def rate(string): try: value = float(string) except ValueError: msg = f"The rate {repr(string)} cannot be coerced to float" raise argparse.ArgumentTypeError(msg) if not (0 <= value <= 1): msg = f"The rate {repr(string)} is not between 0 and 1" raise argparse.ArgumentTypeError(msg) return value def scale(string): try: value = float(string) except ValueError: msg = f"The scale {repr(string)} cannot be coerced to float" raise argparse.ArgumentTypeError(msg) if value <= 0: msg = f"The scale {repr(string)} is not strictly positive" raise argparse.ArgumentTypeError(msg) return value def non_negative_integer(string): try: value = int(string) except ValueError: msg = f"The value {repr(string)} cannot be coerced to an integer" raise argparse.ArgumentTypeError(msg) if value < 0: msg = f"The integer {repr(string)} is negative" raise argparse.ArgumentTypeError(msg) return value def positive_integer(string): try: value = int(string) except ValueError: msg = f"The value {repr(string)} cannot be coerced to an integer" raise argparse.ArgumentTypeError(msg) if value <= 0: msg = f"The integer {repr(string)} is negative or zero" raise argparse.ArgumentTypeError(msg) return value def parsed_arguments(args): # When mocodo is called from the command line, `args` is just `sys.argv[1:]`. # When it is imported, `args` is a list of strings constructed with shlex.split(). parser = argparse.ArgumentParser( prog="mocodo", add_help=False, formatter_class=ArgumentDefaultsRawDescriptionHelpFormatter, allow_abbrev=False, ) mocodo_group = parser.add_argument_group() io_group = parser.add_argument_group() aspect_group = parser.add_argument_group() if sys.platform.lower().startswith("darwin"): default_params = { "encodings": ["utf8", "macroman"], "shapes": "copperplate", } elif sys.platform.lower().startswith("win"): default_params = { "encodings": ["utf8", "ISO_8859-15"], "shapes": "trebuchet", } else: # linux default_params = { "encodings": ["utf8", "ISO_8859-15"], "shapes": "serif", } # First, add the arguments that need some preprocessing. # The help messages will be added later, after the localization. mocodo_group.add_argument("--language", metavar="CODE", type=str, ) language_action = mocodo_group._group_actions[-1] io_group.add_argument("--params_path", metavar="PATH", default="params.json", ) params_path_action = io_group._group_actions[-1] io_group.add_argument("--input", "-i", metavar="PATH", default="" ) input_action = io_group._group_actions[-1] (args, remaining_args) = parser.parse_known_args(args) if not args.input: # no input file is specified, use the pristine sandbox source = Path(SCRIPT_DIRECTORY, "resources", "pristine_sandbox.mcd").read_text(encoding="utf8") args.input = "sandbox.mcd" Path(args.input).write_text(source, encoding="utf8") elif not args.input.endswith(".mcd"): # the input file has no extension, assume it is a stem args.input += ".mcd" default_params["input"] = args.input if os.path.exists(args.params_path): default_params.update(json.loads(Path(args.params_path).read_text(encoding="utf8"))) if not default_params["input"]: default_params["input"] = "sandbox.mcd" default_params["language"] = init_localization(default_params.get("language", args.language)) default_params.setdefault("output_dir", os.path.dirname(default_params["input"])) # Add localized messages. mocodo_group.title = _("OPTIONS ON MOCODO ITSELF") io_group.title = _("INPUT/OUTPUT") aspect_group.title = _("ASPECT OF THE GRAPHICAL OUTPUT") language_action.help = _("override the automatic localization of the messages with the given language code (e.g., 'fr', 'en', ...)") params_path_action.help = _("the path of the parameter file. If omitted, use 'params.json' in the input directory. If non existent, use default parameters.") input_action.help = _("the path of the input file. By default, the output files will be generated in the same directory") parser.description = re.sub(r"(?m)^( )", "", _(""" NAME: Mocodo - An Entity-Relation Diagram Generator. DESCRIPTION: Mocodo is an open-source tool for designing and teaching relational databases. It takes as an input a textual description of both entities and associations of an entity-relationship diagram (ERD). It outputs a vectorial drawing in SVG and a relational schema in various formats (SQL, LaTeX, Markdown, etc.). NOTE: Each one of the following values is: - explicitely specified by the user as a command line option; - otherwise, retrieved from a file located at --params_path; - otherwise, retrieved from a file named 'params.json' in the input directory; - otherwise, calculated from a default value, possibly dependant of your system. """)) # fmt: skip parser.epilog = re.sub(r"(?m)^( )", "", _(""" SEE ALSO: Online version https://mocodo.net Source code https://github.com/laowantong/mocodo Documentation https://laowantong.github.io/mocodo/doc/fr_refman.html Cheat sheet for -t https://github.com/laowantong/mocodo/blob/master/doc/fr_cheat_sheet.md LICENSE: MIT CONTACT: Author Aristide Grange Address Université de Lorraine Laboratoire LCOMS - UFR MIM 3 rue Augustin Fresnel 57070 METZ Technopôle France Mail @univ-lorraine.fr """)) # fmt: skip transformations = Transformations(default_params["language"]) mocodo_group.add_argument("--help", action="help", help=_("show this help message, then exit"), ) mocodo_group.add_argument("--version", action="version", version=__version__, help=_("display the version number, then exit"), ) mocodo_group.add_argument("--restore", action="store_true", help=_("recreate a pristine version of the files 'sandbox.mcd' and 'params.json' in the input directory, then exit"), ) io_group.add_argument("--lib", metavar="URL", nargs="?", help=_("remote directory to use as fallback when the input file is not found locally"), ) io_group.add_argument("--output_dir", metavar="PATH", help=_("the directory of the output files"), ) io_group.add_argument("--encodings", metavar="STR", nargs="*", help=_("one or several encodings to be tried successively when reading the input file"), ) io_group.add_argument("--svg_to", choices=["png", "pdf"], nargs="+", default=[], help=_("generate a PNG or a PDF version of the SVG output (requires CairoSVG)"), ) io_group.add_argument("--print_params", action="store_true", help=_("display the contents of the parameter file, then exit"), ) io_group.add_argument("--reuse_geo", action="store_true", help=_("reuse the geometry file of the previous execution"), ) io_group.add_argument("--uid_suffix", metavar="INT", type=non_negative_integer, default=0, help=_("discriminate between multiple SVG of the same interactive diagram"), ) io_group.add_argument("--select", choices=["all", "mcd", "rw", "source", "text", "code", "mocodo", "cv", "mld", "ddl", "sql"], nargs="*", default=argparse.SUPPRESS, # causes no attribute to be added if the argument was not present help=_("under Jupyter Notebook, explicitely state the categories of results to display"), ) io_group.add_argument("--defer", metavar="STR", nargs="*", default=argparse.SUPPRESS, help=_("use an external web-service to further convert the conversion results into the given graphical formats"), ) io_group.add_argument("--mld", action="store_true", help=_("backward compatibility alias for '-t' (with no arguments). Same as '-t markdown' but, under Jupyter Notebook, does not prevent the rendering of the conceptual diagram in the cell output"), ) io_group.add_argument("--is_magic", action="store_true", help=argparse.SUPPRESS, # don't show this argument in the help message ) io_group.add_argument("--transform", "-t", metavar="STR", nargs="*", type=transformations.extract_transformation_subargs, default=argparse.SUPPRESS, help=_("make a new version of the MCD by applying sequentially the given rewriting operations, and/or convert it into the given formats or languages. Under Jupyter Notebook, '-T' respectively replaces the current cell by the textual result, or copies it into the clipboard (pip3 install pyperclip)"), ) io_group.add_argument("--Transform", "--TRANSFORM", "-T", metavar="STR", nargs="*", type=transformations.extract_transformation_subargs, help=argparse.SUPPRESS, # don't show this argument in the help message ) io_group.add_argument("--seed", metavar="FLOAT", nargs="?", type=float, help=_("initial value for the random number generator"), ) io_group.add_argument("--title", metavar="STR", default=_("Untitled"), type=str, help=_("name of the model, used at various places (file system, database, etc.)"), ) aspect_group.add_argument("--df", metavar="STR", type=str, default="DF", help=_("the acronym to be circled in a functional dependency"), ) aspect_group.add_argument("--card_format", metavar="STR", type=str, nargs="?", default="{min_card},{max_card}", help=_("format string for minimal and maximal cardinalities"), ) aspect_group.add_argument("--fk_format", metavar="STR", type=str, nargs="?", default="#{label}", help=_("format string for foreign keys in relational diagram"), ) aspect_group.add_argument("--strengthen_card", metavar="STR", type=str, nargs="?", default="_1,1_", help=_("string for relative cardinalities"), ) aspect_group.add_argument("--flex", metavar="FLOAT", type=float, default=0.75, help=_("flex straight legs whose cardinalities may collide"), ) aspect_group.add_argument("--colors", metavar="STEM_OR_PATH", default="bw", help=_("the color palette to use when generating the drawing. Name (without extension) of a file located in the directory 'colors', or path to a personal file"), ) aspect_group.add_argument("--shapes", metavar="STEM_OR_PATH", help=_("specification of the fonts, dimensions, etc. Name (without extension) of a file located in the directory 'shapes', or path to a personal file"), ) aspect_group.add_argument("--scale", metavar="RATE", type=scale, default=1, help=_("scale the diagram by the given factor"), ) aspect_group.add_argument("--adjust_width", metavar="RATE", type=scale, default=1, help=_("scale all calculated text widths by the given factor"), ) aspect_group.add_argument("--detect_overlaps", action="store_true", help=_("raise an error when horizontal or vertical legs overlap"), ) aspect_group.add_argument("--no_assoc_ids", action="store_true", help=_("forbid the use of identifiers in associations (according to the Merise standard)"), ) aspect_group.add_argument("--gutters", metavar="STR", nargs="+", type=extract_subargs, default=argparse.SUPPRESS, help=_("set the visibility and the contents of the lateral gutters"), ) parser.set_defaults(**default_params) params = vars(parser.parse_args(remaining_args)) params["script_directory"] = SCRIPT_DIRECTORY params["output_name"] = Path(params["output_dir"]) / Path(params["input"]).stem params["rewrite"] = transformations.operations["rw"] params["convert"] = transformations.operations["cv"] params["args_to_delete"] = transformations.args_to_delete params["opt_to_restore"] = transformations.opt_to_restore params["op_tk_rewritings"] = transformations.op_tk_rewritings params["redirect_output"] = ("-T" in remaining_args or "--Transform" in remaining_args) params["keys_to_hide"] = [ "keys_to_hide", "script_directory", "output_name", "rewrite", "convert", "args_to_delete", "opt_to_restore", "op_tk_rewritings", "redirect_output", "params_path", "Transform", "is_magic", "print_params", "reuse_geo", "input", ] lib = "https://mocodo.net/web/lib" if params["lib"]: parse_url = importlib.import_module("urllib.parse").urlparse if parse_url(params["lib"]).scheme: lib = params["lib"] params["lib"] = lib if params["seed"] is not None: random.seed(params["seed"]) with contextlib.suppress(UnicodeDecodeError, AttributeError): params["title"] = params["title"].decode("utf8") return params ================================================ FILE: mocodo/association.py ================================================ import re from .attribute import * from .leg import * from .mocodo_error import MocodoError from .tools.string_tools import rstrip_digit_or_underline, raw_to_bid class Association: df_counter = 0 @classmethod def reset_df_counter(cls): cls.df_counter = 0 def __init__(self, clause, **params): self.source = clause["source"] self.raw_name = clause["name"] self.bid = raw_to_bid(self.raw_name) # A protected association results in a table, even if this association is a DF. self.is_protected = (clause.get("box_def_prefix") == "+") if clause.get("box_def_prefix") == "-": self.calculate_size = self.calculate_size_when_invisible self.description = lambda *ignored: [] self.is_invisible = True else: self.calculate_size = self.calculate_size_when_visible self.description = self.description_when_visible self.is_invisible = False self.peg_count = 0 self.attributes = [] for attr in clause.get("attrs", []): if attr.get("attribute_label", "") == "": self.attributes.append(PhantomAttribute(attr)) elif attr.get("id_mark", "") == "_": if params.get("no_assoc_ids"): raise MocodoError(52, _('The association "{name}" cannot have an identifier.').format(name=self.raw_name)) self.attributes.append(StrongAttribute(attr)) else: self.attributes.append(SimpleAssociationAttribute(attr)) df_label = params.get("df", "DF") if re.match(fr"^{df_label.upper()}\d*$", self.raw_name.upper()): self.name_view = df_label # strip all digits suffixing a DF name self.bid = f"{df_label}{Association.df_counter}" Association.df_counter += 1 self.kind = "df" else: self.name_view = rstrip_digit_or_underline(self.raw_name) legs = clause["legs"] # A "peg" is a leg that is prefixed by a "/" character. for leg in legs: if leg.get("card_prefix") == "/": self.peg_count += 1 if self.peg_count > 0: if len(legs) < 3: raise MocodoError(51, _('The association "{name}" should have at least 3 legs to become a cluster.').format(name=self.raw_name)) self.kind = "cluster" else: self.kind = "association" self.set_view_strategies() entity_raw_names = [leg["entity"] for leg in clause["legs"]] self.legs = [] for leg_clause in clause["legs"]: entity_raw_name = leg_clause["entity"] rank = leg_clause["rank"] leg = Leg(self, leg_clause, **params) mutiplicity = entity_raw_names.count(entity_raw_name) rank = entity_raw_names[:rank].count(entity_raw_name) leg.set_spin_strategy(0 if mutiplicity == 1 else 2 * rank / (mutiplicity - 1) - 1) self.legs.append(leg) if self.kind == "cluster": candidate_groups = iter([0] + list(range(self.peg_count - 1, 0, -1))) for leg in self.legs: if leg.kind == "cluster_peg": group_number = str(next(candidate_groups)) for other_leg in self.legs: if other_leg is leg: continue other_leg.append_candidate_group(group_number) elif self.kind == "df": for leg in self.legs: if leg.card.endswith(("1", "X")): break else: raise MocodoError(37, _('An association named "{df_label}" must have at least one leg with a maximal cardinality of 1.').format(df_label=df_label)) # fmt: skip def get_note_data(self): result = {} for leg in self.legs: result[leg.lid] = { "subject": leg.entity_bid, "predicate": self.name_view, "min": leg.card[:1], "max": leg.card[-1:], "objects": [other.entity_bid for other in self.legs if other is not leg], "note": leg.note, } return result def register_boxes(self, boxes): self.boxes = boxes def register_mcd_has_cif(self, mcd_has_cif): self.mcd_has_cif = mcd_has_cif for leg in self.legs: leg.register_mcd_has_cif(mcd_has_cif) def calculate_size_when_invisible(self, *ignored): self.w = 0 self.h = 0 def calculate_size_when_visible(self, style, get_font_metrics): cartouche_font = get_font_metrics(style["association_cartouche_font"]) self.get_cartouche_string_width = cartouche_font.get_pixel_width self.cartouche_height = cartouche_font.get_pixel_height() attribute_font = get_font_metrics(style["association_attribute_font"]) self.attribute_height = attribute_font.get_pixel_height() self.calculate_size_depending_on_kind(style, get_font_metrics) self.w += self.w % 2 self.h += self.h % 2 for leg in self.legs: leg.calculate_size(style, get_font_metrics) def register_center(self, geo): self.cx = geo["cx"][self.bid] self.cy = geo["cy"][self.bid] self.l = self.cx - self.w // 2 self.r = self.cx + self.w // 2 self.t = self.cy - self.h // 2 self.b = self.cy + self.h // 2 def set_view_strategies(self): def calculate_size_when_df(style, get_font_metrics): self.w = self.h = max( style["round_rect_margin_width"] * 2 + self.get_cartouche_string_width(self.name_view), style["round_rect_margin_width"] * 2 + self.cartouche_height ) def calculate_size_when_default(style, get_font_metrics): for attribute in self.attributes: attribute.calculate_size(style, get_font_metrics) cartouche_and_attribute_widths = [a.w for a in self.attributes] + [self.get_cartouche_string_width(self.name_view)] self.w = 2 * style["round_rect_margin_width"] + max(cartouche_and_attribute_widths) self.h = max(1, len(self.attributes)) * (self.attribute_height + style["line_skip_height"]) \ - style["line_skip_height"] \ + 2 * style["rect_margin_height"] \ + 2 * style["round_rect_margin_height"] \ + self.cartouche_height self.w += self.w % 2 self.h += self.h % 2 def description_when_df(style): return [ ( "circle", { "stroke_depth": style["box_stroke_depth"], "stroke_color": style["association_stroke_color"], "color": style["association_cartouche_color"], "cx": self.cx, "cy": self.cy, "r": self.w // 2, }, ), ( "text", { "text": self.name_view, "text_color": style['association_cartouche_text_color'], "x": self.l + style["round_rect_margin_width"], "y": self.t + style["round_rect_margin_height"] + style["df_text_height_ratio"] * self.cartouche_height, "family": style["association_cartouche_font"]["family"], "size": style["association_cartouche_font"]["size"], }, ) ] def optional_description_for_cluster(style): if self.kind != "cluster" or self.mcd_has_cif: return [] if len(self.legs) > 3: # The calculation of a hull with more than 3 legs is not implemented yet. return [] result = [] tweak = 1 for (i, leg) in enumerate(self.legs): if leg.kind == "cluster_peg": other_legs = self.legs[:i] + self.legs[i+1:] e1 = other_legs[0].entity # the first entity of the other legs e2 = other_legs[-1].entity # the last one, either the same as e1 or not points = self.cluster_hull(e1, e2, style["card_margin"] * tweak) if not points: # complex hulls are not implemented yet continue path = points_to_rounded_path(points, style["round_corner_radius"] // 2) result.extend([ ( "polygon", { "path": path, "stroke_color": None, "stroke_depth": 0, "color": style["entity_color"], "opacity": 0.3 / self.peg_count, }, ), ( "dot_polygon", { "path": path, "stroke_color": style['entity_stroke_color'], "stroke_depth": style["box_stroke_depth"], "color": None, "dash_gap": style["dash_width"], }, ) ]) tweak += 1 return result def description_when_default(style): result = [] result.extend(optional_description_for_cluster(style)) if self.is_protected: result.append( ( "round_rect", { "x": self.l - style["card_margin"] * 2, "y": self.t - style["card_margin"] * 2, "w": self.w + style["card_margin"] * 4, "h": self.h + style["card_margin"] * 4, "radius": style["round_corner_radius"] // 2, "stroke_color": style['entity_stroke_color'] + "55", # alpha channel "stroke_depth": style["box_stroke_depth"], "color": style["entity_color"] + "55", # alpha channel }, ) ) x = self.l y = self.t w = self.w h = self.attribute_height + style["round_rect_margin_height"] + style["rect_margin_height"] r = style["round_corner_radius"] result.append( ( "upper_round_rect", { "x0": x + w - r, "y0": y, "y1": y + h, "y2": y + r, "w": w, "r": r, "color": style['association_cartouche_color'], "stroke_color": style['association_cartouche_color'], "stroke_depth": 0, }, ) ) y = self.t + self.attribute_height + style["round_rect_margin_height"] + style["rect_margin_height"] h = self.h - self.attribute_height - style["round_rect_margin_height"] - style["rect_margin_height"] result.append( ( "lower_round_rect", { "x0": x + w, "y0": y, "y1": h - r, "x1": x + r, "w": w, "r": r, "color": style['association_color'], "stroke_color": style['association_color'], "stroke_depth": 0, }, ) ) result.append( ( "round_rect", { "x": self.l, "y": self.t, "w": self.w, "h": self.h, "radius": r, "color": style['transparent_color'], "stroke_color": style['association_stroke_color'], "stroke_depth": style["box_stroke_depth"], }, ) ) result.append( ( "line", { "x0": self.l, "y0": self.t + self.attribute_height + style["round_rect_margin_height"] + style["rect_margin_height"], "x1": self.r, "y1": self.t + self.attribute_height + style["round_rect_margin_height"] + style["rect_margin_height"], "stroke_color": style['association_stroke_color'], "stroke_depth": style["inner_stroke_depth"], }, ) ) result.append( ( "text", { "text": self.name_view, "text_color": style['association_cartouche_text_color'], "x": self.cx - self.get_cartouche_string_width(self.name_view) // 2, "y": self.t + style["rect_margin_height"] + style["cartouche_text_height_ratio"] * self.cartouche_height, "family": style["association_cartouche_font"]["family"], "size": style["association_cartouche_font"]["size"], }, ), ) x = self.cx - self.w // 2 + style["round_rect_margin_width"] dx = 0 dy = style["round_rect_margin_height"] + self.cartouche_height + 2 * style["rect_margin_height"] - self.h // 2 for attribute in self.attributes: result.extend(attribute.description(style, x, self.cy, dx, dy)) dy += self.attribute_height + style["line_skip_height"] return result if self.kind == "df": self.calculate_size_depending_on_kind = calculate_size_when_df self.description_depending_on_kind = description_when_df else: self.calculate_size_depending_on_kind = calculate_size_when_default self.description_depending_on_kind = description_when_default def description_when_visible(self, style, geo): self.saved_card_description = [] result = [] result.append(("comment", {"text": f"Association {self.bid}"})) result.append( ( "begin_component", { "page": self.page, "visibility": "hidden" if self.page else "visible", } ) ) result.extend(self.leg_descriptions(style, geo)) result.append(("begin_group", {})) result.extend(self.description_depending_on_kind(style)) result.append(("end", {})) result.extend(self.saved_card_description) # need to be displayed after the clusters for more readability result.append(("end", {})) return result def leg_descriptions(self, style, geo): result = [] for leg in self.legs: result.extend(leg.description(style, geo)) self.saved_card_description.extend(leg.saved_card_description) return result def cluster_hull(self, e1, e2, card_margin): x_min = min(box.l for box in (self, e1, e2)) y_min = min(box.t for box in (self, e1, e2)) x_max = max(box.r for box in (self, e1, e2)) y_max = max(box.b for box in (self, e1, e2)) x = x_min - card_margin // 2 y = y_min - card_margin // 2 w = x_max - x_min + card_margin h = y_max - y_min + card_margin x1_min = min(box.l for box in (self, e1)) y1_min = min(box.t for box in (self, e1)) x1_max = max(box.r for box in (self, e1)) y1_max = max(box.b for box in (self, e1)) x1 = x1_min - card_margin // 2 y1 = y1_min - card_margin // 2 w1 = x1_max - x1_min + card_margin h1 = y1_max - y1_min + card_margin x2_min = min(box.l for box in (self, e2)) y2_min = min(box.t for box in (self, e2)) x2_max = max(box.r for box in (self, e2)) y2_max = max(box.b for box in (self, e2)) x2 = x2_min - card_margin // 2 y2 = y2_min - card_margin // 2 w2 = x2_max - x2_min + card_margin h2 = y2_max - y2_min + card_margin e1_same_row = e1.t <= self.t < self.b <= e1.b or self.t <= e1.t < e1.b <= self.b e2_same_row = e2.t <= self.t < self.b <= e2.b or self.t <= e2.t < e2.b <= self.b e1_same_col = e1.l <= self.l < self.r <= e1.r or self.l <= e1.l < e1.r <= self.r e2_same_col = e2.l <= self.l < self.r <= e2.r or self.l <= e2.l < e2.r <= self.r if e1_same_row and e2_same_row or e1_same_col and e2_same_col: return [x, y, x+w, y, x+w, y+h, x, y+h] elif e1_same_row and e2_same_col: if e1.cx < self.cx: if e2.cy < self.cy: # 2 # 1 a return [x, y1, x2, y1, x2, y, x+w, y, x+w, y+h, x, y+h] else: # 1 a # 2 return [x, y, x+w, y, x+w, y+h, x2, y+h, x2, y1+h1, x, y1+h1] else: if e2.cy < self.cy: # 2 # a 1 return [x, y, x2+w2, y, x2+w2, y1, x+w, y1, x+w, y+h, x, y+h] else: # a 1 # 2 return [x, y, x+w, y, x+w, y1+h1, x2+w2, y1+h1, x2+w2, y+h, x, y+h] elif e2_same_row and e1_same_col: if e2.cx < self.cx: if e1.cy < self.cy: # 1 # 2 a return [x, y2, x1, y2, x1, y, x+w, y, x+w, y+h, x, y+h] else: # 2 a # 1 return [x, y, x+w, y, x+w, y+h, x1, y+h, x1, y2+h2, x, y2+h2] else: if e1.cy < self.cy: # 1 # a 2 return [x, y, x1+w1, y, x1+w1, y2, x+w, y2, x+w, y+h, x, y+h] else: # a 2 # 1 return [x, y, x+w, y, x+w, y2+h2, x1+w1, y2+h2, x1+w1, y+h, x, y+h] else: return [] def points_to_rounded_path(points, r): result = [] points.extend(points[:6]) for i in range(0, len(points) - 6, 2): (x1, y1, x2, y2, x3, y3) = points[i:i+6] if x1 < x2 and y2 > y3: # 3 # 1 2 (x2, c, dx, dy) = (x2 - r, 0, r, -r) elif x1 < x2 and y2 < y3: # 1 2 # 3 (x2, c, dx, dy) = (x2 - r, 1, r, r) elif x1 > x2 and y2 < y3: # 2 1 # 3 (x2, c, dx, dy) = (x2 + r, 0, -r, r) elif x1 > x2 and y2 > y3: # 3 # 2 1 (x2, c, dx, dy) = (x2 + r, 1, -r, -r) elif y1 < y2 and x2 > x3: # 1 # 3 2 (y2, c, dx, dy) = (y2 - r, 1, -r, r) elif y1 < y2 and x2 < x3: # 1 # 2 3 (y2, c, dx, dy) = (y2 - r, 0, r, r) elif y1 > y2 and x2 < x3: # 2 3 # 1 (y2, c, dx, dy) = (y2 + r, 1, r, -r) elif y1 > y2 and x2 > x3: # 3 2 # 1 (y2, c, dx, dy) = (y2 + r, 0, -r, -r) result.append(f"L{x2} {y2} a{r} {r} 0 0 {c} {dx} {dy}") result[0:0] = [f"M{x2+dx} {y2+dy}"] return " ".join(result) ================================================ FILE: mocodo/attribute.py ================================================ from .tools.string_tools import raw_to_bid class Attribute: # The following three static attributes will be updated by Mcd.add_attributes() method. # They are here to simplify the writing of some tests. id_gutter_strong_string = "ID" id_gutter_weak_string = "id" id_gutter_alts = dict(zip("123456789", "123456789")) def __init__(self, attribute): self.label = attribute.get("attribute_label", "") self.label_view = self.label # may be updated in self.register_foreign_key_status() self.rank = attribute["rank"] self.datatype = attribute.get("datatype", "") self.primary_entity_bid = raw_to_bid(attribute.get("that_table", "")) self.id_groups = set(attribute.get("id_groups", "").replace("0", "")) self.id_text = " ".join(map(self.id_gutter_alts.get, sorted(self.id_groups))) self.id_gutter_width = 0 # For anything but entities def register_foreign_key_status(self, attribute, fk_format): that_table = attribute.get("that_table") if not that_table: return self.primary_key_label = attribute.get("that_table_attribute_label") self.label_view = fk_format.format(label=self.label) def calculate_size(self, style, get_font_metrics): self.attribute_font = style[self.font_type] self.font = get_font_metrics(self.attribute_font) self.w = self.font.get_pixel_width(self.label_view) self.h = self.font.get_pixel_height() self.id_width = self.font.get_pixel_width(self.id_text) def set_id_gutter_width(self, id_gutter_width): self.id_gutter_width = id_gutter_width def description(self, style, x, y, dx, dy): result = [ ( "text", { "x": x + dx, "y": y + round(dy + style["attribute_text_height_ratio"] * self.h, 1), "text": self.label_view, "text_color": style[f"{self.box_type}_attribute_text_color"], "family": self.attribute_font["family"], "size": self.attribute_font["size"], } ) ] if self.id_gutter_width: result.append( ( "text", { "x": x + (self.id_gutter_width - self.id_width) // 2 - style["rect_margin_width"], "y": y + round(dy + style["attribute_text_height_ratio"] * self.h, 1), "text": self.id_text, "text_color": style[f"{self.box_type}_attribute_text_color"], "family": self.attribute_font["family"], "size": self.attribute_font["size"], } ) ) return result class SimpleEntityAttribute(Attribute): def __init__(self, attribute): Attribute.__init__(self, attribute) self.box_type = "entity" self.font_type = "entity_attribute_font" self.kind = "simple" class SimpleAssociationAttribute(Attribute): def __init__(self, attribute): Attribute.__init__(self, attribute) self.box_type = "association" self.font_type = "association_attribute_font" self.kind = "simple_association" class IdentifierAttribute(Attribute): def __init__(self, attribute): Attribute.__init__(self, attribute) self.box_type = "entity" self.font_type = "entity_attribute_font" self.id_groups.add("0") def calculate_size(self, *args): Attribute.calculate_size(self, *args) class StrongAttribute(IdentifierAttribute): def __init__(self, attribute): IdentifierAttribute.__init__(self, attribute) self.id_text = f"{self.id_text} {self.id_gutter_strong_string}".lstrip() self.kind = "strong" def description(self, style, x, y, dx, dy): return IdentifierAttribute.description(self, style, x, y, dx, dy) + [ ( "line", { "x0": x + dx, "y0": y + dy + self.h + style["underline_skip_height"], "x1": x + dx + self.w, "y1": y + dy + self.h + style["underline_skip_height"], "stroke_depth": style["underline_depth"], "stroke_color": style['entity_attribute_text_color'], } ) ] class WeakAttribute(IdentifierAttribute): def __init__(self, attribute): IdentifierAttribute.__init__(self, attribute) self.id_text = f"{self.id_text} {self.id_gutter_weak_string}".lstrip() self.kind = "weak" def description(self, style, x, y, dx, dy): return IdentifierAttribute.description(self, style, x, y, dx, dy) + [ ( "dash_line", { "x0": x + dx, "y0": y + dy + self.h + style["underline_skip_height"], "x1": x + dx + self.w, "y1": y + dy + self.h + style["underline_skip_height"], "dash_width": style["dash_width"], "stroke_depth": style["underline_depth"], "stroke_color": style['entity_attribute_text_color'], } ) ] class PhantomAttribute(Attribute): def __init__(self, attribute): Attribute.__init__(self, attribute) self.kind = "phantom" self.font_type = "entity_attribute_font" # dummy def description(self, *_): return [] class InheritanceAttribute(Attribute): def __init__(self, attribute): Attribute.__init__(self, attribute) self.kind = "inheritance" ================================================ FILE: mocodo/common.py ================================================ import json import numbers import sys from pathlib import Path import contextlib import importlib from .mocodo_error import MocodoError def contract_path(path): """ Contract a full path to one with ~, if it's under the user's home directory. """ home = str(Path.home()) absolute_path = str(Path(path).resolve()) if absolute_path.startswith(home): return absolute_path.replace(home, '~', 1) else: return absolute_path class Printer: def __init__(self, quiet=False): self.accumulator = [] if quiet: self.write = self.accumulator.append else: self.write = self.safe_print_for_PHP def safe_print_for_PHP(self, s): """ It seems that when called from PHP, Python is unable to guess correctly the encoding of the standard output. """ try: print(s, file=sys.stdout) except UnicodeEncodeError: print(s.encode("utf8"), file=sys.stdout) class Common: def __init__(self, params): self.params = params def output_success_message(self, path): return _('Output file "{filename}" successfully generated.').format(filename=contract_path(path)) def update_source(self, source): path = Path(f"{self.params['output_name']}.mcd") path.write_text(source, encoding="utf8") return _('Source file "{filename}" successfully updated.').format(filename=contract_path(path)) def load_input_file(self): # Try to read the file locally. path = Path(self.params["input"]) if path.is_file(): for encoding in self.params["encodings"]: with contextlib.suppress(UnicodeError): self.encoding = encoding return path.read_text(encoding=encoding).replace('"', '') raise MocodoError(5, _('Unable to read "{filename}" with any of the following encodings: "{encodings}".').format(filename=self.params["input"], encodings= ", ".join(self.params["encodings"]))) # fmt: skip # The file is not found locally, try to retrieve it from the server. self.encoding = self.params["encodings"][0] requests = importlib.import_module("requests") with contextlib.suppress(requests.exceptions.ConnectionError): response = requests.get(f"{self.params['lib']}/{path.name}") if response.status_code == 200: response.encoding = 'utf-8' # Force the response encoding to UTF-8 source = response.text.replace('"', '') # Save the file locally. path.parent.mkdir(parents=True, exist_ok=True) path.write_text(source, encoding=self.encoding) return source # The file is not found locally nor on the server. raise MocodoError(2, _('The file "{input}" doesn\'t exist.').format(input=path)) # fmt: skip def load_style(self): def load_by_name(name): path = Path(self.params[name]).with_suffix(".json") if path.is_file(): try: return json.loads(path.read_text(encoding="utf8")) except: raise MocodoError(3, _('Problem with "{name}" file "{path}".').format(name=name, path=path)) # fmt: skip path = Path(self.params["script_directory"], "resources", name, path) try: return json.loads(path.read_text(encoding="utf8")) except: raise MocodoError(3, _('Problem with "{name}" file "{path}".').format(name=name, path=path)) # fmt: skip def may_apply_scaling(shapes): if self.params["scale"] == 1: return scale = self.params["scale"] for key in shapes: if key.endswith("font"): shapes[key]["size"] = shapes[key]["size"] * scale elif not key.endswith("ratio") and isinstance(shapes[key], numbers.Number): shapes[key] *= scale def ensure_margins_are_integer(shapes): # Some nasty failures are known to occur otherwise. for key in shapes: if "margin" in key: shapes[key] = int(shapes[key] + 0.5) style = {} style.update(load_by_name("colors")) shapes = load_by_name("shapes") may_apply_scaling(shapes) ensure_margins_are_integer(shapes) style.update(shapes) style["transparent_color"] = None return style ================================================ FILE: mocodo/constraint.py ================================================ import re from .attribute import * from .leg import ConstraintLeg from .tools.string_tools import is_a_description, raw_to_bid class Constraint: counter = 0 @classmethod def reset_counter(cls): cls.counter = 0 def __init__(self, clause): self.source = clause["source"] self.raw_name = clause.get("name", "Anonymous") self.name_view = clause.get("name", "") Constraint.counter += 1 self.bid = f'{raw_to_bid(self.raw_name)}_CONSTRAINT_#{Constraint.counter}' self.note = clause.get("constraint_note") self.legs = [] for target in clause.get("constraint_targets", []): self.legs.append(ConstraintLeg(self, target.get("constraint_leg", ""), target["name"])) if self.legs: self.coords = clause.get("constraint_coords", []) else: # When there is neither coord nor target, the constraint is placed at the top right corner # which is obviously undesirable, forcing the user to specify at least one coord. self.coords = clause.get("constraint_coords", [0, 0]) self.kind= "constraint" def register_boxes(self, boxes): self.boxes = boxes def calculate_size(self, style, get_font_metrics): constraint_font = get_font_metrics(style["constraint_font"]) self.get_constraint_string_width = constraint_font.get_pixel_width self.constraint_height = constraint_font.get_pixel_height() if self.name_view.strip(): size = max(self.get_constraint_string_width(self.name_view), self.constraint_height) else: size = self.get_constraint_string_width("W") * len(self.name_view) self.w = self.h = style["constraint_margin"] * 2 + size def register_center(self, geo): self.page_width = geo["width"] self.page_height = geo["height"] if self.coords: # The center of a constraint is either at a certain ratio of the width and height of the # page, or aligned with the center of a box. if isinstance(self.coords[0], (float, int)): self.cx = self.coords[0] * self.page_width // 100 else: self.cx = geo["cx"][raw_to_bid(self.coords[0])] if isinstance(self.coords[1], (float, int)): self.cy = self.coords[1] * self.page_height // 100 else: self.cy = geo["cy"][raw_to_bid(self.coords[1])] else: # The center of a constraint is the barycenter of the centers of its boxes self.cx = sum(geo["cx"][leg.bid] for leg in self.legs) / len(self.legs) self.cy = sum(geo["cy"][leg.bid] for leg in self.legs) / len(self.legs) self.l = self.cx - self.w // 2 self.r = self.cx + self.w // 2 self.t = self.cy - self.h // 2 self.b = self.cy + self.h // 2 def _description(self, style): return [ ( "circle_with_note" if is_a_description(self.note) else "circle", { "stroke_depth": style["constraint_stroke_depth"], "stroke_color": style["constraint_stroke_color"], "color": style["constraint_background_color"], "cx": self.cx, "cy": self.cy, "r": self.w // 2, "note": self.note, }, ), ( "text_above_note", { "text": self.name_view, "text_color": style['constraint_text_color'], "x": self.cx - self.get_constraint_string_width(self.name_view) / 2, "y": self.cy + self.constraint_height * style["constraint_text_height_tweak"], "family": style["card_font"]["family"], "size": style["card_font"]["size"], }, ), ] def description(self, style, geo): result = [] result.append(("comment", {"text": f"Constraint {self.bid}"})) result.append( ( "begin_component", { "page": self.page, "visibility": "hidden" if self.page else "visible", } ) ) result.extend(self.leg_descriptions(style, geo)) result.append(("begin_group", {})) result.extend(self._description(style)) result.append(("end", {})) result.append(("end", {})) return result def leg_descriptions(self, style, geo): result = [] for leg in self.legs: result.extend(leg.description(style, geo)) return result def invert_coords_horizontal_mirror(self): if self.coords and isinstance(self.coords[1], (float, int)): self.coords[1] = 100 - self.coords[1] self.source = re.sub(r"(.+):.+", fr"\1: {self.coords[0]}, {self.coords[1]}", self.source) def invert_coords_vertical_mirror(self): if self.coords and isinstance(self.coords[0], (float, int)): self.coords[0] = 100 - self.coords[0] self.source = re.sub(r"(.+):.+", fr"\1: {self.coords[0]}, {self.coords[1]}", self.source) def invert_coords_diagonal_mirror(self): if self.coords: (self.coords[0], self.coords[1]) = (self.coords[1], self.coords[0]) self.source = re.sub(r"(.+):.+", fr"\1: {self.coords[0]}, {self.coords[1]}", self.source) ================================================ FILE: mocodo/convert/__init__.py ================================================ ================================================ FILE: mocodo/convert/_ast.py ================================================ from ..tools.parser_tools import parse_source, Token def ast_to_yaml(node, indent=""): indent += " " if isinstance(node, Token): yield f"{indent}- type: {node.type}" yield f"{indent} value: {repr(node.value)}" else: yield f"{indent}- type: {node.data}" yield f"{indent} children:" for child in node.children: yield from ast_to_yaml(child, indent) def run(source, subargs=None, common=None): tree = parse_source(source) return { "stem_suffix": "_ast", "text": "\n".join(ast_to_yaml(tree)), "extension": "yaml", "to_defer": False, "highlight": "yaml", } ================================================ FILE: mocodo/convert/_chen.py ================================================ from collections import Counter import itertools import re __import__("sys").path[0:0] = ["."] from ..parse_mcd import Visitor from ..tools.parser_tools import first_child, parse_source, is_identifier from ..rewrite import _split as split from ..tools.graphviz_tools import create_name_to_index from ..tools.string_tools import wrap_label, rstrip_digit_or_underline from .. import __version__ class Chen(Visitor): def __init__(self, subargs, common): self.no_attrs = "attrs" not in subargs subargs.pop("attrs", None) self.subargs = subargs self.df_counter = itertools.count() self.df_label = common.params["df"] self.name_to_index = create_name_to_index() # Entities and their attributes self.entity_nodes = [] self.weak_box_indexes = set() self.strengthening_leg_count = Counter() self.ent_attr_nodes = [] self.ent_key_attr_nodes = [] self.ent_attr_edges = [] self.not_gerund_nodes = set() # Associations and their attributes self.rel_nodes = [] self.rel_attr_nodes = [] self.rel_attr_edges = [] # Legs with their participation self.partial_edges = [] self.total_edges = [] # Invisible boxes self.invisible_boxes = set() def entity_or_table_attr(self, tree): id_groups = str(first_child(tree, "id_groups")) id_mark = str(first_child(tree, "id_mark")) attr = str(first_child(tree, "attr")) tree.children = [(id_groups, id_mark, attr)] def entity_clause(self, tree): ent_name = str(first_child(tree, "box_name")) ent_index = self.name_to_index(f"ent_{ent_name}") if first_child(tree, "box_def_prefix") == "-": self.invisible_boxes.add(ent_index) return ent_name = rstrip_digit_or_underline(ent_name) self.entity_nodes.append((ent_index, ent_name)) # Entity attributes if self.no_attrs: return for (i, node) in enumerate(tree.find_data("entity_or_table_attr")): (id_groups, id_mark, attr_label) = node.children[0] attr_label = str(attr_label) if attr_label == "": continue # don't create a node for a spacer attribute attr_index = self.name_to_index(f"ent_attr_{ent_index}_{attr_label}") if is_identifier(i, id_groups, id_mark): self.ent_key_attr_nodes.append((attr_index, attr_label, ent_index)) else: self.ent_attr_nodes.append((attr_index, attr_label)) self.ent_attr_edges.append((ent_index, attr_index)) def assoc_clause(self, tree): legs = [node for node in tree.find_data("assoc_leg")] # Accumulate the association as a new relationship assoc_name = str(first_child(tree, "assoc_name_def").children[0]) if assoc_name == self.df_label: assoc_name = f"{assoc_name}{next(self.df_counter)}" assoc_index = self.name_to_index(f"assoc_{assoc_name}") if first_child(tree, "box_def_prefix") == "-": self.invisible_boxes.add(assoc_index) return assoc_name = rstrip_digit_or_underline(assoc_name) self.rel_nodes.append((assoc_index, assoc_name)) min_maxs = [node.children[0].value for node in tree.find_data("card")] ent_names = [str(first_child(leg, "entity_name_ref").children[0]) for leg in legs] edge_accs = [self.partial_edges if c[0] == "0" else self.total_edges for c in min_maxs] ent_indexes = [self.name_to_index(f"ent_{ent_name}") for ent_name in ent_names] if len(legs) == 2: weaks = [first_child(leg, "card_prefix") == "_" for leg in legs] if any(weaks): # the binary association is "weak", in the Chen sense self.weak_box_indexes.add(assoc_index) # If both legs are *N, change one with *M if min_maxs[0][1] == min_maxs[1][1] == "N": min_maxs[0] = f"{min_maxs[0][0]}M" for i in range(2): # The 1-i is to get the other leg, i.e., to look across instead of to look here edge_accs[i].append((ent_indexes[i], assoc_index, min_maxs[1-i][1])) if weaks[i]: # the entity is distinguished by a _11 self.weak_box_indexes.add(ent_indexes[i]) self.strengthening_leg_count[ent_indexes[i]] += 1 else: # we are sure that it is not an associative entity self.not_gerund_nodes.add(ent_indexes[i]) else: # The Chen's card is 1 if the min-max card has a "/" prefix, N otherwise chen_cards = ["1" if first_child(leg, "card_prefix") == "/" else "N" for leg in legs] for (chen_card, edge_acc, ent_index) in zip(chen_cards, edge_accs, ent_indexes): edge_acc.append((ent_index, assoc_index, chen_card)) self.not_gerund_nodes.add(ent_index) # Association attributes if self.no_attrs: return for node in tree.find_data("attr"): attr_label = str(node.children[0]) if attr_label == "": continue # don't create a node for a spacer attribute attr_index = self.name_to_index(f"assoc_attr_{assoc_index}_{attr_label}") self.rel_attr_nodes.append((attr_index, attr_label)) self.rel_attr_edges.append((assoc_index, attr_index)) def start(self, tree): # Since the traversal is depth-first, this is in fact a post-treatment for (ent_index, strengthening_leg_count) in self.strengthening_leg_count.items(): if strengthening_leg_count < 2: self.not_gerund_nodes.add(ent_index) self.partial_edges = [(x, y, c) for (x, y, c) in self.partial_edges if x not in self.invisible_boxes and y not in self.invisible_boxes] self.total_edges = [(x, y, c) for (x, y, c) in self.total_edges if x not in self.invisible_boxes and y not in self.invisible_boxes] def get_graphviz(self, common): style = common.load_style() result = [] result.append(f'// Generated by Mocodo {__version__}\n') result.append(f'graph{{') result.append(f' bgcolor="{style["background_color"]}"') if common.params["seed"] is not None: result.append(f' start={common.params["seed"]}') for (subsubopt, subsubarg) in self.subargs.items(): result.append(f' {subsubopt}={subsubarg}') result.append(f'\n // Entities') result.append(f' node [') result.append(f' shape=box') result.append(f' style=filled') result.append(f' penwidth={style["box_stroke_depth"]}') result.append(f' color="{style["entity_stroke_color"]}"') result.append(f' fillcolor="{style["entity_cartouche_color"]}"') result.append(f' fontcolor="{style["entity_cartouche_text_color"]}"') result.append(f' ]') for (index, name) in self.entity_nodes: name = "\\n".join(wrap_label(name)) if index in self.not_gerund_nodes: if index in self.weak_box_indexes: result.append(f' {index} [label="{name}",peripheries=2]') else: result.append(f' {index} [label="{name}"]') if len(self.entity_nodes) > len(self.not_gerund_nodes): result.append(f'\n // Associative entities') for (index, name) in self.entity_nodes: name = "\\n".join(wrap_label(name)) if index not in self.not_gerund_nodes: result.append(f' {index} [label="{name}",shape=Mdiamond]') if self.ent_attr_nodes or self.ent_key_attr_nodes: result.append(f'\n // Normal entity attributes') result.append(f' node [') result.append(f' shape=oval') result.append(f' penwidth={style["box_stroke_depth"]}') result.append(f' color="{style["entity_stroke_color"]}"') result.append(f' fillcolor="{style["entity_color"]}"') result.append(f' fontcolor="{style["entity_attribute_text_color"]}"') result.append(f' ]') for (index, name) in self.ent_attr_nodes: name = "\\n".join(wrap_label(name)) result.append(f' {index} [label="{name}"]') result.append(f'\n // Weak and strong entity attributes') for (index, name, ent_index) in self.ent_key_attr_nodes: name = "
".join(wrap_label(name)) if ent_index in self.weak_box_indexes: result.append(f' {index} [label=<{name}> style="dashed,filled"]') else: result.append(f' {index} [label=<{name}>]') if self.rel_attr_nodes: result.append(f'\n // Relationship attributes') result.append(f' node [') result.append(f' color="{style["association_stroke_color"]}"') result.append(f' fillcolor="{style["association_color"]}"') result.append(f' fontcolor="{style["association_attribute_text_color"]}"') result.append(f' ]') for (index, name) in self.rel_attr_nodes: name = "\\n".join(wrap_label(name)) result.append(f' {index} [label="{name}"]') if self.rel_nodes: result.append(f'\n // Relationships') result.append(f' node [') result.append(f' shape=diamond') result.append(f' height=0.7') result.append(f' penwidth={style["box_stroke_depth"]}') result.append(f' color="{style["association_stroke_color"]}"') result.append(f' fillcolor="{style["association_cartouche_color"]}"') result.append(f' fontcolor="{style["association_cartouche_text_color"]}"') result.append(f' ]') for (index, name) in self.rel_nodes: name = "\\n".join(wrap_label(name)) if index in self.weak_box_indexes: result.append(f' {index} [label="{name}",peripheries=2]') else: result.append(f' {index} [label="{name}"]') if self.ent_attr_edges or self.rel_attr_edges: result.append(f'\n // Edges between entities and attributes') result.append(f' edge [') result.append(f' penwidth={style["box_stroke_depth"]}') result.append(f' color="{style["entity_stroke_color"]}"') result.append(f' ]') for (ent_index, attr_index) in sorted(self.ent_attr_edges): result.append(f' {ent_index} -- {attr_index}') result.append(f'\n // Edges between relationships and attributes') result.append(f' edge [color="{style["association_stroke_color"]}"]') useless = True for (assoc_index, attr_index) in sorted(self.rel_attr_edges): result.append(f' {assoc_index} -- {attr_index}') useless = False if useless: result.pop() result.pop() if self.partial_edges or self.total_edges: seen_edges = set() result.append(f'\n // Edges between entities and relationships') result.append(f' edge [') result.append(f' penwidth={style["leg_stroke_depth"]}') result.append(f' color="{style["leg_stroke_color"]}:{style["leg_stroke_color"]}"') # default to total participation result.append(f' fontcolor="{style["card_text_color"]}"') result.append(f' labeldistance=2') result.append(f' headlabel=1') result.append(f' ]') for label in "1MN": useless = label != "1" if label != "1": result.append(f' edge [headlabel={label}]') suffix = "" for edges in (self.total_edges, self.partial_edges): for (ent_index, assoc_index, edge_label) in sorted(edges): if label == edge_label: if (ent_index, assoc_index) in seen_edges: (assoc_index, ent_index) = (ent_index, assoc_index) result.append(f' {ent_index} -- {assoc_index}{suffix}') seen_edges.add((ent_index, assoc_index)) useless = False suffix = f' [color="{style["leg_stroke_color"]}"]' # from now on, partial participation if useless: result.pop() result.append("}") result = "\n".join(result) useless_colors = [ 'bgcolor="#FFFFFF"', 'color="#000000"', 'fontcolor="#000000"', ] for useless_color in useless_colors: result = re.sub(fr'(?m)^ *{useless_color}\n', "", result) return result def get_text_for_testing(self): nodes = {} for (i, x) in self.entity_nodes: if i in self.not_gerund_nodes: if i in self.weak_box_indexes: nodes[i] = f"[[{x}]]" else: nodes[i] = f"[{x}]" else: nodes[i] = f"[<{x}>]" for (i, x) in self.rel_nodes: if i in self.weak_box_indexes: nodes[i] = f"<<{x}>>" else: nodes[i] = f"<{x}>" for (i, x, j) in self.ent_key_attr_nodes: if j in self.weak_box_indexes: nodes[i] = f"(.{x}.)" else: nodes[i] = f"(_{x}_)" nodes.update((i, f"({x})") for (i, x) in self.ent_attr_nodes + self.rel_attr_nodes) result = [] for (x, y, c) in self.partial_edges: result.append(f" {nodes[x]} --{c}-- {nodes[y]}") for (x, y, c) in self.total_edges: result.append(f" {nodes[x]} =={c}== {nodes[y]}") for (x, y) in self.ent_attr_edges + self.rel_attr_edges: result.append(f" {nodes[x]} -- {nodes[y]}") return "\n".join(sorted(result)) def run(source, subargs=None, common=None, testing=False): subargs = subargs or {} source = split.run(source) tree = parse_source(source) extractor = Chen(subargs, common) extractor.visit(tree) if testing: return extractor.get_text_for_testing() text = extractor.get_graphviz(common) return { "stem_suffix": "_erd_chen", "text": text, "extension": "gv", "to_defer": True, "highlight": "graphviz", } ================================================ FILE: mocodo/convert/_crow.py ================================================ import importlib __import__("sys").path[0:0] = ["."] from ..mocodo_error import subsubopt_error from ..parse_mcd import Visitor from ..tools.parser_tools import first_child, is_identifier from ..tools.string_tools import rstrip_digit_or_underline from ..tools.graphviz_tools import create_name_to_index class Crow(Visitor): def __init__(self): self.name_to_index = create_name_to_index() self.tables = {} self.links = [] self.has_no_datatype = True self.invisible_boxes = set() def entity_or_table_attr(self, tree): id_groups = str(first_child(tree, "id_groups")) id_mark = str(first_child(tree, "id_mark")) attr = str(first_child(tree, "attr")) datatype = str(first_child(tree, "datatype")) if datatype: self.has_no_datatype = False tree.children = [(id_groups, id_mark, attr, datatype)] def entity_clause(self, tree): ent_name = first_child(tree, "box_name").value if first_child(tree, "box_def_prefix") == "-": self.invisible_boxes.add(ent_name) return ent_index = self.name_to_index(ent_name) ent_name = rstrip_digit_or_underline(ent_name) attrs = [] has_id = False for (i, node) in enumerate(tree.find_data("entity_or_table_attr")): (id_groups, id_mark, attr, datatype) = node.children[0] is_id = is_identifier(i, id_groups, id_mark) has_id = has_id or is_id attrs.append((datatype, attr, is_id)) self.tables[ent_index] = (ent_name, has_id, attrs) def assoc_clause(self, tree): assoc_name = first_child(tree, "assoc_name_def").children[0].value if first_child(tree, "box_def_prefix") == "-": self.invisible_boxes.add(assoc_name) return cards = [node.children[0].value for node in tree.find_data("card")] assert len(cards) == 2 assoc_name = rstrip_digit_or_underline(assoc_name) kind = ".." if any(s.children[0].value == "_" for s in tree.find_data("card_prefix")) else "--" entities = tree.find_data("entity_name_ref") ent_1 = first_child(next(entities), "entity_name_ref").children[0] ent_2 = first_child(next(entities), "entity_name_ref").children[0] self.links.append((ent_1, cards[1], kind, cards[0], ent_2, assoc_name)) def start(self, tree): visible_links = [] for (ent_1, card_1, kind, card_2, ent_2, assoc_name) in self.links: if ent_1 in self.invisible_boxes or ent_2 in self.invisible_boxes: continue visible_links.append((ent_1, card_1, kind, card_2, ent_2, assoc_name)) def run(source, subargs, common=None): extension = "mmd" if "mmd" in subargs or "mermaid" in subargs else "gv" try: module = importlib.import_module(f".convert.crow_{extension}", package="mocodo") except ModuleNotFoundError: raise subsubopt_error(extension) text = module.run(source, subargs, common) if extension == "mmd": return { "stem_suffix": "_erd_crow", "text": text, "extension": "mmd", "to_defer": True, "highlight": "mermaid", } else: return { "stem_suffix": "_erd_crow", "text": text, "extension": "gv", "to_defer": True, "highlight": "graphviz", } ================================================ FILE: mocodo/convert/_data_dict.py ================================================ __import__("sys").path[0:0] = ["."] import re from ..tools.string_tools import markdown_table from ..parse_mcd import Transformer from ..tools.parser_tools import parse_source, first_child from ..mocodo_error import subsubopt_error DEFAULT_COLUMN_NAMES = { "en": { "label": "Label of the attribute", "type": "Type", "box": "Entity or relationship", }, "fr": { "label": "Libellé de l'attribut", "type": "Type", "box": "Entité ou association", }, } class AttributeListExtractor(Transformer): # depth-first, post-order def __init__(self): self.boxes = {} self.typed_attribute_accumulator = [] self.assoc_clause = self._box_clause self.entity_clause = self._box_clause def _box_clause(self, children): # Associate the box name with the accumulated list of attributes box_def_prefix = first_child(children[0], "box_def_prefix") if box_def_prefix != "-": if not box_def_prefix: box_name = first_child(children[0], "box_name") else: box_name = first_child(children[1], "box_name") self.boxes[box_name] = self.typed_attribute_accumulator[:] self.typed_attribute_accumulator = [] def attr(self, children): # Accumulate a couple (attribute name, data type placeholder) self.typed_attribute_accumulator.append((children[0].value, "")) def datatype(self, children): # Replace the last data type placeholder with the actual data type (name, _) = self.typed_attribute_accumulator.pop() self.typed_attribute_accumulator.append((name, children[0].value)) def finalize(self, common, subargs): language = common.params["language"] actual_colons = {} for kind in list(subargs): m = re.match(r"^([*_`]*)(.+)\1$", kind) if m[2] in ("label", "type", "box"): actual_colons[m[2]] = m[1] subargs[m[2]] = subargs.pop(kind) if not actual_colons: actual_colons["box"] = subargs["box"] = "" actual_colons["label"] = subargs["label"] = "" actual_colons["type"] = subargs["type"] = "" self.header = [] self.projectors = [] for (kind, col_name) in subargs.items(): if kind in ("tsv", "md", "markdown"): continue if kind not in actual_colons: raise subsubopt_error(kind) self.header.append(col_name or DEFAULT_COLUMN_NAMES[language][kind]) self.projectors.append(kind) self.rows = [] for (box, attributes) in self.boxes.items(): for (attr, datatype) in attributes: d = {"box": box, "label": attr, "type": datatype} row = [d[p] for p in self.projectors] self.rows.append(row) self.md_tags = [actual_colons[p] for p in self.projectors] self.rows.sort() return len(actual_colons) def get_tsv(self): result = [] if len(self.header) > 1: result.append("\t".join(self.header)) result.extend(map("\t".join, self.rows)) return "\n".join(result) def get_markdown(self): if len(self.header) > 1: # output a table only if there are at least two columns result = [] previous_leftmost = None for row in [self.header] + self.rows: if row[0] == previous_leftmost: row[0] = '"' else: previous_leftmost = row[0] result.append(f"{tag}{cell}{tag}" for (tag, cell) in zip(self.md_tags, row)) return markdown_table(result) else: # otherwise, output a list return "\n".join([f"- {self.md_tags[0]}{row[0]}{self.md_tags[0]}" for row in self.rows]) def run(source, subargs=None, common=None): tree = parse_source(source) extractor = AttributeListExtractor() extractor.transform(tree) column_count = extractor.finalize(common, subargs) if "tsv" in subargs: return { "stem_suffix": f"_data_dict_{column_count}", "text": extractor.get_tsv(), "extension": "tsv", "to_defer": False, "highlight": "tsv", } else: # Markdown is the default return { "stem_suffix": f"_data_dict_{column_count}", "text": extractor.get_markdown(), "extension": "md", "to_defer": False, "highlight": "markdown", } ================================================ FILE: mocodo/convert/_prompt.py ================================================ from ..parse_mcd import Visitor from ..tools.parser_tools import parse_source, reconstruct_source, first_child from pathlib import Path class CreateLegNotePlaceholder(Visitor): def assoc_leg(self, tree): """ Create or replace the leg note with a placeholder. """ leg_note = first_child(tree, "leg_note") if leg_note: leg_note.value = "?" else: box_name = first_child(tree, "box_name") box_name.value = f"[?] {box_name.value}" def comment(self, tree): """ Suppress the comment line. """ tree.children = [] class CreateTypePlaceholder(Visitor): def typed_attr(self, tree): if len(tree.children) == 1: tree.children[0].children[0].value += f" [?]" def CreatePlaceholder(subarg): if subarg == "cards": return CreateLegNotePlaceholder() elif subarg == "types": return CreateTypePlaceholder() else: raise ValueError(f"Unknown subarg: {subarg}") def get_prompt(source, chat_dir, subarg): prompt = Path(chat_dir, f"{subarg}_fr.md").read_text(encoding="utf8") examples = [] for path in sorted(Path(chat_dir, f"{subarg}_examples").glob("*.mcd")): (i, name) = path.stem.split("_", 1) example_source = path.read_text(encoding="utf8") if name == "input": examples.append(f"## Example {i}\n\n### Question\n\n{backticks(example_source)}") elif name == "output": examples.append(f"### Answer\n\n{backticks(example_source)}") examples = "\n".join(examples) tree = parse_source(source) visitor = CreatePlaceholder(subarg) visitor.visit(tree) question = backticks(reconstruct_source(tree)) return prompt.format(examples=examples, question=question) def backticks(text): text = text.strip('\n') return f"```mocodo\n{text}\n```\n" def run(source, subargs, common): chat_dir = Path(common.params["script_directory"], "resources", "prompts", "chat") prompt = "" for subarg in subargs: if subarg in ("cards", "types"): prompt = get_prompt(source, chat_dir, subarg) break return { "stem_suffix": f"_prompt_for_{subarg}", "text": prompt, "extension": "md", "to_defer": False, "highlight": "md", } ================================================ FILE: mocodo/convert/_share.py ================================================ from ..tools.string_tools import urlsafe_encoding def run(source, subargs=None, common=None): return { "stem_suffix": "_url", "text": f"https://www.mocodo.net/?mcd={urlsafe_encoding(source)}", "extension": "url", "to_defer": True, "highlight": "url", } ================================================ FILE: mocodo/convert/_uml.py ================================================ import itertools import gettext __import__("sys").path[0:0] = ["."] from ..parse_mcd import Visitor from ..tools.parser_tools import first_child, parse_source, is_identifier from ..rewrite import ( _split as split, _drain as drain, ) from .. import __version__ MIN_MAX_IN_UML = { "01": "1..*", "0N": "*", "11": "1", "1N": "1..*", } gettext.NullTranslations().install() INHERITANCE_IN_UML = { "XT": _("{complete, disjoint}"), # Partition "X": _("{incomplete, disjoint}"), # Exclusivité "T": _("{complete, overlapping}"), # Totalité "": _("{incomplete, overlapping}"), } class UmlClassDiagram(Visitor): def __init__(self, preamble, common): self.common = common self.preamble = preamble or "" # preamble may be None self.id_counter = itertools.count() self.df_label = common.params["df"] self.has_no_datatype = True self.acc = [] self.invisible_boxes = set() def entity_or_table_attr(self, tree): id_groups = str(first_child(tree, "id_groups")) id_mark = str(first_child(tree, "id_mark")) attr = str(first_child(tree, "attr")) datatype = str(first_child(tree, "datatype")) if datatype: self.has_no_datatype = False tree.children = [(id_groups, id_mark, attr, datatype)] def entity_clause(self, tree): ent_name = str(first_child(tree, "box_name")) if first_child(tree, "box_def_prefix") == "-": self.invisible_boxes.add(ent_name) return attributes = [] for (i, node) in enumerate(tree.find_data("entity_or_table_attr")): (id_groups, id_mark, attr_label, datatype) = node.children[0] attr_label = str(attr_label) if attr_label == "": continue # ignore spacer attributes attributes.append((is_identifier(i, id_groups, id_mark), attr_label, datatype)) self.acc.append({ "kind": "table", "name": ent_name, "attributes": attributes, }) self.acc.append({"kind": "spacer"}) def assoc_clause(self, tree): legs = [node for node in tree.find_data("assoc_leg")] assoc_name = str(first_child(tree, "assoc_name_def").children[0]) if first_child(tree, "box_def_prefix") == "-": self.invisible_boxes.add(assoc_name) return min_maxs = [node.children[0].value for node in tree.find_data("card")] ent_names = [str(first_child(leg, "entity_name_ref").children[0]) for leg in legs] typed_attrs = [] for node in list(tree.find_data("typed_attr")): attr_label = str(first_child(node, "attr")) if attr_label == "": continue # don't create a node for a spacer attribute datatype = str(first_child(node, "datatype")) typed_attrs.append((attr_label, datatype)) if len(legs) == 2: weaks = [first_child(leg, "card_prefix") == "_" for leg in legs] self.acc.append({ "kind": "binary_link", "tables": ent_names, "table_1": ent_names[0], "card_1": MIN_MAX_IN_UML.get(min_maxs[1], "*"), "composition_1": weaks[1], "table_2": ent_names[1], "card_2": MIN_MAX_IN_UML.get(min_maxs[0], "*"), "composition_2": weaks[0], "assoc_name": assoc_name, }) if typed_attrs: self.acc.append({ "kind": "binary_link_attrs", "tables": ent_names, "table_1": ent_names[0], "table_2": ent_names[1], "assoc_name": assoc_name, }) else: # The card is 1 (11) if the min-max card has a "/" prefix, 1..* or * otherwise diamond = f"N_ARY_{next(self.id_counter)}" self.acc.append({ "kind": "n_ary_link", "diamond": diamond, "tables": ent_names, "cards": ["1" if first_child(leg, "card_prefix") == "/" else MIN_MAX_IN_UML.get(first_child(leg, "card"), "*") for leg in legs], "assoc_name": assoc_name, }) if typed_attrs: self.acc.append({ "kind": "n_ary_link_attrs", "diamond": diamond, "tables": ent_names, "assoc_name": assoc_name, }) if typed_attrs: self.acc.append({ "kind": "table", "name": assoc_name, "attributes": [(False, attr_label, datatype) for (attr_label, datatype) in typed_attrs], "tables": ent_names, }) self.acc.append({"kind": "spacer"}) def inheritance_clause(self, tree): inheritance_name = str(first_child(tree, "inheritance_name")) box_names = [str(first_child(leg, "box_name")) for leg in tree.find_data("box_name")] box_names.reverse() # not sure why, but the boxes seem to come in reverse order self.acc.append({ "kind": "generalization", "set": INHERITANCE_IN_UML[inheritance_name], "parent": box_names[0], "children": [box_name for box_name in box_names[1:]], "id": f"GENERALIZATION_{next(self.id_counter)}", }) self.acc.append({"kind": "spacer"}) def start(self, tree): self.acc = list(filter(lambda d: not self.invisible_boxes.intersection(d.get("tables", [])), self.acc)) def get_plantuml(self): style = self.common.load_style() result = [] if not self.preamble.startswith("-"): result.append(f"' Generated by Mocodo {__version__}\n") result.append(f'@startuml "{self.common.params["title"]}"\n') result.append(f'!define Table(x) class "x" << (T,{style["entity_color"]}) >>') result.append("!define pk(x) x") if self.preamble.startswith("-"): self.preamble = self.preamble[1:] else: result.append("\n".join([ "hide methods", "left to right direction", f'skinparam groupInheritance 2', f'skinparam lineThickness {style["box_stroke_depth"]}', f'skinparam lineColor {style["association_stroke_color"]}', f'skinparam backgroundColor {style["background_color"]}', f'skinparam classAttributeFontColor {style["entity_attribute_text_color"]}', f'skinparam classAttributeFontName Monospaced', f'skinparam classAttributeFontSize 14', f'skinparam classBackgroundColor {style["entity_color"]}', f'skinparam classBorderColor {style["entity_stroke_color"]}', f'skinparam classBorderThickness {style["box_stroke_depth"]}', f'skinparam classFontColor {style["entity_cartouche_text_color"]}', f'skinparam classFontName Arial', f'skinparam classFontSize 18', f'skinparam classHeaderBackgroundColor {style["entity_cartouche_color"]}', ])) result.append(self.preamble.replace(r"\n", "\n")) # the user-defined preamble comes raw for element in self.acc: if element["kind"] == "table": max_attr_length = max([len(attr) for (_, attr, _) in element["attributes"]], default=0) result.append(f'Table("{element["name"]}") {{') for (is_id, attr, datatype) in element["attributes"]: suffix = "" if datatype: tabs = " " * (max_attr_length - len(attr) + 1) suffix = f"{tabs}{datatype}" if is_id: attr = f"pk({attr})" result.append(f" {{field}} + {attr}{suffix}") result.append(f"}}") elif element["kind"] == "binary_link": head = "*" if element["composition_1"] else "-" tail = "*" if element["composition_2"] else "-" element["link"] = f"{head}-{tail}" if element["assoc_name"].upper() == self.df_label.upper(): result.append('"{table_1}" "{card_1}" {link} "{card_2}" "{table_2}"'.format(**element)) else: result.append('"{table_1}" "{card_1}" {link} "{card_2}" "{table_2}": "{assoc_name}"'.format(**element)) elif element["kind"] == "n_ary_link": result.append(f"diamond {element['diamond']}") for (table_name, card) in zip(element["tables"], element["cards"]): result.append(f'{element["diamond"]} -- "{card}" "{table_name}"') elif element["kind"] == "binary_link_attrs": result.append('("{table_1}", "{table_2}") .. "{assoc_name}"'.format(**element)) elif element["kind"] == "n_ary_link_attrs": result.append('{diamond} "{assoc_name}" .. "{assoc_name}"'.format(**element)) elif element["kind"] == "generalization": if element["set"]: result.append(f'note "{element["set"]}" as {element["id"]}') result.extend(f'{element["id"]} -[dotted]- {child}' for child in element["children"]) result.extend(f'{element["parent"]} <|-- {child}' for child in element["children"]) else: # spacer result.append("") result.append(f"@enduml\n") return "\n".join(result) def run(source, subargs=None, common=None): (language, preamble) = next(iter(subargs.items()), ("plantuml", "")) source = split.run(source) source = drain.run(source) tree = parse_source(source) extractor = UmlClassDiagram(preamble, common) extractor.visit(tree) if True: # language == "plantuml": text = extractor.get_plantuml() extension = "puml" highlight = "puml" else: text = extractor.get_mermaid() extension = "mmd" highlight = "mermaid" return { "stem_suffix": "_uml", "text": text, "extension": extension, "to_defer": True, "highlight": highlight, } ================================================ FILE: mocodo/convert/crow_gv.py ================================================ __import__("sys").path[0:0] = ["."] from ._crow import Crow from ..tools.parser_tools import parse_source from ..rewrite import ( _drain as drain, _explode as explode, _split as split, ) from ..tools.string_tools import rstrip_digit_or_underline from ..tools.graphviz_tools import NODE_OPTIONS_TEMPLATE, table_as_label from .. import __version__ SUFFIX = "_crows_foot_erd.gv" GV_CARD = { "01": "teeodot", "0N": "crowodot", "11": "teetee", "1N": "crowtee", } class CrowGv(Crow): def get_text(self, common): style = common.load_style() ent_table_style = { "stroke_color": style["entity_stroke_color"], "cell_bg_color": style["entity_color"], "header_bg_color": style["entity_cartouche_color"], "header_font_color": style["entity_cartouche_text_color"], "header_font_size": style["entity_cartouche_font"]["size"], "cell_font_color": style["entity_attribute_text_color"], "cell_font_size": style["entity_attribute_font"]["size"], } assoc_table_style = { "stroke_color": style["association_stroke_color"], "cell_bg_color": style["association_color"], "header_bg_color": style["association_cartouche_color"], "header_font_color": style["association_cartouche_text_color"], "header_font_size": style["association_cartouche_font"]["size"], "cell_font_color": style["association_attribute_text_color"], "cell_font_size": style["association_attribute_font"]["size"], } acc = [] acc.append(f'// Generated by Mocodo {__version__}\n') acc.append(f'digraph{{') acc.append(f' layout=dot') acc.append(f' bgcolor="{style["background_color"]}"') acc.append(f' nodesep=0.5') # increase spacing between multiedges acc.append(f'\n // Nodes') for (i, table_style) in enumerate([assoc_table_style, ent_table_style]): node_options = NODE_OPTIONS_TEMPLATE.format(**ent_table_style) acc.append(f' node [{node_options}]') for (ent_index, (ent_name, has_id, attrs)) in self.tables.items(): if i != has_id: continue attrs = [(("PK" if is_id else " "), a, (t or " ")) for (t, a, is_id) in attrs] row_format = "|c|lR|" if self.has_no_datatype: attrs = [attr[:-1] for attr in attrs] row_format = row_format.replace("R|", "|") label = table_as_label(ent_name, attrs, row_format, table_style) acc.append(f' {ent_index} [label=<{label}>]') acc.append(f'\n // Edges') acc.append(f' edge [') acc.append(f' penwidth={style["leg_stroke_depth"]}') acc.append(f' color="{style["leg_stroke_color"]}"') acc.append(f' fontcolor="{style["card_text_color"]}"') acc.append(f' fontname="{style["card_font"]["family"]}"') acc.append(f' fontsize={style["card_font"]["size"]}') acc.append(f' dir=both') # bidirectional arrows, otherwise the tail doesn't appear acc.append(f' ]') for (ent_1, card_1, kind, card_2, ent_2, assoc_name) in self.links: ent_index_1 = self.name_to_index(ent_1) ent_index_2 = self.name_to_index(ent_2) tail = GV_CARD.get(card_1, "crowodot") head = GV_CARD.get(card_2, "crowodot") kind = " style=dotted" if kind == ".." else "" label = "" if self.tables[ent_index_1][1] and self.tables[ent_index_2][1]: label = f' label="{rstrip_digit_or_underline(assoc_name)}"' acc.append(f' {ent_index_1} -> {ent_index_2} [arrowhead="{head}" arrowtail="{tail}"{label}{kind}]') acc.append('}') return "\n".join(acc) def run(source, subargs, common=None): source = drain.run(source) source = split.run(source) source = explode.run(source, {"arity": "2.5", "weak": True}, common.params) tree = parse_source(source) extractor = CrowGv() extractor.visit(tree) result = extractor.get_text(common) return result ================================================ FILE: mocodo/convert/crow_mmd.py ================================================ import re __import__("sys").path[0:0] = ["."] from ._crow import Crow from ..tools.parser_tools import parse_source from ..rewrite import ( op_tk, _drain as drain, _explode as explode, _split as split, ) from ..tools.string_tools import rstrip_digit_or_underline from .. import __version__ SUFFIX = "_crows_foot_erd.mmd" LEFT_CARD = { "01": "|o", "0N": "}o", "11": "||", "1N": "}|", } RIGHT_CARD = { "01": "o|", "0N": "o{", "11": "||", "1N": "|{", } def sanitize_type(s): # In Mermaid syntax, the type values must begin with an alphabetic character # and may contain digits, hyphens, underscores, parentheses and square brackets. # This seems a too specific operation to be offered by op_tk. s = s.replace( ",", "-" ) # as long as https://github.com/mermaid-js/mermaid/issues/1546 is not fixed s = re.sub(r"[^-_0-9A-Za-z()[\]]", "_", s) s = re.sub(r"__+", "_", s) s = s.strip("_") return s class CrowMmd(Crow): def get_text(self): result = [] result.append("erDiagram") result.append(f" %% Generated by Mocodo {__version__}") for (name, has_id, attrs) in self.tables.values(): result.append(f" {name} {{") for (datatype, attr, is_id) in attrs: datatype = sanitize_type(datatype) if datatype else "TYPE" pk = " PK" if is_id else "" result.append(f" {datatype} {attr}{pk}") result.append(f" }}") for (ent_1, card_1, kind, card_2, ent_2, assoc_name) in self.links: ent_1 = rstrip_digit_or_underline(ent_1) ent_2 = rstrip_digit_or_underline(ent_2) card_1 = LEFT_CARD.get(card_1, "}|") card_2 = RIGHT_CARD.get(card_2, "|{") result.append(f" {ent_1} {card_1}{kind}{card_2} {ent_2}: {assoc_name}") return "\n".join(result) def run(source, subargs, common=None): source = op_tk.run(source, "ascii", {"labels": 1}, common.params) source = op_tk.run(source, "snake", {"labels": 1}, common.params) source = drain.run(source) source = split.run(source) source = explode.run(source, {"arity": "2.5", "weak": True}, common.params) tree = parse_source(source) extractor = CrowMmd() extractor.visit(tree) result = extractor.get_text() return result ================================================ FILE: mocodo/convert/read_template.py ================================================ from bisect import bisect_left from pathlib import Path from ..tools import load_mini_yaml from ..mocodo_error import MocodoError def read_template(stem_or_path, official_template_dir): def traverse_templates(stem_or_path, template_stack, already_seen): if stem_or_path.endswith(".yaml"): path = Path(stem_or_path).absolute() else: path = official_template_dir / f"{stem_or_path}.yaml" (folder, name) = (path.parent, path.stem) if stem_or_path in already_seen: raise MocodoError(30, _('Circular inheritance in template "{name}.yaml" of "{folder}.').format(name=name, folder=folder)) # fmt: skip already_seen.add(stem_or_path) if not path.is_file(): raise MocodoError(31, _('Template "{name}.yaml" not found in "{folder}".').format(name=name, folder=folder)) # fmt: skip try: template = load_mini_yaml.run(path) except: raise MocodoError(32, _('Unable to decode template "{name}.yaml" of "{folder}".').format(name=name, folder=folder)) # fmt: skip for (key, array) in template.items(): if isinstance(array, dict): raise MocodoError(34, _('Template "{name}.yaml" of "{folder}" contains a YAML object as value of key "{key}".').format(name=name, folder=folder, key=key)) # fmt: skip if not isinstance(array, list): continue previous_order = None for d in array: if not isinstance(d, dict): raise MocodoError(35, _('Template "{name}.yaml" of "{folder}" contains a YAML array as value of key "{key}" which does not contain only YAML objects.').format(name=name, folder=official_template_dir, key=key)) # fmt: skip if "order" not in d: raise MocodoError(36, _('Template "{name}.yaml" of "{folder}" contains a YAML array as value of key "{key}" which does not contain only objects having an "order" key.').format(name=name, folder=official_template_dir, key=key)) # fmt: skip order = d["order"] if not isinstance(order, (int, float)): raise MocodoError(38, _('Template "{name}.yaml" of "{folder}" contains a YAML array as value of key "{key}" where the "order" key is not associated to a number.').format(name=name, folder=official_template_dir, key=key)) if previous_order is not None and order <= previous_order: raise MocodoError(39, _('Template "{name}.yaml" of "{folder}" contains a YAML array as value of key "{key}" where the "order" keys are not sorted in ascending order.').format(name=name, folder=official_template_dir, key=key)) previous_order = order template_stack.append(template) if "parent" in template: return traverse_templates(template["parent"], template_stack, already_seen) else: return reversed(template_stack) result = {} for template in traverse_templates(stem_or_path, [], set()): for key in template: if not isinstance(result.get(key), list): # create or update a scalar value result[key] = template[key] else: if template[key] == []: result[key] = [] # update a non-empty list of dictionaries having an "order" key for new_dictionary in template[key]: order = new_dictionary["order"] orders = [d["order"] for d in result[key]] # Prior to Python 3.10, bisect_left has no `key` argument i = bisect_left(orders, order) if i < len(result[key]) and result[key][i]["order"] == order: # a dictionary with the same order already exists if set(new_dictionary.keys()).issubset({"order", "comment"}): # the new dictionary defines nothing: remove the existing dictionary del result[key][i] else: # update the existing dictionary in place result[key][i].update(new_dictionary) else: # insert the new dictionary at the right place result[key].insert(i, new_dictionary) result.pop("parent", None) return result ================================================ FILE: mocodo/convert/relations.py ================================================ import collections from pathlib import Path import re from mocodo.tools.various import first_missing_positive from ..mocodo_error import MocodoError from .. import __version__ def set_defaults(template): result = { "transform_attribute": [], "transform_label": [], "transform_title": [], "transform_datatype": [], "transform_optionality": [], "label_role_separator": " ", "compose_primary_key": "_{label}_", "compose_normal_attribute": "{label}", "compose_foreign_key": "#{label}", "compose_primary_foreign_key": "_#{label}_", "add_unicity_constraints": [], "add_optionality_constraints": [], "transform_relation_name": [], "column_separator": ", ", "compose_relation": "{this_relation_name} ({columns})", "deleted_relation_separator": "", "compose_deleted_relation": "", "compose_deleted_relations": "", "transform_forced_relation": [], "transform_relation": [], "relation_separator": "\n", "compose_relational_schema": "{relations}", "transform_relational_schema": [], } result.update(template) result.setdefault("compose_ex_foreign_key", result["compose_normal_attribute"]) result.setdefault("compose_primary_ex_foreign_key", result["compose_primary_key"]) result.setdefault("compose_association_attribute", result["compose_normal_attribute"]) result.setdefault("compose_association_primary_key", result["compose_primary_key"]) result.setdefault("compose_deleted_child_attribute", result["compose_normal_attribute"]) result.setdefault("compose_deleted_child_discriminator_", result["compose_normal_attribute"]) result.setdefault("compose_deleted_child_discriminator_T", result["compose_normal_attribute"]) result.setdefault("compose_deleted_child_discriminator_X", result["compose_normal_attribute"]) result.setdefault("compose_deleted_child_discriminator_XT", result["compose_normal_attribute"]) result.setdefault("compose_deleted_child_entity_name", result["compose_normal_attribute"]) result.setdefault("compose_deleted_child_foreign_key", result["compose_foreign_key"]) result.setdefault("compose_deleted_parent_discriminator_", result["compose_normal_attribute"]) result.setdefault("compose_deleted_parent_discriminator_T", result["compose_normal_attribute"]) result.setdefault("compose_deleted_parent_discriminator_X", result["compose_normal_attribute"]) result.setdefault("compose_deleted_parent_discriminator_XT", result["compose_normal_attribute"]) result.setdefault("compose_deleted_parent_attribute", result["compose_normal_attribute"]) result.setdefault("compose_deleted_parent_foreign_key", result["compose_foreign_key"]) result.setdefault("compose_deleted_parent_primary_key", result["compose_primary_key"]) result.setdefault("compose_stopped_foreign_key", result["compose_foreign_key"]) result.setdefault("compose_outer_attribute", result["compose_normal_attribute"]) result.setdefault("compose_outer_primary_key", result["compose_primary_key"]) result.setdefault("compose_parent_primary_key", result["compose_primary_foreign_key"]) result.setdefault("compose_strengthening_primary_foreign_key", result["compose_primary_foreign_key"]) result.setdefault("compose_strengthening_primary_ex_foreign_key", result["compose_primary_key"]) result.setdefault("compose_unsourced_foreign_key", result["compose_normal_attribute"]) result.setdefault("compose_unsourced_primary_foreign_key", result["compose_primary_key"]) result.setdefault("compose_ex_foreign_key", result["compose_normal_attribute"]) result.setdefault("compose_primary_ex_foreign_key", result["compose_primary_key"]) result.setdefault("compose_deleted_child_ex_foreign_key", result["compose_normal_attribute"]) result.setdefault("compose_deleted_parent_ex_foreign_key", result["compose_normal_attribute"]) result.setdefault("compose_stopped_ex_foreign_key", result["compose_normal_attribute"]) result.setdefault("compose_unsourced_ex_foreign_key", result["compose_normal_attribute"]) result.setdefault("compose_unsourced_primary_ex_foreign_key", result["compose_primary_key"]) return result class Relations: def __init__(self, mcd, params): self.mcd = mcd self.output_stem = Path(params["output_name"]).stem self.ensure_no_reciprocical_relative_entities() self.freeze_strengthening_foreign_key_migration = set() self.relations = {} self.relations_from_entities() self.inheritance_parent_or_children_to_delete = self.find_inheritance_parent_or_children_to_delete() self.strengthen_children() self.strengthen_parents() self.strengthen_weak_identifiers() self.process_associations() self.process_inheritances() self.delete_inheritance_parent_or_children_to_delete() self.delete_deletable_relations() self.make_primary_keys_first() self.relations = dict(sorted(self.relations.items())) def get_text(self, template): def transform(string, transformation): for d in template[transformation]: while True: try: (string, n) = re.subn(d["search"], d["replace"], str(string)) except: raise MocodoError(27, _('Cannot compile the regular expression "{regex}" or the remplacement string "{replace}" in a relation template producing "*{stem_suffix}.{extension}" files.').format(regex=d["search"], replace=d["replace"], stem_suffix=template["stem_suffix"], extension=template["extension"])) # fmt: skip if n == 0 or not d.get("iterated"): break return string template = set_defaults(template) def make_non_disambiguated_labels_from_attributes(): for relation in self.relations.values(): for column in relation["columns"]: column["non_disambiguated_label"] = transform(column["attribute"], "transform_attribute") make_non_disambiguated_labels_from_attributes() def make_labels_from_non_disambiguated_labels(): for relation in self.relations.values(): for column in relation["columns"]: if not column["leg_note"]: column["label"] = column["non_disambiguated_label"] elif column["leg_note"].startswith("-"): column["label"] = column["leg_note"][1:] elif column["leg_note"].startswith("+"): column["label"] = column["non_disambiguated_label"] + column["leg_note"][1:] elif " " in column["leg_note"]: column["label"] = column["non_disambiguated_label"] else: column["label"] = column["non_disambiguated_label"] + template["label_role_separator"] + column["leg_note"] # After labels have been disambiguated by roles, ensure all of them are distinct. for relation in self.relations.values(): occurrences = collections.Counter(column["label"] for column in relation["columns"]) occurrences = dict(c for c in occurrences.items() if c[1] > 1) for column in reversed(relation["columns"]): if column["label"] in occurrences and column["nature"] != "primary_key": occurrences[column["label"]] -= 1 column["label"] = column["label"] + template["label_role_separator"] + str(occurrences[column["label"]] + 1) make_labels_from_non_disambiguated_labels() def transform_labels(): # after labels have been disambiguated by roles for relation in self.relations.values(): for column in relation["columns"]: column["label"] = transform(column["label"], "transform_label") transform_labels() def add_fillers(): for relation in self.relations.values(): label_max_length = max(len(column["label"]) for column in relation["columns"]) for column in relation["columns"]: column["filler"] = " " * (label_max_length - len(column["label"]) + 1) add_fillers() data = {} data["stem"] = self.output_stem data["title"] = transform(self.mcd.title, "transform_title") data["version"] = __version__ lines = [] for (__, relation) in sorted(self.relations.items()): # For the double underscore, see __main__.py data["this_relation_name"] = transform(relation["this_relation_name"], "transform_relation_name") data["is_forced"] = relation["is_forced"] fields = [] for column in relation["columns"]: column["datatype"] = transform(column["datatype"], "transform_datatype") column["optionality"] = transform(column["optionality"], "transform_optionality") data.update(column) field = template["compose_%s" % column["nature"]].format(**data) if column["unicities"]: field = transform(field, "add_unicity_constraints").format(**data) if column["optionality"]: field = transform(field, "add_optionality_constraints").format(**data) fields.append(field) data["columns"] = template["column_separator"].join(fields) line = template["compose_relation"].format(**data) if relation["is_forced"]: line = transform(line, "transform_forced_relation") line = transform(line, "transform_relation") lines.append(line) if template.get("stem_suffix") == "_mld" and template.get("extension") == "mcd": # relational diagram lines = self.map_mcd_layout_onto_mld(lines) data["relations"] = template["relation_separator"].join(lines) if self.deleted_relations: lines = [] for deleted_relation in self.deleted_relations: lines.append(template["compose_deleted_relation"].format(this_relation_name=deleted_relation)) deleted_relation_lines = template["deleted_relation_separator"].join(lines) data["deleted_relations"] = template["compose_deleted_relations"].format(deleted_relation_lines=deleted_relation_lines) else: data["deleted_relations"] = "" data["relations"] = template["compose_relational_schema"].format(**data) result = transform(data["relations"], "transform_relational_schema") return result # private def map_mcd_layout_onto_mld(self, lines): rows = [[]] all_commas = [True] * self.mcd.col_count for row in self.mcd.rows: for (i, box) in enumerate(row): for line in lines: if line.startswith(box.name_view + ":"): rows[-1].append(line) all_commas[i] = False break else: rows[-1].append(":") rows.append([]) rows.pop() for row in rows: row[:] = [x for (x, all_comma) in zip(row, all_commas) if not all_comma] lines = [] for row in rows: lines.append(":") for x in row: lines.append(x) lines.append(":") lines.append("\n") lines.pop() return lines def may_retrieve_distant_leg_note(self, leg, attribute): if leg.entity_bid in self.inheritance_parent_or_children_to_delete: for d in self.relations[leg.entity.bid]["columns"]: if all(d[k] == attribute[k] for k in ("attribute", "adjacent_source", "association_name")): return d["leg_note"] return leg.note def may_retrieve_distant_outer_source(self, leg, attribute): if leg.entity_bid in self.inheritance_parent_or_children_to_delete: for d in self.relations[leg.entity_bid]["columns"]: if d["attribute"] == attribute["attribute"] and d["adjacent_source"] == attribute["adjacent_source"]: return d["outer_source"] return leg.entity.name_view def ensure_no_reciprocical_relative_entities(self): for association in self.mcd.associations.values(): if association.is_invisible: continue weak_count = 0 for leg in association.legs: if leg.kind == "strengthening": weak_count += 1 if weak_count == 2: raise MocodoError(11, _('Reciprocal relative identification around {association}.').format(association=association.bid)) # fmt: skip @staticmethod def pop_optionality_from_datatype(attribute, is_primary = False): (datatype, n) = re.subn(r"(?i) *\bnot +null\b *", " ", attribute.datatype) attribute.datatype = datatype.strip() # suppress the NOT NULL, if any if n: # A NOT NULL has been suppressed return "!" # the attribute is mandatory (as explicitely stated) (datatype, n) = re.subn(r"(?i) *\bnull\b *", " ", attribute.datatype) attribute.datatype = datatype.strip() # suppress the NULL, if any if is_primary: return "!" # the attribute is mandatory (even if not explicitely stated) if n: # A NULL has been suppressed return "?" # the attribute is optional (as explicitely stated) return "" # the attribute is neither optional nor mandatory def relations_from_entities(self): for (name, entity) in self.mcd.entities.items(): if entity.is_invisible: continue self.relations[name] = { "this_relation_name": entity.name_view, "is_forced": False, # an entity naturally results in a relation. No need to force it. "is_protected": entity.is_protected, "columns": [], "existing_unicity_numbers": set(), } for attribute in entity.attributes: if attribute.label.strip() == "": continue # ignore empty attributes nature = "primary_key" if attribute.kind in ("strong", "weak") else "normal_attribute" unicities = "".join(c for c in sorted(attribute.id_groups) if c != "0") is_primary = (nature == "primary_key") optionality = self.pop_optionality_from_datatype(attribute, is_primary) self.relations[name]["columns"].append({ "attribute": attribute.label, "optionality": optionality, "datatype": attribute.datatype, "adjacent_source": None, "outer_source": None, "association_name": None, "leg_note": None, "is_primary": is_primary, "nature": nature, "unicities": unicities, }) self.relations[name]["existing_unicity_numbers"].update(map(int, unicities)) def strengthen_weak_identifiers(self): for entity in self.mcd.entities.values(): entity.is_strong_or_strengthened = not entity.strengthening_legs remaining_entities = [entity for entity in self.mcd.entities.values() if not entity.is_strong_or_strengthened and not entity.is_invisible] while remaining_entities: for entity in remaining_entities: strengthening_entities_via_associations = [] for strengthening_leg in entity.strengthening_legs: association = strengthening_leg.association for other_leg in association.legs: other_entity = other_leg.entity if other_entity == entity: continue if not other_entity.is_strong_or_strengthened: break # weak entity linked to a weak entity strengthening_entities_via_associations.append((other_entity, association)) else: continue break if strengthening_entities_via_associations: for (strengthening_entity, association) in strengthening_entities_via_associations: # find the potential note on the strengthening leg for leg in association.legs: if leg.entity_bid == strengthening_entity.bid: leg_note = leg.note break else: leg_note = None # Patch for #118: if a child disappears but strengthens a weak entity, the outer source # of this entity must be the outer source of this child. Otherwise, the relational diagram # will contains the reference: #parent_id > CHILD > parent_id where CHILD no more exists. # This doesn't fix #119: in the case of cascading inheritance, the outer source should be # searched in the parent of the parent, etc. outer_source = strengthening_entity.name_view if strengthening_entity.bid in self.inheritance_parent_or_children_to_delete: for attribute in self.relations[leg.entity_bid]["columns"]: if attribute["is_primary"]: outer_source = attribute["outer_source"] break # migrate the whole primary key of the strengthening entity into the weak one self.relations[entity.bid]["columns"][0:0] = [{ "attribute": attribute["attribute"], "optionality": "!", "datatype": attribute["datatype"], "adjacent_source": strengthening_entity.name_view, "outer_source": outer_source, "association_name": association.name_view, "leg_note": leg_note, "is_primary": True, "nature": "strengthening_primary_foreign_key", "unicities": "", } for attribute in self.relations[strengthening_entity.bid]["columns"] if attribute["is_primary"]] self.freeze_strengthening_foreign_key_migration.add((entity.bid, association.bid, strengthening_entity.bid)) remaining_entities.remove(entity) entity.is_strong_or_strengthened = True break else: if len(remaining_entities) == 1: raise MocodoError(16, _('A weak entity (here, {entity}) cannot be strengthened by itself.').format(entity=remaining_entities[0].bid)) # fmt: skip else: remaining_entity_names = ", ".join('"%s"' % entity.raw_name for entity in remaining_entities) raise MocodoError(17, _('Cycle of weak entities in {entities}.').format(entities=remaining_entity_names)) # fmt: skip def find_inheritance_parent_or_children_to_delete(self): result = set() for inheritance in self.mcd.inheritances: parent_leg = inheritance.legs[0] if inheritance.kind in ("<-", "<="): for child_leg in inheritance.legs[1:]: result.add(child_leg.entity_bid) elif inheritance.kind == "=>": if "T" not in inheritance.name_view: raise MocodoError(25, _('Totality (/T\\ or /XT\\) is mandatory for "=>" inheritance of parent "{name}".').format(name=parent_leg.entity_raw_name)) # fmt: skip result.add(parent_leg.entity_bid) return result def strengthen_children(self): """ Migrate the parent's identifier in the children. This is obviously necessary if the parent must disappear (`=>` + totality) or its attributes copied in its children. Otherwise, the children must disappear. But in the case they are connected to other entities, they need an identifier to make possible to apply the remaining rules. """ for inheritance in self.mcd.inheritances: parent_leg = inheritance.legs[0] to_be_deleted = parent_leg.entity_bid in self.inheritance_parent_or_children_to_delete for child_leg in inheritance.legs[1:]: self.relations[child_leg.entity_bid]["columns"][0:0] = [{ "attribute": attribute["attribute"], "optionality": "!", "datatype": attribute["datatype"], "adjacent_source": parent_leg.entity.name_view, "outer_source": parent_leg.entity.name_view, "association_name": inheritance.name_view, "leg_note": None, "is_primary": True, "nature": "deleted_parent_primary_key" if to_be_deleted else "parent_primary_key", "unicities": "", } for attribute in self.relations[parent_leg.entity_bid]["columns"] if attribute["is_primary"]] def strengthen_parents(self): """ Migrate the optional children's discriminators in their parent when it disappears (totality + =>). In this case, this discriminator should further migrate with the identifier of the parent. """ for inheritance in self.mcd.inheritances: if inheritance.kind == "=>": parent_leg = inheritance.legs[0] self.relations[parent_leg.entity_bid]["columns"].extend({ "attribute": attribute.label, "optionality": "!" if 'T' in inheritance.name_view else "?", "datatype": attribute.datatype or "UNSIGNED_INT_PLACEHOLDER", "adjacent_source": None, "outer_source": None, "association_name": inheritance.name_view, "leg_note": None, "is_primary": False, "nature": f"deleted_parent_discriminator_{inheritance.name_view}", "unicities": "", } for attribute in inheritance.attributes) def process_associations(self): for association in self.mcd.associations.values(): if association.is_invisible: continue is_cluster = (association.kind == "cluster") df_leg = None for leg in association.legs: if leg.entity.is_invisible: continue if leg.card[1] == "1": df_leg = leg if leg.card[0] == "1": break # elect the first leg with cardinality 11 if df_leg is None or association.is_protected: # make a relation of this association self.relations[association.bid] = { "this_relation_name": association.name_view, "is_forced": bool(df_leg), # if this association has a 11 leg, being here means it is protected: it must be forced into a relation "columns": [], "existing_unicity_numbers": set(), } for leg in association.legs: if leg.entity.is_invisible: continue for attribute in self.relations[leg.entity_bid]["columns"]: if attribute["is_primary"]: outer_source = self.may_retrieve_distant_outer_source(leg, attribute) if is_cluster: self.relations[association.bid]["columns"].append({ # gather all migrant attributes "attribute": attribute["attribute"], "optionality": "!", "datatype": attribute["datatype"], "adjacent_source": leg.entity.name_view, "outer_source": outer_source, "association_name": association.name_view, "leg_note": leg.note, "is_primary": leg.is_in_elected_group, "nature": "primary_foreign_key" if leg.is_in_elected_group else "stopped_foreign_key", "unicities": leg.unicities, }) self.relations[association.bid]["existing_unicity_numbers"].update(map(int, leg.unicities)) elif association.is_protected and df_leg is not None and leg is not df_leg: self.relations[association.bid]["columns"].append({ # gather all migrant attributes "attribute": attribute["attribute"], "optionality": "!", "datatype": attribute["datatype"], "adjacent_source": leg.entity.name_view, "outer_source": outer_source, "association_name": association.name_view, "leg_note": leg.note, "is_primary": False, "nature": "stopped_foreign_key", "unicities": "", }) else: self.relations[association.bid]["columns"].append({ # gather all migrant attributes "attribute": attribute["attribute"], "optionality": "!", "datatype": attribute["datatype"], "adjacent_source": leg.entity.name_view, "outer_source": outer_source, "association_name": association.name_view, "leg_note": leg.note, "is_primary": True, "nature": "unsourced_primary_foreign_key" if outer_source is None else "primary_foreign_key", "unicities": "", }) elif attribute["nature"].startswith("deleted_parent_discriminator"): self.relations[association.bid]["columns"].append({ "attribute": attribute["attribute"], "optionality": "!", "datatype": attribute["datatype"], "adjacent_source": leg.entity.name_view, "outer_source": None, "association_name": association.name_view, "leg_note": leg.note, "is_primary": False, "nature": attribute["nature"], "unicities": "", }) self.relations[association.bid]["columns"].extend({ # and the attributes already existing in the association "attribute": attribute.label, "optionality": "!" if attribute.kind == "strong" else self.pop_optionality_from_datatype(attribute), "datatype": attribute.datatype, "adjacent_source": None, "outer_source": None, "association_name": association.name_view, "leg_note": None, "is_primary": attribute.kind == "strong", "nature": "association_primary_key" if attribute.kind == "strong" else "association_attribute", "unicities": "", } for attribute in association.attributes if attribute.label.strip() != "" ) continue # No relation will be created from this association. # Check the number of /?1 legs (pegs) df_pegs = [] for leg in association.legs: if leg.entity.is_invisible: continue if leg.card[1] == "1" and leg.kind == "cluster_peg": df_pegs.append(leg) if df_pegs: # A cluster with at least one /?1 leg (peg) for df_peg in df_pegs: # All migrating attribute must belong to the same new unicity group unicities = str(first_missing_positive(self.relations[df_peg.entity_bid]["existing_unicity_numbers"])) self.relations[df_leg.entity_bid]["existing_unicity_numbers"].update(map(int, unicities)) for leg in association.legs: if leg is df_peg: continue for attribute in list(self.relations[leg.entity_bid]["columns"]): # traverse a copy... # ... to prevent an infinite migration of the child discriminator if attribute["is_primary"]: # Their primary keys must migrate in `entity_name`. outer_source = self.may_retrieve_distant_outer_source(leg, attribute) self.relations[df_peg.entity_bid]["columns"].append({ "attribute": attribute["attribute"], "optionality": "!", "datatype": attribute["datatype"], "adjacent_source": leg.entity.name_view, "outer_source": outer_source, "association_name": association.name_view, "leg_note": leg.note, "is_primary": False, "nature": "unsourced_foreign_key" if outer_source is None else "foreign_key", "unicities": unicities, # NB: technically, an unsourced foreign key is not foreign anymore }) # The association attributes must migrate only once, df_leg is elected. # Delay the addition of the association attributes after the else part. else: # A normal DF association. Traverse the other legs to find the attributes to migrate. for leg in association.legs: if leg.entity.is_invisible: continue if leg is not df_leg: if (df_leg.entity_bid, association.bid, leg.entity_bid) not in self.freeze_strengthening_foreign_key_migration: unicities = "" if leg.card[1] == "1": # *1 --(DF)-- 11 => the migrating attribute must be made unique, find a new unicity group unicities = str(first_missing_positive(self.relations[df_leg.entity_bid]["existing_unicity_numbers"])) for attribute in list(self.relations[leg.entity_bid]["columns"]): # traverse a copy... # ... to prevent an infinite migration of the child discriminator optionality = "!" if df_leg.card[0] == "1" else "?" if attribute["is_primary"]: # Their primary keys must migrate in df_leg.entity_bid. outer_source = self.may_retrieve_distant_outer_source(leg, attribute) self.relations[df_leg.entity_bid]["columns"].append({ "attribute": attribute["attribute"], "optionality": optionality, "datatype": attribute["datatype"], "adjacent_source": leg.entity.name_view, "outer_source": outer_source, "association_name": association.name_view, "leg_note": leg.note, "is_primary": False, "nature": "unsourced_foreign_key" if outer_source is None else "foreign_key", "unicities": unicities, # NB: technically, an unsourced foreign key is not foreign anymore }) self.relations[df_leg.entity_bid]["existing_unicity_numbers"].update(map(int, unicities)) elif attribute["nature"].startswith("deleted_parent_discriminator"): self.relations[df_leg.entity_bid]["columns"].append({ "attribute": attribute["attribute"], "optionality": optionality, "datatype": attribute["datatype"], "adjacent_source": leg.entity.name_view, "outer_source": None, "association_name": association.name_view, "leg_note": leg.note, "is_primary": False, "nature": attribute["nature"], "unicities": "", }) # Add the attributes already existing in the association self.relations[df_leg.entity_bid]["columns"].extend([{ "attribute": attribute.label, "optionality": "!" if attribute.kind == "strong" else self.pop_optionality_from_datatype(attribute), "datatype": attribute.datatype, "association_name": association.name_view, "adjacent_source": None, "outer_source": None, "leg_note": None, "is_primary": attribute.kind == "strong", "nature": "outer_primary_key" if attribute.kind == "strong" else "outer_attribute", "unicities": "", } for attribute in association.attributes if attribute.label.strip() != ""]) def process_inheritances(self): for inheritance in self.mcd.inheritances: parent_leg = inheritance.legs[0] if inheritance.kind == "=>": # total migration: parent > children for child_leg in inheritance.legs[1:]: # migrate the parent's attributes, except those of nature "deleted_parent_discriminator" self.relations[child_leg.entity_bid]["columns"][0:0] = [{ "attribute": attribute["attribute"], "optionality": attribute["optionality"], "datatype": attribute["datatype"], "adjacent_source": parent_leg.entity.name_view, "outer_source": self.may_retrieve_distant_outer_source(parent_leg, attribute), "association_name": inheritance.name_view, "leg_note": self.may_retrieve_distant_leg_note(parent_leg, attribute), "is_primary": False, "nature": "deleted_parent_foreign_key" if attribute["nature"] == "foreign_key" else "deleted_parent_attribute", "unicities": "", } for attribute in self.relations[parent_leg.entity_bid]["columns"] if not attribute["is_primary"] and not attribute["nature"].startswith("deleted_parent_discriminator")] else: # migration: triangle attributes > parent self.relations[parent_leg.entity_bid]["columns"].extend({ "attribute": attribute.label, "optionality": "!" if 'T' in inheritance.name_view else "?", "datatype": attribute.datatype or "UNSIGNED_INT_PLACEHOLDER", "adjacent_source": None, "outer_source": None, "association_name": inheritance.name_view, "leg_note": None, "is_primary": False, "nature": f"deleted_child_discriminator_{inheritance.name_view}", # "", "X", "T" or "XT" "unicities": "", } for attribute in inheritance.attributes) if inheritance.kind in ("<-", "<="): # migration: children > parent, and suppress children for child_leg in inheritance.legs[1:]: if inheritance.kind == "<=": # make the child's name a boolean attribute of the parent self.relations[parent_leg.entity_bid]["columns"].append({ "attribute": _('is {name}').format(name=child_leg.entity_bid.lower()), "optionality": "!", "datatype": "BOOLEAN_PLACEHOLDER", "adjacent_source": child_leg.entity.name_view, "outer_source": child_leg.entity.name_view, "association_name": inheritance.name_view, "leg_note": None, "is_primary": False, "nature": "deleted_child_entity_name", "unicities": "", }) # migrate all child's attributes for attribute in self.relations[child_leg.entity_bid]["columns"]: if attribute["nature"].endswith("parent_primary_key"): continue # except the "strengthening" parent identifier self.relations[parent_leg.entity_bid]["columns"].append({ "attribute": attribute["attribute"], "optionality": "?", "datatype": attribute["datatype"], "adjacent_source": child_leg.entity.name_view, "outer_source": self.may_retrieve_distant_outer_source(child_leg, attribute), "association_name": inheritance.name_view, "leg_note": self.may_retrieve_distant_leg_note(child_leg, attribute), "is_primary": False, "nature": "deleted_child_foreign_key" if attribute["nature"] == "foreign_key" else "deleted_child_attribute", "unicities": "", }) def delete_inheritance_parent_or_children_to_delete(self): for entity_to_delete in self.inheritance_parent_or_children_to_delete: del self.relations[entity_to_delete] def delete_deletable_relations(self): deleted_outer_sources = set() for (bid, relation) in list(self.relations.items()): if not relation.get("is_protected") and all(column["nature"] == "primary_key" for column in relation["columns"]): del self.relations[bid] deleted_outer_sources.add(relation["this_relation_name"]) for relation in self.relations.values(): for column in relation["columns"]: if column["outer_source"] in deleted_outer_sources: column["nature"] = column["nature"].replace("foreign", "ex_foreign") self.deleted_relations = sorted(deleted_outer_sources) def make_primary_keys_first(self): for relation in self.relations.values(): relation["columns"].sort(key=lambda column: not column["is_primary"]) ================================================ FILE: mocodo/dev/json_to_yaml_templates.py ================================================ from pathlib import Path from json import loads def repr_single(s): """repr() but with single quotes""" return "'" + repr('"' + s)[2:] def convert(source_path, target_path): for path in sorted(source_path.glob("*.json"), reverse=True): print(path) data = loads(path.read_text(encoding="utf8")) result = [] for (key, stuff) in data.items(): if isinstance(stuff, str): stuff = repr_single(stuff).replace(r"\\", "\\").replace("\\'", "''") result.append(f"{key}: {stuff}") elif isinstance(stuff, list): result.append(f"{key}:") for d in stuff: sub_result = [] for (k, v) in d.items(): if isinstance(v, str): v = repr_single(v).replace(r"\\", "\\").replace("\\'", "''") else: v = str(v) sub_result.append(f"{k}: {v}") result.append(" - " + "\n ".join(sub_result)) elif isinstance(stuff, bool): result.append(f"{key}: {str(stuff).lower()}") elif stuff is None: result.append(f"{key}: null") else: result.append(f"{key}: {stuff}") result.append("") dest = Path(target_path, path.stem + ".yaml") dest.write_text("\n".join(result), encoding="utf8") if __name__ == "__main__": convert(Path("test/test_data/templates"), Path("test/test_data/new_templates")) ================================================ FILE: mocodo/dev/update_sql_reserved_words.py ================================================ from pathlib import Path import re import requests url = "https://modern-sql.com/reserved-words-empirical-list.html" text = requests.get(url).text words = {} for (y, word) in re.findall(r'y="(\d+)">(.+?)', text): words[str(int(y) - 14)] = word words_by_dialect = {} dialect = None for chunk in text.split(''): if m := re.search(r'rotate\(-45\)">(.+?)', chunk): dialect = m[1] words_by_dialect[dialect] = [] for (href, y) in re.findall(r'', chunk): if href != "none": words_by_dialect[dialect].append(words[y]) dialects = { "Apache Derby": "", "BigQuery": "", "Db2 (LUW)": "", "H2": "", "MariaDB": "", "MySQL": "mysql.yaml", "Oracle DB": "oracle.yaml", "PostgreSQL": "postgresql.yaml", "SQL Server": "mssql.yaml", "SQLite": "sqlite.yaml", } for (dialect, filename) in dialects.items(): if not filename: continue alternative = "|".join(words_by_dialect[dialect]) path = Path("mocodo", "resources", "relation_templates", filename) text = path.read_text(encoding="utf8") text = re.sub(r"('Protect reserved keywords'\n search: ).+", fr"\1'(?i)^({alternative})$'", text) path.write_text(text, encoding="utf8") print(f"{filename} updated.") ================================================ FILE: mocodo/dev/update_transfo_metadata.py ================================================ from pathlib import Path from collections import defaultdict import json import os from mocodo.argument_parser import init_localization, Transformations from mocodo.tools import load_mini_yaml # Build the index of templates folder = Path(f"mocodo/resources/relation_templates") aliases = defaultdict(list) metadata = defaultdict(dict) for path in sorted(folder.glob("*.yaml")): if "-" in path.name: continue data = load_mini_yaml.run(path) if "help_en" in data: metadata[path.stem] = { "category": "cv", "help_en": data["help_en"], "help_fr": data["help_fr"], "help_zh": data["help_zh"], "aliases": [], } if "fr_examples" in data: fr_examples = {} for d in data["fr_examples"]: fr_examples[d["example"]] = d["explanation"] metadata[path.stem]["fr_examples"] = fr_examples elif "parent" in data: aliases[data["parent"]].append(path.stem) for (parent, children) in aliases.items(): metadata[parent]["aliases"] = children result = json.dumps(metadata, indent=2, ensure_ascii=False) result = result.replace("[\n ", "[") result = result.replace("\n ]", "]") result = result.replace(",\n ", ", ") Path(folder, "_index.json").write_text(result + "\n", encoding="utf8") print(f"File written: {folder}/_index.json") # Build the graph of templates edges = [] for path in sorted(folder.glob("*.yaml")): data = load_mini_yaml.run(path) data.pop("help_fr", None) data.pop("help_en", None) data.pop("fr_examples", None) color = min(9, len(data)) font_color = "white" if color > 5 else "black" if "parent" in data: edges.append(f'"{data["parent"]}" -> "{path.stem}"') edges.append(f'"{path.stem}" [fillcolor={color} fontcolor={font_color}]') edges = "\n ".join(edges) result = f'digraph G {{\n rankdir=LR\n edge [dir="back"]\n node [shape=box style="rounded,filled" colorscheme=reds9 fontname="Arial" penwidth=0]\n {edges}\n}}\n' path = Path(folder, "_graph.gv") path.write_text(result, encoding="utf8") os.system(f"dot -Tsvg {folder}/_graph.gv > {folder}/_graph.svg") path.unlink() print(f"File written: {folder}/_graph.svg") # Build the cheat sheet init_localization("fr") metadata = Transformations("fr").metadata tables = {} for category in ["rw", "cv"]: rows = [] rows.append(("Sous-option", "Description", "Exemples", "Explications")) rows.append((":--", ":--", ":--", ":--")) for (option, data) in sorted(metadata.items()): if data["category"] != category: continue title = f" title=\"Alias : {', '.join(data['aliases'])}.\"" if data.get("aliases") else "" option = f'{option}' row = [option, data['help']] if not data.get(f"fr_examples"): rows.append(row + ["", ""]) continue for (example, description) in data[f"fr_examples"].items(): example = example.replace("\n", "\\n") example = f"`` {example} ``" row.extend([example, description]) rows.append(row) row = ["", ""] # empty cells for the first two columns tables[category] = "\n".join("| " + " | ".join(row) + " |" for row in rows) text = f""" ### Opérations de conversion {tables['cv']} ### Opérations de réécriture {tables['rw']} """.strip() output_path = Path("doc/fr_cheat_sheet.md") output_path.write_text(text, encoding="utf8") print(f"File written: {output_path}") ================================================ FILE: mocodo/diagram_link.py ================================================ from .mocodo_error import MocodoError class DiagramLink: def __init__(self, entities, foreign_entity, foreign_key): self.foreign_entity = foreign_entity self.foreign_key = foreign_key try: self.primary_entity = entities[foreign_key.primary_entity_bid] except KeyError: raise MocodoError(14, _('Attribute "{attribute}" in entity "{entity_1}" references an unknown entity "{entity_2}".').format(attribute=foreign_key.label, entity_1=foreign_entity.bid, entity_2=foreign_key.primary_entity_bid)) # fmt: skip for candidate in self.primary_entity.attributes: if candidate.label.lstrip("#") == foreign_key.primary_key_label.lstrip("#"): self.primary_key = candidate break else: raise MocodoError(15, _('Attribute "{attribute_1}" in entity "{entity_1}" references an unknown attribute "{attribute_2}" in entity "{entity_2}".').format(attribute_1=foreign_key.label, entity_1=foreign_entity.bid, attribute_2=foreign_key.primary_key_label, entity_2=foreign_key.primary_entity_bid)) # fmt: skip def calculate_size(self, style, *ignored): self.fdx = self.foreign_entity.w // 2 self.pdx = self.primary_entity.w // 2 self.fdy = ( -self.foreign_entity.h // 2 + 3 * style["rect_margin_height"] + self.foreign_entity.cartouche_height + (self.foreign_key.rank + 0.5) * (self.foreign_entity.attribute_height + style["line_skip_height"]) ) self.pdy = ( -self.primary_entity.h // 2 + 3 * style["rect_margin_height"] + self.primary_entity.cartouche_height + (self.primary_key.rank + 0.5) * (self.primary_entity.attribute_height + style["line_skip_height"]) ) self.offset = 2 * (style["card_margin"] + style["card_max_width"]) def description(self, style, geo): result = [("comment", {"text": f'Link from "{self.foreign_key.primary_key_label}" ({self.foreign_entity.bid}) to "{self.primary_key.label}" ({self.primary_entity.bid})'})] spins = ( [(-1, -1), (1, -1), (-1, 1), (1, 1)] if self.foreign_key.rank % 2 else [(1, 1), (-1, 1), (1, -1), (-1, -1)] ) (fs, ps) = min( spins, key=lambda fs_ps: abs( geo["cx"][self.foreign_entity.bid] + self.fdx * fs_ps[0] - geo["cx"][self.primary_entity.bid] - self.pdx * fs_ps[1] ), ) xf = geo["cx"][self.foreign_entity.bid] + self.fdx * fs yf = geo["cy"][self.foreign_entity.bid] + self.fdy xp = geo["cx"][self.primary_entity.bid] + self.pdx * ps yp = geo["cy"][self.primary_entity.bid] + self.pdy result.append( ( "curve", { "x0": xf, "y0": yf, "x1": xf + (xp - xf) / 2 if fs != ps else xf + self.offset * fs, "y1": yf + (yp - yf) / 2, "x2": xf + (xp - xf) / 3 if fs != ps else xp + self.offset * ps, "y2": yp, "x3": xp, "y3": yp, "stroke_color": style["leg_stroke_color"], "stroke_depth": style["leg_stroke_depth"], } ) ) result.append( ( "arrow", { "x0": xp, "y0": yp, "x1": xp + ps * style["arrow_width"], "y1": yp - ps * style["arrow_half_height"], "x2": xp + ps * style["arrow_axis"], "y2": yp, "x3": xp + ps * style["arrow_width"], "y3": yp + ps * style["arrow_half_height"], "stroke_color": style["leg_stroke_color"], }, ), ) result.append( ( "circle", { "cx": xf, "cy": yf, "r": style["box_stroke_depth"], "stroke_depth": style["box_stroke_depth"], "stroke_color": style["leg_stroke_color"], "color": style["leg_stroke_color"], } ) ) return result ================================================ FILE: mocodo/entity.py ================================================ from collections import defaultdict from .attribute import * from .tools.string_tools import rstrip_digit_or_underline, raw_to_bid class Entity: def __init__(self, clause): self.source = clause["source"] self.raw_name = clause["name"] # A protected entity results in a table, even if all its columns are part of its primary key. self.is_protected = (clause.get("box_def_prefix") == "+") self.bid = raw_to_bid(self.raw_name) self.name_view = rstrip_digit_or_underline(self.raw_name) self.attributes = clause.get("attrs", []) self.legs = [] # iterating over box's legs does nothing if it is not an association self.kind = "entity" if clause.get("box_def_prefix") == "-": self.calculate_size = self.calculate_size_when_invisible self.description = lambda *ignored: [] self.is_invisible = True else: self.calculate_size = self.calculate_size_when_visible self.description = self.description_when_visible self.is_invisible = False self.has_alt_identifier = False def add_attributes(self, legs_to_strengthen, is_child, fk_format): weak_entity = bool(legs_to_strengthen) self.strengthening_legs = legs_to_strengthen for (i, a) in enumerate(self.attributes): id_mark = a.get("id_mark","") explicit_underscore = "0" in a.get("id_groups", "") or a.get("id_groups", "") == "" if a.get("attribute_label", "") == "": attribute = PhantomAttribute(a) elif is_child: attribute = SimpleEntityAttribute(a) elif i == 0 and id_mark != "_": attribute = WeakAttribute(a) if weak_entity else StrongAttribute(a) elif i == 0 and id_mark == "_" and not explicit_underscore: attribute = WeakAttribute(a) if weak_entity else StrongAttribute(a) elif i != 0 and id_mark == "_" and explicit_underscore: attribute = WeakAttribute(a) if weak_entity else StrongAttribute(a) else: attribute = SimpleEntityAttribute(a) attribute.register_foreign_key_status(a, fk_format) self.attributes[i] = attribute self.candidates = defaultdict(set) for a in self.attributes: for id_group in a.id_groups: self.candidates[id_group].add(a.label) self.candidates = dict(self.candidates) if len(self.candidates) > 1: self.has_alt_identifier = True def register_boxes(self, boxes): self.boxes = boxes def set_id_gutter_visibility(self, is_visible): self.show_id_gutter = is_visible def calculate_size_when_invisible(self, *ignored): self.w = 0 self.h = 0 def calculate_size_when_visible(self, style, get_font_metrics): cartouche_font = get_font_metrics(style["entity_cartouche_font"]) self.get_cartouche_string_width = cartouche_font.get_pixel_width self.cartouche_height = cartouche_font.get_pixel_height() attribute_font = get_font_metrics(style["entity_attribute_font"]) self.attribute_height = attribute_font.get_pixel_height() for attribute in self.attributes: attribute.calculate_size(style, get_font_metrics) cartouche_and_attribute_widths = [] cartouche_and_attribute_widths.append(self.get_cartouche_string_width(self.name_view)) cartouche_and_attribute_widths.extend(a.w for a in self.attributes) self.id_gutter_width = 0 if self.show_id_gutter: self.id_gutter_width = 2 * style["rect_margin_width"] if self.attributes: self.id_gutter_width += max(attribute.id_width for attribute in self.attributes) self.w = 2 * style["rect_margin_width"] + self.id_gutter_width + max(cartouche_and_attribute_widths) self.h = ( len(self.attributes) * (self.attribute_height + style["line_skip_height"]) - style["line_skip_height"] + 4 * style["rect_margin_height"] + self.cartouche_height ) self.w += self.w % 2 self.h += self.h % 2 for attribute in self.attributes: attribute.set_id_gutter_width(self.id_gutter_width) def register_center(self, geo): self.cx = geo["cx"][self.bid] self.cy = geo["cy"][self.bid] self.l = self.cx - self.w // 2 self.r = self.cx + self.w // 2 self.t = self.cy - self.h // 2 self.b = self.cy + self.h // 2 def description_when_visible(self, style, geo): result = [] result.append(("comment", {"text": f"Entity {self.bid}"})) result.append( ( "begin_component", { "page": self.page, "visibility": "hidden" if self.page else "visible", } ) ) result.append(("begin_group", {})) result.append( ( "rect", { # upper part background "x": self.l, "y": self.t, "w": self.w, "h": self.cartouche_height + 2 * style["rect_margin_height"], "color": style["entity_cartouche_color"], "stroke_color": "none", "stroke_depth": 0, "opacity": 1, }, ) ) result.append( ( # lower part background (with or without a margin for the left gutter) "rect", { "x": self.l + self.id_gutter_width, "y": self.t + self.cartouche_height + 2 * style["rect_margin_height"], "w": self.w - self.id_gutter_width, "h": self.h - self.cartouche_height - 2 * style["rect_margin_height"], "color": style["entity_color"], "stroke_color": "none", "stroke_depth": 0, "opacity": 1, }, ) ) if self.show_id_gutter: result.append( ( # id_gutter background "rect", { "x": self.l, "y": self.t + self.cartouche_height + 2 * style["rect_margin_height"], "w": self.id_gutter_width, "h": self.h - self.cartouche_height - 2 * style["rect_margin_height"], "color": style["id_gutter_color"], "stroke_color": "none", "stroke_depth": 0, "opacity": 1, }, ) ) result.append( ( # line at the right of the left gutter "line", { "x0": self.l + self.id_gutter_width, "y0": self.t + self.cartouche_height + 2 * style["rect_margin_height"], "x1": self.l + self.id_gutter_width, "y1": self.b, "stroke_color": style["entity_stroke_color"], "stroke_depth": style["inner_stroke_depth"] / 4, }, ) ) result.append( ( # outer frame "rect", { "x": self.l, "y": self.t, "w": self.w, "h": self.h, "color": style["transparent_color"], "stroke_color": style["entity_stroke_color"], "stroke_depth": style["box_stroke_depth"], "opacity": 1, }, ) ) result.append( ( # line between upper and lower part "line", { "x0": self.l, "y0": self.t + self.cartouche_height + 2 * style["rect_margin_height"], "x1": self.r, "y1": self.t + self.cartouche_height + 2 * style["rect_margin_height"], "stroke_color": style["entity_stroke_color"], "stroke_depth": style["inner_stroke_depth"], }, ) ) result.append(("end", {})) result.append( ( # cartouche text "text", { "x": self.cx - self.get_cartouche_string_width(self.name_view) // 2, "y": self.t + style["rect_margin_height"] + style["cartouche_text_height_ratio"] * self.cartouche_height, "text_color": style["entity_cartouche_text_color"], "family": style["entity_cartouche_font"]["family"], "size": style["entity_cartouche_font"]["size"], "text": self.name_view, }, ) ) x = self.cx - self.w // 2 + style["rect_margin_width"] dx = self.id_gutter_width dy = self.cartouche_height + 3 * style["rect_margin_height"] - self.h // 2 for attribute in self.attributes: result.extend(attribute.description(style, x, self.cy, dx, dy)) dy += self.attribute_height + style["line_skip_height"] result.append(("end", {})) return result ================================================ FILE: mocodo/font_metrics.py ================================================ import json from pathlib import Path def font_metrics_factory(params): class FontMetrics: data = json.loads(Path(params["script_directory"]).joinpath("resources").joinpath("font_metrics.json").read_text(encoding="utf8")) def __init__(self, font): if font["family"] not in self.data["fonts"]: font["family"] = "Courier New" metrics = self.data["fonts"][font["family"]] self.font_height = int(round(metrics["height"] * font["size"] / self.data["size"])) alphabet = self.data["alphabet"] self.width = {c: ord(x) for (c, x) in zip(alphabet, metrics.get("widths", []))} self.default_width = metrics["default"] self.ratio = font["size"] * metrics.get("correction", 1) / self.data["size"] self.ratio *= params["adjust_width"] def get_pixel_height(self): return self.font_height def get_pixel_width(self, s): width = sum(self.width.get(c, self.default_width) for c in s) return int(round(self.ratio * width)) + 1 return FontMetrics ================================================ FILE: mocodo/grid.py ================================================ import itertools import math class Grid(list): def __init__(self, max_number_of_nodes=100, min_balance=0.5): """Create a list of couples of dimensions. The nth couple is the tightest balanced rectangle having at least n cells. A balanced rectangle satisfies: length / width > min_balance. [None, (1, 1), (2, 2), (2, 2), (2, 2), (3, 2), (3, 2), (3, 3), ...] """ list.__init__(self) max_square_side = int(math.ceil(math.sqrt(max_number_of_nodes))) for n in range(max_square_side * max_square_side, 0, -1): for i in range(n, int(math.ceil(math.sqrt(n))) - 1, -1): if n % i == 0 and n / i / i > min_balance: self.insert(0, (i, n // i)) break else: self.insert(0, self[0]) self.insert(0, None) self[2:4] = [(2, 1), (3, 1)] # tweak the 2- and 3-box grids def get_nth_next(self, index, nth): """Return the nth next distinct rectangle after the index-th one.""" acc = set() for i in itertools.count(index): acc.add(self[i]) if len(acc) > nth: return self[i] def get_markdown(self): """Return the grid as a nicely formatted table. For the docs only.""" (w, h) = map(max, zip(*self[1:])) result = \ [ [""] + list(map(lambda i: "**%s**" % i, range(1, w + 1))) ] + \ [["---"] * (w + 1)] + \ [ ["**%s**" % row] + [""] * w for row in range(1, h + 1) ] for (n, (col, row)) in enumerate(self[1:], 1): result[row + 1][col] += (", %s" % n if result[row + 1][col] else str(n)) return "\n".join(["| %s |" % " | ".join(row) for row in result]) if __name__ == "__main__": grid = Grid() print(grid.get_markdown()) ================================================ FILE: mocodo/guess_title.py ================================================ from collections import Counter import contextlib from importlib import import_module import re from pathlib import Path from .tools.parser_tools import parse_source from .tools.string_tools import rstrip_digit_or_underline BLACKLIST = { "fr": ["date", "calendrier", "période"], "en": ["date", "calendar", "period"] } def guess_title(source, language): """ Find the name of the most referenced entity in the MCD and pluralize it. """ blacklist = BLACKLIST.get(language, []) tree = parse_source(source) names = [] for node in tree.find_data("entity_name_ref"): name = rstrip_digit_or_underline(node.children[0].children[0]) if name.lower() not in blacklist: names.append(name) counter = Counter(names) if not counter: return "" title = counter.most_common(1)[0][0] title = re.sub(r"[^\w '\._-]", "-", title) with contextlib.suppress(ModuleNotFoundError): pluralize = import_module(f"tools.pluralize_{language}").pluralize title = " ".join(map(pluralize, title.split())) return title.capitalize() def may_update_params_with_guessed_title(source, params): if not params.get("guess_title"): return language = params["language"][:2] title = guess_title(source, language) if not title: return Path(f"{params['output_name']}_new_title.txt").write_text(title, encoding="utf8") params["title"] = title params["output_name"] = str(Path(params["output_dir"], title)) ================================================ FILE: mocodo/inheritance.py ================================================ from math import sqrt from .attribute import * from .leg import * from .tools.string_tools import rstrip_digit_or_underline, raw_to_bid TRIANGLE_ALTITUDE = sqrt(3) / 2 INCIRCLE_RADIUS = 1 / sqrt(12) class Inheritance: counter = 0 @classmethod def reset_counter(cls): cls.counter = 0 def __init__(self, clause, **params): self.source = clause["source"] self.raw_name = clause["name"] if clause["name"] != "TX" else "XT" Inheritance.counter += 1 parent_bid = raw_to_bid(clause["legs"][0]["entity"]) self.bid = f'{parent_bid}_PARENT_#{Inheritance.counter}' self.name_view = rstrip_digit_or_underline(clause["name"]) self.attributes = [InheritanceAttribute(attr) for attr in clause.get("attrs", [])] for leg_clause in clause["legs"]: leg_clause["kind"] = "-" leg_clause["arrow"] = False inheritance_arrow = clause["inheritance_arrow"] if inheritance_arrow == "<-": clause["legs"][0]["arrow"] = True elif inheritance_arrow == "->": for leg_clause in clause["legs"][1:]: leg_clause["arrow"] = True elif inheritance_arrow == "<=": clause["legs"][0]["arrow"] = True for leg_clause in clause["legs"][1:]: leg_clause["kind"] = "=" elif inheritance_arrow == "=>": clause["legs"][0]["kind"] = "=" for leg_clause in clause["legs"][1:]: leg_clause["arrow"] = True else: clause["legs"][0]["arrow"] = True self.legs = [InheritanceLeg(self, leg, **params) for leg in clause["legs"]] self.kind = inheritance_arrow.replace("--", "-").replace("==", "=") def register_boxes(self, boxes): self.boxes = boxes def calculate_size(self, style, get_font_metrics): cartouche_font = get_font_metrics(style["association_cartouche_font"]) self.get_cartouche_string_width = cartouche_font.get_pixel_width self.cartouche_height = cartouche_font.get_pixel_height() attribute_font = get_font_metrics(style["association_attribute_font"]) self.attribute_height = attribute_font.get_pixel_height() self.w = self.h = 2 * (style["round_rect_margin_width"] + self.cartouche_height) self.w += self.w % 2 self.h += self.h % 2 for leg in self.legs: leg.calculate_size(style, get_font_metrics) def register_center(self, geo): self.cx = geo["cx"][self.bid] self.cy = geo["cy"][self.bid] self.l = self.cx - self.w // 2 self.r = self.cx + self.w // 2 self.t = self.cy - self.h // 2 self.b = self.cy + self.h // 2 def description(self, style, geo): result = [] result.append(("comment", {"text": f"Inheritance {self.bid}"})) result.append( ( "begin_component", { "page": self.page, "visibility": "hidden" if self.page else "visible", } ) ) result.extend(self.leg_descriptions(style, geo)) result.append(("begin_group", {})) result.extend( [ ( "triangle", { "stroke_depth": style["box_stroke_depth"], "stroke_color": style['association_stroke_color'], "color": style['association_cartouche_color'], "x1": self.cx, "x2": self.l, "x3": self.r, "y1": self.cy - (TRIANGLE_ALTITUDE - INCIRCLE_RADIUS) * self.w, "y2": self.cy + INCIRCLE_RADIUS * self.w, "y3": self.cy + INCIRCLE_RADIUS * self.w, }, ), ( "text", { "text": self.name_view, "text_color": style['association_cartouche_text_color'], "x": self.cx - self.get_cartouche_string_width(self.name_view) // 2, "y": self.cy + self.cartouche_height // 3, "family": style["association_cartouche_font"]["family"], "size": style["association_cartouche_font"]["size"], }, ), ] ) result.append(("end", {})) result.append(("end", {})) return result def leg_descriptions(self, style, geo): result = [] for leg in self.legs: result.extend(leg.description(style, geo)) return result ================================================ FILE: mocodo/leg.py ================================================ import operator from math import hypot, sqrt from .mocodo_error import MocodoError from .tools.string_tools import surrounds, raw_to_bid class Leg: def __init__(self, association, leg_clause, **params): params["card_format"] = params.get("card_format", "{min_card},{max_card}") self.card = leg_clause.get("card", "XX") if self.card == "XX" or leg_clause.get("card_hidden") == "-": self.card_view = " " elif "X" in self.card: self.card_view = self.card.replace("X", "") else: self.card_view = params["card_format"].format(min_card=self.card[0], max_card=self.card[1]) self.has_underlined_card = False if leg_clause.get("card_prefix") == "_": if self.card != "11": # silently ignore the prefix self.kind = "leg" del leg_clause["card_prefix"] else: self.kind = "strengthening" self.card_view = params.get("strengthen_card", "_1,1_") if surrounds(self.card_view, "_"): self.has_underlined_card = True self.card_view = self.card_view[1:-1] elif association.kind == "cluster": self.kind = "cluster_peg" if leg_clause.get("card_prefix") == "/" else "cluster_leg" else: self.kind = "leg" self.arrow = leg_clause.get("leg_arrow", "") self.peg = ("o" if self.kind == "cluster_peg" and not self.arrow else "") self.note = leg_clause.get("leg_note") self.association = association self.entity_raw_name = leg_clause["entity"] self.entity_bid = raw_to_bid(self.entity_raw_name) self.twist = False self.lid = None self.unicities = "" self.is_in_elected_group = False def register_entity(self, entity): self.entity = entity def register_mcd_has_cif(self, mcd_has_cif): self.mcd_has_cif = mcd_has_cif if self.mcd_has_cif: self.peg = "" def append_candidate_group(self, candidate_number: str): if candidate_number == "0": self.is_in_elected_group = True else: # concatenate the candidate number to the beginning of the string to ensure sorting self.unicities = candidate_number + self.unicities def calculate_size(self, style, get_font_metrics): font = get_font_metrics(style["card_font"]) self.h = font.get_pixel_height() self.w = font.get_pixel_width(self.card_view) def set_spin_strategy(self, spin): self.spin = spin self.description = self._curved_description if spin else self._straight_description if self.lid is None: spin_str = str(round(spin, 2)).replace(".", "_") # avoid dot for the web version self.lid = f"{self.association.bid},{self.entity_bid},{spin_str}" def _straight_description(self, style, geo): result = [] ex = self.entity.cx ey = self.entity.cy ew = self.entity.w // 2 eh = self.entity.h // 2 ax = self.association.cx ay = self.association.cy aw = self.association.w // 2 ah = self.association.h // 2 card_margin = style["card_margin"] cw = self.w + 2 * card_margin ch = self.h + 2 * card_margin leg = straight_leg_factory(ex, ey, ew, eh, ax, ay, aw, ah, cw, ch, card_margin) if self.peg: (x, y, a, b) = leg.arrow_pos(self.arrow, 1) c = hypot(a, b) (cos, sin) = (a / c, b / c) result.append( ( "circle", { "cx": x + cos * style["arrow_width"] / 2, "cy": y - sin * style["arrow_width"] / 2, "r": style["arrow_width"] / 2, "stroke_color": style["entity_stroke_color"], "stroke_depth": style["leg_stroke_depth"], "color": style["entity_color"]+ "55", # alpha channel }, ), ) result.append( ( "line", { "x0": ex, "y0": ey, "x1": ax, "y1": ay, "stroke_color": style["leg_stroke_color"], "stroke_depth": style["leg_stroke_depth"], }, ) ) (x, y) = leg.card_pos(self.twist, geo["shift"][self.lid]) tx = x + card_margin ty = y - card_margin - style["card_baseline"] self.saved_card_description = [] if self.note: self.saved_card_description.append( ( "text_with_note", { "x": tx, "y": ty, "text_color": style["card_text_color"], "family": style["card_font"]["family"], "size": style["card_font"]["size"], "text": self.card_view, "note": self.note.lstrip("+").lstrip("-") }, ) ) else: self.saved_card_description.append( ( "text", { "x": tx, "y": ty, "text_color": style["card_text_color"], "family": style["card_font"]["family"], "size": style["card_font"]["size"], "text": self.card_view, }, ) ) if self.has_underlined_card: self.saved_card_description.append( ( "line", { "x0": tx, "y0": ty - style["card_underline_skip_height"], "x1": tx + self.w, "y1": ty - style["card_underline_skip_height"], "stroke_color": style["card_text_color"], "stroke_depth": style["card_underline_depth"], }, ) ) if self.arrow: (x, y, a, b) = leg.arrow_pos(self.arrow, geo["ratio"][self.lid]) c = hypot(a, b) (cos, sin) = (a / c, b / c) result.append( ( "arrow", { "x0": x, "y0": y, "x1": x + style["arrow_width"] * cos - style["arrow_half_height"] * sin, "y1": y - style["arrow_half_height"] * cos - style["arrow_width"] * sin, "x2": x + style["arrow_axis"] * cos, "y2": y - style["arrow_axis"] * sin, "x3": x + style["arrow_width"] * cos + style["arrow_half_height"] * sin, "y3": y + style["arrow_half_height"] * cos - style["arrow_width"] * sin, "stroke_color": style["leg_stroke_color"], }, ), ) return result def _curved_description(self, style, geo): result = [] ex = self.entity.cx ey = self.entity.cy ew = self.entity.w // 2 eh = self.entity.h // 2 ax = self.association.cx ay = self.association.cy aw = self.association.w // 2 ah = self.association.h // 2 card_margin = style["card_margin"] cw = self.w + 2 * card_margin ch = self.h + 2 * card_margin spin = self.spin leg = curved_leg_factory(ex, ey, ew, eh, ax, ay, aw, ah, cw, ch, card_margin, spin) (x0, y0, x1, y1, x2, y2, x3, y3) = leg.points if self.peg: (x, y, a, b) = leg.arrow_pos(self.arrow, 1) c = hypot(a, b) (cos, sin) = (a / c, b / c) result.append( ( "circle", { "cx": x + cos * style["arrow_width"] / 2, "cy": y - sin * style["arrow_width"] / 2, "r": style["arrow_width"] / 2, "stroke_color": style["entity_stroke_color"], "stroke_depth": style["leg_stroke_depth"], "color": style["entity_color"]+ "55", # alpha channel }, ), ) result.append( ( "curve", { "x0": x0, "y0": y0, "x1": x1, "y1": y1, "x2": x2, "y2": y2, "x3": x3, "y3": y3, "stroke_color": style["leg_stroke_color"], "stroke_depth": style["leg_stroke_depth"], }, ) ) (x, y) = leg.card_pos(geo["shift"][self.lid]) tx = x + card_margin ty = y - card_margin - style["card_baseline"] self.saved_card_description = [] if self.note: self.saved_card_description.append( ( "text_with_note", { "x": tx, "y": ty, "text_color": style["card_text_color"], "family": style["card_font"]["family"], "size": style["card_font"]["size"], "text": self.card_view, "note": self.note.lstrip("+").lstrip("-"), }, ) ) else: self.saved_card_description.append( ( "text", { "x": tx, "y": ty, "text_color": style["card_text_color"], "family": style["card_font"]["family"], "size": style["card_font"]["size"], "text": self.card_view, }, ) ) if self.has_underlined_card: self.saved_card_description.append( ( "line", { "x0": tx, "y0": ty - style["card_underline_skip_height"], "x1": tx + self.w, "y1": ty - style["card_underline_skip_height"], "stroke_color": style["card_text_color"], "stroke_depth": style["card_underline_depth"], }, ) ) if self.arrow: (x, y, a, b) = leg.arrow_pos(self.arrow, geo["ratio"][self.lid]) c = hypot(a, b) (cos, sin) = (a / c, b / c) result.append( ( "arrow", { "x0": x, "y0": y, "x1": x + style["arrow_width"] * cos - style["arrow_half_height"] * sin, "y1": y - style["arrow_half_height"] * cos - style["arrow_width"] * sin, "x2": x + style["arrow_axis"] * cos, "y2": y - style["arrow_axis"] * sin, "x3": x + style["arrow_width"] * cos + style["arrow_half_height"] * sin, "y3": y + style["arrow_half_height"] * cos - style["arrow_width"] * sin, "stroke_color": style["leg_stroke_color"], }, ), ) return result class InheritanceLeg: def __init__(self, inheritance, leg, **params): self.kind = leg["kind"] # "-" or "=" self.arrow = leg["arrow"] self.inheritance = inheritance self.entity_bid = raw_to_bid(leg["entity"]) self.entity_raw_name = leg["entity"] self.lid = f"{self.inheritance.bid} / {self.entity_bid}" def register_entity(self, entity): self.entity = entity def calculate_size(self, style, get_font_metrics): font = get_font_metrics(style["card_font"]) self.h = font.get_pixel_height() self.w = font.get_pixel_width("--") def description(self, style, geo): result = [] ex = self.entity.cx ey = self.entity.cy ew = self.entity.w // 2 eh = self.entity.h // 2 ax = self.inheritance.cx ay = self.inheritance.cy aw = self.inheritance.w // 2 ah = self.inheritance.h // 2 card_margin = style["card_margin"] cw = self.w + 2 * card_margin ch = self.h + 2 * card_margin leg = straight_leg_factory(ex, ey, ew, eh, ax, ay, aw, ah, cw, ch, card_margin) if self.kind == "=": for d in (style["leg_stroke_depth"], -style["leg_stroke_depth"]): (x0, y0, x1, y1) = orthogonal_translation(ex, ey, ax, ay, d) result.append( ( "line", { "x0": x0, "y0": y0, "x1": x1, "y1": y1, "stroke_color": style["leg_stroke_color"], "stroke_depth": style["leg_stroke_depth"], }, ) ) else: result.append( ( "line", { "x0": ex, "y0": ey, "x1": ax, "y1": ay, "stroke_color": style["leg_stroke_color"], "stroke_depth": style["leg_stroke_depth"], }, ) ) if self.arrow: (x, y, a, b) = leg.arrow_pos(self.arrow, geo["ratio"][self.lid]) c = hypot(a, b) (cos, sin) = (a / c, b / c) result.append( ( "arrow", { "x0": x, "y0": y, "x1": x + style["arrow_width"] * cos - style["arrow_half_height"] * sin, "y1": y - style["arrow_half_height"] * cos - style["arrow_width"] * sin, "x2": x + style["arrow_axis"] * cos, "y2": y - style["arrow_axis"] * sin, "x3": x + style["arrow_width"] * cos + style["arrow_half_height"] * sin, "y3": y + style["arrow_half_height"] * cos - style["arrow_width"] * sin, "stroke_color": style["leg_stroke_color"], }, ), ) return result class ConstraintLeg: def __init__(self, constraint, kind, box_raw_name): self.constraint = constraint self.kind = kind self.bid = raw_to_bid(box_raw_name) self.entity_raw_name = box_raw_name self.lid = None def register_box(self, box): self.box = box def description(self, style, geo): if self.kind == "": # a phantom leg, useful to tweak the barycenter return [] result = [] bx = self.box.cx by = self.box.cy bw = self.box.w // 2 bh = self.box.h // 2 cx = self.constraint.cx cy = self.constraint.cy cw = self.constraint.w // 2 ch = self.constraint.h // 2 leg = straight_leg_factory(bx, by, bw, bh, cx, cy, cw, ch) kind = self.kind for direction in "<>": if direction in self.kind: (x, y, a, b) = leg.arrow_pos(direction, 1) c = hypot(a, b) or 1 (cos, sin) = (a / c, b / c) result.append( ( "arrow", { "x0": x, "y0": y, "x1": x + style["arrow_width"] * cos - style["arrow_half_height"] * sin, "y1": y - style["arrow_half_height"] * cos - style["arrow_width"] * sin, "x2": x + style["arrow_axis"] * cos, "y2": y - style["arrow_axis"] * sin, "x3": x + style["arrow_width"] * cos + style["arrow_half_height"] * sin, "y3": y + style["arrow_half_height"] * cos - style["arrow_width"] * sin, "stroke_color": style["constraint_stroke_color"], }, ), ) kind = kind.replace(direction, "") if kind[:1] == "-": result.append( ( "line", { "x0": bx, "y0": by, "x1": cx, "y1": cy, "stroke_color": style["constraint_stroke_color"], "stroke_depth": style["constraint_stroke_depth"], } ) ) elif kind[:1] == ".": result.append( ( "dot_line", { "x0": bx, "y0": by, "x1": cx, "y1": cy, "stroke_color": style["constraint_stroke_color"], "stroke_depth": style["constraint_dot_stroke_depth"], "dash_gap": style["constraint_dot_gap_width"], } ) ) else: raise NotImplementedError return result def line_intersection(ex, ey, w, h, ax, ay, cmp=lambda x, y: (x > y) - (x < y)): if ax == ex: return (ax, ey + cmp(ay, ey) * h) if ay == ey: return (ex + cmp(ax, ex) * w, ay) x = ex + cmp(ax, ex) * w y = ey + (ay - ey) * (x - ex) / (ax - ex) if abs(y - ey) > h: y = ey + cmp(ay, ey) * h x = ex + (ax - ex) * (y - ey) / (ay - ey) return (x, y) def straight_leg_factory(ex, ey, ew, eh, ax, ay, aw, ah, cw=0, ch=0, card_margin=0): def card_pos(twist, shift): compare = operator.lt if twist else operator.le correction = 1 - abs(abs(ax - ex) - abs(ay - ey)) / hypot(ax - ex, ay - ey) correction = card_margin * 1.4142 * correction - shift (xg, yg) = line_intersection(ex, ey, ew, eh + ch, ax, ay) (xb, yb) = line_intersection(ex, ey, ew + cw, eh, ax, ay) if compare(xg, xb): if compare(xg, ex): if compare(yb, ey): return (xb - correction, yb) else: return (xb - correction, yb + ch) else: if compare(yb, ey): return (xg, yg + ch - correction) else: return (xg, yg + correction) else: if compare(xb, ex): if compare(yb, ey): return (xg - cw, yg + ch - correction) else: return (xg - cw, yg + correction) else: if compare(yb, ey): return (xb - cw + correction, yb) else: return (xb - cw + correction, yb + ch) def arrow_pos(direction, ratio): (x0, y0) = line_intersection(ex, ey, ew, eh, ax, ay) (x1, y1) = line_intersection(ax, ay, aw, ah, ex, ey) if direction == "<": (x0, y0, x1, y1) = (x1, y1, x0, y0) (x, y) = (ratio * x0 + (1 - ratio) * x1, ratio * y0 + (1 - ratio) * y1) return (x, y, x1 - x0, y0 - y1) straight_leg_factory.card_pos = card_pos straight_leg_factory.arrow_pos = arrow_pos return straight_leg_factory def curved_leg_factory(ex, ey, ew, eh, ax, ay, aw, ah, cw, ch, card_margin, spin): def bisection(predicate): (a, b) = (0, 1) while abs(b - a) > 0.0001: m = (a + b) / 2 if predicate(bezier(m)): a = m else: b = m return m def intersection(left, top, right, bottom): (x, y) = bezier(bisection(lambda p: left <= p[0] <= right and top <= p[1] <= bottom)) return (int(round(x)), int(round(y))) # avoid comparing floats def card_pos(shift): diagonal = hypot(ax - ex, ay - ey) correction = card_margin * 1.4142 * (1 - abs(abs(ax - ex) - abs(ay - ey)) / diagonal) (top, bot) = (ey - eh, ey + eh) (TOP, BOT) = (top - ch, bot + ch) (lef, rig) = (ex - ew, ex + ew) (LEF, RIG) = (lef - cw, rig + cw) (xr, yr) = intersection(LEF, TOP, RIG, BOT) (xg, yg) = intersection(lef, TOP, rig, BOT) (xb, yb) = intersection(LEF, top, RIG, bot) if spin > 0: if (yr == BOT and xr <= rig) or (xr == LEF and yr >= bot): return ( max(x for (x, y) in ((xr, yr), (xg, yg), (xb, yb)) if y >= bot) - correction + shift, bot + ch, ) if (xr == RIG and yr >= top) or yr == BOT: return ( rig, min(y for (x, y) in ((xr, yr), (xg, yg), (xb, yb)) if x >= rig) + correction + shift, ) if (yr == TOP and xr >= lef) or xr == RIG: return ( min(x for (x, y) in ((xr, yr), (xg, yg), (xb, yb)) if y <= top) + correction + shift - cw, TOP + ch, ) return ( LEF, max(y for (x, y) in ((xr, yr), (xg, yg), (xb, yb)) if x <= lef) - correction + shift + ch, ) if (yr == BOT and xr >= lef) or (xr == RIG and yr >= bot): return ( min(x for (x, y) in ((xr, yr), (xg, yg), (xb, yb)) if y >= bot) + correction + shift - cw, bot + ch, ) if xr == RIG or (yr == TOP and xr >= rig): return ( rig, max(y for (x, y) in ((xr, yr), (xg, yg), (xb, yb)) if x >= rig) - correction + shift + ch, ) if yr == TOP or (xr == LEF and yr <= top): return ( max(x for (x, y) in ((xr, yr), (xg, yg), (xb, yb)) if y <= top) - correction + shift, TOP + ch, ) return ( LEF, min(y for (x, y) in ((xr, yr), (xg, yg), (xb, yb)) if x <= lef) + correction + shift, ) def arrow_pos(direction, ratio): t0 = bisection(lambda p: abs(p[0] - ax) > aw or abs(p[1] - ay) > ah) t3 = bisection(lambda p: abs(p[0] - ex) < ew and abs(p[1] - ey) < eh) if direction == "<": (t0, t3) = (t3, t0) tc = t0 + (t3 - t0) * ratio (xc, yc) = bezier(tc) (x, y) = derivate(tc) if direction == "<": (x, y) = (-x, -y) return (xc, yc, x, -y) diagonal = hypot(ax - ex, ay - ey) (x, y) = line_intersection(ex, ey, ew + cw / 2, eh + ch / 2, ax, ay) k = cw * abs((ay - ey) / diagonal) + ch * abs((ax - ex) / diagonal) (x, y) = (x - spin * k * (ay - ey) / diagonal, y + spin * k * (ax - ex) / diagonal) (hx, hy) = (2 * x - (ex + ax) / 2, 2 * y - (ey + ay) / 2) (x1, y1) = (ex + (hx - ex) * 2 / 3, ey + (hy - ey) * 2 / 3) (x2, y2) = (ax + (hx - ax) * 2 / 3, ay + (hy - ay) * 2 / 3) (kax, kay) = (ex - 2 * hx + ax, ey - 2 * hy + ay) (kbx, kby) = (2 * hx - 2 * ex, 2 * hy - 2 * ey) bezier = lambda t: (kax * t * t + kbx * t + ex, kay * t * t + kby * t + ey) derivate = lambda t: (2 * kax * t + kbx, 2 * kay * t + kby) curved_leg_factory.points = (ex, ey, x1, y1, x2, y2, ax, ay) curved_leg_factory.card_pos = card_pos curved_leg_factory.arrow_pos = arrow_pos return curved_leg_factory def orthogonal_translation(x1, y1, x2, y2, d): if x1 == x2: return (x1+d, y1, x1+d, y2) slope = (y2 - y1) / (x2 - x1) dy = sqrt(d * d / (slope * slope + 1)) dx = -slope * dy if d > 0: return (x1+dx, y1+dy, x2+dx, y2+dy) else: return (x1-dx, y1-dy, x2-dx, y2-dy) ================================================ FILE: mocodo/magic.py ================================================ import argparse import contextlib import importlib import json import re import shlex import warnings from base64 import b64encode from itertools import takewhile from pathlib import Path from IPython import get_ipython from IPython.display import HTML, SVG, Code, Image, Markdown, display from .__main__ import Printer, Runner from .mocodo_error import MocodoError # ANSI color codes OK = "\033[92m" WARNING = "\033[1m\033[38;5;166m" FAIL = "\033[1m\033[91m" RESET = "\033[0m" IPYTHON = get_ipython() def update_cell(content): IPYTHON.set_next_input(content, replace=True) PARAM_TEMPLATE = '''\ # You may edit and run the following lines import json, pathlib params = """\\ {stdoutdata}""" try: json.loads(params) except: raise RuntimeError("Invalid JSON. Check your syntax on https://jsonlint.com.") pathlib.Path("{output_dir}/params.json").write_text(params, encoding="utf8");''' OUTPUT_PART_HEADER = re.sub(" +", "", """

{label}
""") def read_and_cleanup_text(path): text = path.read_text(encoding="utf8") text = re.sub(r"(?m).+Generated by Mocodo.+\n+", "", text) return text def display_converted_file(path, hide_header): if not (hide_header and path.name.endswith("_mld.md")): display(Markdown(OUTPUT_PART_HEADER.format(label=path.relative_to(Path.cwd())))) extension=path.suffix[1:] if extension == "svg": # Fix a maximum width for SVG images: # https://stackoverflow.com/questions/51452569/how-to-resize-rescale-a-svg-graphic-in-an-ipython-jupyter-notebook svg = b64encode(path.read_bytes()).decode("utf8") # Use Markdown instead of HTML to avoid a grey background. display(Markdown(f'')) elif extension == "md": display(Markdown(read_and_cleanup_text(path))) elif extension == "png": display(Image(filename=path, unconfined=False)) elif extension == "html": text = path.read_text(encoding="utf8") text = re.sub('.+\n', "", text) display(HTML(text)) elif extension == "tex": display(Code(filename=path, language="latex")) elif extension == "url": print(path.read_text(encoding="utf8")) # make the link clickable elif extension == "tsv": try: df = importlib.import_module("pandas").read_csv(path, sep="\t", header=0, index_col=False) df = df.style.set_properties(**{'text-align': 'left'}).set_table_styles([ dict(selector='th', props=[('text-align', 'left')] ) ]) display(df) except ImportError: display(Markdown(f'```\n{path.read_text(encoding="utf8")}\n```')) else: display(Markdown(f"```{extension}\n{read_and_cleanup_text(path)}\n```")) def mocodo(line="", source=""): """ Mocodo IPython magic extension Magic methods: %mocodo [command line options] %%mocodo [command line options] < MCD ... > Usage: %load_ext mocodo """ if source == "": # When this function is NOT invoked as a magic command, the first (and normally unique) # argument can consist of several lines, separated by "\n". The first line is the magic # command, the other ones (if any) are the source of the MCD. Redistribute the lines to # conform to the magic command's expected format. line = re.sub(r"^\s*%%?mocodo\b *", "", line) lines = line.split("\n") if len(lines) > 1: source = "\n".join(lines[1:]) line = lines[0] parser = argparse.ArgumentParser(add_help=False) parser.add_argument("--input", "-i") parser.add_argument("--output_dir") (args, remaining_args) = parser.parse_known_args(shlex.split(line)) remaining_args = list(takewhile(lambda x: not x.startswith("#"), remaining_args)) new_args = remaining_args[:] mocodo_notebook_dir = Path.cwd() if mocodo_notebook_dir.name != "mocodo_notebook": mocodo_notebook_dir = mocodo_notebook_dir / "mocodo_notebook" mocodo_notebook_dir.mkdir(parents=True, exist_ok=True) if not args.input: input_path = mocodo_notebook_dir / "sandbox.mcd" input_path.write_text(source, encoding="utf8") else: input_path = Path(args.input) if not input_path.suffix: input_path = input_path.with_suffix(".mcd") if not args.output_dir: output_dir = mocodo_notebook_dir else: output_dir = Path(args.output_dir) output_dir.mkdir(parents=True, exist_ok=True) output_path_radical = output_dir / input_path.stem remaining_args.extend([ "--input", str(input_path), "--output_dir", str(output_dir), "--is_magic", ]) # may override user's provided options stdoutdata = "" stderrdata = "" printer = Printer(quiet=True) try: run = Runner(remaining_args, printer) except SystemExit: # raised by argparse with certain arguments: --help, --version return try: stdoutdata = run() except MocodoError as err: stderrdata = str(err) if "--print_params" in remaining_args: update_cell(PARAM_TEMPLATE.format(stdoutdata=stdoutdata, output_dir=output_dir.relative_to(Path.cwd()))) return response_path = Path(f"{output_path_radical}_response_for_magic_command.json") try: response = json.loads(response_path.read_text(encoding="utf8")) except (json.decoder.JSONDecodeError, FileNotFoundError): response = {} finally: with contextlib.suppress(FileNotFoundError): # From Python 3.8, use the missing_ok argument response_path.unlink() rewritten_source = response.get("rewritten_source", "") redirect_output = response.get("redirect_output", False) converted_file_paths = response.get("converted_file_paths", []) if stderrdata: message = stderrdata if rewritten_source: message = f"{rewritten_source}\n\n{message}" warnings.formatwarning = lambda x, *args, **kargs : str(x) warnings.warn(message) return if rewritten_source and not rewritten_source.startswith("%%mocodo"): new_args = " ".join(filter(lambda x: x not in response["args_to_delete"], new_args)) new_args = response["opt_to_restore"] + new_args rewritten_source = f"%%mocodo{new_args}\n{rewritten_source}" for select in response.get("select", []): if select == "mcd": svg_path = output_path_radical.with_suffix(".svg") if svg_path.is_file() and input_path.stat().st_mtime <= svg_path.stat().st_mtime: display(SVG(filename=svg_path)) elif select == "rw": path = output_path_radical.with_suffix(".mcd") display(Markdown(OUTPUT_PART_HEADER.format(label=path.relative_to(output_dir)))) print(rewritten_source or source) elif select == "cv": for converted_file_path in converted_file_paths: display_converted_file(Path(converted_file_path), hide_header=response.get("mld")) if redirect_output: if rewritten_source: update_cell(rewritten_source) if converted_file_paths: # Copy the last converted file source to the clipboard with contextlib.suppress(ImportError): pyperclip = importlib.import_module("pyperclip") converted_file_path = Path(converted_file_paths[-1]) pyperclip.copy(converted_file_path.read_text(encoding="utf8")) print(f'⧉ {OK}The contents of "{converted_file_path.relative_to(output_dir)}" has been copied to the clipboard.{RESET}') ================================================ FILE: mocodo/mcd.py ================================================ import itertools from collections import defaultdict from hashlib import md5 import json from pathlib import Path from .association import Association from .attribute import Attribute from .constraint import Constraint from .diagram_link import DiagramLink from .entity import Entity from .grid import Grid from .inheritance import Inheritance from .mocodo_error import MocodoError from .phantom import Phantom from .tools.string_tools import raw_to_bid from .tools.parser_tools import extract_clauses def cmp(x, y): return (x > y) - (x < y) SYS_MAXINT = 9223372036854775807 # an integer larger than any practical list or string index class Mcd: def __init__(self, source, get_font_metrics=None, **params): def calculate_uid(): h = md5(source.encode("utf-8")).hexdigest() if params.get("uid_suffix"): return f"{h[:8]}_{params['uid_suffix']}" else: return h[:8] def create(): self.entities = {} self.associations = {} self.constraints = [] self.inheritances = [] mcd_has_cif = False self.commented_lines = [] self.constraint_clauses = [] seen = set() self.rows = [[]] pages = defaultdict(list) for clause in extract_clauses(source): indentation = clause.get("indent", "") if clause["type"] == "break": self.rows.append([]) continue if clause["type"] == "comment": if not self.rows[-1]: self.commented_lines.append(clause["text"]) continue if clause["type"] == "phantoms": phantoms = [Phantom() for _ in range(clause["count"])] if self.rows[-1]: self.rows[-1].extend(phantoms) else: self.rows.append(phantoms) continue if clause["type"] == "constraint": element = Constraint(clause) if element.name_view == "CIF": mcd_has_cif = True self.constraints.append(element) pages[indentation].append(element) self.constraint_clauses.append(clause) continue if clause["type"] == "inheritance": element = Inheritance(clause, **params) self.inheritances.append(element) pages[indentation].append(element) else: if clause["type"] == "association": element = Association(clause, **params) if element.bid in self.associations: raise MocodoError(7, _('Duplicate association "{name}". If you want to make two associations appear with the same name, you must suffix it with a number.').format(name=element.raw_name)) # fmt: skip self.associations[element.bid] = element pages[indentation].append(element) elif clause["type"] == "entity": element = Entity(clause) if element.bid in self.entities: raise MocodoError(6, _('Duplicate entity "{name}". If you want to make two entities appear with the same name, you must suffix it with a number.').format(name=element.raw_name)) # fmt: skip self.entities[element.bid] = element pages[indentation].append(element) else: raise NotImplementedError if element.bid in seen: raise MocodoError(8, _('One entity and one association share the same name "{name}".').format(name=element.raw_name)) # fmt: skip seen.add(element.bid) self.rows[-1].append(element) if not seen: raise MocodoError(4, _('The ERD "{title}" is empty.').format(title=params["title"])) # fmt: skip self.rows = [row for row in self.rows if row] self.col_count = max(len(row) for row in self.rows) self.row_count = len(self.rows) self.page_count = len(pages) for (i, (indentation, elements)) in enumerate(sorted(pages.items(), key=lambda x: x[0])): for element in elements: element.page = i self.header = "\n".join(self.commented_lines) + "\n\n" if self.commented_lines else "" for association in self.associations.values(): association.register_mcd_has_cif(mcd_has_cif) def add_legs(): for association in self.associations.values(): for leg in association.legs: if leg.entity_bid in self.entities: entity = self.entities[leg.entity_bid] elif leg.entity_bid in self.associations: raise MocodoError(18, _('Association "{association}" linked to another association "{entity}"!').format(association=association.bid, entity=leg.entity_bid)) # fmt: skip else: raise MocodoError(1, _('Association "{association}" linked to an unknown entity "{entity}"!').format(association=association.bid, entity=leg.entity_bid)) # fmt: skip leg.register_entity(entity) for inheritance in self.inheritances: for leg in inheritance.legs: if leg.entity_bid in self.entities: entity = self.entities[leg.entity_bid] elif leg.entity_bid in self.associations: raise MocodoError(44, _('Inheritance "{inheritance}" linked to an association "{entity}"!').format(inheritance=inheritance.bid, entity=leg.entity_bid)) else: raise MocodoError(42, _('Inheritance "{inheritance}" linked to an unknown entity "{entity}"!').format(inheritance=inheritance.bid, entity=leg.entity_bid)) leg.register_entity(entity) for constraint in self.constraints: for leg in constraint.legs: if leg.bid in self.associations: box = self.associations[leg.bid] elif leg.bid in self.entities: box = self.entities[leg.bid] else: raise MocodoError(40, _('Constraint "{constraint}" linked to an unknown entity or association "{box}"!').format(constraint=constraint.bid, box=leg.bid)) # fmt: skip leg.register_box(box) for coord in constraint.coords: if isinstance(coord, (float, int)): continue bid = raw_to_bid(coord) if bid in self.associations or bid in self.entities: continue raise MocodoError(43, _('Constraint "{constraint}" aligned with an unknown entity or association "{box}"!').format(constraint=constraint.bid, box=bid)) # fmt: skip def add_attributes(): strengthening_legs = dict((entity_bid, []) for entity_bid in self.entities) for association in self.associations.values(): for leg in association.legs: if leg.kind == "strengthening": strengthening_legs[leg.entity_bid].append(leg) children = set() for inheritance in self.inheritances: for leg in inheritance.legs[1:]: # the first leg is the parent children.add(leg.entity_bid) # the other legs are its children Attribute.id_gutter_strong_string = params["id_gutter_strong_string"] Attribute.id_gutter_weak_string = params["id_gutter_weak_string"] Attribute.id_gutter_alts = params["id_gutter_alts"] for (entity_bid, entity) in self.entities.items(): entity.add_attributes( legs_to_strengthen=strengthening_legs[entity_bid], is_child=entity_bid in children, fk_format=params.get("fk_format", "#{label}") ) self.has_alt_identifier = any(entity.has_alt_identifier for entity in self.entities.values()) def check_weak_entities_without_discriminator(): too_weak_entities = defaultdict(int) for association in self.associations.values(): for leg in association.legs: if leg.kind != "strengthening": continue for attribute in leg.entity.attributes: if attribute.kind == "weak": break else: # the weak entity has no discriminator. # Ensure the max cards of the other legs are 1 for other_leg in association.legs: if other_leg is leg: continue if other_leg.card[1] != "1": # Otherwise, accumulate them too_weak_entities[leg.entity.bid] += 1 for (too_weak_entity, count) in too_weak_entities.items(): if count < 2: # one isolated "too weak entity" raise MocodoError(50, _('The weak entity "{entity}" should have a discriminator.').format(entity=too_weak_entity)) # fmt: skip def set_id_gutter_visibility(): flag = params["id_gutter_visibility"] is_visible = flag == "on" or (flag == "auto" and self.has_alt_identifier) for entity in self.entities.values(): entity.set_id_gutter_visibility(is_visible) def tweak_straight_cards(): coordinates = {} for (j, row) in enumerate(self.rows): for (i, box) in enumerate(row): coordinates[box] = (i, j) d = defaultdict(list) tweakable_legs = {} for association in self.associations.values(): if association.is_invisible: continue for leg in association.legs: (ei, ej) = coordinates[leg.entity] (ai, aj) = coordinates[leg.association] vector = (cmp(ai, ei), cmp(aj, ej)) vector = (" SN"[cmp(aj, ej)] + " EW"[cmp(ai, ei)]).strip() d[leg.entity].append(vector) tweakable_legs[(leg.entity, vector)] = leg flex = params.get("flex", 0) for (entity, vectors) in d.items(): for vector in vectors: leg = tweakable_legs[(entity, vector)] if not leg.card_view.strip(): continue elif vector == "E": if vectors.count("E") == 1 and "SE" in vectors and "NE" not in vectors: leg.twist = True elif vector == "S": if vectors.count("S") == 1 and "SE" in vectors and "SW" not in vectors: leg.twist = True elif vector == "W": if vectors.count("W") == 1 and "SW" in vectors and "NW" not in vectors: leg.twist = True elif vector == "N": if vectors.count("N") == 1 and "NE" in vectors and "NW" not in vectors: leg.twist = True elif flex == 0: continue elif vector == "SE" and vectors.count("SE") == 1: if vectors.count("E") > 1: leg.set_spin_strategy(flex) elif vectors.count("S") > 1: leg.set_spin_strategy(-flex) elif vector == "SW" and vectors.count("SW") == 1: if vectors.count("S") > 1: leg.set_spin_strategy(flex) elif vectors.count("W") > 1: leg.set_spin_strategy(-flex) elif vector == "NW" and vectors.count("NW") == 1: if vectors.count("W") > 1: leg.set_spin_strategy(flex) elif vectors.count("N") > 1: leg.set_spin_strategy(-flex) elif vector == "NE" and vectors.count("NE") == 1: if vectors.count("N") > 1: leg.set_spin_strategy(flex) elif vectors.count("E") > 1: leg.set_spin_strategy(-flex) def add_diagram_links(): self.diagram_links = [] for entity in self.entities.values(): for attribute in entity.attributes: if attribute.primary_entity_bid: self.diagram_links.append(DiagramLink(self.entities, entity, attribute)) def may_center(): for row in self.rows: n = self.col_count - len(row) if n: row[0:0] = [Phantom() for i in range(n // 2)] row.extend(Phantom() for i in range(n // 2 + n % 2)) def make_boxes(): i = itertools.count() self.boxes = [] for row in self.rows: for box in row: box.index = next(i) self.boxes.append(box) box.register_boxes(self.boxes) self.box_count = len(self.boxes) # The following keys are actually created by __main__.py. # Using `get` instead of `[]` is for testing purposes only. params.setdefault("id_gutter_strong_string", "ID") params.setdefault("id_gutter_weak_string", "id") params.setdefault("id_gutter_alts", dict(zip("123456789", "123456789"))) params.setdefault("id_gutter_visibility", "auto") self.get_font_metrics = get_font_metrics Phantom.reset_counter() Association.reset_df_counter() Inheritance.reset_counter() Constraint.reset_counter() self.uid = calculate_uid() create() self.update_footer() add_legs() add_attributes() check_weak_entities_without_discriminator() set_id_gutter_visibility() add_diagram_links() may_center() make_boxes() tweak_straight_cards() self.title = params.get("title", "Untitled") def get_notes_data(self): notes_data = {} for association in self.associations.values(): components = association.get_note_data() for (lid, leg) in components.items(): leg["subject"] = self.entities[leg["subject"]].name_view leg["objects"] = [self.entities[bid].name_view for bid in leg["objects"]] note = leg.pop("note") leg["jsonl"] = json.dumps(leg, ensure_ascii=False) leg["tsv"] = "\t".join(str(leg[k]) for k in ["subject", "predicate", "min", "max", "objects"]) leg["note"] = note notes_data[lid] = leg return notes_data def update_notes_data(self, notes_data): for association in self.associations.values(): for leg in association.legs: if leg.lid in notes_data: leg.note = notes_data[leg.lid] def update_footer(self): constraint_sources = [constraint.source for constraint in self.constraints] self.footer = "\n\n" + "\n".join(constraint_sources) if constraint_sources else "" def get_layout_data(self): successors = [set() for i in range(self.box_count)] # use `set` to deduplicate reflexive associations multiplicity = defaultdict(int) # but count the multiplicity (1 or 2) of each link for inheritance in self.inheritances: for leg in inheritance.legs: successors[inheritance.index].add(leg.entity.index) successors[leg.entity.index].add(inheritance.index) multiplicity[(inheritance.index, leg.entity.index)] += 1 multiplicity[(leg.entity.index, inheritance.index)] += 1 if self.associations: for association in self.associations.values(): for leg in association.legs: successors[association.index].add(leg.entity.index) successors[leg.entity.index].add(association.index) multiplicity[(association.index, leg.entity.index)] += 1 multiplicity[(leg.entity.index, association.index)] += 1 else: for diagram_link in self.diagram_links: fei = diagram_link.foreign_entity.index pei = diagram_link.primary_entity.index if fei != pei: successors[fei].add(pei) successors[pei].add(fei) multiplicity[(fei, pei)] += 1 multiplicity[(pei, fei)] += 1 links = tuple((node, child) for (node, children) in enumerate(successors) for child in children if node < child) return { "links": links, "successors": successors, "col_count": self.col_count, "row_count": self.row_count, "multiplicity": dict(multiplicity) } def get_layout(self): return [box.index for row in self.rows for box in row] def get_row_text(self, row): return "\n".join(box.source for box in row) def get_non_phantom_count(self): return sum(box.kind != "phantom" for box in self.boxes) def set_layout(self, layout, col_count=None, row_count=None, **kwargs): if col_count and row_count: (self.col_count, self.row_count) = (col_count, row_count) def get_or_create_box(index): return Phantom() if layout[index] is None else self.boxes[layout[index]] i = itertools.count() self.rows = [[get_or_create_box(next(i)) for x in range(self.col_count)] for y in range(self.row_count)] def suppress_empty_rows(y): while self.rows: # there's at least one row for box in self.rows[y]: if box.kind != "phantom": return del self.rows[y] self.row_count -= 1 suppress_empty_rows(0) suppress_empty_rows(-1) def suppress_empty_cols(x): while self.rows[0]: # there's at least one column for row in self.rows: if row[x].kind != "phantom": return for row in self.rows: del row[x] self.col_count -= 1 suppress_empty_cols(0) suppress_empty_cols(-1) def get_clauses(self): result = self.header if self.associations: result += "\n\n".join(self.get_row_text(row) for row in self.rows) else: result += "\n\n".join(":\n" + "\n:\n".join(self.get_row_text(row).split("\n")) + "\n:" for row in self.rows) return result + self.footer def get_vertically_flipped_clauses(self): for constraint in self.constraints: constraint.invert_coords_horizontal_mirror() self.update_footer() return self.header + "\n\n".join(self.get_row_text(row) for row in self.rows[::-1]) + self.footer def get_horizontally_flipped_clauses(self): for constraint in self.constraints: constraint.invert_coords_vertical_mirror() self.update_footer() return self.header + "\n\n".join(self.get_row_text(row[::-1]) for row in self.rows) + self.footer def get_diagonally_flipped_clauses(self): for constraint in self.constraints: constraint.invert_coords_diagonal_mirror() self.update_footer() return self.header + "\n\n".join(self.get_row_text(row) for row in zip(*self.rows)) + self.footer def get_refitted_clauses(self, nth_fit_or_col_count, row_count=None): if row_count: col_count = nth_fit_or_col_count else: nth_fit = nth_fit_or_col_count grid = Grid(len(self.boxes) + 100) # make sure there are enough precalculated grids start = len(self.entities) + len(self.associations) # number of nonempty boxes (col_count, row_count) = grid.get_nth_next(start, nth_fit) result = [] i = 0 for box in self.boxes: if box.kind != "phantom": if i % col_count == 0 and i: result.append("") result.append(" " * box.page + box.source.rstrip()) i += 1 for i in range(i, col_count * row_count): if i % col_count == 0 and i: result.append("") result.append(":") return self.header + "\n".join(result) + self.footer def calculate_or_retrieve_geo(self, params): geo_path = Path(f"{params['output_name']}_geo.json") if geo_path.is_file() and params["scale"] == 1 and params["reuse_geo"]: try: web_geo = json.loads(geo_path.read_text(encoding="utf8")) except: raise MocodoError(33, _('Unable to reuse the geometry file "{filename}".').format(filename=geo_path)) # fmt: skip # convert lists of couples to dicts geo = {} for (k, v) in web_geo.items(): if isinstance(v, list): geo[k] = dict(v) else: geo[k] = v return geo geo = { "width": self.w, "height": self.h, "cx": { box.bid: box.x + box.w // 2 for row in self.rows for box in row if box.kind != "phantom" }, "cy": { box.bid: box.y + box.h // 2 for row in self.rows for box in row if box.kind != "phantom" }, "shift": { leg.lid: 0 for row in self.rows for box in row for leg in box.legs if hasattr(leg, "card_view")}, "ratio": { leg.lid: 1.0 for row in self.rows for box in row for leg in box.legs if leg.arrow }, } web_geo = {k: list(v.items()) if isinstance(v, dict) else v for (k, v) in geo.items()} text = json.dumps(web_geo, indent=2, ensure_ascii=False) text = text.replace("\n ", " ") text = text.replace("\n ]", " ]") text = text + "\n" try: geo_path.write_text(text, encoding="utf8") except IOError: raise MocodoError(34, _('Unable to save geometry file "{filename}".').format(filename=geo_path)) # fmt: skip return geo def calculate_size(self, style): def increase_margins_in_presence_of_clusters(): if not self.associations: # relational diagram or MCD without associations return factor = 1 + max(a.peg_count for a in self.associations.values()) style["margin"] *= factor style["card_margin"] *= factor def card_max_width(): get_pixel_width = self.get_font_metrics(style["card_font"]).get_pixel_width cardinalities = {"0,N"} # default value, in case there is no cardinalities at all for association in self.associations.values(): for leg in association.legs: cardinalities.add(leg.card_view.strip("_")) return max(map(get_pixel_width, cardinalities)) # def calculate_sizes(): for row in self.rows: for (i, box) in enumerate(row): box.calculate_size(style, self.get_font_metrics) max_box_width_per_column[i] = max(box.w, max_box_width_per_column[i]) for diagram_link in self.diagram_links: diagram_link.calculate_size(style, self.get_font_metrics) for constraint in self.constraints: constraint.calculate_size(style, self.get_font_metrics) # def make_horizontal_layout(): self.w = style["margin"] for row in self.rows: horizontal_offset = style["margin"] for (i, box) in enumerate(row): box.x = horizontal_offset + (max_box_width_per_column[i] - box.w) // 2 horizontal_offset += max_box_width_per_column[i] + join_width self.w = max(self.w, horizontal_offset) self.w += style["margin"] - join_width # def compress_horizontally(): dx = 0 for i in range(1, self.col_count): dx = SYS_MAXINT for row in self.rows: b1 = row[i-1] b2 = row[i] space = b2.x - b1.x - b1.w - join_width dx = min(dx, space) for row in self.rows: row[i].x -= dx self.w -= dx # def make_vertical_layout(): vertical_offset = style["margin"] for row in self.rows: max_box_height = max(box.h for box in row) for box in row: box.y = vertical_offset + (max_box_height - box.h) // 2 vertical_offset += max_box_height + join_height self.h = vertical_offset + style["margin"] - join_height # def compress_vertically(): dy = 0 for j in range(1, self.row_count): dy = SYS_MAXINT for (i2, b2) in enumerate(self.rows[j]): y1_max = 0 for (i1, b1) in enumerate(self.rows[j-1]): if (i1 == i2) or (b1.x < b2.x < b1.x + b1.w + join_width) or (b1.x - join_width < b2.x + b2.w < b1.x + b1.w): y1_max = max(y1_max, b1.y + b1.h) space = b2.y - y1_max - join_height dy = min(dy, space) for box in self.rows[j]: box.y -= dy self.h -= dy # increase_margins_in_presence_of_clusters() style["card_max_width"] = card_max_width() style["card_max_height"] = self.get_font_metrics(style["card_font"]).get_pixel_height() join_width = 2 * style["card_margin"] + style["card_max_width"] join_height = 2 * style["card_margin"] + style["card_max_height"] max_box_width_per_column = [0] * self.col_count calculate_sizes() make_horizontal_layout() compress_horizontally() make_vertical_layout() compress_vertically() def description(self, style, geo): for box in self.boxes: box.register_center(geo) for constraint in self.constraints: constraint.register_center(geo) result = [] for element in self.entities.values(): result.extend(element.description(style, geo)) for element in self.associations.values(): result.extend(element.description(style, geo)) for element in self.inheritances: result.extend(element.description(style, geo)) for element in self.diagram_links: result.extend(element.description(style, geo)) for element in self.constraints: result.extend(element.description(style, geo)) result.append(("comment", {"text": "Notes"})) result.append( ( "notes", { "height_threshold": geo["height"] - style["note_overlay_height"] - style["card_margin"], "overlay_height": style["note_overlay_height"], "x": geo["width"] // 2, "y_top": style["note_baseline"], "y_bottom": geo["height"] - style["note_overlay_height"] + style["note_baseline"], "y": geo["height"] - style["note_overlay_height"], "color": style["note_color"], "text_color": style["note_text_color"], "opacity": style["note_opacity"], "font_family": style["note_font"]["family"], "font_size": style["note_font"]["size"], } ) ) if self.page_count > 1: diameter = style["note_overlay_height"] / 4 result.append(("comment", {"text": "Pager"})) for i in range(self.page_count): result.append( ( "pager_dot", { "cx": geo["width"] / 2 - (self.page_count - 1 - 2 * i) * diameter, "cy": geo["height"] + 2 * diameter, "r": diameter / 2, "color": "lightgray" if i else "gray", "page": i, } ) ) result.append(("pager", {"page_count": self.page_count})) return result def get_overlaps(self): """ Detect the cases when two legs overlap each other or when a leg overlaps a box. The detection is based on the assumption that the boxes are arranged in a grid (and doesn't take into account any tweaks that may have been applied to the box centers). To unify the detection, the boxes are treated as zero-length legs. Only horizontal and vertical overlaps are detected. """ coordinates = {} for (j, row) in enumerate(self.rows): for (i, box) in enumerate(row): coordinates[box] = (i, j) segments = defaultdict(list) for entity in self.entities.values(): if entity.is_invisible: continue (ie, je) = coordinates[entity] segments["i", ie].append((je, je, entity, entity)) segments["j", je].append((ie, ie, entity, entity)) for association in self.associations.values(): if association.is_invisible: continue (ia, ja) = coordinates[association] segments[("i", ia)].append((ja, ja, association, association)) segments[("j", ja)].append((ia, ia, association, association)) for leg in association.legs: (ie, je) = coordinates[leg.entity] if ia == ie: segments[("i", ia)].append((*sorted([ja, je]), association, leg.entity)) elif ja == je: segments[("j", ja)].append((*sorted([ia, ie]), association, leg.entity)) result = [] for quadruples in segments.values(): quadruples.sort() for (l1, r1, e1, a1), (l2, r2, e2, a2) in zip(quadruples, quadruples[1:]): if e1.bid == e2.bid and a1.bid == a2.bid: # reflexive association continue if l2 < r1: result.append((e1.bid, a1.bid, e2.bid, a2.bid)) return result if __name__=="__main__": from .argument_parser import parsed_arguments source = """ CLIENT: Réf. client, Nom, Prénom, Adresse PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num commande, Date, Montant INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité PRODUIT: Réf. produit, Libellé, Prix unitaire """.replace(" ", "").split("\n") params = parsed_arguments() mcd = Mcd(source, **params) print(mcd.get_horizontally_flipped_clauses()) ================================================ FILE: mocodo/mcd_to_svg.py ================================================ from pathlib import Path import re from .mocodo_error import MocodoError from . import __version__ try: from cairosvg import svg2png, svg2pdf except: def svg2png(**kargs): raise MocodoError(13, _("PNG and PDF generation requires cairosvg to be installed")) # fmt: skip svg2pdf = svg2png def main(mcd, common, suppress_mocodo_header=False): mocodo_header = "" if suppress_mocodo_header else f"\n" style = common.load_style() mcd.calculate_size(style) geo = mcd.calculate_or_retrieve_geo(common.params) try: description = mcd.description(style, geo) except KeyError: # retry silently with a fresh geo file if the previous one concerns another MCD Path(f"{common.params['output_name']}_geo.json").unlink() geo = mcd.calculate_or_retrieve_geo(common.params) description = mcd.description(style, geo) description = [ ( "preamble", { "width": geo["width"], "height": geo["height"], "total_height": geo["height"] + (mcd.page_count > 1 and style["note_overlay_height"]), } ), ( "background", { "width": geo["width"], "height": geo["height"], "background_color": style["background_color"], } ), *description, ] has_note_card = False tabs = 0 categories = { # the order is that of the drawing in the SVG "": [], "Constraint": [], "Inheritance": [], "Association": [], "Entity": [], "Link": [], "Notes": [], "Pager": [], } category = "" for (key, mapping) in description: mapping["mcd_uid"] = mcd.uid has_note_card |= key.endswith("with_note") if key == "comment": category = mapping["text"].partition(" ")[0] for (k, v) in mapping.items(): if isinstance(v, float): mapping[k] = int(v) if v.is_integer() else round(v, 2) elif isinstance(v, str): mapping[k] = html_escape(v) elif v is None: mapping[k] = "none" mapping["mocodo_header"] = mocodo_header # print(key, mapping, end="\n") tabs -= (key == "end") categories[category].append('\t' * tabs + svg_elements[key].format_map(mapping)) tabs += key.startswith("begin") if not has_note_card: del categories["Notes"] text = "\n".join(sum(categories.values(), [])) + "\n" is_interactive = categories.get("Notes") or categories.get("Pager") if not is_interactive: text = re.sub(r'', "", text) path = Path(f"{common.params['output_name']}.svg") path.write_text(text, encoding="utf8") resulting_paths = [path] if categories.pop("Notes", []) + categories.pop("Pager"): # don't use `or` to avoid short-circuit text = "\n".join(sum(categories.values(), [])) + "\n" text = re.sub( r"(?m)^<\?xml .+\n", text) text = re.sub(r' (onmouseover|onmouseout|style|id)=".+?"', "", text) path = Path(f"{common.params['output_name']}_static.svg") path.write_text(text, encoding="utf8") resulting_paths.append(path) svg = bytes(text, "utf-8") for (format, function) in (("png", svg2png), ("pdf", svg2pdf)): if format in common.params["svg_to"]: path = Path(f"{common.params['output_name']}.{format}") function(bytestring=svg, write_to=str(path)) resulting_paths.append(path) return resulting_paths def html_escape( text, table={ "&": "&", ">": ">", "<": "<", '"': """, "'": "’", # neither ' nor ' make the job in notes "-": "-", # prevent double hyphen in SVG comments: https://stackoverflow.com/questions/10842131/xml-comments-and }): return "".join(table.get(c, c) for c in text) svg_elements = { "preamble": """\n{mocodo_header}""", "background": """""", "comment": """\n""", "begin_component": """""", "begin_group": """""", "end": """""", "text": """{text}""", "text_above_note": """{text}""", "text_with_note": """{text}""", "line": """""", "dash_line": """""", "dot_line": """""", "rect": """""", "polygon": """""", "dot_polygon": """""", "circle": """""", "circle_with_note": """""", "pager_dot": """""", "triangle": """""", "arrow": """""", "curve": """""", "round_rect": """""", "upper_round_rect": """""", "lower_round_rect": """""", "notes": """ """.replace(" ", ""), "pager": """""".replace(" ", ""), } if __name__ == "__main__": from .argument_parser import parsed_arguments from .common import Common from .mcd import Mcd clauses = """ CLIENT: Réf. client, Nom, Prénom, Adresse PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num commande, Date, Montant INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité PRODUIT: Réf. produit, Libellé, Prix unitaire """ params = parsed_arguments() common = Common(params) mcd = Mcd(clauses, **params) main(mcd, common) ================================================ FILE: mocodo/mocodo_error.py ================================================ import textwrap class MocodoError(Exception): def __init__(self, errno, message): message = f"Mocodo Err.{errno} - {message}" message = "\n".join( [ "\n".join( textwrap.wrap( line, 88, break_long_words=False, replace_whitespace=False, ) ) for line in message.splitlines() if line.strip() != "" ] ) self.errno = errno super(MocodoError, self).__init__(message) def subarg_error(subsubopt, subsubarg): return MocodoError(19, _('Invalid sub-argument: "{subsubopt}={subsubarg}".').format(subsubopt=subsubopt, subsubarg=subsubarg)) # fmt: skip def subsubopt_error(subsubopt): return MocodoError(20, _('Invalid sub-sub-option: "{subsubopt}".').format(subsubopt=subsubopt)) # fmt: skip def subopt_error(opt, subopt): raise MocodoError(21, _('Unknown "{opt}" sub-option: "{subopt}".').format(opt=opt, subopt=subopt)) # fmt: skip ================================================ FILE: mocodo/parse_mcd.py ================================================ # The file was automatically generated by Lark v1.1.7 __version__ = "1.1.7" # # # Lark Stand-alone Generator Tool # ---------------------------------- # Generates a stand-alone LALR(1) parser # # Git: https://github.com/erezsh/lark # Author: Erez Shinan (erezshin@gmail.com) # # # >>> LICENSE # # This tool and its generated code use a separate license from Lark, # and are subject to the terms of the Mozilla Public License, v. 2.0. # If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # # If you wish to purchase a commercial license for this tool and its # generated code, you may contact me via email or otherwise. # # If MPL2 is incompatible with your free or open-source project, # contact me and we'll work it out. # # from abc import ABC, abstractmethod from collections.abc import Sequence from types import ModuleType from typing import ( TypeVar, Generic, Type, Tuple, List, Dict, Iterator, Collection, Callable, Optional, FrozenSet, Any, Union, Iterable, IO, TYPE_CHECKING, overload, Pattern as REPattern, ClassVar, Set, Mapping ) class LarkError(Exception): pass class ConfigurationError(LarkError, ValueError): pass def assert_config(value, options: Collection, msg='Got %r, expected one of %s'): if value not in options: raise ConfigurationError(msg % (value, options)) class GrammarError(LarkError): pass class ParseError(LarkError): pass class LexError(LarkError): pass T = TypeVar('T') class UnexpectedInput(LarkError): #-- line: int column: int pos_in_stream = None state: Any _terminals_by_name = None def get_context(self, text: str, span: int=40) -> str: #-- assert self.pos_in_stream is not None, self pos = self.pos_in_stream start = max(pos - span, 0) end = pos + span if not isinstance(text, bytes): before = text[start:pos].rsplit('\n', 1)[-1] after = text[pos:end].split('\n', 1)[0] return before + after + '\n' + ' ' * len(before.expandtabs()) + '^\n' else: before = text[start:pos].rsplit(b'\n', 1)[-1] after = text[pos:end].split(b'\n', 1)[0] return (before + after + b'\n' + b' ' * len(before.expandtabs()) + b'^\n').decode("ascii", "backslashreplace") def match_examples(self, parse_fn: 'Callable[[str], Tree]', examples: Union[Mapping[T, Iterable[str]], Iterable[Tuple[T, Iterable[str]]]], token_type_match_fallback: bool=False, use_accepts: bool=True ) -> Optional[T]: #-- assert self.state is not None, "Not supported for this exception" if isinstance(examples, Mapping): examples = examples.items() candidate = (None, False) for i, (label, example) in enumerate(examples): assert not isinstance(example, str), "Expecting a list" for j, malformed in enumerate(example): try: parse_fn(malformed) except UnexpectedInput as ut: if ut.state == self.state: if ( use_accepts and isinstance(self, UnexpectedToken) and isinstance(ut, UnexpectedToken) and ut.accepts != self.accepts ): logger.debug("Different accepts with same state[%d]: %s != %s at example [%s][%s]" % (self.state, self.accepts, ut.accepts, i, j)) continue if ( isinstance(self, (UnexpectedToken, UnexpectedEOF)) and isinstance(ut, (UnexpectedToken, UnexpectedEOF)) ): if ut.token == self.token: ## logger.debug("Exact Match at example [%s][%s]" % (i, j)) return label if token_type_match_fallback: ## if (ut.token.type == self.token.type) and not candidate[-1]: logger.debug("Token Type Fallback at example [%s][%s]" % (i, j)) candidate = label, True if candidate[0] is None: logger.debug("Same State match at example [%s][%s]" % (i, j)) candidate = label, False return candidate[0] def _format_expected(self, expected): if self._terminals_by_name: d = self._terminals_by_name expected = [d[t_name].user_repr() if t_name in d else t_name for t_name in expected] return "Expected one of: \n\t* %s\n" % '\n\t* '.join(expected) class UnexpectedEOF(ParseError, UnexpectedInput): #-- expected: 'List[Token]' def __init__(self, expected, state=None, terminals_by_name=None): super(UnexpectedEOF, self).__init__() self.expected = expected self.state = state from .lexer import Token self.token = Token("", "") ## self.pos_in_stream = -1 self.line = -1 self.column = -1 self._terminals_by_name = terminals_by_name def __str__(self): message = "Unexpected end-of-input. " message += self._format_expected(self.expected) return message class UnexpectedCharacters(LexError, UnexpectedInput): #-- allowed: Set[str] considered_tokens: Set[Any] def __init__(self, seq, lex_pos, line, column, allowed=None, considered_tokens=None, state=None, token_history=None, terminals_by_name=None, considered_rules=None): super(UnexpectedCharacters, self).__init__() ## self.line = line self.column = column self.pos_in_stream = lex_pos self.state = state self._terminals_by_name = terminals_by_name self.allowed = allowed self.considered_tokens = considered_tokens self.considered_rules = considered_rules self.token_history = token_history if isinstance(seq, bytes): self.char = seq[lex_pos:lex_pos + 1].decode("ascii", "backslashreplace") else: self.char = seq[lex_pos] self._context = self.get_context(seq) def __str__(self): message = "No terminal matches '%s' in the current parser context, at line %d col %d" % (self.char, self.line, self.column) message += '\n\n' + self._context if self.allowed: message += self._format_expected(self.allowed) if self.token_history: message += '\nPrevious tokens: %s\n' % ', '.join(repr(t) for t in self.token_history) return message class UnexpectedToken(ParseError, UnexpectedInput): #-- expected: Set[str] considered_rules: Set[str] interactive_parser: 'InteractiveParser' def __init__(self, token, expected, considered_rules=None, state=None, interactive_parser=None, terminals_by_name=None, token_history=None): super(UnexpectedToken, self).__init__() ## self.line = getattr(token, 'line', '?') self.column = getattr(token, 'column', '?') self.pos_in_stream = getattr(token, 'start_pos', None) self.state = state self.token = token self.expected = expected ## self._accepts = NO_VALUE self.considered_rules = considered_rules self.interactive_parser = interactive_parser self._terminals_by_name = terminals_by_name self.token_history = token_history @property def accepts(self) -> Set[str]: if self._accepts is NO_VALUE: self._accepts = self.interactive_parser and self.interactive_parser.accepts() return self._accepts def __str__(self): message = ("Unexpected token %r at line %s, column %s.\n%s" % (self.token, self.line, self.column, self._format_expected(self.accepts or self.expected))) if self.token_history: message += "Previous tokens: %r\n" % self.token_history return message class VisitError(LarkError): #-- obj: 'Union[Tree, Token]' orig_exc: Exception def __init__(self, rule, obj, orig_exc): message = 'Error trying to process rule "%s":\n\n%s' % (rule, orig_exc) super(VisitError, self).__init__(message) self.rule = rule self.obj = obj self.orig_exc = orig_exc class MissingVariableError(LarkError): pass import sys, re import logging logger: logging.Logger = logging.getLogger("lark") logger.addHandler(logging.StreamHandler()) ## ## logger.setLevel(logging.CRITICAL) NO_VALUE = object() T = TypeVar("T") def classify(seq: Iterable, key: Optional[Callable] = None, value: Optional[Callable] = None) -> Dict: d: Dict[Any, Any] = {} for item in seq: k = key(item) if (key is not None) else item v = value(item) if (value is not None) else item try: d[k].append(v) except KeyError: d[k] = [v] return d def _deserialize(data: Any, namespace: Dict[str, Any], memo: Dict) -> Any: if isinstance(data, dict): if '__type__' in data: ## class_ = namespace[data['__type__']] return class_.deserialize(data, memo) elif '@' in data: return memo[data['@']] return {key:_deserialize(value, namespace, memo) for key, value in data.items()} elif isinstance(data, list): return [_deserialize(value, namespace, memo) for value in data] return data _T = TypeVar("_T", bound="Serialize") class Serialize: #-- def memo_serialize(self, types_to_memoize: List) -> Any: memo = SerializeMemoizer(types_to_memoize) return self.serialize(memo), memo.serialize() def serialize(self, memo = None) -> Dict[str, Any]: if memo and memo.in_types(self): return {'@': memo.memoized.get(self)} fields = getattr(self, '__serialize_fields__') res = {f: _serialize(getattr(self, f), memo) for f in fields} res['__type__'] = type(self).__name__ if hasattr(self, '_serialize'): self._serialize(res, memo) ## return res @classmethod def deserialize(cls: Type[_T], data: Dict[str, Any], memo: Dict[int, Any]) -> _T: namespace = getattr(cls, '__serialize_namespace__', []) namespace = {c.__name__:c for c in namespace} fields = getattr(cls, '__serialize_fields__') if '@' in data: return memo[data['@']] inst = cls.__new__(cls) for f in fields: try: setattr(inst, f, _deserialize(data[f], namespace, memo)) except KeyError as e: raise KeyError("Cannot find key for class", cls, e) if hasattr(inst, '_deserialize'): inst._deserialize() ## return inst class SerializeMemoizer(Serialize): #-- __serialize_fields__ = 'memoized', def __init__(self, types_to_memoize: List) -> None: self.types_to_memoize = tuple(types_to_memoize) self.memoized = Enumerator() def in_types(self, value: Serialize) -> bool: return isinstance(value, self.types_to_memoize) def serialize(self) -> Dict[int, Any]: ## return _serialize(self.memoized.reversed(), None) @classmethod def deserialize(cls, data: Dict[int, Any], namespace: Dict[str, Any], memo: Dict[Any, Any]) -> Dict[int, Any]: ## return _deserialize(data, namespace, memo) try: import regex _has_regex = True except ImportError: _has_regex = False if sys.version_info >= (3, 11): import re._parser as sre_parse import re._constants as sre_constants else: import sre_parse import sre_constants categ_pattern = re.compile(r'\\p{[A-Za-z_]+}') def get_regexp_width(expr: str) -> Union[Tuple[int, int], List[int]]: if _has_regex: ## ## ## regexp_final = re.sub(categ_pattern, 'A', expr) else: if re.search(categ_pattern, expr): raise ImportError('`regex` module must be installed in order to use Unicode categories.', expr) regexp_final = expr try: ## return [int(x) for x in sre_parse.parse(regexp_final).getwidth()] ## except sre_constants.error: if not _has_regex: raise ValueError(expr) else: ## ## c = regex.compile(regexp_final) if c.match('') is None: ## return 1, int(sre_constants.MAXREPEAT) else: return 0, int(sre_constants.MAXREPEAT) from collections import OrderedDict class Meta: empty: bool line: int column: int start_pos: int end_line: int end_column: int end_pos: int orig_expansion: 'List[TerminalDef]' match_tree: bool def __init__(self): self.empty = True _Leaf_T = TypeVar("_Leaf_T") Branch = Union[_Leaf_T, 'Tree[_Leaf_T]'] class Tree(Generic[_Leaf_T]): #-- data: str children: 'List[Branch[_Leaf_T]]' def __init__(self, data: str, children: 'List[Branch[_Leaf_T]]', meta: Optional[Meta]=None) -> None: self.data = data self.children = children self._meta = meta @property def meta(self) -> Meta: if self._meta is None: self._meta = Meta() return self._meta def __repr__(self): return 'Tree(%r, %r)' % (self.data, self.children) def _pretty_label(self): return self.data def _pretty(self, level, indent_str): yield f'{indent_str*level}{self._pretty_label()}' if len(self.children) == 1 and not isinstance(self.children[0], Tree): yield f'\t{self.children[0]}\n' else: yield '\n' for n in self.children: if isinstance(n, Tree): yield from n._pretty(level+1, indent_str) else: yield f'{indent_str*(level+1)}{n}\n' def pretty(self, indent_str: str=' ') -> str: #-- return ''.join(self._pretty(0, indent_str)) def __rich__(self, parent:Optional['rich.tree.Tree']=None) -> 'rich.tree.Tree': #-- return self._rich(parent) def _rich(self, parent): if parent: tree = parent.add(f'[bold]{self.data}[/bold]') else: import rich.tree tree = rich.tree.Tree(self.data) for c in self.children: if isinstance(c, Tree): c._rich(tree) else: tree.add(f'[green]{c}[/green]') return tree def __eq__(self, other): try: return self.data == other.data and self.children == other.children except AttributeError: return False def __ne__(self, other): return not (self == other) def __hash__(self) -> int: return hash((self.data, tuple(self.children))) def iter_subtrees(self) -> 'Iterator[Tree[_Leaf_T]]': #-- queue = [self] subtrees = OrderedDict() for subtree in queue: subtrees[id(subtree)] = subtree ## queue += [c for c in reversed(subtree.children) ## if isinstance(c, Tree) and id(c) not in subtrees] del queue return reversed(list(subtrees.values())) def iter_subtrees_topdown(self): #-- stack = [self] stack_append = stack.append stack_pop = stack.pop while stack: node = stack_pop() if not isinstance(node, Tree): continue yield node for child in reversed(node.children): stack_append(child) def find_pred(self, pred: 'Callable[[Tree[_Leaf_T]], bool]') -> 'Iterator[Tree[_Leaf_T]]': #-- return filter(pred, self.iter_subtrees()) def find_data(self, data: str) -> 'Iterator[Tree[_Leaf_T]]': #-- return self.find_pred(lambda t: t.data == data) from functools import wraps, update_wrapper from inspect import getmembers, getmro _Return_T = TypeVar('_Return_T') _Return_V = TypeVar('_Return_V') _Leaf_T = TypeVar('_Leaf_T') _Leaf_U = TypeVar('_Leaf_U') _R = TypeVar('_R') _FUNC = Callable[..., _Return_T] _DECORATED = Union[_FUNC, type] class _DiscardType: #-- def __repr__(self): return "lark.visitors.Discard" Discard = _DiscardType() ## class _Decoratable: #-- @classmethod def _apply_v_args(cls, visit_wrapper): mro = getmro(cls) assert mro[0] is cls libmembers = {name for _cls in mro[1:] for name, _ in getmembers(_cls)} for name, value in getmembers(cls): ## if name.startswith('_') or (name in libmembers and name not in cls.__dict__): continue if not callable(value): continue ## if isinstance(cls.__dict__[name], _VArgsWrapper): continue setattr(cls, name, _VArgsWrapper(cls.__dict__[name], visit_wrapper)) return cls def __class_getitem__(cls, _): return cls class Transformer(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]): #-- __visit_tokens__ = True ## def __init__(self, visit_tokens: bool=True) -> None: self.__visit_tokens__ = visit_tokens def _call_userfunc(self, tree, new_children=None): ## children = new_children if new_children is not None else tree.children try: f = getattr(self, tree.data) except AttributeError: return self.__default__(tree.data, children, tree.meta) else: try: wrapper = getattr(f, 'visit_wrapper', None) if wrapper is not None: return f.visit_wrapper(f, tree.data, children, tree.meta) else: return f(children) except GrammarError: raise except Exception as e: raise VisitError(tree.data, tree, e) def _call_userfunc_token(self, token): try: f = getattr(self, token.type) except AttributeError: return self.__default_token__(token) else: try: return f(token) except GrammarError: raise except Exception as e: raise VisitError(token.type, token, e) def _transform_children(self, children): for c in children: if isinstance(c, Tree): res = self._transform_tree(c) elif self.__visit_tokens__ and isinstance(c, Token): res = self._call_userfunc_token(c) else: res = c if res is not Discard: yield res def _transform_tree(self, tree): children = list(self._transform_children(tree.children)) return self._call_userfunc(tree, children) def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: #-- return self._transform_tree(tree) def __mul__( self: 'Transformer[_Leaf_T, Tree[_Leaf_U]]', other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V,]]' ) -> 'TransformerChain[_Leaf_T, _Return_V]': #-- return TransformerChain(self, other) def __default__(self, data, children, meta): #-- return Tree(data, children, meta) def __default_token__(self, token): #-- return token def merge_transformers(base_transformer=None, **transformers_to_merge): #-- if base_transformer is None: base_transformer = Transformer() for prefix, transformer in transformers_to_merge.items(): for method_name in dir(transformer): method = getattr(transformer, method_name) if not callable(method): continue if method_name.startswith("_") or method_name == "transform": continue prefixed_method = prefix + "__" + method_name if hasattr(base_transformer, prefixed_method): raise AttributeError("Cannot merge: method '%s' appears more than once" % prefixed_method) setattr(base_transformer, prefixed_method, method) return base_transformer class InlineTransformer(Transformer): ## def _call_userfunc(self, tree, new_children=None): ## children = new_children if new_children is not None else tree.children try: f = getattr(self, tree.data) except AttributeError: return self.__default__(tree.data, children, tree.meta) else: return f(*children) class TransformerChain(Generic[_Leaf_T, _Return_T]): transformers: 'Tuple[Union[Transformer, TransformerChain], ...]' def __init__(self, *transformers: 'Union[Transformer, TransformerChain]') -> None: self.transformers = transformers def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: for t in self.transformers: tree = t.transform(tree) return cast(_Return_T, tree) def __mul__( self: 'TransformerChain[_Leaf_T, Tree[_Leaf_U]]', other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V]]' ) -> 'TransformerChain[_Leaf_T, _Return_V]': return TransformerChain(*self.transformers + (other,)) class Transformer_InPlace(Transformer): #-- def _transform_tree(self, tree): ## return self._call_userfunc(tree) def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: for subtree in tree.iter_subtrees(): subtree.children = list(self._transform_children(subtree.children)) return self._transform_tree(tree) class Transformer_NonRecursive(Transformer): #-- def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: ## rev_postfix = [] q: List[Branch[_Leaf_T]] = [tree] while q: t = q.pop() rev_postfix.append(t) if isinstance(t, Tree): q += t.children ## stack: List = [] for x in reversed(rev_postfix): if isinstance(x, Tree): size = len(x.children) if size: args = stack[-size:] del stack[-size:] else: args = [] res = self._call_userfunc(x, args) if res is not Discard: stack.append(res) elif self.__visit_tokens__ and isinstance(x, Token): res = self._call_userfunc_token(x) if res is not Discard: stack.append(res) else: stack.append(x) result, = stack ## ## ## ## return cast(_Return_T, result) class Transformer_InPlaceRecursive(Transformer): #-- def _transform_tree(self, tree): tree.children = list(self._transform_children(tree.children)) return self._call_userfunc(tree) ## class VisitorBase: def _call_userfunc(self, tree): return getattr(self, tree.data, self.__default__)(tree) def __default__(self, tree): #-- return tree def __class_getitem__(cls, _): return cls class Visitor(VisitorBase, ABC, Generic[_Leaf_T]): #-- def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: #-- for subtree in tree.iter_subtrees(): self._call_userfunc(subtree) return tree def visit_topdown(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: #-- for subtree in tree.iter_subtrees_topdown(): self._call_userfunc(subtree) return tree class Visitor_Recursive(VisitorBase, Generic[_Leaf_T]): #-- def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: #-- for child in tree.children: if isinstance(child, Tree): self.visit(child) self._call_userfunc(tree) return tree def visit_topdown(self,tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: #-- self._call_userfunc(tree) for child in tree.children: if isinstance(child, Tree): self.visit_topdown(child) return tree class Interpreter(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]): #-- def visit(self, tree: Tree[_Leaf_T]) -> _Return_T: ## ## ## return self._visit_tree(tree) def _visit_tree(self, tree: Tree[_Leaf_T]): f = getattr(self, tree.data) wrapper = getattr(f, 'visit_wrapper', None) if wrapper is not None: return f.visit_wrapper(f, tree.data, tree.children, tree.meta) else: return f(tree) def visit_children(self, tree: Tree[_Leaf_T]) -> List: return [self._visit_tree(child) if isinstance(child, Tree) else child for child in tree.children] def __getattr__(self, name): return self.__default__ def __default__(self, tree): return self.visit_children(tree) _InterMethod = Callable[[Type[Interpreter], _Return_T], _R] def visit_children_decor(func: _InterMethod) -> _InterMethod: #-- @wraps(func) def inner(cls, tree): values = cls.visit_children(tree) return func(cls, values) return inner ## def _apply_v_args(obj, visit_wrapper): try: _apply = obj._apply_v_args except AttributeError: return _VArgsWrapper(obj, visit_wrapper) else: return _apply(visit_wrapper) class _VArgsWrapper: #-- base_func: Callable def __init__(self, func: Callable, visit_wrapper: Callable[[Callable, str, list, Any], Any]): if isinstance(func, _VArgsWrapper): func = func.base_func ## self.base_func = func ## self.visit_wrapper = visit_wrapper update_wrapper(self, func) def __call__(self, *args, **kwargs): return self.base_func(*args, **kwargs) def __get__(self, instance, owner=None): try: ## ## g = type(self.base_func).__get__ except AttributeError: return self else: return _VArgsWrapper(g(self.base_func, instance, owner), self.visit_wrapper) def __set_name__(self, owner, name): try: f = type(self.base_func).__set_name__ except AttributeError: return else: f(self.base_func, owner, name) def _vargs_inline(f, _data, children, _meta): return f(*children) def _vargs_meta_inline(f, _data, children, meta): return f(meta, *children) def _vargs_meta(f, _data, children, meta): return f(meta, children) def _vargs_tree(f, data, children, meta): return f(Tree(data, children, meta)) def v_args(inline: bool = False, meta: bool = False, tree: bool = False, wrapper: Optional[Callable] = None) -> Callable[[_DECORATED], _DECORATED]: #-- if tree and (meta or inline): raise ValueError("Visitor functions cannot combine 'tree' with 'meta' or 'inline'.") func = None if meta: if inline: func = _vargs_meta_inline else: func = _vargs_meta elif inline: func = _vargs_inline elif tree: func = _vargs_tree if wrapper is not None: if func is not None: raise ValueError("Cannot use 'wrapper' along with 'tree', 'meta' or 'inline'.") func = wrapper def _visitor_args_dec(obj): return _apply_v_args(obj, func) return _visitor_args_dec TOKEN_DEFAULT_PRIORITY = 0 class Symbol(Serialize): __slots__ = ('name',) name: str is_term: ClassVar[bool] = NotImplemented def __init__(self, name: str) -> None: self.name = name def __eq__(self, other): assert isinstance(other, Symbol), other return self.is_term == other.is_term and self.name == other.name def __ne__(self, other): return not (self == other) def __hash__(self): return hash(self.name) def __repr__(self): return '%s(%r)' % (type(self).__name__, self.name) fullrepr = property(__repr__) def renamed(self, f): return type(self)(f(self.name)) class Terminal(Symbol): __serialize_fields__ = 'name', 'filter_out' is_term: ClassVar[bool] = True def __init__(self, name, filter_out=False): self.name = name self.filter_out = filter_out @property def fullrepr(self): return '%s(%r, %r)' % (type(self).__name__, self.name, self.filter_out) def renamed(self, f): return type(self)(f(self.name), self.filter_out) class NonTerminal(Symbol): __serialize_fields__ = 'name', is_term: ClassVar[bool] = False class RuleOptions(Serialize): __serialize_fields__ = 'keep_all_tokens', 'expand1', 'priority', 'template_source', 'empty_indices' keep_all_tokens: bool expand1: bool priority: Optional[int] template_source: Optional[str] empty_indices: Tuple[bool, ...] def __init__(self, keep_all_tokens: bool=False, expand1: bool=False, priority: Optional[int]=None, template_source: Optional[str]=None, empty_indices: Tuple[bool, ...]=()) -> None: self.keep_all_tokens = keep_all_tokens self.expand1 = expand1 self.priority = priority self.template_source = template_source self.empty_indices = empty_indices def __repr__(self): return 'RuleOptions(%r, %r, %r, %r)' % ( self.keep_all_tokens, self.expand1, self.priority, self.template_source ) class Rule(Serialize): #-- __slots__ = ('origin', 'expansion', 'alias', 'options', 'order', '_hash') __serialize_fields__ = 'origin', 'expansion', 'order', 'alias', 'options' __serialize_namespace__ = Terminal, NonTerminal, RuleOptions def __init__(self, origin, expansion, order=0, alias=None, options=None): self.origin = origin self.expansion = expansion self.alias = alias self.order = order self.options = options or RuleOptions() self._hash = hash((self.origin, tuple(self.expansion))) def _deserialize(self): self._hash = hash((self.origin, tuple(self.expansion))) def __str__(self): return '<%s : %s>' % (self.origin.name, ' '.join(x.name for x in self.expansion)) def __repr__(self): return 'Rule(%r, %r, %r, %r)' % (self.origin, self.expansion, self.alias, self.options) def __hash__(self): return self._hash def __eq__(self, other): if not isinstance(other, Rule): return False return self.origin == other.origin and self.expansion == other.expansion from copy import copy try: ## has_interegular = bool(interegular) except NameError: has_interegular = False class Pattern(Serialize, ABC): value: str flags: Collection[str] raw: Optional[str] type: ClassVar[str] def __init__(self, value: str, flags: Collection[str] = (), raw: Optional[str] = None) -> None: self.value = value self.flags = frozenset(flags) self.raw = raw def __repr__(self): return repr(self.to_regexp()) ## def __hash__(self): return hash((type(self), self.value, self.flags)) def __eq__(self, other): return type(self) == type(other) and self.value == other.value and self.flags == other.flags @abstractmethod def to_regexp(self) -> str: raise NotImplementedError() @property @abstractmethod def min_width(self) -> int: raise NotImplementedError() @property @abstractmethod def max_width(self) -> int: raise NotImplementedError() def _get_flags(self, value): for f in self.flags: value = ('(?%s:%s)' % (f, value)) return value class PatternStr(Pattern): __serialize_fields__ = 'value', 'flags', 'raw' type: ClassVar[str] = "str" def to_regexp(self) -> str: return self._get_flags(re.escape(self.value)) @property def min_width(self) -> int: return len(self.value) @property def max_width(self) -> int: return len(self.value) class PatternRE(Pattern): __serialize_fields__ = 'value', 'flags', 'raw', '_width' type: ClassVar[str] = "re" def to_regexp(self) -> str: return self._get_flags(self.value) _width = None def _get_width(self): if self._width is None: self._width = get_regexp_width(self.to_regexp()) return self._width @property def min_width(self) -> int: return self._get_width()[0] @property def max_width(self) -> int: return self._get_width()[1] class TerminalDef(Serialize): __serialize_fields__ = 'name', 'pattern', 'priority' __serialize_namespace__ = PatternStr, PatternRE name: str pattern: Pattern priority: int def __init__(self, name: str, pattern: Pattern, priority: int = TOKEN_DEFAULT_PRIORITY) -> None: assert isinstance(pattern, Pattern), pattern self.name = name self.pattern = pattern self.priority = priority def __repr__(self): return '%s(%r, %r)' % (type(self).__name__, self.name, self.pattern) def user_repr(self) -> str: if self.name.startswith('__'): ## return self.pattern.raw or self.name else: return self.name _T = TypeVar('_T', bound="Token") class Token(str): #-- __slots__ = ('type', 'start_pos', 'value', 'line', 'column', 'end_line', 'end_column', 'end_pos') __match_args__ = ('type', 'value') type: str start_pos: Optional[int] value: Any line: Optional[int] column: Optional[int] end_line: Optional[int] end_column: Optional[int] end_pos: Optional[int] @overload def __new__( cls, type: str, value: Any, start_pos: Optional[int] = None, line: Optional[int] = None, column: Optional[int] = None, end_line: Optional[int] = None, end_column: Optional[int] = None, end_pos: Optional[int] = None ) -> 'Token': ... @overload def __new__( cls, type_: str, value: Any, start_pos: Optional[int] = None, line: Optional[int] = None, column: Optional[int] = None, end_line: Optional[int] = None, end_column: Optional[int] = None, end_pos: Optional[int] = None ) -> 'Token': ... def __new__(cls, *args, **kwargs): if "type_" in kwargs: warnings.warn("`type_` is deprecated use `type` instead", DeprecationWarning) if "type" in kwargs: raise TypeError("Error: using both 'type' and the deprecated 'type_' as arguments.") kwargs["type"] = kwargs.pop("type_") return cls._future_new(*args, **kwargs) @classmethod def _future_new(cls, type, value, start_pos=None, line=None, column=None, end_line=None, end_column=None, end_pos=None): inst = super(Token, cls).__new__(cls, value) inst.type = type inst.start_pos = start_pos inst.value = value inst.line = line inst.column = column inst.end_line = end_line inst.end_column = end_column inst.end_pos = end_pos return inst @overload def update(self, type: Optional[str] = None, value: Optional[Any] = None) -> 'Token': ... @overload def update(self, type_: Optional[str] = None, value: Optional[Any] = None) -> 'Token': ... def update(self, *args, **kwargs): if "type_" in kwargs: warnings.warn("`type_` is deprecated use `type` instead", DeprecationWarning) if "type" in kwargs: raise TypeError("Error: using both 'type' and the deprecated 'type_' as arguments.") kwargs["type"] = kwargs.pop("type_") return self._future_update(*args, **kwargs) def _future_update(self, type: Optional[str] = None, value: Optional[Any] = None) -> 'Token': return Token.new_borrow_pos( type if type is not None else self.type, value if value is not None else self.value, self ) @classmethod def new_borrow_pos(cls: Type[_T], type_: str, value: Any, borrow_t: 'Token') -> _T: return cls(type_, value, borrow_t.start_pos, borrow_t.line, borrow_t.column, borrow_t.end_line, borrow_t.end_column, borrow_t.end_pos) def __reduce__(self): return (self.__class__, (self.type, self.value, self.start_pos, self.line, self.column)) def __repr__(self): return 'Token(%r, %r)' % (self.type, self.value) def __deepcopy__(self, memo): return Token(self.type, self.value, self.start_pos, self.line, self.column) def __eq__(self, other): if isinstance(other, Token) and self.type != other.type: return False return str.__eq__(self, other) __hash__ = str.__hash__ class LineCounter: __slots__ = 'char_pos', 'line', 'column', 'line_start_pos', 'newline_char' def __init__(self, newline_char): self.newline_char = newline_char self.char_pos = 0 self.line = 1 self.column = 1 self.line_start_pos = 0 def __eq__(self, other): if not isinstance(other, LineCounter): return NotImplemented return self.char_pos == other.char_pos and self.newline_char == other.newline_char def feed(self, token: Token, test_newline=True): #-- if test_newline: newlines = token.count(self.newline_char) if newlines: self.line += newlines self.line_start_pos = self.char_pos + token.rindex(self.newline_char) + 1 self.char_pos += len(token) self.column = self.char_pos - self.line_start_pos + 1 class UnlessCallback: def __init__(self, scanner): self.scanner = scanner def __call__(self, t): res = self.scanner.match(t.value, 0) if res: _value, t.type = res return t class CallChain: def __init__(self, callback1, callback2, cond): self.callback1 = callback1 self.callback2 = callback2 self.cond = cond def __call__(self, t): t2 = self.callback1(t) return self.callback2(t) if self.cond(t2) else t2 def _get_match(re_, regexp, s, flags): m = re_.match(regexp, s, flags) if m: return m.group(0) def _create_unless(terminals, g_regex_flags, re_, use_bytes): tokens_by_type = classify(terminals, lambda t: type(t.pattern)) assert len(tokens_by_type) <= 2, tokens_by_type.keys() embedded_strs = set() callback = {} for retok in tokens_by_type.get(PatternRE, []): unless = [] for strtok in tokens_by_type.get(PatternStr, []): if strtok.priority != retok.priority: continue s = strtok.pattern.value if s == _get_match(re_, retok.pattern.to_regexp(), s, g_regex_flags): unless.append(strtok) if strtok.pattern.flags <= retok.pattern.flags: embedded_strs.add(strtok) if unless: callback[retok.name] = UnlessCallback(Scanner(unless, g_regex_flags, re_, match_whole=True, use_bytes=use_bytes)) new_terminals = [t for t in terminals if t not in embedded_strs] return new_terminals, callback class Scanner: def __init__(self, terminals, g_regex_flags, re_, use_bytes, match_whole=False): self.terminals = terminals self.g_regex_flags = g_regex_flags self.re_ = re_ self.use_bytes = use_bytes self.match_whole = match_whole self.allowed_types = {t.name for t in self.terminals} self._mres = self._build_mres(terminals, len(terminals)) def _build_mres(self, terminals, max_size): ## ## ## postfix = '$' if self.match_whole else '' mres = [] while terminals: pattern = u'|'.join(u'(?P<%s>%s)' % (t.name, t.pattern.to_regexp() + postfix) for t in terminals[:max_size]) if self.use_bytes: pattern = pattern.encode('latin-1') try: mre = self.re_.compile(pattern, self.g_regex_flags) except AssertionError: ## return self._build_mres(terminals, max_size // 2) mres.append(mre) terminals = terminals[max_size:] return mres def match(self, text, pos): for mre in self._mres: m = mre.match(text, pos) if m: return m.group(0), m.lastgroup def _regexp_has_newline(r: str): #-- return '\n' in r or '\\n' in r or '\\s' in r or '[^' in r or ('(?s' in r and '.' in r) class LexerState: #-- __slots__ = 'text', 'line_ctr', 'last_token' text: str line_ctr: LineCounter last_token: Optional[Token] def __init__(self, text: str, line_ctr: Optional[LineCounter]=None, last_token: Optional[Token]=None): self.text = text self.line_ctr = line_ctr or LineCounter(b'\n' if isinstance(text, bytes) else '\n') self.last_token = last_token def __eq__(self, other): if not isinstance(other, LexerState): return NotImplemented return self.text is other.text and self.line_ctr == other.line_ctr and self.last_token == other.last_token def __copy__(self): return type(self)(self.text, copy(self.line_ctr), self.last_token) class LexerThread: #-- def __init__(self, lexer: 'Lexer', lexer_state: LexerState): self.lexer = lexer self.state = lexer_state @classmethod def from_text(cls, lexer: 'Lexer', text: str): return cls(lexer, LexerState(text)) def lex(self, parser_state): return self.lexer.lex(self.state, parser_state) def __copy__(self): return type(self)(self.lexer, copy(self.state)) _Token = Token _Callback = Callable[[Token], Token] class Lexer(ABC): #-- @abstractmethod def lex(self, lexer_state: LexerState, parser_state: Any) -> Iterator[Token]: return NotImplemented def make_lexer_state(self, text): #-- return LexerState(text) def _check_regex_collisions(terminal_to_regexp: Dict[TerminalDef, str], comparator, strict_mode, max_collisions_to_show=8): if not comparator: comparator = interegular.Comparator.from_regexes(terminal_to_regexp) ## ## max_time = 2 if strict_mode else 0.2 ## if comparator.count_marked_pairs() >= max_collisions_to_show: return for group in classify(terminal_to_regexp, lambda t: t.priority).values(): for a, b in comparator.check(group, skip_marked=True): assert a.priority == b.priority ## comparator.mark(a, b) ## message = f"Collision between Terminals {a.name} and {b.name}. " try: example = comparator.get_example_overlap(a, b, max_time).format_multiline() except ValueError: ## example = "No example could be found fast enough. However, the collision does still exists" if strict_mode: raise LexError(f"{message}\n{example}") logger.warning("%s The lexer will choose between them arbitrarily.\n%s", message, example) if comparator.count_marked_pairs() >= max_collisions_to_show: logger.warning("Found 8 regex collisions, will not check for more.") return class BasicLexer(Lexer): terminals: Collection[TerminalDef] ignore_types: FrozenSet[str] newline_types: FrozenSet[str] user_callbacks: Dict[str, _Callback] callback: Dict[str, _Callback] re: ModuleType def __init__(self, conf: 'LexerConf', comparator=None) -> None: terminals = list(conf.terminals) assert all(isinstance(t, TerminalDef) for t in terminals), terminals self.re = conf.re_module if not conf.skip_validation: ## terminal_to_regexp = {} for t in terminals: regexp = t.pattern.to_regexp() try: self.re.compile(regexp, conf.g_regex_flags) except self.re.error: raise LexError("Cannot compile token %s: %s" % (t.name, t.pattern)) if t.pattern.min_width == 0: raise LexError("Lexer does not allow zero-width terminals. (%s: %s)" % (t.name, t.pattern)) if t.pattern.type == "re": terminal_to_regexp[t] = regexp if not (set(conf.ignore) <= {t.name for t in terminals}): raise LexError("Ignore terminals are not defined: %s" % (set(conf.ignore) - {t.name for t in terminals})) if has_interegular: _check_regex_collisions(terminal_to_regexp, comparator, conf.strict) elif conf.strict: raise LexError("interegular must be installed for strict mode. Use `pip install 'lark[interegular]'`.") ## self.newline_types = frozenset(t.name for t in terminals if _regexp_has_newline(t.pattern.to_regexp())) self.ignore_types = frozenset(conf.ignore) terminals.sort(key=lambda x: (-x.priority, -x.pattern.max_width, -len(x.pattern.value), x.name)) self.terminals = terminals self.user_callbacks = conf.callbacks self.g_regex_flags = conf.g_regex_flags self.use_bytes = conf.use_bytes self.terminals_by_name = conf.terminals_by_name self._scanner = None def _build_scanner(self): terminals, self.callback = _create_unless(self.terminals, self.g_regex_flags, self.re, self.use_bytes) assert all(self.callback.values()) for type_, f in self.user_callbacks.items(): if type_ in self.callback: ## self.callback[type_] = CallChain(self.callback[type_], f, lambda t: t.type == type_) else: self.callback[type_] = f self._scanner = Scanner(terminals, self.g_regex_flags, self.re, self.use_bytes) @property def scanner(self): if self._scanner is None: self._build_scanner() return self._scanner def match(self, text, pos): return self.scanner.match(text, pos) def lex(self, state: LexerState, parser_state: Any) -> Iterator[Token]: with suppress(EOFError): while True: yield self.next_token(state, parser_state) def next_token(self, lex_state: LexerState, parser_state: Any = None) -> Token: line_ctr = lex_state.line_ctr while line_ctr.char_pos < len(lex_state.text): res = self.match(lex_state.text, line_ctr.char_pos) if not res: allowed = self.scanner.allowed_types - self.ignore_types if not allowed: allowed = {""} raise UnexpectedCharacters(lex_state.text, line_ctr.char_pos, line_ctr.line, line_ctr.column, allowed=allowed, token_history=lex_state.last_token and [lex_state.last_token], state=parser_state, terminals_by_name=self.terminals_by_name) value, type_ = res if type_ not in self.ignore_types: t = Token(type_, value, line_ctr.char_pos, line_ctr.line, line_ctr.column) line_ctr.feed(value, type_ in self.newline_types) t.end_line = line_ctr.line t.end_column = line_ctr.column t.end_pos = line_ctr.char_pos if t.type in self.callback: t = self.callback[t.type](t) if not isinstance(t, Token): raise LexError("Callbacks must return a token (returned %r)" % t) lex_state.last_token = t return t else: if type_ in self.callback: t2 = Token(type_, value, line_ctr.char_pos, line_ctr.line, line_ctr.column) self.callback[type_](t2) line_ctr.feed(value, type_ in self.newline_types) ## raise EOFError(self) class ContextualLexer(Lexer): lexers: Dict[str, BasicLexer] root_lexer: BasicLexer def __init__(self, conf: 'LexerConf', states: Dict[str, Collection[str]], always_accept: Collection[str]=()) -> None: terminals = list(conf.terminals) terminals_by_name = conf.terminals_by_name trad_conf = copy(conf) trad_conf.terminals = terminals if has_interegular and not conf.skip_validation: comparator = interegular.Comparator.from_regexes({t: t.pattern.to_regexp() for t in terminals}) else: comparator = None lexer_by_tokens: Dict[FrozenSet[str], BasicLexer] = {} self.lexers = {} for state, accepts in states.items(): key = frozenset(accepts) try: lexer = lexer_by_tokens[key] except KeyError: accepts = set(accepts) | set(conf.ignore) | set(always_accept) lexer_conf = copy(trad_conf) lexer_conf.terminals = [terminals_by_name[n] for n in accepts if n in terminals_by_name] lexer = BasicLexer(lexer_conf, comparator) lexer_by_tokens[key] = lexer self.lexers[state] = lexer assert trad_conf.terminals is terminals trad_conf.skip_validation = True ## self.root_lexer = BasicLexer(trad_conf, comparator) def lex(self, lexer_state: LexerState, parser_state: Any) -> Iterator[Token]: try: while True: lexer = self.lexers[parser_state.position] yield lexer.next_token(lexer_state, parser_state) except EOFError: pass except UnexpectedCharacters as e: ## ## try: last_token = lexer_state.last_token ## token = self.root_lexer.next_token(lexer_state, parser_state) raise UnexpectedToken(token, e.allowed, state=parser_state, token_history=[last_token], terminals_by_name=self.root_lexer.terminals_by_name) except UnexpectedCharacters: raise e ## _ParserArgType: 'TypeAlias' = 'Literal["earley", "lalr", "cyk", "auto"]' _LexerArgType: 'TypeAlias' = 'Union[Literal["auto", "basic", "contextual", "dynamic", "dynamic_complete"], Type[Lexer]]' _Callback = Callable[[Token], Token] class LexerConf(Serialize): __serialize_fields__ = 'terminals', 'ignore', 'g_regex_flags', 'use_bytes', 'lexer_type' __serialize_namespace__ = TerminalDef, terminals: Collection[TerminalDef] re_module: ModuleType ignore: Collection[str] postlex: 'Optional[PostLex]' callbacks: Dict[str, _Callback] g_regex_flags: int skip_validation: bool use_bytes: bool lexer_type: Optional[_LexerArgType] strict: bool def __init__(self, terminals: Collection[TerminalDef], re_module: ModuleType, ignore: Collection[str]=(), postlex: 'Optional[PostLex]'=None, callbacks: Optional[Dict[str, _Callback]]=None, g_regex_flags: int=0, skip_validation: bool=False, use_bytes: bool=False, strict: bool=False): self.terminals = terminals self.terminals_by_name = {t.name: t for t in self.terminals} assert len(self.terminals) == len(self.terminals_by_name) self.ignore = ignore self.postlex = postlex self.callbacks = callbacks or {} self.g_regex_flags = g_regex_flags self.re_module = re_module self.skip_validation = skip_validation self.use_bytes = use_bytes self.strict = strict self.lexer_type = None def _deserialize(self): self.terminals_by_name = {t.name: t for t in self.terminals} def __deepcopy__(self, memo=None): return type(self)( deepcopy(self.terminals, memo), self.re_module, deepcopy(self.ignore, memo), deepcopy(self.postlex, memo), deepcopy(self.callbacks, memo), deepcopy(self.g_regex_flags, memo), deepcopy(self.skip_validation, memo), deepcopy(self.use_bytes, memo), ) class ParserConf(Serialize): __serialize_fields__ = 'rules', 'start', 'parser_type' def __init__(self, rules, callbacks, start): assert isinstance(start, list) self.rules = rules self.callbacks = callbacks self.start = start self.parser_type = None from functools import partial, wraps from itertools import product class ExpandSingleChild: def __init__(self, node_builder): self.node_builder = node_builder def __call__(self, children): if len(children) == 1: return children[0] else: return self.node_builder(children) class PropagatePositions: def __init__(self, node_builder, node_filter=None): self.node_builder = node_builder self.node_filter = node_filter def __call__(self, children): res = self.node_builder(children) if isinstance(res, Tree): ## ## ## ## res_meta = res.meta first_meta = self._pp_get_meta(children) if first_meta is not None: if not hasattr(res_meta, 'line'): ## res_meta.line = getattr(first_meta, 'container_line', first_meta.line) res_meta.column = getattr(first_meta, 'container_column', first_meta.column) res_meta.start_pos = getattr(first_meta, 'container_start_pos', first_meta.start_pos) res_meta.empty = False res_meta.container_line = getattr(first_meta, 'container_line', first_meta.line) res_meta.container_column = getattr(first_meta, 'container_column', first_meta.column) res_meta.container_start_pos = getattr(first_meta, 'container_start_pos', first_meta.start_pos) last_meta = self._pp_get_meta(reversed(children)) if last_meta is not None: if not hasattr(res_meta, 'end_line'): res_meta.end_line = getattr(last_meta, 'container_end_line', last_meta.end_line) res_meta.end_column = getattr(last_meta, 'container_end_column', last_meta.end_column) res_meta.end_pos = getattr(last_meta, 'container_end_pos', last_meta.end_pos) res_meta.empty = False res_meta.container_end_line = getattr(last_meta, 'container_end_line', last_meta.end_line) res_meta.container_end_column = getattr(last_meta, 'container_end_column', last_meta.end_column) res_meta.container_end_pos = getattr(last_meta, 'container_end_pos', last_meta.end_pos) return res def _pp_get_meta(self, children): for c in children: if self.node_filter is not None and not self.node_filter(c): continue if isinstance(c, Tree): if not c.meta.empty: return c.meta elif isinstance(c, Token): return c elif hasattr(c, '__lark_meta__'): return c.__lark_meta__() def make_propagate_positions(option): if callable(option): return partial(PropagatePositions, node_filter=option) elif option is True: return PropagatePositions elif option is False: return None raise ConfigurationError('Invalid option for propagate_positions: %r' % option) class ChildFilter: def __init__(self, to_include, append_none, node_builder): self.node_builder = node_builder self.to_include = to_include self.append_none = append_none def __call__(self, children): filtered = [] for i, to_expand, add_none in self.to_include: if add_none: filtered += [None] * add_none if to_expand: filtered += children[i].children else: filtered.append(children[i]) if self.append_none: filtered += [None] * self.append_none return self.node_builder(filtered) class ChildFilterLALR(ChildFilter): #-- def __call__(self, children): filtered = [] for i, to_expand, add_none in self.to_include: if add_none: filtered += [None] * add_none if to_expand: if filtered: filtered += children[i].children else: ## filtered = children[i].children else: filtered.append(children[i]) if self.append_none: filtered += [None] * self.append_none return self.node_builder(filtered) class ChildFilterLALR_NoPlaceholders(ChildFilter): #-- def __init__(self, to_include, node_builder): self.node_builder = node_builder self.to_include = to_include def __call__(self, children): filtered = [] for i, to_expand in self.to_include: if to_expand: if filtered: filtered += children[i].children else: ## filtered = children[i].children else: filtered.append(children[i]) return self.node_builder(filtered) def _should_expand(sym): return not sym.is_term and sym.name.startswith('_') def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indices: List[bool]): ## if _empty_indices: assert _empty_indices.count(False) == len(expansion) s = ''.join(str(int(b)) for b in _empty_indices) empty_indices = [len(ones) for ones in s.split('0')] assert len(empty_indices) == len(expansion)+1, (empty_indices, len(expansion)) else: empty_indices = [0] * (len(expansion)+1) to_include = [] nones_to_add = 0 for i, sym in enumerate(expansion): nones_to_add += empty_indices[i] if keep_all_tokens or not (sym.is_term and sym.filter_out): to_include.append((i, _should_expand(sym), nones_to_add)) nones_to_add = 0 nones_to_add += empty_indices[len(expansion)] if _empty_indices or len(to_include) < len(expansion) or any(to_expand for i, to_expand,_ in to_include): if _empty_indices or ambiguous: return partial(ChildFilter if ambiguous else ChildFilterLALR, to_include, nones_to_add) else: ## return partial(ChildFilterLALR_NoPlaceholders, [(i, x) for i,x,_ in to_include]) class AmbiguousExpander: #-- def __init__(self, to_expand, tree_class, node_builder): self.node_builder = node_builder self.tree_class = tree_class self.to_expand = to_expand def __call__(self, children): def _is_ambig_tree(t): return hasattr(t, 'data') and t.data == '_ambig' ## ## ## ## ambiguous = [] for i, child in enumerate(children): if _is_ambig_tree(child): if i in self.to_expand: ambiguous.append(i) child.expand_kids_by_data('_ambig') if not ambiguous: return self.node_builder(children) expand = [child.children if i in ambiguous else (child,) for i, child in enumerate(children)] return self.tree_class('_ambig', [self.node_builder(list(f)) for f in product(*expand)]) def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens): to_expand = [i for i, sym in enumerate(expansion) if keep_all_tokens or ((not (sym.is_term and sym.filter_out)) and _should_expand(sym))] if to_expand: return partial(AmbiguousExpander, to_expand, tree_class) class AmbiguousIntermediateExpander: #-- def __init__(self, tree_class, node_builder): self.node_builder = node_builder self.tree_class = tree_class def __call__(self, children): def _is_iambig_tree(child): return hasattr(child, 'data') and child.data == '_iambig' def _collapse_iambig(children): #-- ## ## if children and _is_iambig_tree(children[0]): iambig_node = children[0] result = [] for grandchild in iambig_node.children: collapsed = _collapse_iambig(grandchild.children) if collapsed: for child in collapsed: child.children += children[1:] result += collapsed else: new_tree = self.tree_class('_inter', grandchild.children + children[1:]) result.append(new_tree) return result collapsed = _collapse_iambig(children) if collapsed: processed_nodes = [self.node_builder(c.children) for c in collapsed] return self.tree_class('_ambig', processed_nodes) return self.node_builder(children) def inplace_transformer(func): @wraps(func) def f(children): ## tree = Tree(func.__name__, children) return func(tree) return f def apply_visit_wrapper(func, name, wrapper): if wrapper is _vargs_meta or wrapper is _vargs_meta_inline: raise NotImplementedError("Meta args not supported for internal transformer") @wraps(func) def f(children): return wrapper(func, name, children, None) return f class ParseTreeBuilder: def __init__(self, rules, tree_class, propagate_positions=False, ambiguous=False, maybe_placeholders=False): self.tree_class = tree_class self.propagate_positions = propagate_positions self.ambiguous = ambiguous self.maybe_placeholders = maybe_placeholders self.rule_builders = list(self._init_builders(rules)) def _init_builders(self, rules): propagate_positions = make_propagate_positions(self.propagate_positions) for rule in rules: options = rule.options keep_all_tokens = options.keep_all_tokens expand_single_child = options.expand1 wrapper_chain = list(filter(None, [ (expand_single_child and not rule.alias) and ExpandSingleChild, maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous, options.empty_indices if self.maybe_placeholders else None), propagate_positions, self.ambiguous and maybe_create_ambiguous_expander(self.tree_class, rule.expansion, keep_all_tokens), self.ambiguous and partial(AmbiguousIntermediateExpander, self.tree_class) ])) yield rule, wrapper_chain def create_callback(self, transformer=None): callbacks = {} default_handler = getattr(transformer, '__default__', None) if default_handler: def default_callback(data, children): return default_handler(data, children, None) else: default_callback = self.tree_class for rule, wrapper_chain in self.rule_builders: user_callback_name = rule.alias or rule.options.template_source or rule.origin.name try: f = getattr(transformer, user_callback_name) wrapper = getattr(f, 'visit_wrapper', None) if wrapper is not None: f = apply_visit_wrapper(f, user_callback_name, wrapper) elif isinstance(transformer, Transformer_InPlace): f = inplace_transformer(f) except AttributeError: f = partial(default_callback, user_callback_name) for w in wrapper_chain: f = w(f) if rule in callbacks: raise GrammarError("Rule '%s' already exists" % (rule,)) callbacks[rule] = f return callbacks class LALR_Parser(Serialize): def __init__(self, parser_conf, debug=False, strict=False): analysis = LALR_Analyzer(parser_conf, debug=debug, strict=strict) analysis.compute_lalr() callbacks = parser_conf.callbacks self._parse_table = analysis.parse_table self.parser_conf = parser_conf self.parser = _Parser(analysis.parse_table, callbacks, debug) @classmethod def deserialize(cls, data, memo, callbacks, debug=False): inst = cls.__new__(cls) inst._parse_table = IntParseTable.deserialize(data, memo) inst.parser = _Parser(inst._parse_table, callbacks, debug) return inst def serialize(self, memo: Any = None) -> Dict[str, Any]: return self._parse_table.serialize(memo) def parse_interactive(self, lexer, start): return self.parser.parse(lexer, start, start_interactive=True) def parse(self, lexer, start, on_error=None): try: return self.parser.parse(lexer, start) except UnexpectedInput as e: if on_error is None: raise while True: if isinstance(e, UnexpectedCharacters): s = e.interactive_parser.lexer_thread.state p = s.line_ctr.char_pos if not on_error(e): raise e if isinstance(e, UnexpectedCharacters): ## if p == s.line_ctr.char_pos: s.line_ctr.feed(s.text[p:p+1]) try: return e.interactive_parser.resume_parse() except UnexpectedToken as e2: if (isinstance(e, UnexpectedToken) and e.token.type == e2.token.type == '$END' and e.interactive_parser == e2.interactive_parser): ## raise e2 e = e2 except UnexpectedCharacters as e2: e = e2 class ParseConf: __slots__ = 'parse_table', 'callbacks', 'start', 'start_state', 'end_state', 'states' def __init__(self, parse_table, callbacks, start): self.parse_table = parse_table self.start_state = self.parse_table.start_states[start] self.end_state = self.parse_table.end_states[start] self.states = self.parse_table.states self.callbacks = callbacks self.start = start class ParserState: __slots__ = 'parse_conf', 'lexer', 'state_stack', 'value_stack' def __init__(self, parse_conf, lexer, state_stack=None, value_stack=None): self.parse_conf = parse_conf self.lexer = lexer self.state_stack = state_stack or [self.parse_conf.start_state] self.value_stack = value_stack or [] @property def position(self): return self.state_stack[-1] ## def __eq__(self, other): if not isinstance(other, ParserState): return NotImplemented return len(self.state_stack) == len(other.state_stack) and self.position == other.position def __copy__(self): return type(self)( self.parse_conf, self.lexer, ## copy(self.state_stack), deepcopy(self.value_stack), ) def copy(self): return copy(self) def feed_token(self, token, is_end=False): state_stack = self.state_stack value_stack = self.value_stack states = self.parse_conf.states end_state = self.parse_conf.end_state callbacks = self.parse_conf.callbacks while True: state = state_stack[-1] try: action, arg = states[state][token.type] except KeyError: expected = {s for s in states[state].keys() if s.isupper()} raise UnexpectedToken(token, expected, state=self, interactive_parser=None) assert arg != end_state if action is Shift: ## assert not is_end state_stack.append(arg) value_stack.append(token if token.type not in callbacks else callbacks[token.type](token)) return else: ## rule = arg size = len(rule.expansion) if size: s = value_stack[-size:] del state_stack[-size:] del value_stack[-size:] else: s = [] value = callbacks[rule](s) _action, new_state = states[state_stack[-1]][rule.origin.name] assert _action is Shift state_stack.append(new_state) value_stack.append(value) if is_end and state_stack[-1] == end_state: return value_stack[-1] class _Parser: def __init__(self, parse_table, callbacks, debug=False): self.parse_table = parse_table self.callbacks = callbacks self.debug = debug def parse(self, lexer, start, value_stack=None, state_stack=None, start_interactive=False): parse_conf = ParseConf(self.parse_table, self.callbacks, start) parser_state = ParserState(parse_conf, lexer, state_stack, value_stack) if start_interactive: return InteractiveParser(self, parser_state, parser_state.lexer) return self.parse_from_state(parser_state) def parse_from_state(self, state, last_token=None): #-- try: token = last_token for token in state.lexer.lex(state): state.feed_token(token) end_token = Token.new_borrow_pos('$END', '', token) if token else Token('$END', '', 0, 1, 1) return state.feed_token(end_token, True) except UnexpectedInput as e: try: e.interactive_parser = InteractiveParser(self, state, state.lexer) except NameError: pass raise e except Exception as e: if self.debug: print("") print("STATE STACK DUMP") print("----------------") for i, s in enumerate(state.state_stack): print('%d)' % i , s) print("") raise class Action: def __init__(self, name): self.name = name def __str__(self): return self.name def __repr__(self): return str(self) Shift = Action('Shift') Reduce = Action('Reduce') class ParseTable: def __init__(self, states, start_states, end_states): self.states = states self.start_states = start_states self.end_states = end_states def serialize(self, memo): tokens = Enumerator() states = { state: {tokens.get(token): ((1, arg.serialize(memo)) if action is Reduce else (0, arg)) for token, (action, arg) in actions.items()} for state, actions in self.states.items() } return { 'tokens': tokens.reversed(), 'states': states, 'start_states': self.start_states, 'end_states': self.end_states, } @classmethod def deserialize(cls, data, memo): tokens = data['tokens'] states = { state: {tokens[token]: ((Reduce, Rule.deserialize(arg, memo)) if action==1 else (Shift, arg)) for token, (action, arg) in actions.items()} for state, actions in data['states'].items() } return cls(states, data['start_states'], data['end_states']) class IntParseTable(ParseTable): @classmethod def from_ParseTable(cls, parse_table): enum = list(parse_table.states) state_to_idx = {s:i for i,s in enumerate(enum)} int_states = {} for s, la in parse_table.states.items(): la = {k:(v[0], state_to_idx[v[1]]) if v[0] is Shift else v for k,v in la.items()} int_states[ state_to_idx[s] ] = la start_states = {start:state_to_idx[s] for start, s in parse_table.start_states.items()} end_states = {start:state_to_idx[s] for start, s in parse_table.end_states.items()} return cls(int_states, start_states, end_states) def _wrap_lexer(lexer_class): future_interface = getattr(lexer_class, '__future_interface__', False) if future_interface: return lexer_class else: class CustomLexerWrapper(Lexer): def __init__(self, lexer_conf): self.lexer = lexer_class(lexer_conf) def lex(self, lexer_state, parser_state): return self.lexer.lex(lexer_state.text) return CustomLexerWrapper def _deserialize_parsing_frontend(data, memo, lexer_conf, callbacks, options): parser_conf = ParserConf.deserialize(data['parser_conf'], memo) cls = (options and options._plugins.get('LALR_Parser')) or LALR_Parser parser = cls.deserialize(data['parser'], memo, callbacks, options.debug) parser_conf.callbacks = callbacks return ParsingFrontend(lexer_conf, parser_conf, options, parser=parser) _parser_creators: 'Dict[str, Callable[[LexerConf, Any, Any], Any]]' = {} class ParsingFrontend(Serialize): __serialize_fields__ = 'lexer_conf', 'parser_conf', 'parser' lexer_conf: LexerConf parser_conf: ParserConf options: Any def __init__(self, lexer_conf: LexerConf, parser_conf: ParserConf, options, parser=None): self.parser_conf = parser_conf self.lexer_conf = lexer_conf self.options = options ## if parser: ## self.parser = parser else: create_parser = _parser_creators.get(parser_conf.parser_type) assert create_parser is not None, "{} is not supported in standalone mode".format( parser_conf.parser_type ) self.parser = create_parser(lexer_conf, parser_conf, options) ## lexer_type = lexer_conf.lexer_type self.skip_lexer = False if lexer_type in ('dynamic', 'dynamic_complete'): assert lexer_conf.postlex is None self.skip_lexer = True return if isinstance(lexer_type, type): assert issubclass(lexer_type, Lexer) self.lexer = _wrap_lexer(lexer_type)(lexer_conf) elif isinstance(lexer_type, str): create_lexer = { 'basic': create_basic_lexer, 'contextual': create_contextual_lexer, }[lexer_type] self.lexer = create_lexer(lexer_conf, self.parser, lexer_conf.postlex, options) else: raise TypeError("Bad value for lexer_type: {lexer_type}") if lexer_conf.postlex: self.lexer = PostLexConnector(self.lexer, lexer_conf.postlex) def _verify_start(self, start=None): if start is None: start_decls = self.parser_conf.start if len(start_decls) > 1: raise ConfigurationError("Lark initialized with more than 1 possible start rule. Must specify which start rule to parse", start_decls) start ,= start_decls elif start not in self.parser_conf.start: raise ConfigurationError("Unknown start rule %s. Must be one of %r" % (start, self.parser_conf.start)) return start def _make_lexer_thread(self, text: str): cls = (self.options and self.options._plugins.get('LexerThread')) or LexerThread return text if self.skip_lexer else cls.from_text(self.lexer, text) def parse(self, text: str, start=None, on_error=None): chosen_start = self._verify_start(start) kw = {} if on_error is None else {'on_error': on_error} stream = self._make_lexer_thread(text) return self.parser.parse(stream, chosen_start, **kw) def parse_interactive(self, text: Optional[str]=None, start=None): ## ## chosen_start = self._verify_start(start) if self.parser_conf.parser_type != 'lalr': raise ConfigurationError("parse_interactive() currently only works with parser='lalr' ") stream = self._make_lexer_thread(text) ## return self.parser.parse_interactive(stream, chosen_start) def _validate_frontend_args(parser, lexer) -> None: assert_config(parser, ('lalr', 'earley', 'cyk')) if not isinstance(lexer, type): ## expected = { 'lalr': ('basic', 'contextual'), 'earley': ('basic', 'dynamic', 'dynamic_complete'), 'cyk': ('basic', ), }[parser] assert_config(lexer, expected, 'Parser %r does not support lexer %%r, expected one of %%s' % parser) def _get_lexer_callbacks(transformer, terminals): result = {} for terminal in terminals: callback = getattr(transformer, terminal.name, None) if callback is not None: result[terminal.name] = callback return result class PostLexConnector: def __init__(self, lexer, postlexer): self.lexer = lexer self.postlexer = postlexer def lex(self, lexer_state, parser_state): i = self.lexer.lex(lexer_state, parser_state) return self.postlexer.process(i) def create_basic_lexer(lexer_conf, parser, postlex, options) -> BasicLexer: cls = (options and options._plugins.get('BasicLexer')) or BasicLexer return cls(lexer_conf) def create_contextual_lexer(lexer_conf: LexerConf, parser, postlex, options) -> ContextualLexer: cls = (options and options._plugins.get('ContextualLexer')) or ContextualLexer states: Dict[str, Collection[str]] = {idx:list(t.keys()) for idx, t in parser._parse_table.states.items()} always_accept: Collection[str] = postlex.always_accept if postlex else () return cls(lexer_conf, states, always_accept=always_accept) def create_lalr_parser(lexer_conf: LexerConf, parser_conf: ParserConf, options=None) -> LALR_Parser: debug = options.debug if options else False strict = options.strict if options else False cls = (options and options._plugins.get('LALR_Parser')) or LALR_Parser return cls(parser_conf, debug=debug, strict=strict) _parser_creators['lalr'] = create_lalr_parser class PostLex(ABC): @abstractmethod def process(self, stream: Iterator[Token]) -> Iterator[Token]: return stream always_accept: Iterable[str] = () class LarkOptions(Serialize): #-- start: List[str] debug: bool strict: bool transformer: 'Optional[Transformer]' propagate_positions: Union[bool, str] maybe_placeholders: bool cache: Union[bool, str] regex: bool g_regex_flags: int keep_all_tokens: bool tree_class: Any parser: _ParserArgType lexer: _LexerArgType ambiguity: 'Literal["auto", "resolve", "explicit", "forest"]' postlex: Optional[PostLex] priority: 'Optional[Literal["auto", "normal", "invert"]]' lexer_callbacks: Dict[str, Callable[[Token], Token]] use_bytes: bool edit_terminals: Optional[Callable[[TerminalDef], TerminalDef]] import_paths: 'List[Union[str, Callable[[Union[None, str, PackageResource], str], Tuple[str, str]]]]' source_path: Optional[str] OPTIONS_DOC = """ **=== General Options ===** start The start symbol. Either a string, or a list of strings for multiple possible starts (Default: "start") debug Display debug information and extra warnings. Use only when debugging (Default: ``False``) When used with Earley, it generates a forest graph as "sppf.png", if 'dot' is installed. strict Throw an exception on any potential ambiguity, including shift/reduce conflicts, and regex collisions. transformer Applies the transformer to every parse tree (equivalent to applying it after the parse, but faster) propagate_positions Propagates positional attributes into the 'meta' attribute of all tree branches. Sets attributes: (line, column, end_line, end_column, start_pos, end_pos, container_line, container_column, container_end_line, container_end_column) Accepts ``False``, ``True``, or a callable, which will filter which nodes to ignore when propagating. maybe_placeholders When ``True``, the ``[]`` operator returns ``None`` when not matched. When ``False``, ``[]`` behaves like the ``?`` operator, and returns no value at all. (default= ``True``) cache Cache the results of the Lark grammar analysis, for x2 to x3 faster loading. LALR only for now. - When ``False``, does nothing (default) - When ``True``, caches to a temporary file in the local directory - When given a string, caches to the path pointed by the string regex When True, uses the ``regex`` module instead of the stdlib ``re``. g_regex_flags Flags that are applied to all terminals (both regex and strings) keep_all_tokens Prevent the tree builder from automagically removing "punctuation" tokens (Default: ``False``) tree_class Lark will produce trees comprised of instances of this class instead of the default ``lark.Tree``. **=== Algorithm Options ===** parser Decides which parser engine to use. Accepts "earley" or "lalr". (Default: "earley"). (there is also a "cyk" option for legacy) lexer Decides whether or not to use a lexer stage - "auto" (default): Choose for me based on the parser - "basic": Use a basic lexer - "contextual": Stronger lexer (only works with parser="lalr") - "dynamic": Flexible and powerful (only with parser="earley") - "dynamic_complete": Same as dynamic, but tries *every* variation of tokenizing possible. ambiguity Decides how to handle ambiguity in the parse. Only relevant if parser="earley" - "resolve": The parser will automatically choose the simplest derivation (it chooses consistently: greedy for tokens, non-greedy for rules) - "explicit": The parser will return all derivations wrapped in "_ambig" tree nodes (i.e. a forest). - "forest": The parser will return the root of the shared packed parse forest. **=== Misc. / Domain Specific Options ===** postlex Lexer post-processing (Default: ``None``) Only works with the basic and contextual lexers. priority How priorities should be evaluated - "auto", ``None``, "normal", "invert" (Default: "auto") lexer_callbacks Dictionary of callbacks for the lexer. May alter tokens during lexing. Use with caution. use_bytes Accept an input of type ``bytes`` instead of ``str``. edit_terminals A callback for editing the terminals before parse. import_paths A List of either paths or loader functions to specify from where grammars are imported source_path Override the source of from where the grammar was loaded. Useful for relative imports and unconventional grammar loading **=== End of Options ===** """ if __doc__: __doc__ += OPTIONS_DOC ## ## ## ## ## ## _defaults: Dict[str, Any] = { 'debug': False, 'strict': False, 'keep_all_tokens': False, 'tree_class': None, 'cache': False, 'postlex': None, 'parser': 'earley', 'lexer': 'auto', 'transformer': None, 'start': 'start', 'priority': 'auto', 'ambiguity': 'auto', 'regex': False, 'propagate_positions': False, 'lexer_callbacks': {}, 'maybe_placeholders': True, 'edit_terminals': None, 'g_regex_flags': 0, 'use_bytes': False, 'import_paths': [], 'source_path': None, '_plugins': {}, } def __init__(self, options_dict: Dict[str, Any]) -> None: o = dict(options_dict) options = {} for name, default in self._defaults.items(): if name in o: value = o.pop(name) if isinstance(default, bool) and name not in ('cache', 'use_bytes', 'propagate_positions'): value = bool(value) else: value = default options[name] = value if isinstance(options['start'], str): options['start'] = [options['start']] self.__dict__['options'] = options assert_config(self.parser, ('earley', 'lalr', 'cyk', None)) if self.parser == 'earley' and self.transformer: raise ConfigurationError('Cannot specify an embedded transformer when using the Earley algorithm. ' 'Please use your transformer on the resulting parse tree, or use a different algorithm (i.e. LALR)') if o: raise ConfigurationError("Unknown options: %s" % o.keys()) def __getattr__(self, name: str) -> Any: try: return self.__dict__['options'][name] except KeyError as e: raise AttributeError(e) def __setattr__(self, name: str, value: str) -> None: assert_config(name, self.options.keys(), "%r isn't a valid option. Expected one of: %s") self.options[name] = value def serialize(self, memo = None) -> Dict[str, Any]: return self.options @classmethod def deserialize(cls, data: Dict[str, Any], memo: Dict[int, Union[TerminalDef, Rule]]) -> "LarkOptions": return cls(data) ## ## _LOAD_ALLOWED_OPTIONS = {'postlex', 'transformer', 'lexer_callbacks', 'use_bytes', 'debug', 'g_regex_flags', 'regex', 'propagate_positions', 'tree_class', '_plugins'} _VALID_PRIORITY_OPTIONS = ('auto', 'normal', 'invert', None) _VALID_AMBIGUITY_OPTIONS = ('auto', 'resolve', 'explicit', 'forest') _T = TypeVar('_T', bound="Lark") class Lark(Serialize): #-- source_path: str source_grammar: str grammar: 'Grammar' options: LarkOptions lexer: Lexer parser: 'ParsingFrontend' terminals: Collection[TerminalDef] def __init__(self, grammar: 'Union[Grammar, str, IO[str]]', **options) -> None: self.options = LarkOptions(options) re_module: types.ModuleType ## use_regex = self.options.regex if use_regex: if _has_regex: re_module = regex else: raise ImportError('`regex` module must be installed if calling `Lark(regex=True)`.') else: re_module = re ## if self.options.source_path is None: try: self.source_path = grammar.name ## except AttributeError: self.source_path = '' else: self.source_path = self.options.source_path ## try: read = grammar.read ## except AttributeError: pass else: grammar = read() cache_fn = None cache_sha256 = None if isinstance(grammar, str): self.source_grammar = grammar if self.options.use_bytes: if not isascii(grammar): raise ConfigurationError("Grammar must be ascii only, when use_bytes=True") if self.options.cache: if self.options.parser != 'lalr': raise ConfigurationError("cache only works with parser='lalr' for now") unhashable = ('transformer', 'postlex', 'lexer_callbacks', 'edit_terminals', '_plugins') options_str = ''.join(k+str(v) for k, v in options.items() if k not in unhashable) from . import __version__ s = grammar + options_str + __version__ + str(sys.version_info[:2]) cache_sha256 = sha256_digest(s) if isinstance(self.options.cache, str): cache_fn = self.options.cache else: if self.options.cache is not True: raise ConfigurationError("cache argument must be bool or str") try: username = getpass.getuser() except Exception: ## ## ## username = "unknown" cache_fn = tempfile.gettempdir() + "/.lark_cache_%s_%s_%s_%s.tmp" % (username, cache_sha256, *sys.version_info[:2]) old_options = self.options try: with FS.open(cache_fn, 'rb') as f: logger.debug('Loading grammar from cache: %s', cache_fn) ## for name in (set(options) - _LOAD_ALLOWED_OPTIONS): del options[name] file_sha256 = f.readline().rstrip(b'\n') cached_used_files = pickle.load(f) if file_sha256 == cache_sha256.encode('utf8') and verify_used_files(cached_used_files): cached_parser_data = pickle.load(f) self._load(cached_parser_data, **options) return except FileNotFoundError: ## pass except Exception: ## logger.exception("Failed to load Lark from cache: %r. We will try to carry on.", cache_fn) ## ## self.options = old_options ## self.grammar, used_files = load_grammar(grammar, self.source_path, self.options.import_paths, self.options.keep_all_tokens) else: assert isinstance(grammar, Grammar) self.grammar = grammar if self.options.lexer == 'auto': if self.options.parser == 'lalr': self.options.lexer = 'contextual' elif self.options.parser == 'earley': if self.options.postlex is not None: logger.info("postlex can't be used with the dynamic lexer, so we use 'basic' instead. " "Consider using lalr with contextual instead of earley") self.options.lexer = 'basic' else: self.options.lexer = 'dynamic' elif self.options.parser == 'cyk': self.options.lexer = 'basic' else: assert False, self.options.parser lexer = self.options.lexer if isinstance(lexer, type): assert issubclass(lexer, Lexer) ## else: assert_config(lexer, ('basic', 'contextual', 'dynamic', 'dynamic_complete')) if self.options.postlex is not None and 'dynamic' in lexer: raise ConfigurationError("Can't use postlex with a dynamic lexer. Use basic or contextual instead") if self.options.ambiguity == 'auto': if self.options.parser == 'earley': self.options.ambiguity = 'resolve' else: assert_config(self.options.parser, ('earley', 'cyk'), "%r doesn't support disambiguation. Use one of these parsers instead: %s") if self.options.priority == 'auto': self.options.priority = 'normal' if self.options.priority not in _VALID_PRIORITY_OPTIONS: raise ConfigurationError("invalid priority option: %r. Must be one of %r" % (self.options.priority, _VALID_PRIORITY_OPTIONS)) if self.options.ambiguity not in _VALID_AMBIGUITY_OPTIONS: raise ConfigurationError("invalid ambiguity option: %r. Must be one of %r" % (self.options.ambiguity, _VALID_AMBIGUITY_OPTIONS)) if self.options.parser is None: terminals_to_keep = '*' elif self.options.postlex is not None: terminals_to_keep = set(self.options.postlex.always_accept) else: terminals_to_keep = set() ## self.terminals, self.rules, self.ignore_tokens = self.grammar.compile(self.options.start, terminals_to_keep) if self.options.edit_terminals: for t in self.terminals: self.options.edit_terminals(t) self._terminals_dict = {t.name: t for t in self.terminals} ## if self.options.priority == 'invert': for rule in self.rules: if rule.options.priority is not None: rule.options.priority = -rule.options.priority for term in self.terminals: term.priority = -term.priority ## ## ## elif self.options.priority is None: for rule in self.rules: if rule.options.priority is not None: rule.options.priority = None for term in self.terminals: term.priority = 0 ## self.lexer_conf = LexerConf( self.terminals, re_module, self.ignore_tokens, self.options.postlex, self.options.lexer_callbacks, self.options.g_regex_flags, use_bytes=self.options.use_bytes, strict=self.options.strict ) if self.options.parser: self.parser = self._build_parser() elif lexer: self.lexer = self._build_lexer() if cache_fn: logger.debug('Saving grammar to cache: %s', cache_fn) try: with FS.open(cache_fn, 'wb') as f: assert cache_sha256 is not None f.write(cache_sha256.encode('utf8') + b'\n') pickle.dump(used_files, f) self.save(f, _LOAD_ALLOWED_OPTIONS) except IOError as e: logger.exception("Failed to save Lark to cache: %r.", cache_fn, e) if __doc__: __doc__ += "\n\n" + LarkOptions.OPTIONS_DOC __serialize_fields__ = 'parser', 'rules', 'options' def _build_lexer(self, dont_ignore: bool=False) -> BasicLexer: lexer_conf = self.lexer_conf if dont_ignore: from copy import copy lexer_conf = copy(lexer_conf) lexer_conf.ignore = () return BasicLexer(lexer_conf) def _prepare_callbacks(self) -> None: self._callbacks = {} ## if self.options.ambiguity != 'forest': self._parse_tree_builder = ParseTreeBuilder( self.rules, self.options.tree_class or Tree, self.options.propagate_positions, self.options.parser != 'lalr' and self.options.ambiguity == 'explicit', self.options.maybe_placeholders ) self._callbacks = self._parse_tree_builder.create_callback(self.options.transformer) self._callbacks.update(_get_lexer_callbacks(self.options.transformer, self.terminals)) def _build_parser(self) -> "ParsingFrontend": self._prepare_callbacks() _validate_frontend_args(self.options.parser, self.options.lexer) parser_conf = ParserConf(self.rules, self._callbacks, self.options.start) return _construct_parsing_frontend( self.options.parser, self.options.lexer, self.lexer_conf, parser_conf, options=self.options ) def save(self, f, exclude_options: Collection[str] = ()) -> None: #-- data, m = self.memo_serialize([TerminalDef, Rule]) if exclude_options: data["options"] = {n: v for n, v in data["options"].items() if n not in exclude_options} pickle.dump({'data': data, 'memo': m}, f, protocol=pickle.HIGHEST_PROTOCOL) @classmethod def load(cls: Type[_T], f) -> _T: #-- inst = cls.__new__(cls) return inst._load(f) def _deserialize_lexer_conf(self, data: Dict[str, Any], memo: Dict[int, Union[TerminalDef, Rule]], options: LarkOptions) -> LexerConf: lexer_conf = LexerConf.deserialize(data['lexer_conf'], memo) lexer_conf.callbacks = options.lexer_callbacks or {} lexer_conf.re_module = regex if options.regex else re lexer_conf.use_bytes = options.use_bytes lexer_conf.g_regex_flags = options.g_regex_flags lexer_conf.skip_validation = True lexer_conf.postlex = options.postlex return lexer_conf def _load(self: _T, f: Any, **kwargs) -> _T: if isinstance(f, dict): d = f else: d = pickle.load(f) memo_json = d['memo'] data = d['data'] assert memo_json memo = SerializeMemoizer.deserialize(memo_json, {'Rule': Rule, 'TerminalDef': TerminalDef}, {}) options = dict(data['options']) if (set(kwargs) - _LOAD_ALLOWED_OPTIONS) & set(LarkOptions._defaults): raise ConfigurationError("Some options are not allowed when loading a Parser: {}" .format(set(kwargs) - _LOAD_ALLOWED_OPTIONS)) options.update(kwargs) self.options = LarkOptions.deserialize(options, memo) self.rules = [Rule.deserialize(r, memo) for r in data['rules']] self.source_path = '' _validate_frontend_args(self.options.parser, self.options.lexer) self.lexer_conf = self._deserialize_lexer_conf(data['parser'], memo, self.options) self.terminals = self.lexer_conf.terminals self._prepare_callbacks() self._terminals_dict = {t.name: t for t in self.terminals} self.parser = _deserialize_parsing_frontend( data['parser'], memo, self.lexer_conf, self._callbacks, self.options, ## ) return self @classmethod def _load_from_dict(cls, data, memo, **kwargs): inst = cls.__new__(cls) return inst._load({'data': data, 'memo': memo}, **kwargs) @classmethod def open(cls: Type[_T], grammar_filename: str, rel_to: Optional[str]=None, **options) -> _T: #-- if rel_to: basepath = os.path.dirname(rel_to) grammar_filename = os.path.join(basepath, grammar_filename) with open(grammar_filename, encoding='utf8') as f: return cls(f, **options) @classmethod def open_from_package(cls: Type[_T], package: str, grammar_path: str, search_paths: 'Sequence[str]'=[""], **options) -> _T: #-- package_loader = FromPackageLoader(package, search_paths) full_path, text = package_loader(None, grammar_path) options.setdefault('source_path', full_path) options.setdefault('import_paths', []) options['import_paths'].append(package_loader) return cls(text, **options) def __repr__(self): return 'Lark(open(%r), parser=%r, lexer=%r, ...)' % (self.source_path, self.options.parser, self.options.lexer) def lex(self, text: str, dont_ignore: bool=False) -> Iterator[Token]: #-- lexer: Lexer if not hasattr(self, 'lexer') or dont_ignore: lexer = self._build_lexer(dont_ignore) else: lexer = self.lexer lexer_thread = LexerThread.from_text(lexer, text) stream = lexer_thread.lex(None) if self.options.postlex: return self.options.postlex.process(stream) return stream def get_terminal(self, name: str) -> TerminalDef: #-- return self._terminals_dict[name] def parse_interactive(self, text: Optional[str]=None, start: Optional[str]=None) -> 'InteractiveParser': #-- return self.parser.parse_interactive(text, start=start) def parse(self, text: str, start: Optional[str]=None, on_error: 'Optional[Callable[[UnexpectedInput], bool]]'=None) -> 'ParseTree': #-- return self.parser.parse(text, start=start, on_error=on_error) class DedentError(LarkError): pass class Indenter(PostLex, ABC): paren_level: int indent_level: List[int] def __init__(self) -> None: self.paren_level = 0 self.indent_level = [0] assert self.tab_len > 0 def handle_NL(self, token: Token) -> Iterator[Token]: if self.paren_level > 0: return yield token indent_str = token.rsplit('\n', 1)[1] ## indent = indent_str.count(' ') + indent_str.count('\t') * self.tab_len if indent > self.indent_level[-1]: self.indent_level.append(indent) yield Token.new_borrow_pos(self.INDENT_type, indent_str, token) else: while indent < self.indent_level[-1]: self.indent_level.pop() yield Token.new_borrow_pos(self.DEDENT_type, indent_str, token) if indent != self.indent_level[-1]: raise DedentError('Unexpected dedent to column %s. Expected dedent to %s' % (indent, self.indent_level[-1])) def _process(self, stream): for token in stream: if token.type == self.NL_type: yield from self.handle_NL(token) else: yield token if token.type in self.OPEN_PAREN_types: self.paren_level += 1 elif token.type in self.CLOSE_PAREN_types: self.paren_level -= 1 assert self.paren_level >= 0 while len(self.indent_level) > 1: self.indent_level.pop() yield Token(self.DEDENT_type, '') assert self.indent_level == [0], self.indent_level def process(self, stream): self.paren_level = 0 self.indent_level = [0] return self._process(stream) ## @property def always_accept(self): return (self.NL_type,) @property @abstractmethod def NL_type(self) -> str: raise NotImplementedError() @property @abstractmethod def OPEN_PAREN_types(self) -> List[str]: raise NotImplementedError() @property @abstractmethod def CLOSE_PAREN_types(self) -> List[str]: raise NotImplementedError() @property @abstractmethod def INDENT_type(self) -> str: raise NotImplementedError() @property @abstractmethod def DEDENT_type(self) -> str: raise NotImplementedError() @property @abstractmethod def tab_len(self) -> int: raise NotImplementedError() class PythonIndenter(Indenter): NL_type = '_NEWLINE' OPEN_PAREN_types = ['LPAR', 'LSQB', 'LBRACE'] CLOSE_PAREN_types = ['RPAR', 'RSQB', 'RBRACE'] INDENT_type = '_INDENT' DEDENT_type = '_DEDENT' tab_len = 8 import pickle, zlib, base64 DATA = ( {'parser': {'lexer_conf': {'terminals': [{'@': 0}, {'@': 1}, {'@': 2}, {'@': 3}, {'@': 4}, {'@': 5}, {'@': 6}, {'@': 7}, {'@': 8}, {'@': 9}, {'@': 10}, {'@': 11}, {'@': 12}, {'@': 13}, {'@': 14}, {'@': 15}, {'@': 16}, {'@': 17}, {'@': 18}, {'@': 19}, {'@': 20}, {'@': 21}, {'@': 22}, {'@': 23}, {'@': 24}, {'@': 25}, {'@': 26}, {'@': 27}, {'@': 28}, {'@': 29}, {'@': 30}], 'ignore': [], 'g_regex_flags': 0, 'use_bytes': False, 'lexer_type': 'contextual', '__type__': 'LexerConf'}, 'parser_conf': {'rules': [{'@': 31}, {'@': 32}, {'@': 33}, {'@': 34}, {'@': 35}, {'@': 36}, {'@': 37}, {'@': 38}, {'@': 39}, {'@': 40}, {'@': 41}, {'@': 42}, {'@': 43}, {'@': 44}, {'@': 45}, {'@': 46}, {'@': 47}, {'@': 48}, {'@': 49}, {'@': 50}, {'@': 51}, {'@': 52}, {'@': 53}, {'@': 54}, {'@': 55}, {'@': 56}, {'@': 57}, {'@': 58}, {'@': 59}, {'@': 60}, {'@': 61}, {'@': 62}, {'@': 63}, {'@': 64}, {'@': 65}, {'@': 66}, {'@': 67}, {'@': 68}, {'@': 69}, {'@': 70}, {'@': 71}, {'@': 72}, {'@': 73}, {'@': 74}, {'@': 75}, {'@': 76}, {'@': 77}, {'@': 78}, {'@': 79}, {'@': 80}, {'@': 81}, {'@': 82}, {'@': 83}, {'@': 84}, {'@': 85}, {'@': 86}, {'@': 87}, {'@': 88}, {'@': 89}, {'@': 90}, {'@': 91}, {'@': 92}, {'@': 93}, {'@': 94}, {'@': 95}, {'@': 96}, {'@': 97}, {'@': 98}, {'@': 99}, {'@': 100}, {'@': 101}, {'@': 102}, {'@': 103}, {'@': 104}, {'@': 105}, {'@': 106}, {'@': 107}, {'@': 108}, {'@': 109}, {'@': 110}, {'@': 111}, {'@': 112}, {'@': 113}, {'@': 114}, {'@': 115}, {'@': 116}, {'@': 117}, {'@': 118}, {'@': 119}, {'@': 120}, {'@': 121}, {'@': 122}, {'@': 123}, {'@': 124}, {'@': 125}, {'@': 126}, {'@': 127}, {'@': 128}, {'@': 129}, {'@': 130}, {'@': 131}, {'@': 132}, {'@': 133}, {'@': 134}, {'@': 135}, {'@': 136}, {'@': 137}, {'@': 138}, {'@': 139}, {'@': 140}, {'@': 141}, {'@': 142}, {'@': 143}, {'@': 144}, {'@': 145}, {'@': 146}, {'@': 147}, {'@': 148}, {'@': 149}, {'@': 150}, {'@': 151}, {'@': 152}, {'@': 153}, {'@': 154}, {'@': 155}, {'@': 156}, {'@': 157}, {'@': 158}, {'@': 159}, {'@': 160}, {'@': 161}, {'@': 162}, {'@': 163}, {'@': 164}, {'@': 165}, {'@': 166}, {'@': 167}, {'@': 168}, {'@': 169}, {'@': 170}, {'@': 171}, {'@': 172}, {'@': 173}, {'@': 174}, {'@': 175}, {'@': 176}, {'@': 177}, {'@': 178}, {'@': 179}, {'@': 180}, {'@': 181}, {'@': 182}, {'@': 183}, {'@': 184}, {'@': 185}, {'@': 186}, {'@': 187}, {'@': 188}, {'@': 189}, {'@': 190}, {'@': 191}, {'@': 192}, {'@': 193}, {'@': 194}, {'@': 195}, {'@': 196}, {'@': 197}, {'@': 198}, {'@': 199}, {'@': 200}, {'@': 201}, {'@': 202}, {'@': 203}, {'@': 204}, {'@': 205}, {'@': 206}, {'@': 207}, {'@': 208}, {'@': 209}, {'@': 210}, {'@': 211}, {'@': 212}, {'@': 213}, {'@': 214}, {'@': 215}, {'@': 216}, {'@': 217}, {'@': 218}], 'start': ['start'], 'parser_type': 'lalr', '__type__': 'ParserConf'}, 'parser': {'tokens': {0: 'PHANTOMS', 1: 'SLASH', 2: 'BOX_NAME', 3: 'PERCENT', 4: 'BREAK', 5: 'CONSTRAINT_LPAREN', 6: 'NL', 7: 'MINUS', 8: 'INDENT', 9: '$END', 10: 'PLUS', 11: 'RBRACKET', 12: 'seq{constraint_target}', 13: 'constraint_target', 14: 'box_name', 15: 'COLON', 16: 'CONSTRAINT_LEG', 17: 'SP', 18: 'constraint_leg', 19: 'box_name_ref', 20: '__constraint_target_star_1', 21: 'COMMA', 22: 'leg_arrow', 23: 'LBRACKET', 24: 'LEG_ARROW', 25: 'entity_name_ref', 26: 'inheritance_parent', 27: 'comment', 28: 'assoc_name_def', 29: 'box_def_prefix', 30: 'assoc_clause', 31: 'constraint_clause', 32: 'entity_clause', 33: 'entity_name_def', 34: 'inheritance_clause', 35: 'phantoms', 36: 'clause', 37: '__ANON_1', 38: 'seq{inheritance_child}', 39: 'inheritance_child', 40: 'ATTR', 41: 'HASHTAG', 42: 'inheritance_name', 43: 'BACKSLASH', 44: 'INHERITANCE_NAME', 45: 'indent', 46: 'start', 47: 'line', 48: '__start_star_0', 49: 'break_', 50: 'NUMBER', 51: 'constraint_coords', 52: '_constraint_coord', 53: 'CONSTRAINT_RPAREN', 54: 'constraint_name', 55: 'CONSTRAINT_NAME', 56: '__seq{inheritance_child}_star_7', 57: 'note', 58: '__ANON_0', 59: 'leg_note', 60: 'INHERITANCE_ARROW', 61: 'inheritance_arrow', 62: 'MORETHAN', 63: 'ID_MARK', 64: 'attr', 65: 'assoc_attr', 66: 'typed_attr', 67: 'seq{assoc_attr}', 68: '_id_symbols', 69: 'entity_or_table_attr', 70: 'id_mark', 71: 'ID_GROUPS', 72: 'id_groups', 73: 'that_table', 74: 'foreign_reference', 75: 'this_table_attr', 76: 'seq{entity_or_table_attr}', 77: 'CARD', 78: 'card', 79: 'seq{typed_attr}', 80: 'constraint_note', 81: '_assoc_card', 82: 'card_prefix', 83: 'assoc_leg', 84: 'card_hidden', 85: '__seq{typed_attr}_star_6', 86: 'seq{assoc_leg}', 87: '__seq{constraint_target}_star_5', 88: '__seq{assoc_attr}_star_2', 89: 'that_table_attr', 90: '__seq{entity_or_table_attr}_star_4', 91: 'datatype', 92: '__seq{assoc_leg}_star_3'}, 'states': {0: {0: (1, {'@': 119}), 1: (1, {'@': 119}), 2: (1, {'@': 119}), 3: (1, {'@': 119}), 4: (1, {'@': 119}), 5: (1, {'@': 119}), 6: (1, {'@': 119}), 7: (1, {'@': 119}), 8: (1, {'@': 119}), 9: (1, {'@': 119}), 10: (1, {'@': 119})}, 1: {11: (0, 173)}, 2: {12: (0, 310), 13: (0, 241), 14: (0, 215), 15: (0, 117), 2: (0, 60), 16: (0, 237), 6: (0, 126), 17: (0, 221), 18: (0, 239), 19: (0, 224), 20: (0, 232)}, 3: {6: (1, {'@': 68}), 15: (1, {'@': 68}), 21: (1, {'@': 68})}, 4: {0: (1, {'@': 43}), 1: (1, {'@': 43}), 2: (1, {'@': 43}), 5: (1, {'@': 43}), 3: (1, {'@': 43}), 4: (1, {'@': 43}), 6: (1, {'@': 43}), 7: (1, {'@': 43}), 8: (1, {'@': 43}), 9: (1, {'@': 43}), 10: (1, {'@': 43})}, 5: {21: (1, {'@': 59}), 15: (1, {'@': 99})}, 6: {0: (1, {'@': 50}), 2: (1, {'@': 50}), 3: (1, {'@': 50}), 6: (1, {'@': 50}), 7: (1, {'@': 50}), 1: (1, {'@': 50}), 4: (1, {'@': 50}), 5: (1, {'@': 50}), 8: (1, {'@': 50}), 9: (1, {'@': 50}), 10: (1, {'@': 50})}, 7: {17: (0, 249), 22: (0, 93), 23: (0, 70), 24: (0, 362), 25: (0, 79), 2: (0, 60), 14: (0, 341)}, 8: {17: (0, 323), 26: (0, 123), 2: (0, 60), 25: (0, 246), 14: (0, 341)}, 9: {6: (1, {'@': 82}), 15: (1, {'@': 82}), 21: (1, {'@': 82})}, 10: {0: (1, {'@': 49}), 2: (1, {'@': 49}), 3: (1, {'@': 49}), 6: (1, {'@': 49}), 7: (1, {'@': 49}), 1: (1, {'@': 49}), 4: (1, {'@': 49}), 5: (1, {'@': 49}), 8: (1, {'@': 49}), 9: (1, {'@': 49}), 10: (1, {'@': 49})}, 11: {15: (0, 228), 23: (0, 251), 12: (0, 250), 13: (0, 241), 14: (0, 215), 2: (0, 60), 6: (0, 218), 19: (0, 224), 20: (0, 232), 16: (0, 237), 17: (0, 221), 18: (0, 239)}, 12: {5: (0, 42), 14: (0, 5), 1: (0, 21), 2: (0, 60), 27: (0, 273), 28: (0, 82), 0: (0, 86), 6: (0, 284), 10: (0, 19), 29: (0, 26), 30: (0, 10), 31: (0, 36), 3: (0, 13), 32: (0, 6), 33: (0, 53), 34: (0, 34), 7: (0, 160), 35: (0, 297), 36: (0, 4)}, 13: {37: (0, 262)}, 14: {0: (1, {'@': 182}), 1: (1, {'@': 182}), 2: (1, {'@': 182}), 5: (1, {'@': 182}), 3: (1, {'@': 182}), 4: (1, {'@': 182}), 6: (1, {'@': 182}), 7: (1, {'@': 182}), 8: (1, {'@': 182}), 9: (1, {'@': 182}), 10: (1, {'@': 182})}, 15: {2: (0, 60), 38: (0, 302), 39: (0, 47), 25: (0, 38), 14: (0, 341)}, 16: {0: (1, {'@': 202}), 1: (1, {'@': 202}), 2: (1, {'@': 202}), 5: (1, {'@': 202}), 3: (1, {'@': 202}), 4: (1, {'@': 202}), 6: (1, {'@': 202}), 7: (1, {'@': 202}), 8: (1, {'@': 202}), 9: (1, {'@': 202}), 10: (1, {'@': 202})}, 17: {0: (1, {'@': 113}), 1: (1, {'@': 113}), 2: (1, {'@': 113}), 3: (1, {'@': 113}), 4: (1, {'@': 113}), 5: (1, {'@': 113}), 6: (1, {'@': 113}), 7: (1, {'@': 113}), 8: (1, {'@': 113}), 9: (1, {'@': 113}), 10: (1, {'@': 113})}, 18: {0: (1, {'@': 167}), 1: (1, {'@': 167}), 2: (1, {'@': 167}), 3: (1, {'@': 167}), 4: (1, {'@': 167}), 5: (1, {'@': 167}), 6: (1, {'@': 167}), 7: (1, {'@': 167}), 8: (1, {'@': 167}), 9: (1, {'@': 167}), 10: (1, {'@': 167})}, 19: {2: (1, {'@': 36})}, 20: {40: (1, {'@': 104}), 6: (1, {'@': 104}), 41: (1, {'@': 104}), 21: (1, {'@': 104})}, 21: {42: (0, 293), 43: (0, 274), 44: (0, 279)}, 22: {6: (0, 69)}, 23: {6: (1, {'@': 64}), 15: (1, {'@': 64}), 21: (1, {'@': 64})}, 24: {5: (0, 42), 4: (0, 14), 1: (0, 21), 45: (0, 12), 27: (0, 30), 14: (0, 5), 2: (0, 60), 46: (0, 50), 28: (0, 82), 0: (0, 86), 47: (0, 16), 10: (0, 19), 29: (0, 26), 48: (0, 29), 30: (0, 10), 31: (0, 36), 3: (0, 13), 35: (0, 40), 49: (0, 67), 8: (0, 89), 36: (0, 46), 32: (0, 6), 33: (0, 53), 34: (0, 34), 7: (0, 160), 6: (0, 268), 9: (1, {'@': 40})}, 25: {38: (0, 158), 2: (0, 60), 39: (0, 47), 25: (0, 38), 14: (0, 341)}, 26: {14: (0, 5), 2: (0, 60), 33: (0, 143), 28: (0, 105)}, 27: {0: (1, {'@': 158}), 1: (1, {'@': 158}), 2: (1, {'@': 158}), 3: (1, {'@': 158}), 4: (1, {'@': 158}), 5: (1, {'@': 158}), 6: (1, {'@': 158}), 7: (1, {'@': 158}), 8: (1, {'@': 158}), 9: (1, {'@': 158}), 10: (1, {'@': 158})}, 28: {0: (1, {'@': 161}), 1: (1, {'@': 161}), 2: (1, {'@': 161}), 3: (1, {'@': 161}), 4: (1, {'@': 161}), 5: (1, {'@': 161}), 6: (1, {'@': 161}), 7: (1, {'@': 161}), 8: (1, {'@': 161}), 9: (1, {'@': 161}), 10: (1, {'@': 161})}, 29: {5: (0, 42), 14: (0, 5), 1: (0, 21), 45: (0, 12), 27: (0, 30), 2: (0, 60), 28: (0, 82), 0: (0, 86), 10: (0, 19), 49: (0, 94), 29: (0, 26), 30: (0, 10), 31: (0, 36), 3: (0, 13), 35: (0, 40), 47: (0, 96), 8: (0, 89), 36: (0, 46), 32: (0, 6), 33: (0, 53), 34: (0, 34), 7: (0, 160), 6: (0, 268), 4: (0, 14), 9: (1, {'@': 39})}, 30: {0: (1, {'@': 46}), 1: (1, {'@': 46}), 2: (1, {'@': 46}), 5: (1, {'@': 46}), 3: (1, {'@': 46}), 4: (1, {'@': 46}), 6: (1, {'@': 46}), 7: (1, {'@': 46}), 8: (1, {'@': 46}), 9: (1, {'@': 46}), 10: (1, {'@': 46})}, 31: {0: (1, {'@': 163}), 1: (1, {'@': 163}), 2: (1, {'@': 163}), 3: (1, {'@': 163}), 4: (1, {'@': 163}), 5: (1, {'@': 163}), 6: (1, {'@': 163}), 7: (1, {'@': 163}), 8: (1, {'@': 163}), 9: (1, {'@': 163}), 10: (1, {'@': 163})}, 32: {39: (0, 47), 17: (0, 288), 2: (0, 60), 25: (0, 38), 38: (0, 182), 14: (0, 341)}, 33: {6: (0, 84)}, 34: {0: (1, {'@': 52}), 2: (1, {'@': 52}), 3: (1, {'@': 52}), 6: (1, {'@': 52}), 7: (1, {'@': 52}), 1: (1, {'@': 52}), 4: (1, {'@': 52}), 5: (1, {'@': 52}), 8: (1, {'@': 52}), 9: (1, {'@': 52}), 10: (1, {'@': 52})}, 35: {0: (1, {'@': 166}), 1: (1, {'@': 166}), 2: (1, {'@': 166}), 3: (1, {'@': 166}), 4: (1, {'@': 166}), 5: (1, {'@': 166}), 6: (1, {'@': 166}), 7: (1, {'@': 166}), 8: (1, {'@': 166}), 9: (1, {'@': 166}), 10: (1, {'@': 166})}, 36: {0: (1, {'@': 51}), 2: (1, {'@': 51}), 3: (1, {'@': 51}), 6: (1, {'@': 51}), 7: (1, {'@': 51}), 1: (1, {'@': 51}), 4: (1, {'@': 51}), 5: (1, {'@': 51}), 8: (1, {'@': 51}), 9: (1, {'@': 51}), 10: (1, {'@': 51})}, 37: {0: (1, {'@': 155}), 1: (1, {'@': 155}), 2: (1, {'@': 155}), 3: (1, {'@': 155}), 4: (1, {'@': 155}), 5: (1, {'@': 155}), 6: (1, {'@': 155}), 7: (1, {'@': 155}), 8: (1, {'@': 155}), 9: (1, {'@': 155}), 10: (1, {'@': 155})}, 38: {6: (1, {'@': 176}), 15: (1, {'@': 176}), 21: (1, {'@': 176})}, 39: {15: (0, 255), 6: (0, 259)}, 40: {0: (1, {'@': 45}), 1: (1, {'@': 45}), 2: (1, {'@': 45}), 5: (1, {'@': 45}), 3: (1, {'@': 45}), 4: (1, {'@': 45}), 6: (1, {'@': 45}), 7: (1, {'@': 45}), 8: (1, {'@': 45}), 9: (1, {'@': 45}), 10: (1, {'@': 45})}, 41: {50: (0, 312), 19: (0, 325), 2: (0, 60), 51: (0, 299), 14: (0, 215), 52: (0, 217)}, 42: {53: (0, 11), 54: (0, 83), 55: (0, 290)}, 43: {6: (0, 109), 15: (0, 189)}, 44: {11: (0, 153)}, 45: {0: (1, {'@': 149}), 1: (1, {'@': 149}), 2: (1, {'@': 149}), 3: (1, {'@': 149}), 4: (1, {'@': 149}), 5: (1, {'@': 149}), 6: (1, {'@': 149}), 7: (1, {'@': 149}), 8: (1, {'@': 149}), 9: (1, {'@': 149}), 10: (1, {'@': 149})}, 46: {0: (1, {'@': 47}), 1: (1, {'@': 47}), 2: (1, {'@': 47}), 5: (1, {'@': 47}), 3: (1, {'@': 47}), 4: (1, {'@': 47}), 6: (1, {'@': 47}), 7: (1, {'@': 47}), 8: (1, {'@': 47}), 9: (1, {'@': 47}), 10: (1, {'@': 47})}, 47: {21: (0, 104), 56: (0, 154), 6: (1, {'@': 200}), 15: (1, {'@': 200})}, 48: {57: (0, 170), 58: (0, 118), 59: (0, 194), 11: (0, 176)}, 49: {17: (0, 278), 39: (0, 47), 38: (0, 181), 2: (0, 60), 25: (0, 38), 14: (0, 341)}, 50: {}, 51: {25: (0, 246), 26: (0, 364), 2: (0, 60), 14: (0, 341)}, 52: {0: (1, {'@': 147}), 1: (1, {'@': 147}), 2: (1, {'@': 147}), 3: (1, {'@': 147}), 4: (1, {'@': 147}), 5: (1, {'@': 147}), 6: (1, {'@': 147}), 7: (1, {'@': 147}), 8: (1, {'@': 147}), 9: (1, {'@': 147}), 10: (1, {'@': 147})}, 53: {15: (0, 120)}, 54: {0: (1, {'@': 56}), 1: (1, {'@': 56}), 2: (1, {'@': 56}), 3: (1, {'@': 56}), 4: (1, {'@': 56}), 5: (1, {'@': 56}), 6: (1, {'@': 56}), 7: (1, {'@': 56}), 8: (1, {'@': 56}), 9: (1, {'@': 56}), 10: (1, {'@': 56})}, 55: {6: (1, {'@': 216}), 21: (1, {'@': 216})}, 56: {60: (0, 136), 17: (0, 111), 61: (0, 165)}, 57: {2: (0, 60), 38: (0, 289), 39: (0, 47), 25: (0, 38), 14: (0, 341)}, 58: {11: (0, 195)}, 59: {2: (0, 60), 39: (0, 47), 25: (0, 38), 38: (0, 172), 14: (0, 341)}, 60: {6: (1, {'@': 179}), 15: (1, {'@': 179}), 21: (1, {'@': 179}), 17: (1, {'@': 179}), 60: (1, {'@': 179}), 62: (1, {'@': 179})}, 61: {63: (1, {'@': 188})}, 62: {6: (1, {'@': 217}), 15: (1, {'@': 217}), 21: (1, {'@': 217})}, 63: {6: (1, {'@': 69}), 15: (1, {'@': 69}), 21: (1, {'@': 69})}, 64: {0: (1, {'@': 145}), 1: (1, {'@': 145}), 2: (1, {'@': 145}), 3: (1, {'@': 145}), 4: (1, {'@': 145}), 5: (1, {'@': 145}), 6: (1, {'@': 145}), 7: (1, {'@': 145}), 8: (1, {'@': 145}), 9: (1, {'@': 145}), 10: (1, {'@': 145})}, 65: {6: (1, {'@': 80}), 15: (1, {'@': 80}), 21: (1, {'@': 80})}, 66: {0: (1, {'@': 143}), 1: (1, {'@': 143}), 2: (1, {'@': 143}), 3: (1, {'@': 143}), 4: (1, {'@': 143}), 5: (1, {'@': 143}), 6: (1, {'@': 143}), 7: (1, {'@': 143}), 8: (1, {'@': 143}), 9: (1, {'@': 143}), 10: (1, {'@': 143})}, 67: {0: (1, {'@': 201}), 1: (1, {'@': 201}), 2: (1, {'@': 201}), 5: (1, {'@': 201}), 3: (1, {'@': 201}), 4: (1, {'@': 201}), 6: (1, {'@': 201}), 7: (1, {'@': 201}), 8: (1, {'@': 201}), 9: (1, {'@': 201}), 10: (1, {'@': 201})}, 68: {40: (0, 305), 64: (0, 110), 65: (0, 261), 66: (0, 266), 63: (0, 282), 67: (0, 80), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 69: {0: (1, {'@': 159}), 1: (1, {'@': 159}), 2: (1, {'@': 159}), 3: (1, {'@': 159}), 4: (1, {'@': 159}), 5: (1, {'@': 159}), 6: (1, {'@': 159}), 7: (1, {'@': 159}), 8: (1, {'@': 159}), 9: (1, {'@': 159}), 10: (1, {'@': 159})}, 70: {57: (0, 170), 58: (0, 118), 11: (0, 148), 59: (0, 352)}, 71: {6: (1, {'@': 32}), 21: (1, {'@': 32})}, 72: {6: (0, 164), 15: (0, 190)}, 73: {40: (1, {'@': 105}), 6: (1, {'@': 105}), 41: (1, {'@': 105}), 21: (1, {'@': 105})}, 74: {40: (0, 305), 68: (0, 295), 69: (0, 309), 66: (0, 348), 70: (0, 73), 71: (0, 61), 64: (0, 110), 41: (0, 90), 63: (0, 395), 72: (0, 334), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 75: {6: (0, 343)}, 76: {11: (1, {'@': 35})}, 77: {6: (0, 66)}, 78: {0: (1, {'@': 170}), 1: (1, {'@': 170}), 2: (1, {'@': 170}), 3: (1, {'@': 170}), 4: (1, {'@': 170}), 5: (1, {'@': 170}), 6: (1, {'@': 170}), 7: (1, {'@': 170}), 8: (1, {'@': 170}), 9: (1, {'@': 170}), 10: (1, {'@': 170})}, 79: {6: (1, {'@': 71}), 15: (1, {'@': 71}), 21: (1, {'@': 71})}, 80: {6: (0, 275)}, 81: {15: (0, 257), 6: (0, 167)}, 82: {21: (0, 277)}, 83: {53: (0, 264)}, 84: {0: (1, {'@': 151}), 1: (1, {'@': 151}), 2: (1, {'@': 151}), 3: (1, {'@': 151}), 4: (1, {'@': 151}), 5: (1, {'@': 151}), 6: (1, {'@': 151}), 7: (1, {'@': 151}), 8: (1, {'@': 151}), 9: (1, {'@': 151}), 10: (1, {'@': 151})}, 85: {6: (0, 236), 15: (0, 270)}, 86: {0: (1, {'@': 181}), 2: (1, {'@': 181}), 3: (1, {'@': 181}), 6: (1, {'@': 181}), 7: (1, {'@': 181}), 1: (1, {'@': 181}), 4: (1, {'@': 181}), 5: (1, {'@': 181}), 8: (1, {'@': 181}), 9: (1, {'@': 181}), 10: (1, {'@': 181})}, 87: {11: (0, 390)}, 88: {2: (0, 60), 25: (0, 391), 73: (0, 357), 14: (0, 341)}, 89: {0: (1, {'@': 53}), 1: (1, {'@': 53}), 2: (1, {'@': 53}), 5: (1, {'@': 53}), 3: (1, {'@': 53}), 6: (1, {'@': 53}), 7: (1, {'@': 53}), 10: (1, {'@': 53})}, 90: {40: (0, 305), 74: (0, 300), 64: (0, 333), 75: (0, 349)}, 91: {15: (0, 335), 13: (0, 241), 14: (0, 215), 12: (0, 306), 2: (0, 60), 16: (0, 237), 6: (0, 345), 17: (0, 221), 18: (0, 239), 19: (0, 224), 20: (0, 232)}, 92: {6: (0, 28)}, 93: {23: (0, 48), 17: (0, 260), 25: (0, 258), 2: (0, 60), 14: (0, 341)}, 94: {0: (1, {'@': 203}), 1: (1, {'@': 203}), 2: (1, {'@': 203}), 5: (1, {'@': 203}), 3: (1, {'@': 203}), 4: (1, {'@': 203}), 6: (1, {'@': 203}), 7: (1, {'@': 203}), 8: (1, {'@': 203}), 9: (1, {'@': 203}), 10: (1, {'@': 203})}, 95: {15: (0, 328), 6: (0, 0)}, 96: {0: (1, {'@': 204}), 1: (1, {'@': 204}), 2: (1, {'@': 204}), 5: (1, {'@': 204}), 3: (1, {'@': 204}), 4: (1, {'@': 204}), 6: (1, {'@': 204}), 7: (1, {'@': 204}), 8: (1, {'@': 204}), 9: (1, {'@': 204}), 10: (1, {'@': 204})}, 97: {6: (1, {'@': 137}), 15: (1, {'@': 137}), 21: (1, {'@': 137})}, 98: {0: (1, {'@': 160}), 1: (1, {'@': 160}), 2: (1, {'@': 160}), 3: (1, {'@': 160}), 4: (1, {'@': 160}), 5: (1, {'@': 160}), 6: (1, {'@': 160}), 7: (1, {'@': 160}), 8: (1, {'@': 160}), 9: (1, {'@': 160}), 10: (1, {'@': 160})}, 99: {60: (0, 136), 61: (0, 32)}, 100: {0: (1, {'@': 169}), 1: (1, {'@': 169}), 2: (1, {'@': 169}), 3: (1, {'@': 169}), 4: (1, {'@': 169}), 5: (1, {'@': 169}), 6: (1, {'@': 169}), 7: (1, {'@': 169}), 8: (1, {'@': 169}), 9: (1, {'@': 169}), 10: (1, {'@': 169})}, 101: {2: (0, 60), 38: (0, 81), 39: (0, 47), 25: (0, 38), 14: (0, 341)}, 102: {0: (1, {'@': 153}), 1: (1, {'@': 153}), 2: (1, {'@': 153}), 3: (1, {'@': 153}), 4: (1, {'@': 153}), 5: (1, {'@': 153}), 6: (1, {'@': 153}), 7: (1, {'@': 153}), 8: (1, {'@': 153}), 9: (1, {'@': 153}), 10: (1, {'@': 153})}, 103: {6: (1, {'@': 140})}, 104: {2: (0, 60), 39: (0, 62), 25: (0, 38), 14: (0, 341)}, 105: {21: (0, 234)}, 106: {0: (1, {'@': 171}), 1: (1, {'@': 171}), 2: (1, {'@': 171}), 3: (1, {'@': 171}), 4: (1, {'@': 171}), 5: (1, {'@': 171}), 6: (1, {'@': 171}), 7: (1, {'@': 171}), 8: (1, {'@': 171}), 9: (1, {'@': 171}), 10: (1, {'@': 171})}, 107: {6: (1, {'@': 101}), 21: (1, {'@': 101})}, 108: {0: (1, {'@': 152}), 1: (1, {'@': 152}), 2: (1, {'@': 152}), 3: (1, {'@': 152}), 4: (1, {'@': 152}), 5: (1, {'@': 152}), 6: (1, {'@': 152}), 7: (1, {'@': 152}), 8: (1, {'@': 152}), 9: (1, {'@': 152}), 10: (1, {'@': 152})}, 109: {0: (1, {'@': 174}), 1: (1, {'@': 174}), 2: (1, {'@': 174}), 3: (1, {'@': 174}), 4: (1, {'@': 174}), 5: (1, {'@': 174}), 6: (1, {'@': 174}), 7: (1, {'@': 174}), 8: (1, {'@': 174}), 9: (1, {'@': 174}), 10: (1, {'@': 174})}, 110: {23: (0, 319), 6: (1, {'@': 33}), 21: (1, {'@': 33})}, 111: {61: (0, 49), 60: (0, 136)}, 112: {6: (1, {'@': 70}), 15: (1, {'@': 70}), 21: (1, {'@': 70})}, 113: {40: (0, 305), 64: (0, 110), 66: (0, 55), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 114: {17: (0, 330), 2: (0, 60), 14: (0, 215), 19: (0, 381)}, 115: {50: (0, 312), 19: (0, 325), 2: (0, 60), 51: (0, 119), 14: (0, 215), 52: (0, 217)}, 116: {0: (1, {'@': 110}), 1: (1, {'@': 110}), 2: (1, {'@': 110}), 3: (1, {'@': 110}), 4: (1, {'@': 110}), 5: (1, {'@': 110}), 6: (1, {'@': 110}), 7: (1, {'@': 110}), 8: (1, {'@': 110}), 9: (1, {'@': 110}), 10: (1, {'@': 110})}, 117: {50: (0, 312), 19: (0, 325), 2: (0, 60), 51: (0, 370), 14: (0, 215), 52: (0, 217)}, 118: {11: (1, {'@': 38})}, 119: {6: (0, 192)}, 120: {68: (0, 295), 76: (0, 269), 70: (0, 73), 71: (0, 61), 64: (0, 110), 41: (0, 90), 69: (0, 318), 72: (0, 334), 40: (0, 305), 6: (0, 332), 66: (0, 348), 63: (0, 395), 21: (1, {'@': 34})}, 121: {6: (0, 31)}, 122: {14: (0, 215), 13: (0, 342), 2: (0, 60), 17: (0, 221), 16: (0, 237), 18: (0, 239), 19: (0, 224), 20: (0, 232)}, 123: {60: (0, 136), 17: (0, 353), 61: (0, 287)}, 124: {2: (0, 60), 39: (0, 47), 25: (0, 38), 38: (0, 219), 14: (0, 341)}, 125: {24: (1, {'@': 85}), 23: (1, {'@': 85}), 2: (1, {'@': 85}), 17: (1, {'@': 85})}, 126: {0: (1, {'@': 117}), 1: (1, {'@': 117}), 2: (1, {'@': 117}), 3: (1, {'@': 117}), 4: (1, {'@': 117}), 5: (1, {'@': 117}), 6: (1, {'@': 117}), 7: (1, {'@': 117}), 8: (1, {'@': 117}), 9: (1, {'@': 117}), 10: (1, {'@': 117})}, 127: {0: (1, {'@': 97}), 1: (1, {'@': 97}), 2: (1, {'@': 97}), 3: (1, {'@': 97}), 4: (1, {'@': 97}), 5: (1, {'@': 97}), 6: (1, {'@': 97}), 7: (1, {'@': 97}), 8: (1, {'@': 97}), 9: (1, {'@': 97}), 10: (1, {'@': 97})}, 128: {15: (0, 253), 6: (0, 35)}, 129: {0: (1, {'@': 144}), 1: (1, {'@': 144}), 2: (1, {'@': 144}), 3: (1, {'@': 144}), 4: (1, {'@': 144}), 5: (1, {'@': 144}), 6: (1, {'@': 144}), 7: (1, {'@': 144}), 8: (1, {'@': 144}), 9: (1, {'@': 144}), 10: (1, {'@': 144})}, 130: {6: (1, {'@': 79}), 15: (1, {'@': 79}), 21: (1, {'@': 79})}, 131: {77: (0, 356), 78: (0, 280)}, 132: {40: (0, 305), 66: (0, 233), 79: (0, 22), 64: (0, 110), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 133: {21: (0, 338), 6: (1, {'@': 195}), 15: (1, {'@': 195})}, 134: {6: (1, {'@': 215}), 21: (1, {'@': 215})}, 135: {6: (1, {'@': 136}), 15: (1, {'@': 136}), 21: (1, {'@': 136})}, 136: {2: (1, {'@': 186}), 17: (1, {'@': 186})}, 137: {6: (0, 18)}, 138: {6: (0, 37)}, 139: {11: (0, 2), 80: (0, 1), 57: (0, 389), 58: (0, 118)}, 140: {57: (0, 170), 59: (0, 44), 58: (0, 118), 11: (0, 296)}, 141: {11: (0, 142)}, 142: {2: (0, 60), 25: (0, 238), 14: (0, 341)}, 143: {15: (0, 243)}, 144: {40: (0, 305), 79: (0, 77), 66: (0, 233), 64: (0, 110), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 145: {40: (0, 305), 66: (0, 233), 64: (0, 110), 79: (0, 185), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 146: {6: (0, 64)}, 147: {0: (1, {'@': 130}), 1: (1, {'@': 130}), 2: (1, {'@': 130}), 3: (1, {'@': 130}), 4: (1, {'@': 130}), 5: (1, {'@': 130}), 6: (1, {'@': 130}), 7: (1, {'@': 130}), 8: (1, {'@': 130}), 9: (1, {'@': 130}), 10: (1, {'@': 130})}, 148: {2: (0, 60), 25: (0, 112), 14: (0, 341)}, 149: {15: (0, 145), 6: (0, 27)}, 150: {6: (0, 52)}, 151: {17: (0, 15), 39: (0, 47), 2: (0, 60), 25: (0, 38), 38: (0, 43), 14: (0, 341)}, 152: {0: (1, {'@': 121}), 1: (1, {'@': 121}), 2: (1, {'@': 121}), 3: (1, {'@': 121}), 4: (1, {'@': 121}), 5: (1, {'@': 121}), 6: (1, {'@': 121}), 7: (1, {'@': 121}), 8: (1, {'@': 121}), 9: (1, {'@': 121}), 10: (1, {'@': 121})}, 153: {2: (0, 60), 25: (0, 247), 14: (0, 341)}, 154: {21: (0, 248), 6: (1, {'@': 199}), 15: (1, {'@': 199})}, 155: {6: (1, {'@': 77}), 15: (1, {'@': 77}), 21: (1, {'@': 77})}, 156: {6: (1, {'@': 218}), 15: (1, {'@': 218}), 21: (1, {'@': 218})}, 157: {2: (0, 60), 14: (0, 341), 25: (0, 9)}, 158: {6: (0, 98), 15: (0, 132)}, 159: {50: (0, 312), 19: (0, 325), 2: (0, 60), 14: (0, 215), 52: (0, 217), 51: (0, 359)}, 160: {2: (1, {'@': 37})}, 161: {21: (0, 292), 6: (1, {'@': 191}), 15: (1, {'@': 191})}, 162: {6: (1, {'@': 214}), 15: (1, {'@': 214}), 21: (1, {'@': 214})}, 163: {39: (0, 47), 38: (0, 265), 17: (0, 59), 2: (0, 60), 25: (0, 38), 14: (0, 341)}, 164: {0: (1, {'@': 162}), 1: (1, {'@': 162}), 2: (1, {'@': 162}), 3: (1, {'@': 162}), 4: (1, {'@': 162}), 5: (1, {'@': 162}), 6: (1, {'@': 162}), 7: (1, {'@': 162}), 8: (1, {'@': 162}), 9: (1, {'@': 162}), 10: (1, {'@': 162})}, 165: {17: (0, 57), 39: (0, 47), 2: (0, 60), 25: (0, 38), 14: (0, 341), 38: (0, 85)}, 166: {0: (1, {'@': 127}), 1: (1, {'@': 127}), 2: (1, {'@': 127}), 3: (1, {'@': 127}), 4: (1, {'@': 127}), 5: (1, {'@': 127}), 6: (1, {'@': 127}), 7: (1, {'@': 127}), 8: (1, {'@': 127}), 9: (1, {'@': 127}), 10: (1, {'@': 127})}, 167: {0: (1, {'@': 156}), 1: (1, {'@': 156}), 2: (1, {'@': 156}), 3: (1, {'@': 156}), 4: (1, {'@': 156}), 5: (1, {'@': 156}), 6: (1, {'@': 156}), 7: (1, {'@': 156}), 8: (1, {'@': 156}), 9: (1, {'@': 156}), 10: (1, {'@': 156})}, 168: {0: (1, {'@': 172}), 1: (1, {'@': 172}), 2: (1, {'@': 172}), 3: (1, {'@': 172}), 4: (1, {'@': 172}), 5: (1, {'@': 172}), 6: (1, {'@': 172}), 7: (1, {'@': 172}), 8: (1, {'@': 172}), 9: (1, {'@': 172}), 10: (1, {'@': 172})}, 169: {81: (0, 7), 82: (0, 360), 2: (0, 60), 1: (0, 331), 17: (0, 374), 22: (0, 315), 7: (0, 347), 83: (0, 276), 84: (0, 368), 77: (0, 356), 24: (0, 362), 23: (0, 369), 78: (0, 337), 25: (0, 386), 14: (0, 341), 63: (0, 327)}, 170: {11: (1, {'@': 92})}, 171: {15: (0, 303), 6: (0, 313)}, 172: {15: (0, 366), 6: (0, 108)}, 173: {12: (0, 39), 13: (0, 241), 14: (0, 215), 15: (0, 41), 2: (0, 60), 6: (0, 17), 16: (0, 237), 17: (0, 221), 18: (0, 239), 19: (0, 224), 20: (0, 232)}, 174: {50: (0, 312), 19: (0, 325), 2: (0, 60), 51: (0, 324), 14: (0, 215), 52: (0, 217)}, 175: {2: (0, 60), 23: (0, 256), 14: (0, 341), 25: (0, 285)}, 176: {2: (0, 60), 14: (0, 341), 25: (0, 23)}, 177: {59: (0, 58), 57: (0, 170), 58: (0, 118), 11: (0, 294)}, 178: {39: (0, 47), 17: (0, 25), 38: (0, 72), 2: (0, 60), 25: (0, 38), 14: (0, 341)}, 179: {2: (0, 60), 14: (0, 341), 25: (0, 203)}, 180: {6: (1, {'@': 211}), 21: (1, {'@': 211})}, 181: {15: (0, 212), 6: (0, 210)}, 182: {6: (0, 78), 15: (0, 214)}, 183: {0: (1, {'@': 125}), 1: (1, {'@': 125}), 2: (1, {'@': 125}), 3: (1, {'@': 125}), 4: (1, {'@': 125}), 5: (1, {'@': 125}), 6: (1, {'@': 125}), 7: (1, {'@': 125}), 8: (1, {'@': 125}), 9: (1, {'@': 125}), 10: (1, {'@': 125})}, 184: {6: (1, {'@': 73}), 15: (1, {'@': 73}), 21: (1, {'@': 73})}, 185: {6: (0, 206)}, 186: {0: (1, {'@': 154}), 1: (1, {'@': 154}), 2: (1, {'@': 154}), 3: (1, {'@': 154}), 4: (1, {'@': 154}), 5: (1, {'@': 154}), 6: (1, {'@': 154}), 7: (1, {'@': 154}), 8: (1, {'@': 154}), 9: (1, {'@': 154}), 10: (1, {'@': 154})}, 187: {6: (1, {'@': 100}), 21: (1, {'@': 100})}, 188: {57: (0, 170), 59: (0, 141), 58: (0, 118), 11: (0, 179)}, 189: {40: (0, 305), 79: (0, 245), 66: (0, 233), 64: (0, 110), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 190: {40: (0, 305), 79: (0, 92), 66: (0, 233), 64: (0, 110), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 191: {2: (0, 60), 14: (0, 341), 25: (0, 63)}, 192: {0: (1, {'@': 120}), 1: (1, {'@': 120}), 2: (1, {'@': 120}), 3: (1, {'@': 120}), 4: (1, {'@': 120}), 5: (1, {'@': 120}), 6: (1, {'@': 120}), 7: (1, {'@': 120}), 8: (1, {'@': 120}), 9: (1, {'@': 120}), 10: (1, {'@': 120})}, 193: {6: (1, {'@': 60}), 15: (1, {'@': 60}), 21: (1, {'@': 60})}, 194: {11: (0, 227)}, 195: {2: (0, 60), 25: (0, 220), 14: (0, 341)}, 196: {0: (1, {'@': 58}), 1: (1, {'@': 58}), 2: (1, {'@': 58}), 3: (1, {'@': 58}), 4: (1, {'@': 58}), 5: (1, {'@': 58}), 6: (1, {'@': 58}), 7: (1, {'@': 58}), 8: (1, {'@': 58}), 9: (1, {'@': 58}), 10: (1, {'@': 58})}, 197: {6: (1, {'@': 72}), 15: (1, {'@': 72}), 21: (1, {'@': 72})}, 198: {6: (1, {'@': 108}), 21: (1, {'@': 108})}, 199: {0: (1, {'@': 114}), 1: (1, {'@': 114}), 2: (1, {'@': 114}), 3: (1, {'@': 114}), 4: (1, {'@': 114}), 5: (1, {'@': 114}), 6: (1, {'@': 114}), 7: (1, {'@': 114}), 8: (1, {'@': 114}), 9: (1, {'@': 114}), 10: (1, {'@': 114})}, 200: {0: (1, {'@': 122}), 1: (1, {'@': 122}), 2: (1, {'@': 122}), 3: (1, {'@': 122}), 4: (1, {'@': 122}), 5: (1, {'@': 122}), 6: (1, {'@': 122}), 7: (1, {'@': 122}), 8: (1, {'@': 122}), 9: (1, {'@': 122}), 10: (1, {'@': 122})}, 201: {6: (0, 45)}, 202: {6: (1, {'@': 208}), 21: (1, {'@': 208})}, 203: {6: (1, {'@': 67}), 15: (1, {'@': 67}), 21: (1, {'@': 67})}, 204: {6: (1, {'@': 106}), 21: (1, {'@': 106})}, 205: {6: (0, 102)}, 206: {0: (1, {'@': 157}), 1: (1, {'@': 157}), 2: (1, {'@': 157}), 3: (1, {'@': 157}), 4: (1, {'@': 157}), 5: (1, {'@': 157}), 6: (1, {'@': 157}), 7: (1, {'@': 157}), 8: (1, {'@': 157}), 9: (1, {'@': 157}), 10: (1, {'@': 157})}, 207: {0: (1, {'@': 112}), 1: (1, {'@': 112}), 2: (1, {'@': 112}), 3: (1, {'@': 112}), 4: (1, {'@': 112}), 5: (1, {'@': 112}), 6: (1, {'@': 112}), 7: (1, {'@': 112}), 8: (1, {'@': 112}), 9: (1, {'@': 112}), 10: (1, {'@': 112})}, 208: {6: (0, 129), 15: (0, 144)}, 209: {39: (0, 47), 38: (0, 128), 2: (0, 60), 25: (0, 38), 17: (0, 124), 14: (0, 341)}, 210: {0: (1, {'@': 146}), 1: (1, {'@': 146}), 2: (1, {'@': 146}), 3: (1, {'@': 146}), 4: (1, {'@': 146}), 5: (1, {'@': 146}), 6: (1, {'@': 146}), 7: (1, {'@': 146}), 8: (1, {'@': 146}), 9: (1, {'@': 146}), 10: (1, {'@': 146})}, 211: {6: (0, 116)}, 212: {40: (0, 305), 66: (0, 233), 64: (0, 110), 79: (0, 146), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 213: {0: (1, {'@': 148}), 1: (1, {'@': 148}), 2: (1, {'@': 148}), 3: (1, {'@': 148}), 4: (1, {'@': 148}), 5: (1, {'@': 148}), 6: (1, {'@': 148}), 7: (1, {'@': 148}), 8: (1, {'@': 148}), 9: (1, {'@': 148}), 10: (1, {'@': 148})}, 214: {40: (0, 305), 79: (0, 326), 66: (0, 233), 64: (0, 110), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 215: {6: (1, {'@': 139}), 15: (1, {'@': 139}), 21: (1, {'@': 139})}, 216: {25: (0, 387), 2: (0, 60), 14: (0, 341)}, 217: {21: (0, 316)}, 218: {0: (1, {'@': 133}), 1: (1, {'@': 133}), 2: (1, {'@': 133}), 3: (1, {'@': 133}), 4: (1, {'@': 133}), 5: (1, {'@': 133}), 6: (1, {'@': 133}), 7: (1, {'@': 133}), 8: (1, {'@': 133}), 9: (1, {'@': 133}), 10: (1, {'@': 133})}, 219: {6: (0, 244), 15: (0, 392)}, 220: {6: (1, {'@': 75}), 15: (1, {'@': 75}), 21: (1, {'@': 75})}, 221: {2: (1, {'@': 205}), 17: (1, {'@': 205})}, 222: {40: (0, 305), 79: (0, 371), 66: (0, 233), 64: (0, 110), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 223: {60: (0, 136), 17: (0, 99), 61: (0, 151)}, 224: {6: (1, {'@': 138}), 15: (1, {'@': 138}), 21: (1, {'@': 138})}, 225: {24: (1, {'@': 86}), 23: (1, {'@': 86}), 2: (1, {'@': 86}), 17: (1, {'@': 86})}, 226: {0: (1, {'@': 168}), 1: (1, {'@': 168}), 2: (1, {'@': 168}), 3: (1, {'@': 168}), 4: (1, {'@': 168}), 5: (1, {'@': 168}), 6: (1, {'@': 168}), 7: (1, {'@': 168}), 8: (1, {'@': 168}), 9: (1, {'@': 168}), 10: (1, {'@': 168})}, 227: {2: (0, 60), 25: (0, 304), 14: (0, 341)}, 228: {50: (0, 312), 19: (0, 325), 2: (0, 60), 14: (0, 215), 52: (0, 217), 51: (0, 384)}, 229: {0: (1, {'@': 55}), 1: (1, {'@': 55}), 2: (1, {'@': 55}), 3: (1, {'@': 55}), 4: (1, {'@': 55}), 5: (1, {'@': 55}), 6: (1, {'@': 55}), 7: (1, {'@': 55}), 8: (1, {'@': 55}), 9: (1, {'@': 55}), 10: (1, {'@': 55})}, 230: {6: (0, 226), 15: (0, 367)}, 231: {0: (1, {'@': 124}), 1: (1, {'@': 124}), 2: (1, {'@': 124}), 3: (1, {'@': 124}), 4: (1, {'@': 124}), 5: (1, {'@': 124}), 6: (1, {'@': 124}), 7: (1, {'@': 124}), 8: (1, {'@': 124}), 9: (1, {'@': 124}), 10: (1, {'@': 124})}, 232: {17: (0, 330), 2: (0, 60), 14: (0, 215), 19: (0, 97)}, 233: {21: (0, 383), 85: (0, 344), 6: (1, {'@': 198})}, 234: {81: (0, 7), 82: (0, 360), 2: (0, 60), 1: (0, 331), 17: (0, 374), 22: (0, 315), 7: (0, 347), 84: (0, 368), 77: (0, 356), 24: (0, 362), 23: (0, 369), 86: (0, 380), 83: (0, 378), 78: (0, 337), 25: (0, 386), 14: (0, 341), 63: (0, 327)}, 235: {11: (0, 301)}, 236: {0: (1, {'@': 150}), 1: (1, {'@': 150}), 2: (1, {'@': 150}), 3: (1, {'@': 150}), 4: (1, {'@': 150}), 5: (1, {'@': 150}), 6: (1, {'@': 150}), 7: (1, {'@': 150}), 8: (1, {'@': 150}), 9: (1, {'@': 150}), 10: (1, {'@': 150})}, 237: {2: (1, {'@': 177}), 17: (1, {'@': 177})}, 238: {6: (1, {'@': 66}), 15: (1, {'@': 66}), 21: (1, {'@': 66})}, 239: {2: (0, 60), 20: (0, 114), 17: (0, 221), 14: (0, 215), 19: (0, 135)}, 240: {2: (0, 60), 25: (0, 197), 14: (0, 341)}, 241: {87: (0, 133), 21: (0, 122), 6: (1, {'@': 196}), 15: (1, {'@': 196})}, 242: {40: (0, 305), 65: (0, 202), 64: (0, 110), 66: (0, 266), 63: (0, 282), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 243: {68: (0, 295), 70: (0, 73), 71: (0, 61), 64: (0, 110), 41: (0, 90), 69: (0, 318), 72: (0, 334), 40: (0, 305), 6: (0, 320), 66: (0, 348), 76: (0, 373), 63: (0, 395), 21: (1, {'@': 34})}, 244: {0: (1, {'@': 164}), 1: (1, {'@': 164}), 2: (1, {'@': 164}), 3: (1, {'@': 164}), 4: (1, {'@': 164}), 5: (1, {'@': 164}), 6: (1, {'@': 164}), 7: (1, {'@': 164}), 8: (1, {'@': 164}), 9: (1, {'@': 164}), 10: (1, {'@': 164})}, 245: {6: (0, 317)}, 246: {17: (1, {'@': 175}), 60: (1, {'@': 175})}, 247: {6: (1, {'@': 78}), 15: (1, {'@': 78}), 21: (1, {'@': 78})}, 248: {2: (0, 60), 25: (0, 38), 14: (0, 341), 39: (0, 156)}, 249: {2: (0, 60), 23: (0, 188), 14: (0, 341), 25: (0, 3)}, 250: {15: (0, 308), 6: (0, 351)}, 251: {80: (0, 358), 57: (0, 389), 11: (0, 91), 58: (0, 118)}, 252: {40: (0, 305), 66: (0, 233), 64: (0, 110), 79: (0, 205), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 253: {40: (0, 305), 66: (0, 233), 64: (0, 110), 79: (0, 354), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 254: {0: (1, {'@': 116}), 1: (1, {'@': 116}), 2: (1, {'@': 116}), 3: (1, {'@': 116}), 4: (1, {'@': 116}), 5: (1, {'@': 116}), 6: (1, {'@': 116}), 7: (1, {'@': 116}), 8: (1, {'@': 116}), 9: (1, {'@': 116}), 10: (1, {'@': 116})}, 255: {50: (0, 312), 19: (0, 325), 51: (0, 211), 2: (0, 60), 14: (0, 215), 52: (0, 217)}, 256: {57: (0, 170), 59: (0, 340), 58: (0, 118), 11: (0, 385)}, 257: {40: (0, 305), 79: (0, 138), 66: (0, 233), 64: (0, 110), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 258: {6: (1, {'@': 65}), 15: (1, {'@': 65}), 21: (1, {'@': 65})}, 259: {0: (1, {'@': 111}), 1: (1, {'@': 111}), 2: (1, {'@': 111}), 3: (1, {'@': 111}), 4: (1, {'@': 111}), 5: (1, {'@': 111}), 6: (1, {'@': 111}), 7: (1, {'@': 111}), 8: (1, {'@': 111}), 9: (1, {'@': 111}), 10: (1, {'@': 111})}, 260: {23: (0, 379), 2: (0, 60), 25: (0, 322), 14: (0, 341)}, 261: {88: (0, 311), 21: (0, 329), 6: (1, {'@': 190})}, 262: {0: (1, {'@': 54}), 2: (1, {'@': 54}), 3: (1, {'@': 54}), 6: (1, {'@': 54}), 7: (1, {'@': 54}), 1: (1, {'@': 54}), 4: (1, {'@': 54}), 5: (1, {'@': 54}), 8: (1, {'@': 54}), 9: (1, {'@': 54}), 10: (1, {'@': 54})}, 263: {6: (1, {'@': 207}), 21: (1, {'@': 207})}, 264: {23: (0, 139), 13: (0, 241), 12: (0, 95), 14: (0, 215), 2: (0, 60), 19: (0, 224), 20: (0, 232), 6: (0, 152), 16: (0, 237), 17: (0, 221), 18: (0, 239), 15: (0, 115)}, 265: {15: (0, 252), 6: (0, 186)}, 266: {6: (1, {'@': 94}), 21: (1, {'@': 94})}, 267: {40: (0, 305), 67: (0, 394), 64: (0, 110), 65: (0, 261), 66: (0, 266), 63: (0, 282), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 268: {0: (1, {'@': 48}), 1: (1, {'@': 48}), 2: (1, {'@': 48}), 5: (1, {'@': 48}), 3: (1, {'@': 48}), 4: (1, {'@': 48}), 6: (1, {'@': 48}), 7: (1, {'@': 48}), 8: (1, {'@': 48}), 9: (1, {'@': 48}), 10: (1, {'@': 48})}, 269: {6: (0, 127)}, 270: {40: (0, 305), 66: (0, 233), 64: (0, 110), 79: (0, 201), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 271: {6: (0, 363)}, 272: {6: (0, 199)}, 273: {0: (1, {'@': 42}), 1: (1, {'@': 42}), 2: (1, {'@': 42}), 5: (1, {'@': 42}), 3: (1, {'@': 42}), 4: (1, {'@': 42}), 6: (1, {'@': 42}), 7: (1, {'@': 42}), 8: (1, {'@': 42}), 9: (1, {'@': 42}), 10: (1, {'@': 42})}, 274: {17: (0, 51), 26: (0, 223), 2: (0, 60), 25: (0, 246), 14: (0, 341)}, 275: {0: (1, {'@': 57}), 1: (1, {'@': 57}), 2: (1, {'@': 57}), 3: (1, {'@': 57}), 4: (1, {'@': 57}), 5: (1, {'@': 57}), 6: (1, {'@': 57}), 7: (1, {'@': 57}), 8: (1, {'@': 57}), 9: (1, {'@': 57}), 10: (1, {'@': 57})}, 276: {6: (1, {'@': 209}), 15: (1, {'@': 209}), 21: (1, {'@': 209})}, 277: {81: (0, 7), 82: (0, 360), 2: (0, 60), 1: (0, 331), 17: (0, 374), 22: (0, 315), 7: (0, 347), 84: (0, 368), 77: (0, 356), 24: (0, 362), 23: (0, 369), 83: (0, 378), 78: (0, 337), 25: (0, 386), 86: (0, 355), 14: (0, 341), 63: (0, 327)}, 278: {2: (0, 60), 39: (0, 47), 25: (0, 38), 14: (0, 341), 38: (0, 208)}, 279: {43: (1, {'@': 183})}, 280: {24: (1, {'@': 84}), 23: (1, {'@': 84}), 2: (1, {'@': 84}), 17: (1, {'@': 84})}, 281: {6: (0, 200)}, 282: {40: (0, 305), 64: (0, 110), 66: (0, 377), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 283: {89: (0, 204), 64: (0, 198), 40: (0, 305)}, 284: {0: (1, {'@': 44}), 1: (1, {'@': 44}), 2: (1, {'@': 44}), 5: (1, {'@': 44}), 3: (1, {'@': 44}), 4: (1, {'@': 44}), 6: (1, {'@': 44}), 7: (1, {'@': 44}), 8: (1, {'@': 44}), 9: (1, {'@': 44}), 10: (1, {'@': 44})}, 285: {6: (1, {'@': 74}), 15: (1, {'@': 74}), 21: (1, {'@': 74})}, 286: {0: (1, {'@': 126}), 1: (1, {'@': 126}), 2: (1, {'@': 126}), 3: (1, {'@': 126}), 4: (1, {'@': 126}), 5: (1, {'@': 126}), 6: (1, {'@': 126}), 7: (1, {'@': 126}), 8: (1, {'@': 126}), 9: (1, {'@': 126}), 10: (1, {'@': 126})}, 287: {17: (0, 101), 39: (0, 47), 38: (0, 149), 2: (0, 60), 25: (0, 38), 14: (0, 341)}, 288: {2: (0, 60), 38: (0, 230), 39: (0, 47), 25: (0, 38), 14: (0, 341)}, 289: {15: (0, 350), 6: (0, 213)}, 290: {53: (1, {'@': 184})}, 291: {11: (0, 298)}, 292: {81: (0, 7), 82: (0, 360), 2: (0, 60), 1: (0, 331), 17: (0, 374), 22: (0, 315), 7: (0, 347), 84: (0, 368), 77: (0, 356), 24: (0, 362), 23: (0, 369), 83: (0, 307), 78: (0, 337), 25: (0, 386), 14: (0, 341), 63: (0, 327)}, 293: {43: (0, 8)}, 294: {25: (0, 375), 2: (0, 60), 14: (0, 341)}, 295: {40: (0, 305), 64: (0, 110), 66: (0, 187), 41: (0, 393), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 296: {2: (0, 60), 14: (0, 341), 25: (0, 130)}, 297: {0: (1, {'@': 41}), 1: (1, {'@': 41}), 2: (1, {'@': 41}), 5: (1, {'@': 41}), 3: (1, {'@': 41}), 4: (1, {'@': 41}), 6: (1, {'@': 41}), 7: (1, {'@': 41}), 8: (1, {'@': 41}), 9: (1, {'@': 41}), 10: (1, {'@': 41})}, 298: {2: (0, 60), 25: (0, 336), 14: (0, 341)}, 299: {6: (0, 207)}, 300: {6: (1, {'@': 103}), 21: (1, {'@': 103})}, 301: {2: (0, 60), 25: (0, 193), 14: (0, 341)}, 302: {6: (0, 168), 15: (0, 222)}, 303: {50: (0, 312), 19: (0, 325), 2: (0, 60), 51: (0, 281), 14: (0, 215), 52: (0, 217)}, 304: {6: (1, {'@': 63}), 15: (1, {'@': 63}), 21: (1, {'@': 63})}, 305: {6: (1, {'@': 185}), 23: (1, {'@': 185}), 21: (1, {'@': 185}), 62: (1, {'@': 185})}, 306: {15: (0, 174), 6: (0, 166)}, 307: {6: (1, {'@': 210}), 15: (1, {'@': 210}), 21: (1, {'@': 210})}, 308: {50: (0, 312), 19: (0, 325), 2: (0, 60), 51: (0, 346), 14: (0, 215), 52: (0, 217)}, 309: {6: (1, {'@': 212}), 21: (1, {'@': 212})}, 310: {6: (0, 321), 15: (0, 382)}, 311: {21: (0, 242), 6: (1, {'@': 189})}, 312: {21: (1, {'@': 141}), 6: (1, {'@': 141})}, 313: {0: (1, {'@': 123}), 1: (1, {'@': 123}), 2: (1, {'@': 123}), 3: (1, {'@': 123}), 4: (1, {'@': 123}), 5: (1, {'@': 123}), 6: (1, {'@': 123}), 7: (1, {'@': 123}), 8: (1, {'@': 123}), 9: (1, {'@': 123}), 10: (1, {'@': 123})}, 314: {13: (0, 241), 6: (0, 183), 14: (0, 215), 15: (0, 159), 12: (0, 171), 2: (0, 60), 16: (0, 237), 17: (0, 221), 18: (0, 239), 19: (0, 224), 20: (0, 232)}, 315: {25: (0, 155), 17: (0, 175), 2: (0, 60), 23: (0, 177), 14: (0, 341)}, 316: {50: (0, 312), 19: (0, 325), 2: (0, 60), 52: (0, 103), 14: (0, 215)}, 317: {0: (1, {'@': 173}), 1: (1, {'@': 173}), 2: (1, {'@': 173}), 3: (1, {'@': 173}), 4: (1, {'@': 173}), 5: (1, {'@': 173}), 6: (1, {'@': 173}), 7: (1, {'@': 173}), 8: (1, {'@': 173}), 9: (1, {'@': 173}), 10: (1, {'@': 173})}, 318: {90: (0, 388), 21: (0, 339), 6: (1, {'@': 194})}, 319: {91: (0, 87), 57: (0, 76), 58: (0, 118), 11: (0, 71)}, 320: {0: (1, {'@': 96}), 1: (1, {'@': 96}), 2: (1, {'@': 96}), 3: (1, {'@': 96}), 4: (1, {'@': 96}), 5: (1, {'@': 96}), 6: (1, {'@': 96}), 7: (1, {'@': 96}), 8: (1, {'@': 96}), 9: (1, {'@': 96}), 10: (1, {'@': 96})}, 321: {0: (1, {'@': 115}), 1: (1, {'@': 115}), 2: (1, {'@': 115}), 3: (1, {'@': 115}), 4: (1, {'@': 115}), 5: (1, {'@': 115}), 6: (1, {'@': 115}), 7: (1, {'@': 115}), 8: (1, {'@': 115}), 9: (1, {'@': 115}), 10: (1, {'@': 115})}, 322: {6: (1, {'@': 62}), 15: (1, {'@': 62}), 21: (1, {'@': 62})}, 323: {25: (0, 246), 26: (0, 56), 2: (0, 60), 14: (0, 341)}, 324: {6: (0, 286)}, 325: {21: (1, {'@': 142}), 6: (1, {'@': 142})}, 326: {6: (0, 100)}, 327: {77: (1, {'@': 90})}, 328: {50: (0, 312), 19: (0, 325), 2: (0, 60), 51: (0, 75), 14: (0, 215), 52: (0, 217)}, 329: {40: (0, 305), 65: (0, 263), 64: (0, 110), 66: (0, 266), 63: (0, 282), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 330: {2: (1, {'@': 206}), 17: (1, {'@': 206})}, 331: {77: (1, {'@': 91})}, 332: {0: (1, {'@': 98}), 1: (1, {'@': 98}), 2: (1, {'@': 98}), 3: (1, {'@': 98}), 4: (1, {'@': 98}), 5: (1, {'@': 98}), 6: (1, {'@': 98}), 7: (1, {'@': 98}), 8: (1, {'@': 98}), 9: (1, {'@': 98}), 10: (1, {'@': 98})}, 333: {62: (1, {'@': 107})}, 334: {63: (0, 395), 70: (0, 20)}, 335: {50: (0, 312), 19: (0, 325), 2: (0, 60), 14: (0, 215), 52: (0, 217), 51: (0, 271)}, 336: {6: (1, {'@': 81}), 15: (1, {'@': 81}), 21: (1, {'@': 81})}, 337: {24: (1, {'@': 87}), 23: (1, {'@': 87}), 2: (1, {'@': 87}), 17: (1, {'@': 87})}, 338: {18: (0, 239), 14: (0, 215), 2: (0, 60), 17: (0, 221), 16: (0, 237), 13: (0, 162), 19: (0, 224), 20: (0, 232)}, 339: {40: (0, 305), 68: (0, 295), 69: (0, 180), 66: (0, 348), 70: (0, 73), 71: (0, 61), 64: (0, 110), 41: (0, 90), 63: (0, 395), 72: (0, 334), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 340: {11: (0, 240)}, 341: {6: (1, {'@': 88}), 15: (1, {'@': 88}), 21: (1, {'@': 88}), 17: (1, {'@': 88}), 60: (1, {'@': 88}), 62: (1, {'@': 88})}, 342: {6: (1, {'@': 213}), 15: (1, {'@': 213}), 21: (1, {'@': 213})}, 343: {0: (1, {'@': 118}), 1: (1, {'@': 118}), 2: (1, {'@': 118}), 3: (1, {'@': 118}), 4: (1, {'@': 118}), 5: (1, {'@': 118}), 6: (1, {'@': 118}), 7: (1, {'@': 118}), 8: (1, {'@': 118}), 9: (1, {'@': 118}), 10: (1, {'@': 118})}, 344: {21: (0, 113), 6: (1, {'@': 197})}, 345: {0: (1, {'@': 129}), 1: (1, {'@': 129}), 2: (1, {'@': 129}), 3: (1, {'@': 129}), 4: (1, {'@': 129}), 5: (1, {'@': 129}), 6: (1, {'@': 129}), 7: (1, {'@': 129}), 8: (1, {'@': 129}), 9: (1, {'@': 129}), 10: (1, {'@': 129})}, 346: {6: (0, 147)}, 347: {63: (1, {'@': 89}), 1: (1, {'@': 89}), 77: (1, {'@': 89})}, 348: {6: (1, {'@': 102}), 21: (1, {'@': 102})}, 349: {62: (0, 88)}, 350: {40: (0, 305), 66: (0, 233), 64: (0, 110), 79: (0, 150), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 351: {0: (1, {'@': 131}), 1: (1, {'@': 131}), 2: (1, {'@': 131}), 3: (1, {'@': 131}), 4: (1, {'@': 131}), 5: (1, {'@': 131}), 6: (1, {'@': 131}), 7: (1, {'@': 131}), 8: (1, {'@': 131}), 9: (1, {'@': 131}), 10: (1, {'@': 131})}, 352: {11: (0, 191)}, 353: {60: (0, 136), 61: (0, 163)}, 354: {6: (0, 361)}, 355: {15: (0, 68), 6: (0, 196)}, 356: {23: (1, {'@': 178}), 2: (1, {'@': 178}), 24: (1, {'@': 178}), 17: (1, {'@': 178})}, 357: {62: (0, 283)}, 358: {11: (0, 314)}, 359: {6: (0, 231)}, 360: {77: (0, 356), 78: (0, 225)}, 361: {0: (1, {'@': 165}), 1: (1, {'@': 165}), 2: (1, {'@': 165}), 3: (1, {'@': 165}), 4: (1, {'@': 165}), 5: (1, {'@': 165}), 6: (1, {'@': 165}), 7: (1, {'@': 165}), 8: (1, {'@': 165}), 9: (1, {'@': 165}), 10: (1, {'@': 165})}, 362: {23: (1, {'@': 180}), 2: (1, {'@': 180}), 17: (1, {'@': 180})}, 363: {0: (1, {'@': 128}), 1: (1, {'@': 128}), 2: (1, {'@': 128}), 3: (1, {'@': 128}), 4: (1, {'@': 128}), 5: (1, {'@': 128}), 6: (1, {'@': 128}), 7: (1, {'@': 128}), 8: (1, {'@': 128}), 9: (1, {'@': 128}), 10: (1, {'@': 128})}, 364: {17: (0, 372), 60: (0, 136), 61: (0, 209)}, 365: {0: (1, {'@': 95}), 1: (1, {'@': 95}), 2: (1, {'@': 95}), 3: (1, {'@': 95}), 4: (1, {'@': 95}), 5: (1, {'@': 95}), 6: (1, {'@': 95}), 7: (1, {'@': 95}), 8: (1, {'@': 95}), 9: (1, {'@': 95}), 10: (1, {'@': 95})}, 366: {40: (0, 305), 79: (0, 33), 66: (0, 233), 64: (0, 110), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 367: {40: (0, 305), 66: (0, 233), 79: (0, 137), 64: (0, 110), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 368: {77: (0, 356), 78: (0, 125), 1: (0, 331), 63: (0, 327), 82: (0, 131)}, 369: {59: (0, 291), 58: (0, 118), 11: (0, 157), 57: (0, 170)}, 370: {6: (0, 254)}, 371: {6: (0, 106)}, 372: {60: (0, 136), 61: (0, 178)}, 373: {6: (0, 365)}, 374: {23: (0, 140), 2: (0, 60), 25: (0, 65), 14: (0, 341)}, 375: {6: (1, {'@': 76}), 15: (1, {'@': 76}), 21: (1, {'@': 76})}, 376: {0: (1, {'@': 132}), 1: (1, {'@': 132}), 2: (1, {'@': 132}), 3: (1, {'@': 132}), 4: (1, {'@': 132}), 5: (1, {'@': 132}), 6: (1, {'@': 132}), 7: (1, {'@': 132}), 8: (1, {'@': 132}), 9: (1, {'@': 132}), 10: (1, {'@': 132})}, 377: {6: (1, {'@': 93}), 21: (1, {'@': 93})}, 378: {92: (0, 161), 21: (0, 169), 6: (1, {'@': 192}), 15: (1, {'@': 192})}, 379: {57: (0, 170), 58: (0, 118), 59: (0, 235), 11: (0, 216)}, 380: {15: (0, 267), 6: (0, 54)}, 381: {6: (1, {'@': 135}), 15: (1, {'@': 135}), 21: (1, {'@': 135})}, 382: {50: (0, 312), 19: (0, 325), 2: (0, 60), 51: (0, 272), 14: (0, 215), 52: (0, 217)}, 383: {40: (0, 305), 64: (0, 110), 66: (0, 134), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 384: {6: (0, 376)}, 385: {2: (0, 60), 14: (0, 341), 25: (0, 184)}, 386: {6: (1, {'@': 83}), 15: (1, {'@': 83}), 21: (1, {'@': 83})}, 387: {6: (1, {'@': 61}), 15: (1, {'@': 61}), 21: (1, {'@': 61})}, 388: {21: (0, 74), 6: (1, {'@': 193})}, 389: {11: (1, {'@': 134})}, 390: {6: (1, {'@': 31}), 21: (1, {'@': 31})}, 391: {62: (1, {'@': 109})}, 392: {40: (0, 305), 66: (0, 233), 64: (0, 110), 79: (0, 121), 6: (1, {'@': 34}), 21: (1, {'@': 34})}, 393: {74: (0, 107), 64: (0, 333), 75: (0, 349), 40: (0, 305)}, 394: {6: (0, 229)}, 395: {40: (1, {'@': 187}), 6: (1, {'@': 187}), 41: (1, {'@': 187}), 21: (1, {'@': 187})}}, 'start_states': {'start': 24}, 'end_states': {'start': 50}}, '__type__': 'ParsingFrontend'}, 'rules': [{'@': 31}, {'@': 32}, {'@': 33}, {'@': 34}, {'@': 35}, {'@': 36}, {'@': 37}, {'@': 38}, {'@': 39}, {'@': 40}, {'@': 41}, {'@': 42}, {'@': 43}, {'@': 44}, {'@': 45}, {'@': 46}, {'@': 47}, {'@': 48}, {'@': 49}, {'@': 50}, {'@': 51}, {'@': 52}, {'@': 53}, {'@': 54}, {'@': 55}, {'@': 56}, {'@': 57}, {'@': 58}, {'@': 59}, {'@': 60}, {'@': 61}, {'@': 62}, {'@': 63}, {'@': 64}, {'@': 65}, {'@': 66}, {'@': 67}, {'@': 68}, {'@': 69}, {'@': 70}, {'@': 71}, {'@': 72}, {'@': 73}, {'@': 74}, {'@': 75}, {'@': 76}, {'@': 77}, {'@': 78}, {'@': 79}, {'@': 80}, {'@': 81}, {'@': 82}, {'@': 83}, {'@': 84}, {'@': 85}, {'@': 86}, {'@': 87}, {'@': 88}, {'@': 89}, {'@': 90}, {'@': 91}, {'@': 92}, {'@': 93}, {'@': 94}, {'@': 95}, {'@': 96}, {'@': 97}, {'@': 98}, {'@': 99}, {'@': 100}, {'@': 101}, {'@': 102}, {'@': 103}, {'@': 104}, {'@': 105}, {'@': 106}, {'@': 107}, {'@': 108}, {'@': 109}, {'@': 110}, {'@': 111}, {'@': 112}, {'@': 113}, {'@': 114}, {'@': 115}, {'@': 116}, {'@': 117}, {'@': 118}, {'@': 119}, {'@': 120}, {'@': 121}, {'@': 122}, {'@': 123}, {'@': 124}, {'@': 125}, {'@': 126}, {'@': 127}, {'@': 128}, {'@': 129}, {'@': 130}, {'@': 131}, {'@': 132}, {'@': 133}, {'@': 134}, {'@': 135}, {'@': 136}, {'@': 137}, {'@': 138}, {'@': 139}, {'@': 140}, {'@': 141}, {'@': 142}, {'@': 143}, {'@': 144}, {'@': 145}, {'@': 146}, {'@': 147}, {'@': 148}, {'@': 149}, {'@': 150}, {'@': 151}, {'@': 152}, {'@': 153}, {'@': 154}, {'@': 155}, {'@': 156}, {'@': 157}, {'@': 158}, {'@': 159}, {'@': 160}, {'@': 161}, {'@': 162}, {'@': 163}, {'@': 164}, {'@': 165}, {'@': 166}, {'@': 167}, {'@': 168}, {'@': 169}, {'@': 170}, {'@': 171}, {'@': 172}, {'@': 173}, {'@': 174}, {'@': 175}, {'@': 176}, {'@': 177}, {'@': 178}, {'@': 179}, {'@': 180}, {'@': 181}, {'@': 182}, {'@': 183}, {'@': 184}, {'@': 185}, {'@': 186}, {'@': 187}, {'@': 188}, {'@': 189}, {'@': 190}, {'@': 191}, {'@': 192}, {'@': 193}, {'@': 194}, {'@': 195}, {'@': 196}, {'@': 197}, {'@': 198}, {'@': 199}, {'@': 200}, {'@': 201}, {'@': 202}, {'@': 203}, {'@': 204}, {'@': 205}, {'@': 206}, {'@': 207}, {'@': 208}, {'@': 209}, {'@': 210}, {'@': 211}, {'@': 212}, {'@': 213}, {'@': 214}, {'@': 215}, {'@': 216}, {'@': 217}, {'@': 218}], 'options': {'debug': False, 'strict': False, 'keep_all_tokens': False, 'tree_class': None, 'cache': False, 'postlex': None, 'parser': 'lalr', 'lexer': 'contextual', 'transformer': None, 'start': ['start'], 'priority': 'normal', 'ambiguity': 'auto', 'regex': False, 'propagate_positions': False, 'lexer_callbacks': {}, 'maybe_placeholders': False, 'edit_terminals': None, 'g_regex_flags': 0, 'use_bytes': False, 'import_paths': [], 'source_path': None, '_plugins': {}}, '__type__': 'Lark'} ) MEMO = ( {0: {'name': 'NUMBER', 'pattern': {'value': '(?:(?:(?:[0-9])+(?:e|E)(?:(?:\\+|\\-))?(?:[0-9])+|(?:(?:[0-9])+\\.(?:(?:[0-9])+)?|\\.(?:[0-9])+)(?:(?:e|E)(?:(?:\\+|\\-))?(?:[0-9])+)?)|(?:[0-9])+)', 'flags': [], 'raw': None, '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 1: {'name': 'COMMA', 'pattern': {'value': '(?:[^\\S\r\n]+)?,(?:[^\\S\r\n]+)?', 'flags': [], 'raw': None, '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 2: {'name': 'COLON', 'pattern': {'value': '(?:[^\\S\r\n]+)?:(?:[^\\S\r\n]+)?', 'flags': [], 'raw': None, '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 3: {'name': 'NL', 'pattern': {'value': '[^\\S\r\n]*\r?\n', 'flags': [], 'raw': '/[^\\S\\r\\n]*\\r?\\n/', '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': -10, '__type__': 'TerminalDef'}, 4: {'name': 'SLASH', 'pattern': {'value': '/', 'flags': [], 'raw': '"/"', '__type__': 'PatternStr'}, 'priority': 0, '__type__': 'TerminalDef'}, 5: {'name': 'BACKSLASH', 'pattern': {'value': '(?:\\\\)+', 'flags': [], 'raw': None, '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 6: {'name': 'CONSTRAINT_LPAREN', 'pattern': {'value': '(?:[^\\S\r\n]+)?\\(', 'flags': [], 'raw': None, '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 7: {'name': 'CONSTRAINT_RPAREN', 'pattern': {'value': '\\)(?:[^\\S\r\n]+)?', 'flags': [], 'raw': None, '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 8: {'name': 'LBRACKET', 'pattern': {'value': '(?:[^\\S\r\n]+)?\\[', 'flags': [], 'raw': None, '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 9: {'name': 'RBRACKET', 'pattern': {'value': '\\](?:[^\\S\r\n]+)?', 'flags': [], 'raw': None, '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 10: {'name': 'HASHTAG', 'pattern': {'value': '#', 'flags': [], 'raw': '"#"', '__type__': 'PatternStr'}, 'priority': 0, '__type__': 'TerminalDef'}, 11: {'name': 'PERCENT', 'pattern': {'value': '%', 'flags': [], 'raw': '"%"', '__type__': 'PatternStr'}, 'priority': 0, '__type__': 'TerminalDef'}, 12: {'name': 'SP', 'pattern': {'value': '[^\\S\r\n]+', 'flags': [], 'raw': '/[^\\S\\r\\n]+/', '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': -50, '__type__': 'TerminalDef'}, 13: {'name': 'BOX_NAME', 'pattern': {'value': '(?![\\W_\\d])[^=<>,:\n\\\\]*[^-\\[\\\\\\]=<>,:\\s\\\\]', 'flags': [], 'raw': '/(?![\\W_\\d])[^=<>,:\\n\\\\]*[^-\\[\\\\\\]=<>,:\\s\\\\]/', '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 14: {'name': 'ATTR', 'pattern': {'value': '(?=\\w)(?!_)[^\\[\\]>,\r\n!?]*[^\\[\\]>,\\s]', 'flags': [], 'raw': '/(?=\\w)(?!_)[^\\[\\]>,\\r\\n!?]*[^\\[\\]>,\\s]/', '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 15: {'name': 'BREAK', 'pattern': {'value': '^\n', 'flags': [], 'raw': '/^\\n/', '_width': [1, 1], '__type__': 'PatternRE'}, 'priority': 100, '__type__': 'TerminalDef'}, 16: {'name': 'PHANTOMS', 'pattern': {'value': '( *:)+ *[^\\S\r\n]*\r?\n', 'flags': [], 'raw': None, '_width': [2, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 17: {'name': 'INDENT', 'pattern': {'value': ' +|\t+', 'flags': [], 'raw': '/ +|\\t+/', '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 10, '__type__': 'TerminalDef'}, 18: {'name': 'CARD', 'pattern': {'value': '(?![-_\\/])(\\w|\\?){2}(?=[ \t]*[^\\w,\r\n:])', 'flags': [], 'raw': '/(?![-_\\/])(\\w|\\?){2}(?=[ \\t]*[^\\w,\\r\\n:])/', '_width': [2, 2], '__type__': 'PatternRE'}, 'priority': 2, '__type__': 'TerminalDef'}, 19: {'name': 'LEG_ARROW', 'pattern': {'value': '[<>]', 'flags': [], 'raw': '/[<>]/', '_width': [1, 1], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 20: {'name': 'ID_GROUPS', 'pattern': {'value': '\\d+(?=_)', 'flags': [], 'raw': '/\\d+(?=_)/', '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 10, '__type__': 'TerminalDef'}, 21: {'name': 'ID_MARK', 'pattern': {'value': '_', 'flags': [], 'raw': '"_"', '__type__': 'PatternStr'}, 'priority': 0, '__type__': 'TerminalDef'}, 22: {'name': 'MORETHAN', 'pattern': {'value': '(?:[^\\S\r\n]+)?>(?:[^\\S\r\n]+)?', 'flags': [], 'raw': None, '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 23: {'name': 'CONSTRAINT_NAME', 'pattern': {'value': '[^)]{1,3}', 'flags': [], 'raw': '/[^)]{1,3}/', '_width': [1, 3], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 24: {'name': 'CONSTRAINT_LEG', 'pattern': {'value': '?', 'flags': [], 'raw': '/?/', '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 25: {'name': 'INHERITANCE_NAME', 'pattern': {'value': '(XT\\d?|TX\\d?|X\\d?|T\\d?|\\d)', 'flags': [], 'raw': '/(XT\\d?|TX\\d?|X\\d?|T\\d?|\\d)/', '_width': [1, 3], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 26: {'name': 'INHERITANCE_ARROW', 'pattern': {'value': '<==?|<--?|--?>|==?>', 'flags': [], 'raw': '/<==?|<--?|--?>|==?>/', '_width': [2, 3], '__type__': 'PatternRE'}, 'priority': -10, '__type__': 'TerminalDef'}, 27: {'name': 'PLUS', 'pattern': {'value': '+', 'flags': [], 'raw': '"+"', '__type__': 'PatternStr'}, 'priority': 0, '__type__': 'TerminalDef'}, 28: {'name': 'MINUS', 'pattern': {'value': '-', 'flags': [], 'raw': '"-"', '__type__': 'PatternStr'}, 'priority': 0, '__type__': 'TerminalDef'}, 29: {'name': '__ANON_0', 'pattern': {'value': '[^\r\n\\]]+', 'flags': [], 'raw': '/[^\\r\\n\\]]+/', '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 30: {'name': '__ANON_1', 'pattern': {'value': '.*\r?\n', 'flags': [], 'raw': '/.*\\r?\\n/', '_width': [1, 4294967295], '__type__': 'PatternRE'}, 'priority': 0, '__type__': 'TerminalDef'}, 31: {'origin': {'name': Token('RULE', 'typed_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'attr', '__type__': 'NonTerminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'datatype', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 32: {'origin': {'name': Token('RULE', 'typed_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'attr', '__type__': 'NonTerminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 33: {'origin': {'name': Token('RULE', 'typed_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'attr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 34: {'origin': {'name': Token('RULE', 'typed_attr'), '__type__': 'NonTerminal'}, 'expansion': [], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 35: {'origin': {'name': Token('RULE', 'datatype'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'note', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 36: {'origin': {'name': Token('RULE', 'box_def_prefix'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': True, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 37: {'origin': {'name': Token('RULE', 'box_def_prefix'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': True, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 38: {'origin': {'name': Token('RULE', 'note'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '__ANON_0', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 39: {'origin': {'name': Token('RULE', 'start'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '__start_star_0', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 40: {'origin': {'name': Token('RULE', 'start'), '__type__': 'NonTerminal'}, 'expansion': [], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 41: {'origin': {'name': Token('RULE', 'line'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'indent', '__type__': 'NonTerminal'}, {'name': 'phantoms', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 42: {'origin': {'name': Token('RULE', 'line'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'indent', '__type__': 'NonTerminal'}, {'name': 'comment', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 43: {'origin': {'name': Token('RULE', 'line'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'indent', '__type__': 'NonTerminal'}, {'name': 'clause', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 44: {'origin': {'name': Token('RULE', 'line'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'indent', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 45: {'origin': {'name': Token('RULE', 'line'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'phantoms', '__type__': 'NonTerminal'}], 'order': 4, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 46: {'origin': {'name': Token('RULE', 'line'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'comment', '__type__': 'NonTerminal'}], 'order': 5, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 47: {'origin': {'name': Token('RULE', 'line'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'clause', '__type__': 'NonTerminal'}], 'order': 6, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 48: {'origin': {'name': Token('RULE', 'line'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 49: {'origin': {'name': Token('RULE', 'clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'assoc_clause', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 50: {'origin': {'name': Token('RULE', 'clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'entity_clause', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 51: {'origin': {'name': Token('RULE', 'clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'constraint_clause', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 52: {'origin': {'name': Token('RULE', 'clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'inheritance_clause', '__type__': 'NonTerminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 53: {'origin': {'name': Token('RULE', 'indent'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INDENT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 54: {'origin': {'name': Token('RULE', 'comment'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PERCENT', 'filter_out': False, '__type__': 'Terminal'}, {'name': '__ANON_1', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 55: {'origin': {'name': Token('RULE', 'assoc_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'box_def_prefix', '__type__': 'NonTerminal'}, {'name': 'assoc_name_def', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{assoc_leg}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{assoc_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 56: {'origin': {'name': Token('RULE', 'assoc_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'box_def_prefix', '__type__': 'NonTerminal'}, {'name': 'assoc_name_def', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{assoc_leg}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 57: {'origin': {'name': Token('RULE', 'assoc_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'assoc_name_def', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{assoc_leg}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{assoc_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 58: {'origin': {'name': Token('RULE', 'assoc_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'assoc_name_def', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{assoc_leg}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 59: {'origin': {'name': Token('RULE', 'assoc_name_def'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'box_name', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 60: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'leg_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 61: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 62: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 63: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'leg_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 64: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 4, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 65: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 5, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 66: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'leg_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 6, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 67: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 7, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 68: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 8, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 69: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'leg_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 9, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 70: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 10, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 71: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_assoc_card', '__type__': 'NonTerminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 11, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 72: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'leg_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 12, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 73: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 13, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 74: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 14, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 75: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'leg_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 15, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 76: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 16, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 77: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'leg_arrow', '__type__': 'NonTerminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 17, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 78: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'leg_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 18, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 79: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 19, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 80: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 20, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 81: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'leg_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 21, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 82: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 22, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 83: {'origin': {'name': Token('RULE', 'assoc_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 23, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 84: {'origin': {'name': Token('RULE', '_assoc_card'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'card_hidden', '__type__': 'NonTerminal'}, {'name': 'card_prefix', '__type__': 'NonTerminal'}, {'name': 'card', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 85: {'origin': {'name': Token('RULE', '_assoc_card'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'card_hidden', '__type__': 'NonTerminal'}, {'name': 'card', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 86: {'origin': {'name': Token('RULE', '_assoc_card'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'card_prefix', '__type__': 'NonTerminal'}, {'name': 'card', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 87: {'origin': {'name': Token('RULE', '_assoc_card'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'card', '__type__': 'NonTerminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 88: {'origin': {'name': Token('RULE', 'entity_name_ref'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'box_name', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 89: {'origin': {'name': Token('RULE', 'card_hidden'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': True, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 90: {'origin': {'name': Token('RULE', 'card_prefix'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID_MARK', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': True, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 91: {'origin': {'name': Token('RULE', 'card_prefix'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': True, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 92: {'origin': {'name': Token('RULE', 'leg_note'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'note', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 93: {'origin': {'name': Token('RULE', 'assoc_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID_MARK', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'typed_attr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 94: {'origin': {'name': Token('RULE', 'assoc_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'typed_attr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 95: {'origin': {'name': Token('RULE', 'entity_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'box_def_prefix', '__type__': 'NonTerminal'}, {'name': 'entity_name_def', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{entity_or_table_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 96: {'origin': {'name': Token('RULE', 'entity_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'box_def_prefix', '__type__': 'NonTerminal'}, {'name': 'entity_name_def', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 97: {'origin': {'name': Token('RULE', 'entity_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'entity_name_def', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{entity_or_table_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 98: {'origin': {'name': Token('RULE', 'entity_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'entity_name_def', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 99: {'origin': {'name': Token('RULE', 'entity_name_def'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'box_name', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 100: {'origin': {'name': Token('RULE', 'entity_or_table_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_id_symbols', '__type__': 'NonTerminal'}, {'name': 'typed_attr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 101: {'origin': {'name': Token('RULE', 'entity_or_table_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_id_symbols', '__type__': 'NonTerminal'}, {'name': 'HASHTAG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'foreign_reference', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 102: {'origin': {'name': Token('RULE', 'entity_or_table_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'typed_attr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 103: {'origin': {'name': Token('RULE', 'entity_or_table_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'HASHTAG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'foreign_reference', '__type__': 'NonTerminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 104: {'origin': {'name': Token('RULE', '_id_symbols'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'id_groups', '__type__': 'NonTerminal'}, {'name': 'id_mark', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 105: {'origin': {'name': Token('RULE', '_id_symbols'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'id_mark', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 106: {'origin': {'name': Token('RULE', 'foreign_reference'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'this_table_attr', '__type__': 'NonTerminal'}, {'name': 'MORETHAN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'that_table', '__type__': 'NonTerminal'}, {'name': 'MORETHAN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'that_table_attr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 107: {'origin': {'name': Token('RULE', 'this_table_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'attr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 108: {'origin': {'name': Token('RULE', 'that_table_attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'attr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 109: {'origin': {'name': Token('RULE', 'that_table'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 110: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 111: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 112: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 113: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 114: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 115: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 116: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 117: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 118: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 8, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 119: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 9, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 120: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 10, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 121: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_name', '__type__': 'NonTerminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 11, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 122: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 12, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 123: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 13, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 124: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 14, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 125: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_note', '__type__': 'NonTerminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 15, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 126: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 16, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 127: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 17, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 128: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 18, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 129: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RBRACKET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 19, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 130: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 20, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 131: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 21, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 132: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_coords', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 22, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 133: {'origin': {'name': Token('RULE', 'constraint_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CONSTRAINT_RPAREN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 23, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 134: {'origin': {'name': Token('RULE', 'constraint_note'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'note', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 135: {'origin': {'name': Token('RULE', 'constraint_target'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'constraint_leg', '__type__': 'NonTerminal'}, {'name': '__constraint_target_star_1', '__type__': 'NonTerminal'}, {'name': 'box_name_ref', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 136: {'origin': {'name': Token('RULE', 'constraint_target'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'constraint_leg', '__type__': 'NonTerminal'}, {'name': 'box_name_ref', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 137: {'origin': {'name': Token('RULE', 'constraint_target'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '__constraint_target_star_1', '__type__': 'NonTerminal'}, {'name': 'box_name_ref', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 138: {'origin': {'name': Token('RULE', 'constraint_target'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'box_name_ref', '__type__': 'NonTerminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 139: {'origin': {'name': Token('RULE', 'box_name_ref'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'box_name', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 140: {'origin': {'name': Token('RULE', 'constraint_coords'), '__type__': 'NonTerminal'}, 'expansion': [{'name': '_constraint_coord', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': '_constraint_coord', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 141: {'origin': {'name': Token('RULE', '_constraint_coord'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NUMBER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 142: {'origin': {'name': Token('RULE', '_constraint_coord'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'box_name_ref', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 143: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 144: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 145: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 146: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 147: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 148: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 149: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 150: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 151: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 8, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 152: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 9, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 153: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 10, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 154: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 11, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 155: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 12, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 156: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 13, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 157: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 14, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 158: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_name', '__type__': 'NonTerminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 15, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 159: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 16, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 160: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 17, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 161: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 18, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 162: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 19, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 163: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 20, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 164: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 21, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 165: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 22, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 166: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 23, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 167: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 24, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 168: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 25, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 169: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 26, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 170: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 27, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 171: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 28, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 172: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 29, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 173: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'COLON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 30, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 174: {'origin': {'name': Token('RULE', 'inheritance_clause'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BACKSLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_parent', '__type__': 'NonTerminal'}, {'name': 'inheritance_arrow', '__type__': 'NonTerminal'}, {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, {'name': 'NL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 31, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 175: {'origin': {'name': Token('RULE', 'inheritance_parent'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 176: {'origin': {'name': Token('RULE', 'inheritance_child'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'entity_name_ref', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 177: {'origin': {'name': Token('RULE', 'constraint_leg'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_LEG', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 178: {'origin': {'name': Token('RULE', 'card'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CARD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 179: {'origin': {'name': Token('RULE', 'box_name'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BOX_NAME', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 180: {'origin': {'name': Token('RULE', 'leg_arrow'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LEG_ARROW', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 181: {'origin': {'name': Token('RULE', 'phantoms'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PHANTOMS', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 182: {'origin': {'name': Token('RULE', 'break_'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BREAK', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 183: {'origin': {'name': Token('RULE', 'inheritance_name'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INHERITANCE_NAME', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 184: {'origin': {'name': Token('RULE', 'constraint_name'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONSTRAINT_NAME', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 185: {'origin': {'name': Token('RULE', 'attr'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ATTR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 186: {'origin': {'name': Token('RULE', 'inheritance_arrow'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INHERITANCE_ARROW', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 187: {'origin': {'name': Token('RULE', 'id_mark'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID_MARK', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 188: {'origin': {'name': Token('RULE', 'id_groups'), '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID_GROUPS', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 189: {'origin': {'name': 'seq{assoc_attr}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'assoc_attr', '__type__': 'NonTerminal'}, {'name': '__seq{assoc_attr}_star_2', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 190: {'origin': {'name': 'seq{assoc_attr}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'assoc_attr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 191: {'origin': {'name': 'seq{assoc_leg}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'assoc_leg', '__type__': 'NonTerminal'}, {'name': '__seq{assoc_leg}_star_3', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 192: {'origin': {'name': 'seq{assoc_leg}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'assoc_leg', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 193: {'origin': {'name': 'seq{entity_or_table_attr}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'entity_or_table_attr', '__type__': 'NonTerminal'}, {'name': '__seq{entity_or_table_attr}_star_4', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 194: {'origin': {'name': 'seq{entity_or_table_attr}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'entity_or_table_attr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 195: {'origin': {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'constraint_target', '__type__': 'NonTerminal'}, {'name': '__seq{constraint_target}_star_5', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 196: {'origin': {'name': 'seq{constraint_target}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'constraint_target', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 197: {'origin': {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'typed_attr', '__type__': 'NonTerminal'}, {'name': '__seq{typed_attr}_star_6', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 198: {'origin': {'name': 'seq{typed_attr}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'typed_attr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 199: {'origin': {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'inheritance_child', '__type__': 'NonTerminal'}, {'name': '__seq{inheritance_child}_star_7', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 200: {'origin': {'name': 'seq{inheritance_child}', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'inheritance_child', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': Token('RULE', 'seq'), 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 201: {'origin': {'name': '__start_star_0', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'break_', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 202: {'origin': {'name': '__start_star_0', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'line', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 203: {'origin': {'name': '__start_star_0', '__type__': 'NonTerminal'}, 'expansion': [{'name': '__start_star_0', '__type__': 'NonTerminal'}, {'name': 'break_', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 204: {'origin': {'name': '__start_star_0', '__type__': 'NonTerminal'}, 'expansion': [{'name': '__start_star_0', '__type__': 'NonTerminal'}, {'name': 'line', '__type__': 'NonTerminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 205: {'origin': {'name': '__constraint_target_star_1', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 206: {'origin': {'name': '__constraint_target_star_1', '__type__': 'NonTerminal'}, 'expansion': [{'name': '__constraint_target_star_1', '__type__': 'NonTerminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 207: {'origin': {'name': '__seq{assoc_attr}_star_2', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'assoc_attr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 208: {'origin': {'name': '__seq{assoc_attr}_star_2', '__type__': 'NonTerminal'}, 'expansion': [{'name': '__seq{assoc_attr}_star_2', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'assoc_attr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 209: {'origin': {'name': '__seq{assoc_leg}_star_3', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'assoc_leg', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 210: {'origin': {'name': '__seq{assoc_leg}_star_3', '__type__': 'NonTerminal'}, 'expansion': [{'name': '__seq{assoc_leg}_star_3', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'assoc_leg', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 211: {'origin': {'name': '__seq{entity_or_table_attr}_star_4', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_or_table_attr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 212: {'origin': {'name': '__seq{entity_or_table_attr}_star_4', '__type__': 'NonTerminal'}, 'expansion': [{'name': '__seq{entity_or_table_attr}_star_4', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'entity_or_table_attr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 213: {'origin': {'name': '__seq{constraint_target}_star_5', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_target', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 214: {'origin': {'name': '__seq{constraint_target}_star_5', '__type__': 'NonTerminal'}, 'expansion': [{'name': '__seq{constraint_target}_star_5', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'constraint_target', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 215: {'origin': {'name': '__seq{typed_attr}_star_6', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'typed_attr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 216: {'origin': {'name': '__seq{typed_attr}_star_6', '__type__': 'NonTerminal'}, 'expansion': [{'name': '__seq{typed_attr}_star_6', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'typed_attr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 217: {'origin': {'name': '__seq{inheritance_child}_star_7', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_child', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 218: {'origin': {'name': '__seq{inheritance_child}_star_7', '__type__': 'NonTerminal'}, 'expansion': [{'name': '__seq{inheritance_child}_star_7', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inheritance_child', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}} ) Shift = 0 Reduce = 1 def Lark_StandAlone(**kwargs): return Lark._load_from_dict(DATA, MEMO, **kwargs) ================================================ FILE: mocodo/phantom.py ================================================ class Phantom: counter = 0 @classmethod def reset_counter(cls): cls.counter = 0 def __init__(self): self.source = ":" self.name_view = "" Phantom.counter += 1 self.bid = f"PHANTOM_#{Phantom.counter}" self.attributes = [] self.legs = [] # iterating over box's legs does nothing if it is not an association self.kind = "phantom" self.page = 0 def calculate_size(self, *ignored): self.w = 0 self.h = 0 def register_center(self, geo): pass def register_boxes(self, boxes): self.boxes = boxes ================================================ FILE: mocodo/resources/colors/blank.json ================================================ { "association_attribute_text_color": null, "association_cartouche_color": "#FFFFFF", "association_cartouche_text_color": "#000000", "association_color": "#FFFFFF", "association_stroke_color": "#000000", "background_color": null, "card_text_color": null, "constraint_background_color": "#FFFFFF", "constraint_stroke_color": "#000000", "constraint_text_color": "#000000", "entity_attribute_text_color": null, "entity_cartouche_color": "#FFFFFF", "entity_cartouche_text_color": "#000000", "entity_color": "#FFFFFF", "entity_stroke_color": "#000000", "id_gutter_color": "#FFFFFF", "id_gutter_text_color": null, "leg_stroke_color": "#000000", "note_color": "#000000", "note_opacity": 0, "note_text_color": "#FFFFFF" } ================================================ FILE: mocodo/resources/colors/brewer+1.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#dfc27d", "association_cartouche_text_color": "#000000", "association_color": "#f6e8c3", "association_stroke_color": "#bf812d", "background_color": "#f5f5f5", "card_text_color": "#01665e", "constraint_background_color": "#f5f5f5", "constraint_stroke_color": "#bf812d", "constraint_text_color": "#bf812d", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#80cdc1", "entity_cartouche_text_color": "#000000", "entity_color": "#c7eae5", "entity_stroke_color": "#35978f", "id_gutter_color": "#c7eae5", "id_gutter_text_color": "#000000", "leg_stroke_color": "#bf812d", "note_color": "#8c510a", "note_opacity": 0.9, "note_text_color": "#f5f5f5" } ================================================ FILE: mocodo/resources/colors/brewer+2.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#f1b6da", "association_cartouche_text_color": "#000000", "association_color": "#fde0ef", "association_stroke_color": "#de77ae", "background_color": "#f7f7f7", "card_text_color": "#4d9221", "constraint_background_color": "#f7f7f7", "constraint_stroke_color": "#de77ae", "constraint_text_color": "#de77ae", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#b8e186", "entity_cartouche_text_color": "#000000", "entity_color": "#e6f5d0", "entity_stroke_color": "#7fbc41", "id_gutter_color": "#e6f5d0", "id_gutter_text_color": "#000000", "leg_stroke_color": "#de77ae", "note_color": "#c51b7d", "note_opacity": 0.9, "note_text_color": "#f7f7f7" } ================================================ FILE: mocodo/resources/colors/brewer+3.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#c2a5cf", "association_cartouche_text_color": "#000000", "association_color": "#e7d4e8", "association_stroke_color": "#9970ab", "background_color": "#f7f7f7", "card_text_color": "#1b7837", "constraint_background_color": "#f7f7f7", "constraint_stroke_color": "#9970ab", "constraint_text_color": "#9970ab", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#a6dba0", "entity_cartouche_text_color": "#000000", "entity_color": "#d9f0d3", "entity_stroke_color": "#5aae61", "id_gutter_color": "#d9f0d3", "id_gutter_text_color": "#000000", "leg_stroke_color": "#9970ab", "note_color": "#762a83", "note_opacity": 0.9, "note_text_color": "#f7f7f7" } ================================================ FILE: mocodo/resources/colors/brewer+4.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#fdb863", "association_cartouche_text_color": "#000000", "association_color": "#fee0b6", "association_stroke_color": "#e08214", "background_color": "#f7f7f7", "card_text_color": "#542788", "constraint_background_color": "#f7f7f7", "constraint_stroke_color": "#e08214", "constraint_text_color": "#e08214", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#b2abd2", "entity_cartouche_text_color": "#000000", "entity_color": "#d8daeb", "entity_stroke_color": "#8073ac", "id_gutter_color": "#d8daeb", "id_gutter_text_color": "#000000", "leg_stroke_color": "#e08214", "note_color": "#b35806", "note_opacity": 0.9, "note_text_color": "#f7f7f7" } ================================================ FILE: mocodo/resources/colors/brewer+5.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#f4a582", "association_cartouche_text_color": "#000000", "association_color": "#fddbc7", "association_stroke_color": "#d6604d", "background_color": "#f7f7f7", "card_text_color": "#2166ac", "constraint_background_color": "#f7f7f7", "constraint_stroke_color": "#d6604d", "constraint_text_color": "#d6604d", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#92c5de", "entity_cartouche_text_color": "#000000", "entity_color": "#d1e5f0", "entity_stroke_color": "#4393c3", "id_gutter_color": "#d1e5f0", "id_gutter_text_color": "#000000", "leg_stroke_color": "#d6604d", "note_color": "#b2182b", "note_opacity": 0.9, "note_text_color": "#f7f7f7" } ================================================ FILE: mocodo/resources/colors/brewer+6.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#f4a582", "association_cartouche_text_color": "#000000", "association_color": "#fddbc7", "association_stroke_color": "#d6604d", "background_color": "#ffffff", "card_text_color": "#4d4d4d", "constraint_background_color": "#ffffff", "constraint_stroke_color": "#d6604d", "constraint_text_color": "#d6604d", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#bababa", "entity_cartouche_text_color": "#000000", "entity_color": "#e0e0e0", "entity_stroke_color": "#878787", "id_gutter_color": "#e0e0e0", "id_gutter_text_color": "#000000", "leg_stroke_color": "#d6604d", "note_color": "#b2182b", "note_opacity": 0.9, "note_text_color": "#ffffff" } ================================================ FILE: mocodo/resources/colors/brewer+7.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#fdae61", "association_cartouche_text_color": "#000000", "association_color": "#fee090", "association_stroke_color": "#f46d43", "background_color": "#ffffbf", "card_text_color": "#4575b4", "constraint_background_color": "#ffffbf", "constraint_stroke_color": "#f46d43", "constraint_text_color": "#f46d43", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#abd9e9", "entity_cartouche_text_color": "#000000", "entity_color": "#e0f3f8", "entity_stroke_color": "#74add1", "id_gutter_color": "#e0f3f8", "id_gutter_text_color": "#000000", "leg_stroke_color": "#f46d43", "note_color": "#d73027", "note_opacity": 0.9, "note_text_color": "#ffffbf" } ================================================ FILE: mocodo/resources/colors/brewer+8.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#fdae61", "association_cartouche_text_color": "#000000", "association_color": "#fee08b", "association_stroke_color": "#f46d43", "background_color": "#ffffbf", "card_text_color": "#1a9850", "constraint_background_color": "#ffffbf", "constraint_stroke_color": "#f46d43", "constraint_text_color": "#f46d43", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#a6d96a", "entity_cartouche_text_color": "#000000", "entity_color": "#d9ef8b", "entity_stroke_color": "#66bd63", "id_gutter_color": "#d9ef8b", "id_gutter_text_color": "#000000", "leg_stroke_color": "#f46d43", "note_color": "#d73027", "note_opacity": 0.9, "note_text_color": "#ffffbf" } ================================================ FILE: mocodo/resources/colors/brewer+9.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#fdae61", "association_cartouche_text_color": "#000000", "association_color": "#fee08b", "association_stroke_color": "#f46d43", "background_color": "#ffffbf", "card_text_color": "#3288bd", "constraint_background_color": "#ffffbf", "constraint_stroke_color": "#f46d43", "constraint_text_color": "#f46d43", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#abdda4", "entity_cartouche_text_color": "#000000", "entity_color": "#e6f598", "entity_stroke_color": "#66c2a5", "id_gutter_color": "#e6f598", "id_gutter_text_color": "#000000", "leg_stroke_color": "#f46d43", "note_color": "#d53e4f", "note_opacity": 0.9, "note_text_color": "#ffffbf" } ================================================ FILE: mocodo/resources/colors/brewer-1.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#80cdc1", "association_cartouche_text_color": "#000000", "association_color": "#c7eae5", "association_stroke_color": "#35978f", "background_color": "#f5f5f5", "card_text_color": "#8c510a", "constraint_background_color": "#f5f5f5", "constraint_stroke_color": "#35978f", "constraint_text_color": "#35978f", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#dfc27d", "entity_cartouche_text_color": "#000000", "entity_color": "#f6e8c3", "entity_stroke_color": "#bf812d", "id_gutter_color": "#f6e8c3", "id_gutter_text_color": "#000000", "leg_stroke_color": "#35978f", "note_color": "#01665e", "note_opacity": 0.9, "note_text_color": "#f5f5f5" } ================================================ FILE: mocodo/resources/colors/brewer-2.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#b8e186", "association_cartouche_text_color": "#000000", "association_color": "#e6f5d0", "association_stroke_color": "#7fbc41", "background_color": "#f7f7f7", "card_text_color": "#c51b7d", "constraint_background_color": "#f7f7f7", "constraint_stroke_color": "#7fbc41", "constraint_text_color": "#7fbc41", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#f1b6da", "entity_cartouche_text_color": "#000000", "entity_color": "#fde0ef", "entity_stroke_color": "#de77ae", "id_gutter_color": "#fde0ef", "id_gutter_text_color": "#000000", "leg_stroke_color": "#7fbc41", "note_color": "#4d9221", "note_opacity": 0.9, "note_text_color": "#f7f7f7" } ================================================ FILE: mocodo/resources/colors/brewer-3.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#a6dba0", "association_cartouche_text_color": "#000000", "association_color": "#d9f0d3", "association_stroke_color": "#5aae61", "background_color": "#f7f7f7", "card_text_color": "#762a83", "constraint_background_color": "#f7f7f7", "constraint_stroke_color": "#5aae61", "constraint_text_color": "#5aae61", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#c2a5cf", "entity_cartouche_text_color": "#000000", "entity_color": "#e7d4e8", "entity_stroke_color": "#9970ab", "id_gutter_color": "#e7d4e8", "id_gutter_text_color": "#000000", "leg_stroke_color": "#5aae61", "note_color": "#1b7837", "note_opacity": 0.9, "note_text_color": "#f7f7f7" } ================================================ FILE: mocodo/resources/colors/brewer-4.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#b2abd2", "association_cartouche_text_color": "#000000", "association_color": "#d8daeb", "association_stroke_color": "#8073ac", "background_color": "#f7f7f7", "card_text_color": "#b35806", "constraint_background_color": "#f7f7f7", "constraint_stroke_color": "#8073ac", "constraint_text_color": "#8073ac", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#fdb863", "entity_cartouche_text_color": "#000000", "entity_color": "#fee0b6", "entity_stroke_color": "#e08214", "id_gutter_color": "#fee0b6", "id_gutter_text_color": "#000000", "leg_stroke_color": "#8073ac", "note_color": "#542788", "note_opacity": 0.9, "note_text_color": "#f7f7f7" } ================================================ FILE: mocodo/resources/colors/brewer-5.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#92c5de", "association_cartouche_text_color": "#000000", "association_color": "#d1e5f0", "association_stroke_color": "#4393c3", "background_color": "#f7f7f7", "card_text_color": "#b2182b", "constraint_background_color": "#f7f7f7", "constraint_stroke_color": "#4393c3", "constraint_text_color": "#4393c3", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#f4a582", "entity_cartouche_text_color": "#000000", "entity_color": "#fddbc7", "entity_stroke_color": "#d6604d", "id_gutter_color": "#fddbc7", "id_gutter_text_color": "#000000", "leg_stroke_color": "#4393c3", "note_color": "#2166ac", "note_opacity": 0.9, "note_text_color": "#f7f7f7" } ================================================ FILE: mocodo/resources/colors/brewer-6.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#bababa", "association_cartouche_text_color": "#000000", "association_color": "#e0e0e0", "association_stroke_color": "#878787", "background_color": "#ffffff", "card_text_color": "#b2182b", "constraint_background_color": "#ffffff", "constraint_stroke_color": "#878787", "constraint_text_color": "#878787", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#f4a582", "entity_cartouche_text_color": "#000000", "entity_color": "#fddbc7", "entity_stroke_color": "#d6604d", "id_gutter_color": "#fddbc7", "id_gutter_text_color": "#000000", "leg_stroke_color": "#878787", "note_color": "#4d4d4d", "note_opacity": 0.9, "note_text_color": "#ffffff" } ================================================ FILE: mocodo/resources/colors/brewer-7.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#abd9e9", "association_cartouche_text_color": "#000000", "association_color": "#e0f3f8", "association_stroke_color": "#74add1", "background_color": "#ffffbf", "card_text_color": "#d73027", "constraint_background_color": "#ffffbf", "constraint_stroke_color": "#74add1", "constraint_text_color": "#74add1", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#fdae61", "entity_cartouche_text_color": "#000000", "entity_color": "#fee090", "entity_stroke_color": "#f46d43", "id_gutter_color": "#fee090", "id_gutter_text_color": "#000000", "leg_stroke_color": "#74add1", "note_color": "#4575b4", "note_opacity": 0.9, "note_text_color": "#ffffbf" } ================================================ FILE: mocodo/resources/colors/brewer-8.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#a6d96a", "association_cartouche_text_color": "#000000", "association_color": "#d9ef8b", "association_stroke_color": "#66bd63", "background_color": "#ffffbf", "card_text_color": "#d73027", "constraint_background_color": "#ffffbf", "constraint_stroke_color": "#66bd63", "constraint_text_color": "#66bd63", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#fdae61", "entity_cartouche_text_color": "#000000", "entity_color": "#fee08b", "entity_stroke_color": "#f46d43", "id_gutter_color": "#fee08b", "id_gutter_text_color": "#000000", "leg_stroke_color": "#66bd63", "note_color": "#1a9850", "note_opacity": 0.9, "note_text_color": "#ffffbf" } ================================================ FILE: mocodo/resources/colors/brewer-9.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#abdda4", "association_cartouche_text_color": "#000000", "association_color": "#e6f598", "association_stroke_color": "#66c2a5", "background_color": "#ffffbf", "card_text_color": "#d53e4f", "constraint_background_color": "#ffffbf", "constraint_stroke_color": "#66c2a5", "constraint_text_color": "#66c2a5", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#fdae61", "entity_cartouche_text_color": "#000000", "entity_color": "#fee08b", "entity_stroke_color": "#f46d43", "id_gutter_color": "#fee08b", "id_gutter_text_color": "#000000", "leg_stroke_color": "#66c2a5", "note_color": "#3288bd", "note_opacity": 0.9, "note_text_color": "#ffffbf" } ================================================ FILE: mocodo/resources/colors/bw-alpha.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#FFFFFF", "association_cartouche_text_color": "#000000", "association_color": "#FFFFFF", "association_stroke_color": "#000000", "background_color": null, "card_text_color": "#000000", "constraint_background_color": "#FFFFFF", "constraint_stroke_color": "#000000", "constraint_text_color": "#000000", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#FFFFFF", "entity_cartouche_text_color": "#000000", "entity_color": "#FFFFFF", "entity_stroke_color": "#000000", "id_gutter_color": "#FFFFFF", "id_gutter_text_color": "#000000", "leg_stroke_color": "#000000", "note_color": "#000000", "note_opacity": 0.7, "note_text_color": "#FFFFFF" } ================================================ FILE: mocodo/resources/colors/bw.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#FFFFFF", "association_cartouche_text_color": "#000000", "association_color": "#FFFFFF", "association_stroke_color": "#000000", "background_color": "#FFFFFF", "card_text_color": "#000000", "constraint_background_color": "#FFFFFF", "constraint_stroke_color": "#000000", "constraint_text_color": "#000000", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#FFFFFF", "entity_cartouche_text_color": "#000000", "entity_color": "#FFFFFF", "entity_stroke_color": "#000000", "id_gutter_color": "#FFFFFF", "id_gutter_text_color": "#000000", "leg_stroke_color": "#000000", "note_color": "#000000", "note_opacity": 0.7, "note_text_color": "#FFFFFF" } ================================================ FILE: mocodo/resources/colors/dark-desert.json ================================================ { "association_attribute_text_color": "#D3B08C", "association_cartouche_color": "#4E3925", "association_cartouche_text_color": "#D3B08C", "association_color": "#624A32", "association_stroke_color": "#C6A664", "background_color": "#2E2B22", "card_text_color": "#D4C2A1", "constraint_background_color": "#4E3925", "constraint_stroke_color": "#C6A664", "constraint_text_color": "#D3B08C", "entity_attribute_text_color": "#E3D5B3", "entity_cartouche_color": "#5F3C14", "entity_cartouche_text_color": "#E3D5B3", "entity_color": "#7C5020", "entity_stroke_color": "#E5A04B", "id_gutter_color": "#7C5020", "id_gutter_text_color": "#E3D5B3", "leg_stroke_color": "#C6A664", "note_color": "#2D271E", "note_opacity": 0.8, "note_text_color": "#D4C49A" } ================================================ FILE: mocodo/resources/colors/dark-ocean.json ================================================ { "association_attribute_text_color": "#b4c8a1", "association_cartouche_color": "#2d3b22", "association_cartouche_text_color": "#d9e5c3", "association_color": "#3a4a28", "association_stroke_color": "#a6b792", "background_color": "#1a1f1a", "card_text_color": "#b0aabb", "constraint_background_color": "#2d3b22", "constraint_stroke_color": "#a6b792", "constraint_text_color": "#d9e5c3", "entity_attribute_text_color": "#e6e5ea", "entity_cartouche_color": "#1a223a", "entity_cartouche_text_color": "#d9e5c3", "entity_color": "#2a3866", "entity_stroke_color": "#83a5ff", "id_gutter_color": "#2a3866", "id_gutter_text_color": "#e6e5ea", "leg_stroke_color": "#a6b792", "note_color": "#131517", "note_opacity": 0.8, "note_text_color": "#d9e5c3" } ================================================ FILE: mocodo/resources/colors/dark-pond.json ================================================ { "association_attribute_text_color": "#A7BEC1", "association_cartouche_color": "#31572E", "association_cartouche_text_color": "#A7BEC1", "association_color": "#3F5F4E", "association_stroke_color": "#B4D877", "background_color": "#1A2622", "card_text_color": "#C1D9DC", "constraint_background_color": "#31572E", "constraint_stroke_color": "#B4D877", "constraint_text_color": "#A7BEC1", "entity_attribute_text_color": "#C4F1D8", "entity_cartouche_color": "#295943", "entity_cartouche_text_color": "#C4F1D8", "entity_color": "#387F59", "entity_stroke_color": "#6AD1A0", "id_gutter_color": "#387F59", "id_gutter_text_color": "#C4F1D8", "leg_stroke_color": "#B4D877", "note_color": "#1E3932", "note_opacity": 0.8, "note_text_color": "#C7F7E1" } ================================================ FILE: mocodo/resources/colors/dark.json ================================================ { "association_attribute_text_color": "#FFFFFF", "association_cartouche_color": "#444444", "association_cartouche_text_color": "#FFFFFF", "association_color": "#333333", "association_stroke_color": "#AAAAAA", "background_color": "#222222", "card_text_color": "#FFFFFF", "constraint_background_color": "#444444", "constraint_stroke_color": "#AAAAAA", "constraint_text_color": "#FFFFFF", "entity_attribute_text_color": "#FFFFFF", "entity_cartouche_color": "#444444", "entity_cartouche_text_color": "#FFFFFF", "entity_color": "#333333", "entity_stroke_color": "#AAAAAA", "id_gutter_color": "#444444", "id_gutter_text_color": "#FFFFFF", "leg_stroke_color": "#AAAAAA", "note_color": "#444444", "note_opacity": 0.8, "note_text_color": "#FFFFFF" } ================================================ FILE: mocodo/resources/colors/desert.json ================================================ { "association_attribute_text_color": "#7F5D3B", "association_cartouche_color": "#CE9F6F", "association_cartouche_text_color": "#7F5D3B", "association_color": "#E2BE9F", "association_stroke_color": "#896C39", "background_color": null, "card_text_color": "#5C4827", "constraint_background_color": "#FFFFFF", "constraint_stroke_color": "#896C39", "constraint_text_color": "#896C39", "entity_attribute_text_color": "#645530", "entity_cartouche_color": "#FAA945", "entity_cartouche_text_color": "#645530", "entity_color": "#FFB94E", "entity_stroke_color": "#CB7E1F", "id_gutter_color": "#FFB94E", "id_gutter_text_color": "#645530", "leg_stroke_color": "#4C3D30", "note_color": "#18180B", "note_opacity": 0.7, "note_text_color": "#FFFFD0" } ================================================ FILE: mocodo/resources/colors/gray.json ================================================ { "association_attribute_text_color": "#000000", "association_cartouche_color": "#FFFFFF", "association_cartouche_text_color": "#000000", "association_color": "#FFFFFF", "association_stroke_color": "#000000", "background_color": null, "card_text_color": "#000000", "constraint_background_color": "#FFFFFF", "constraint_stroke_color": "#000000", "constraint_text_color": "#000000", "entity_attribute_text_color": "#000000", "entity_cartouche_color": "#DDDDDD", "entity_cartouche_text_color": "#000000", "entity_color": "#EEEEEE", "entity_stroke_color": "#000000", "id_gutter_color": "#EEEEEE", "id_gutter_text_color": "#000000", "leg_stroke_color": "#000000", "note_color": "#000000", "note_opacity": 0.7, "note_text_color": "#FFFFFF" } ================================================ FILE: mocodo/resources/colors/keepsake.json ================================================ { "association_attribute_text_color": "#57483d", "association_cartouche_color": "#c6aea5", "association_cartouche_text_color": "#422f2c", "association_color": "#d3b5b2", "association_stroke_color": "#c6aea5", "background_color": null, "card_text_color": "#57483d", "constraint_background_color": "#FFFFFF", "constraint_stroke_color": "#c6aea5", "constraint_text_color": "#c6aea5", "entity_attribute_text_color": "#57483d", "entity_cartouche_color": "#cbcbb5", "entity_cartouche_text_color": "#422f2c", "entity_color": "#e4e3b4", "entity_stroke_color": "#cbcbb5", "id_gutter_color": "#e4e3b4", "id_gutter_text_color": "#57483d", "leg_stroke_color": "#57483d", "note_color": "#0F0F0A", "note_opacity": 0.7, "note_text_color": "#FFFFD7" } ================================================ FILE: mocodo/resources/colors/mondrian.json ================================================ { "association_attribute_text_color": "#060100", "association_cartouche_color": "#081a84", "association_cartouche_text_color": "#fefeec", "association_color": "#fefeec", "association_stroke_color": "#060100", "background_color": "#fefeec", "card_text_color": "#060100", "constraint_background_color": "#fefeec", "constraint_stroke_color": "#060100", "constraint_text_color": "#060100", "entity_attribute_text_color": "#060100", "entity_cartouche_color": "#c6281b", "entity_cartouche_text_color": "#fefeec", "entity_color": "#fced5f", "entity_stroke_color": "#060100", "id_gutter_color": "#fced5f", "id_gutter_text_color": "#060100", "leg_stroke_color": "#060100", "note_color": "#066A6A", "note_opacity": 0.85, "note_text_color": "#FFFF22" } ================================================ FILE: mocodo/resources/colors/ocean.json ================================================ { "association_attribute_text_color": "#607734", "association_cartouche_color": "#b2bba4", "association_cartouche_text_color": "#27360c", "association_color": "#ccd6ba", "association_stroke_color": "#85956b", "background_color": null, "card_text_color": "#726f83", "constraint_background_color": "#FFFFFF", "constraint_stroke_color": "#85956b", "constraint_text_color": "#85956b", "entity_attribute_text_color": "#3e3c42", "entity_cartouche_color": "#97b8ff", "entity_cartouche_text_color": "#131114", "entity_color": "#c0d4ff", "entity_stroke_color": "#578dff", "id_gutter_color": "#c0d4ff", "id_gutter_text_color": "#3e3c42", "leg_stroke_color": "#726f83", "note_color": "#060707", "note_opacity": 0.7, "note_text_color": "#E2EED0" } ================================================ FILE: mocodo/resources/colors/pond.json ================================================ { "association_attribute_text_color": "#3d484a", "association_cartouche_color": "#9ebd6d", "association_cartouche_text_color": "#3d484a", "association_color": "#c3dbbb", "association_stroke_color": "#8ea243", "background_color": null, "card_text_color": "#2f494e", "constraint_background_color": "#FFFFFF", "constraint_stroke_color": "#8ea243", "constraint_text_color": "#8ea243", "entity_attribute_text_color": "#1b5343", "entity_cartouche_color": "#7ecb9a", "entity_cartouche_text_color": "#1b5343", "entity_color": "#a6efb3", "entity_stroke_color": "#387252", "id_gutter_color": "#a6efb3", "id_gutter_text_color": "#1b5343", "leg_stroke_color": "#476062", "note_color": "#0A2019", "note_opacity": 0.7, "note_text_color": "#E3FFD9" } ================================================ FILE: mocodo/resources/colors/wb.json ================================================ { "association_attribute_text_color": "#FFFFFF", "association_cartouche_color": "#000000", "association_cartouche_text_color": "#FFFFFF", "association_color": "#000000", "association_stroke_color": "#FFFFFF", "background_color": "#FFFFFF", "card_text_color": "#000000", "constraint_background_color": "#FFFFFF", "constraint_stroke_color": "#FFFFFF", "constraint_text_color": "#FFFFFF", "entity_attribute_text_color": "#FFFFFF", "entity_cartouche_color": "#000000", "entity_cartouche_text_color": "#FFFFFF", "entity_color": "#000000", "entity_stroke_color": "#FFFFFF", "id_gutter_color": "#000000", "id_gutter_text_color": "#FFFFFF", "leg_stroke_color": "#000000", "note_color": "#FFFF00", "note_opacity": 0.9, "note_text_color": "#000000" } ================================================ FILE: mocodo/resources/colors/xinnian.json ================================================ { "association_attribute_text_color": "#ffcf00", "association_cartouche_color": "#fd0400", "association_cartouche_text_color": "#ffcf00", "association_color": "#fd0400", "association_stroke_color": "#ffcf00", "background_color": "#d30300", "card_text_color": "#ffcf00", "constraint_background_color": "#d30300", "constraint_stroke_color": "#ffcf00", "constraint_text_color": "#ffcf00", "entity_attribute_text_color": "#ffcf00", "entity_cartouche_color": "#fd0400", "entity_cartouche_text_color": "#ffcf00", "entity_color": "#fd0400", "entity_stroke_color": "#ffcf00", "id_gutter_color": "#fd0400", "id_gutter_text_color": "#ffcf00", "leg_stroke_color": "#ffcf00", "note_color": "#535300", "note_opacity": 0.7, "note_text_color": "#FFFF00" } ================================================ FILE: mocodo/resources/default_datatypes_en.tsv ================================================ Date and Time \bdate$ DATE \bday$ DATE \btime$ TIME \bdatetime$ DATETIME \btimestamp$ TIMESTAMP \bat$ TIMESTAMP \bbefore$ TIMESTAMP \bafter$ TIMESTAMP Identifying and Validating Information \bid(entifier)?$ VARCHAR(8) \bcode$ VARCHAR(8) \bref(erence)?$ VARCHAR(8) \bnum(ber)?$ VARCHAR(8) \bnb$ VARCHAR(8) \buuid$ VARCHAR(36) \bsalt$ BINARY(16) \bhash$ BINARY(64) \btoken$ VARCHAR(255) \bpassword$ VARCHAR(255) \bpwd$ VARCHAR(255) \bdigest$ BINARY(64) \bsignature$ BINARY(64) Internet and Computers \burl$ VARCHAR(2000) \bip$ VARCHAR(15) \bip address$ VARCHAR(15) \bipv4$ VARCHAR(15) \bipv6$ VARCHAR(45) \bcookie$ VARCHAR(255) \bsession$ VARCHAR(255) \bmd5$ BINARY(16) \bpath$ VARCHAR(255) \bstatus$ VARCHAR(20) \bpriority$ SMALLINT \bversion$ VARCHAR(10) Personal information \bname$ VARCHAR(255) \bsex$ CHAR(1) \bgender$ VARCHAR(10) \bage$ SMALLINT Addressing \bpost code$ VARCHAR(20) \bzip code$ VARCHAR(20) \bphone number$ VARCHAR(20) \bphone$ VARCHAR(20) \bfax number$ VARCHAR(20) \bfax$ VARCHAR(20) \bemail$ VARCHAR(255) \bcountry name$ VARCHAR(100) \bcity name$ VARCHAR(100) \bcountry code$ CHAR(2) \baddress$ VARCHAR(30) Financial \b(price|amount|fees|charges|cost|bill)$ DECIMAL(10,2) \barn$ VARCHAR(8) Acquirer Reference Number \barn code$ VARCHAR(8) \bbic$ VARCHAR(11) Bank Identifier Code \bbic code$ VARCHAR(11) \biban$ VARCHAR(34) Maximum value Predicate ^is\b BOOLEAN ^has\b BOOLEAN ^does\b BOOLEAN ^can\b BOOLEAN \bflag$ BOOLEAN Text \btag$ VARCHAR(50) \blabel$ VARCHAR(50) \bbody$ TEXT \bcomment$ TEXT \bdescription$ TEXT \bnote$ TEXT \bmessage$ TEXT \bcontent$ TEXT \binfo(rmation)?s?$ JSON Geographical \blat(itude)?$ DECIMAL(9,6) \blon(gitude)?$ DECIMAL(9,6) \bpos(ition)?$ POINT Grading \bstars$ DECIMAL(3,2) \brating$ DECIMAL(3,2) \brating$ DECIMAL(3,2) \bgrade$ DECIMAL(3,2) Quantity \bquantity$ INTEGER \bcapacity$ INTEGER \border$ INTEGER \bpercent(age)?$ DECIMAL(5,2) \bratio$ DECIMAL(5,2) \bweight$ DECIMAL(10,2) \bsize$ VARCHAR(20) \blength$ DECIMAL(10,2) \bwidth$ DECIMAL(10,2) \bheight$ DECIMAL(10,2) \bvolume$ DECIMAL(10,2) \btemperature$ DECIMAL(5,1) \bhumidity$ DECIMAL(5,2) \bcolor$ VARCHAR(50) Blobs \bimage$ BLOB \bpicture$ BLOB \bphoto$ BLOB \blogo$ BLOB \bicon$ BLOB \bfile$ BLOB \bvideo$ BLOB \baudio$ BLOB \bbinary$ BLOB \bblob$ BLOB ================================================ FILE: mocodo/resources/default_datatypes_fr.tsv ================================================ Date and Time ^date\b DATE ^jour\b DATE ^heure\b TIME ^date et heure\b DATETIME ^horodatage\b TIMESTAMP \ba$ TIMESTAMP \bapres$ TIMESTAMP \bavant$ TIMESTAMP Identifying and Validating Information ^id(entifiant)?\b VARCHAR(8) ^code\b VARCHAR(8) ^ref(erence)?\b VARCHAR(8) ^num(ero)?\b VARCHAR(8) ^uuid\b VARCHAR(36) ^sel\b BINARY(16) ^empreinte\b BINARY(64) ^token\b VARCHAR(255) ^passe\b VARCHAR(255) ^pwd\b VARCHAR(255) ^digest\b BINARY(64) ^signature\b BINARY(64) \bsiren\b CHAR(9) \bsiret\b CHAR(14) Internet and Computers ^url\b VARCHAR(2000) ^ip\b VARCHAR(15) ^adresse ip\b VARCHAR(15) ^ipv4\b VARCHAR(15) ^ipv6\b VARCHAR(45) ^cookie\b VARCHAR(255) ^session\b VARCHAR(255) ^md5\b BINARY(16) ^chemin\b VARCHAR(255) ^statut\b VARCHAR(20) ^priorite\b SMALLINT ^version\b VARCHAR(10) Personal information ^(pre)?nom\b VARCHAR(255) ^sexe\b CHAR(1) ^genre\b VARCHAR(10) ^age\b SMALLINT ^nir\b VARCHAR(15) ^ss\b VARCHAR(15) \bsecu(rite sociale)?\b VARCHAR(15) Addressing ^code postal\b VARCHAR(20) ^tel(ephone)?\b VARCHAR(20) ^fax\b VARCHAR(20) ^e?mail\b VARCHAR(255) ^courriel\b VARCHAR(255) ^(nom )?pays\b VARCHAR(100) ^(nom )?ville\b VARCHAR(100) ^code pays\b CHAR(2) ^adresse\b VARCHAR(30) Financial ^(prix|montant|frais|charges|coût)\b DECIMAL(10,2) ^code nra\b VARCHAR(8) ^nra\b VARCHAR(8) Numéro de Référence de l'Acquéreur, pour identifier les transactions par carte bancaire ^bic\b VARCHAR(11) Bank Identifier Code ^code bic\b VARCHAR(11) ^iban\b VARCHAR(27) France, Monaco, etc. Predicate ^est\b BOOLEAN ^a\b BOOLEAN ^peut\b BOOLEAN ^drapeau\b BOOLEAN Text ^mot clef?\b VARCHAR(50) \blibelle$ VARCHAR(50) ^corps\b TEXT ^commentaire\b TEXT ^desc(r?(iption)?)\b TEXT ^descriptif\b TEXT ^notes?\b TEXT ^message\b TEXT ^avis\b TEXT ^contenu\b TEXT \binfo(rmation)?s?\b JSON Geographical ^lat(itude)?\b DECIMAL(9,6) ^lon(gitude)?\b DECIMAL(9,6) ^pos(ition)?\b POINT Grading \bétoiles$ DECIMAL(3,2) Quantity ^(quantite|qte)\b INTEGER ^(capacite)\b INTEGER ^(nombre|nb)\b INTEGER ^ordre\b INTEGER ^pourcent(age)?\b DECIMAL(5,2) ^ratio\b DECIMAL(5,2) ^poids\b DECIMAL(10,2) ^taille\b VARCHAR(20) ^longueur\b DECIMAL(10,2) ^largeur\b DECIMAL(10,2) ^hauteur\b DECIMAL(10,2) ^vol(ume)?\b DECIMAL(10,2) ^temp(erature)?\b DECIMAL(5,1) ^humidite\b DECIMAL(5,2) ^couleur\b VARCHAR(50) Blobs ^image\b BLOB ^photo\b BLOB ^logo\b BLOB ^icone\b BLOB ^fichier\b BLOB ^video\b BLOB ^audio\b BLOB ^binaire\b BLOB ^blob\b BLOB ================================================ FILE: mocodo/resources/font_metrics.json ================================================ { "alphabet": " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~¡¢£¤¥¦§©«¬®°±²³µ¶·º»¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖŒ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöœ÷øùúûüýþÿ—‘’•…′″‵‶", "fonts": { "Arial Unicode MS": { "default": 142, "widths": "GG[ŽŽä«1UUd–GUGGŽŽŽŽŽŽŽŽŽŽGG–––ŽĄ««¹¹«œÇ¹G€«ŽÕ¹Ç«Ç¹«œ¹«ò««œGGGx€UŽŽ€ŽŽGŽŽ99€9ՎŽŽŽU€GŽ€¹€€€VCV–UŽŽŽŽC޽ޖ½f–UUŽŠG^Žœ««««««Ā¹««««GGGG¹¹ÇÇÇÇÇĀ–Ç¹¹¹¹««œŽŽŽŽŽŽä€ŽŽŽŽGGGGŽŽŽŽŽŽŽò–œŽŽŽŽ€Ž€Ā99ZĀ9_5[", "height": 343 }, "PT Sans Caption": { "default": 152, "widths": "JU`˜˜Ûä>PPcŽ8e=e˜˜˜˜˜˜˜˜˜˜FGŽŽŽ{Ī¥¤ ·–‘«¼fS«‘Þ¼¿¿§•œ¶¡ê®ž™VlVŽtM•¡‡¡™]¡¤QPV󤢡 a€h¥‰×˜ˆ„bBbŽT˜˜˜˜BŽÞ†ŽÀxŽpp£ŒJr†z¥¥¥¥¥¥î ––––ffff¹¼¿¿¿¿¿ąŽ¿¶¶¶¶ž­••••••™™™QQQQ¥¤¢¢¢¢¢āŽ¥¥¥¥ˆ¡ˆæ88ÒS‹S‹", "height": 332 }, "SchoolHouse Cursive B": { "default": 146, "widths": "?QY‰~{n3CWHg#g\"h’@|‹|urgoc<5KgKff¨¡•Ô‡îËæºtÜÃĖÊ ’®™¸ð¶ÏĠß²‰Wdvm&’™‹£Š©Œ’¨ˆÿÇ£°‰~§x’­Í»¦–J\u001bkƒQ…u£Ž4ŽÅ’ŽÅ5Ž^^„H2T’f¨¨¨¨¨¨ë•‡‡‡‡ººººØÊ     áŽ ¶¶¶¶¥’’’’’’¹‹ŒŒŒŒxÇ£££££Âg£’’’’‹‹Ü#;ŽÜĀĀˆ", "height": 313 }, "Arial": { "default": 142, "widths": "GG[ŽŽä«1UUd–GUGGŽŽŽŽŽŽŽŽŽŽGG–––ŽĄ««¹¹«œÇ¹G€«ŽÕ¹Ç«Ç¹«œ¹«ò««œGGGxŽUŽŽ€ŽŽGŽŽ99€9ՎŽŽŽU€GŽ€¹€€€VCV–UŽŽŽŽC޽ޖ½fUU”ŠU^Žœ««««««Ā¹««««GGGG¹¹ÇÇÇÇÇĀ–Ç¹¹¹¹««œŽŽŽŽŽŽä€ŽŽŽŽGGGGŽŽŽŽŽŽŽòœŽŽŽŽ€Ž€Ā99ZĀ0[S‹", "correction": 1.05, "height": 294 }, "Stone Sans ITC TT": { "default": 171, "widths": "Dv«««ĈÒS««Z]S“««««««««««SS›«›ŽÈ¼´¬Ê—ˆ¿Î_^¹ïÆÌ¯Î´“Ÿ¿¹Ď­£Ÿ“¦ˆˆ®…®˜d«¯WW¡W❤®°}tr®Žà”Ž‚v_u¯v««Ã«_«É“ˆml«uu«ºSp’޼¼¼¼¼¼ó¬————````ÊÇÌÌÌÌÌČ«Î¿¿¿¿£­¿ç…˜˜˜˜WWWW¤¯¤¤¤¤¤ý«¤®®®®Ž®ùXYsúZ‘Z‘", "height": 320 }, "HanaMinA": { "default": 154, "height": 282 }, "Verdana": { "default": 163, "widths": "ZevÒ£ĔºEtt£Ò]t]t££££££££££ttÒÒÒŒĀ¯°³Å¢“ÇÀlt±ØÀʚʲ¯ž»¯ý¯ž¯tttÒ££š … ™Z ¢FX˜Fù¢›  m…e¢˜Ò˜˜‡£t£Òe££££t£Ā¥ÒĀ‹Ò‹‹¤£]Œ¥Œ¯¯¯¯¯¯ü³¢¢¢¢llllÇÀÊÊÊÊÊĒÒÊ»»»»ž›Ÿššššššõ…™™™™FFFF¢›››››ûқ¢¢¢¢˜ ˜ĀEEŒÒ]S‹", "height": 311 }, "Academy Engraved LET": { "default": 116, "widths": "UER€„»2LL[7P9D¡bty{‹jŠŒ>;zìÁ¥¹Ã©˜ÇÑ\\qĜûÐÄ¡ÄɆ¯ÁÁěµº•LDLx€Z}ssvuzluvFEz€z›Ázd¯|˜†°KJ§‚Óњzv~© ¯þ´¤ºKŠKz€VbPHfIDG`>8pMŸrTXT\\F@ufšƒsŽZKZ®@Z‘¤9ºd³¹QtNT–¯ITrgèd||||KKKKúњššššÌt    ¤Àbbbbbb¤HIIIIAAAAZrTTTTTz€^uuuus]säFFqÚĀĀˆ", "height": 463 }, "HanaMinB": { "default": 154, "height": 286 }, "BlairMdITC TT": { "default": 242, "widths": "M[MÃóĥñ(op“À[ˆ[‹ïb×ÜÐâêÄåä[\\ÀºÔòďČđðßĘĐ]ÊöÒĽĐęôęĂðÚčîİììãs‹q€€ØêçìÑÅñíT³ÙºēíòÖòåÐÂéÔĊÑÔÄjKh[ʼ©ò€çÕ¹›aˆÕŽ‘òwœ¹ºòòòòòòŧČðððð]]]]đĐęęęęęƈÀęččččìôƕØØØØØØĸçÑÑÑÑTTTTìíòòòòòőÕòééééÔÖÔúFFˆđZ‘Z‘", "height": 307 }, "Beirut": { "default": 177, "widths": "&UUkk«€@UUn€Q€U€e=gceae`deUQk€klܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›U~UU€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“U@U¢Q¢¢¢¢`¢ÛUÌÛoÌll ¢QxUl±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀ€Ç±±±± Ž™ÚƒJJJJšŸî€ŸŸŸŸ†¡†ĀQQÀS‹S‹", "height": 384 }, "Hoefler Text": { "default": 212, "widths": "@a™ƒÉª°°–‡ƒªáŒG^G…mLuŽ€Vސ<<€<̏ŠŽ^oYŽv­{sO?O‹?‘‘’‘?‘°s‡Z‘OMŽq>>>¦ª°°°°°þ‘°ªªªª™Ìu€€€€<<<<ŠŠŠŠŠ×‘ŠŽŽŽŽ–ý55NµS‹S‹", "height": 406 }, "Optima": { "default": 142, "widths": "GUUŽŽä¹GGGr›GUGGŽŽŽŽŽŽŽŽŽŽGG›››dÍ«œ«Ç€€ÇÇGGœ€äÇՎ՜€ŽÇ«ĀœœœU€U›€U€Ž€Ž€G€ŽGG€GՎŽŽŽUdGŽ€Ç€€€UUU›UŽŽŽŽU€À€›Àf›UUŽÍGW€d««««««Õ«€€€€GGGGÇÇÕÕÕÕÕĀ›ÕÇÇÇǜŽŽ€€€€€€Ç€€€€€GGGGŽŽŽŽŽŽŽä›ŽŽŽŽŽ€Ž€ĀGG›ĀS‹S‹", "height": 311 }, "Kannada Sangam MN": { "default": 177, "widths": "C3T˜é½.OJ„¨8Z5R››››››››››58¨‹Ù­£ºµœ•±6~¨‰Î®ÆžÈ¡£ž­Ÿî—£ >O8y¡7‹‹‡‹N‰625Շ‘‘Q€L‡Á|}yT2O«Q¢¢¢¢`¢Õ†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†Ü77ZĀS‹S‹", "height": 416 }, "DecoType Naskh": { "default": 162, "widths": "6bAYY«‹ WW{~QŽWm¢¢¢¢¢¢¢¢¢¢WQ]m]lܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›W>muǝ¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“´,´¢Q¢¢¢¢`¢Û`ÌÛoÌll ¢Qx`l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀ_DZ±±± Ž™ÚƒJJJJšŸîgŸŸŸŸ†¡†ĀQQ¾S‹S‹", "height": 465 }, "Chalkduster": { "default": 184, "widths": "[Ă«Ûë6p™ O›W®°›°ë¤«˜ÍšTk{­|~Ö½«Ð³½¿Ĝž˜“¨¢×¶º¶¾Ç;è›Ý˕´‘®`±•¡ÈŸˆ¯žexÇxă¼¸ž¥‡u°ŸÑ£œ¦xPªh“À£­4šû¸ŸĐq™^^ãÛa¥£š«°µ«±¥ĤŸ¸Ãµ°““•³¸¸µ³µµŋŽë¸»µ´Ý—Á£¨¥­£¨ñ¨«£¨kUkYšÀ¼½µÅÃĠ¸Ã½Ã¸›•£®QR þĀĀˆ", "height": 335 }, "Chalkboard SE": { "default": 146, "widths": "bAt̔е2QQ€ŠAzL‹’’’’’’’’’’I]m}qsÑ¥’–£Œ€ªŸq–…Ô¨º”à–’¨¥ý¨–¨O…L|£L‰~„‡o‡‹@c‡M½ƒ}‡„pnu‡w¸‰‰X@Z¤]} £@z构ßfŽ_T©˜U†žˆ¥¥¥¥¥¥å–Œqqtq§ªºººººèŽº¨¨¨¨–o ‰‰‰‰‰‰Ò~‡‡‡‡DEPSvƒ‰ÇŠ}˜˜˜˜‰k‰£FCsÁĀĀˆ", "height": 365 }, "Hannotate SC": { "default": 155, "widths": "UZR¦“»Ð0LLs“F]D‹››››››››››DFä¢£š¡•„Ÿ¤>‰Ÿ~½ ª•¶§˜—˜–½œ‘¡T†Ts€L‹ƒ€…c~ƒEN‚@¬„„Žr~f|w£€‚x`KaUZ~±¯­G“¥‘–¥g—€€€e‘¢¢¢¢¢¢Óš••••ZZZZ¯ ªªªªªØ‚¬˜˜˜˜‘™˜‹‹‹‹‹‹­€ZZZZ„„„„„„µ–„||||‚…‚ĀAA€ĀR€ˆ", "height": 358 }, "Bodoni 72 Oldstyle": { "default": 125, "widths": "0[ˆˆˆ·»[NNˆˆBNB}Qxsxu}ly}BB‹ˆ‹jýš’§·•‹ª¶PTªÁ¨¸¹—–ª¢é ›N}Nˆ…€f{iznDxz@=x<´}{}wW]I}b™lgeN…N®[‚„€…ˆÎkˆÎˆˆ[[}ˆB\\kjššššššï§••••PPPP·¨¸¸¸¸¸ò¸ªªªª›‡ffffff¨innnn@@@@{}{{{{{¿ˆ{}}}}€}g›CC±Ç?iS‹", "height": 308 }, "Hiragino Mincho ProN": { "default": 204, "widths": "UUW®¡ÔÎ0PPd®PTPd——————————PP€®€˜Ø½ªµÃ¢˜ÃÌZz³—îÌ̞̮¨Å»Ā¸¸©PdP€€€Œš‡›‰TˆžLJŽL枕™˜irV˜ËŠŒ~P€P€U¡¦Œ¦€¡·k®·[®dd¡™Pdk˜½½½½½½øµ¢¢¢¢ZZZZÃÌÌÌÌÌÌą®ÌÅÅÅŸ£˜ŒŒŒŒŒŒÓ‡‰‰‰‰LLLL•ž•••••⮕˜˜˜˜Œ™ŒĀKK¡Ā7\\S‹", "height": 397 }, "Gurmukhi MN": { "default": 128, "widths": "KKcƒ‰ÝÍ.``g•CM9D””””””””””9F›œ›yø®ªËʚ‰Ñ¾Izºï¼Ý’Û¦¥¼­ð­§ªQDP{ŒP‡—™”ežVVh›C[Bk˜Zz|‰‚Œt‹ŒBA–ž–uЭ’œ¯ƒ­¯C@–yÙ¶½‚½–€‡­˜ò™ŠŠ\\k\\ƒjg€Šr‹|[ƒŽ=<}G܎Œ‹‹\\kdŒxÁ~vtgDg•N‰¢ŒDÐ“fd]›“B|“u­­­­­­áœƒƒƒƒCCCC»¶½½½½½ô•¿­­­­Š„—€€€€€€Ír||||====ŒŽŒŒŒŒŒÜŽŒŒŒŒvŒvÅ>>fÅS‹S‹", "height": 314 }, "Nanum Gothic": { "default": 155, "widths": "Hmn››ėºN]]›§N]N`››››››››››NNŒ§ŒŒßº›¬º“Ìº>]¤|øºÉ”ÉŸŒŒºªćªªŒ]÷]§Œ>Œ›|›Œ]››>E…>蛛››]m]›|كw|N>N§Mññ×ñ`YÛñññr‘in ›JŠñЧ±§±±±Ù’…………JJ_c˽ÇdzÇÇݘȤ±¤¤ ˆ¯††êJJ_c˜Ÿò¦Ÿ†™†Ý//ññv{JJ", "height": 295 }, "Gurmukhi MT": { "default": 123, "widths": "XA\\ˆ¢»³5UUf{0Y0]{{{{{{{{{{<<{{{Pܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›Q]Q{T¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“QIQ{Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀ{DZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†á77²S‹S‹", "height": 334 }, "Helvetica Neue": { "default": 142, "widths": "GBmŽŽĀ¡GBBZšGdGUŽŽŽŽŽŽŽŽŽŽGGšššŽÍ¦¯¹´œ“¹B…«Žß¹Ã¦Ã¯¦“¹œíœ¦œBUBš€9‰˜‰˜‰L“Ž99…9ڎ“˜˜U€QŽ€Â…€{U9UšBŽŽŽŽ9ŽÍwšÍfšUUŽšGbwަ¦¦¦¦¦í¹œœœœBBBB´¹ÃÃÃÃÃēšÃ¹¹¹¹¦¦Ž‰‰‰‰‰‰ß‰‰‰‰‰9999“Ž“““““äš“ŽŽŽŽ€˜€ĀGGZĀU†U†", "height": 306 }, "Apple Chancery": { "default": 142, "widths": "GBaŽc¼ã5WWrŽ5]5o„nvž|‘‚„‘55ŽŽŽ]±¸¹ŸØ»´ªÙutͤþâ°«±Ê‡Ã۱津—QYQŽŽrƒ|bƒ_PwˆLDvDʑn|k[[s¡€‹iY4YŽB…u£Ž4ŽÅ’ŽÅEŽ^^„ŽŽL’]¸¸¸¸¸¸öŸ»»»»uuuuØâ°°°°°āްÛÛÛÛ¥“††††††®b____LLLLx‘nnnnnªŽn‹‹Ü55ŽÜĀĀˆ", "height": 405 }, "Libian SC": { "default": 128, "widths": "@Uh€à¢.UUo@…@i€W€‰€–‰€ˆ€<@rÛ¤«¢¨œŽ¢°U…­{Ó¹Ž›ž§¨å¹¢œlŽxĎU}€rrhv€Gc€G±€€’€]jT€€¹€€r”3“Ď€€€Ā€€Ā€€€€ĀĀ€€€€Ā€€€€€€€€€€€€€€€€€€€€€€€€€€äĀ€€€€€€€€}}€€€€€€rrr€HH€€€€€€€€€¹Ā€€€€€€€€ĀĀĀZĀĀĀS‹", "height": 358 }, "Hiragino Kaku Gothic Pro": { "default": 168, "widths": "UP[¨¦áÌ2[[Y¨=\\=y¨¨¨¨©¨¨¨¨¨==ƒ¨ƒ’èÀ¶ÂÂ¥šÁÇ@‚µšòÆÌ©Ìº«¦Æ¸Ă¹°«YyYˆ€€“¥”¥—k¤ž=D“;¦¥n‰dž‹Å‰ŽY`Y‹P–¨‘±_¤’w¨’M¨eež–=nw’ÀÀÀÀÀÀďÂ¥¥¥¥@@@@ÄÆÌÌÌÌÌĊ¨ÌÆÆÆÆ°¥¡““““““ø”————ONNF¥ž     ą¨ žžžžŽ¦ŽĀ=={Ā7_S‹", "height": 409 }, "Apple Braille": { "default": 162, "widths": "XQ`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 285 }, "GungSeo": { "default": 256, "widths": "€Ef¤zø°@EERæEPE€uuuuuuuuuuEE€À€hýª…… …z ­Rk˜…Ø­ … •u“  í““zE€E€Ā@m…h…hMp…@@…@º…uzzZZM…‹º…zmUEUýĀĀĀĀĀ4ĀĀĀĀÅĀĀĀĀ„ĀĀĀĀ¸¸¸¸¸ĀŸ»»»»uuuuĀâ°°°°°ĀĀĀÛÛÛÛ¥Ā††††††Āb____LLLLx‘nnnnn‹Ā‹ĀĀĀĀĀĀĀˆ", "height": 258 }, "Silom": { "default": 171, "widths": "@€•Õ•ëÕ@kk••U•U•««««««««««UU€«€«ë««««••««€•À•ĀÀ««««•€««Ā«««k•k««€««•««€««U€«UĀ««««€•€««Ā«««kkk«€•À««k•ÕÀ•Õ€•••ÕÀU•À«««««««ë«••••€€€€«À«««««ë«««««««««««««««Ā•««««UUUU«««««««Ā•««««««««ÕUU•īZ‘Z‘", "height": 327 }, "Kokonor": { "default": 142, "widths": "OBaŽc¼ã5WWrŽ5]5o„nvž|‘‚„‘55ŽŽŽ]±¸¹ŸØ»´ªÙutͤþâ°«±Ê‡Ã۱津—QYQŽŽrƒ|bƒ_PwˆLDvDʑn|k[[s¡€‹iY4YŽB…u£Ž4ŽÅ’ŽÅEŽ^^„ŽŽL’]¸¸¸¸¸¸öŸ»»»»uuuuØâ°°°°°āްÛÛÛÛ¥“††††††®b____LLLLx‘nnnnnªŽn‹‹Ü55ŽÜĀĀˆ", "height": 527 }, "Nanum Pen Script": { "default": 177, "widths": "HFC¡lŸs?OHhn<_3Q\\1dMwbLWS\\NNsfsZ}vjX]id€t6Mrpœvphwsf~oj¥vm†WœZ_{>\\V][Z_Wj>Ih6Ÿmgbg\\Whig‘bhw\\>\\Mññ×ñ`YÛñññrpii ›JŠñб±±±±±è±‹‹‹‹JJJJ˽ÇÇÇÇÇĀ`ȱ±±± ˆ¯ÚƒJJJJ˜Ÿî|ŸŸŸŸ†™†Ý@@ññv{JJ", "height": 295 }, "Thonburi": { "default": 171, "widths": "UN€««èºNrr‚«NaNŠ««««««««««NN«««’ߺ£§³“Ž­±>~ª•ʵ¼•¼››¤«ºòœœ“rŠr««—ŽœŒš“dœ•>>>³¶¼¼¼¼¼ė«¼««««œ•—ŽŽŽŽŽŽîŒ““““<<<<˜•›››››ÿ«›••••’š’ĈNN«ĈS‹S‹", "height": 352 }, "Zapfino": { "default": 256, "widths": "€ÑÓîƌȵŒêĄ£Ā†Ê“ìę­ċàĎ¾ÜÜ×ܙ”ĀĀĀ®ŴŔĸŊůİàŬŮÐŁĚǠşŹčŞňÓÿƋĚǣŗāĘØìáĀŀ“¶£‰­ƒ‹šªt}À‚á§ ¤}s¤Šñ ‘ªŽªĀëijùęŽÁƚÖĀĀ’Ā  Āŀ˜ç¦ŔŔŔŔŔŔDZŊİİİݝŨşŹŹŹŹŹțĀŹƋƋƋƋāċ™¶¶¶¶¶¶Ä‰ƒƒƒƒtttt–¨     äĀ–¤¤¤¤‘‘ʀ††ŀųĀĀˆ", "height": 865 }, "Kaiti SC": { "default": 131, "widths": "@8h«sÓ»-KKm«8P8€xxxxzxxxxx88«««]믠¥Æ©’ÅÈ[U¾“âÇǓĤ|·°å´ª©E€E€Uƒ…l„nV{ˆ=;}EȈƒ†…XbW‡y«uoo{€{«8i“©­€ĀÃ]«Ã]­PTˆsJU]]¯¯¯¯¯¯ö£©©©©[[[[ÆÇÇÇÇÇÇð®Ç····ª€ƒƒƒƒƒƒ˜lnnnn====ƒ„ƒƒƒƒƒ³Äƒ‡‡‡‡oˆoĀ?C[ĀĀĀĀ‹", "height": 358 }, "Bank Gothic": { "default": 198, "widths": "cbMÅÆÍÓ(UU€ÕUTUGÆÆÆÆÆÆÆÆÆÆUUÕÕÕ©ĀÅËÍÙÁ¬Ûáb«Ë¯íÞá¾áÍÏ Ù®í¸¬¯UGUĀ€€¥¨¥²œ±¸JŸÄ¶·¡·¦ª—°¢Ê£˜ž€€€ÕbÆÆ›Æ€€ÕÕÕTՃƒœ€Uy©ÅÅÅÅÅÅĒÍÁÁÁÁbbbbÙÞáááááěÕáÙÙÙÙ¬¾œ¥¥¥¥¥¥æ¥œœœœJJJJ²¶·····èÕ·°°°°˜¡˜ĀUU—ĀZ‘Z‘", "height": 265 }, "Latin Modern Roman Slanted": { "default": 128, "widths": "UG`ՀÕÇGdd€ÇGUG€€€€€€€€€€€GGÇÇÇyÇÀµ¹Ä®§ÉÀ\\„Ç ëÀǮǼ޹ÀÀćÀÀœG€GŽÀ€€ŽrŽrN€ŽGN‡GՎ€Ž‡dedއ¹‡‡r€G€ŽGrÀÇÀG|¯ŽÇ¯`Ç\\\\ŽœÇkŽyÀÀÀÀÀÀç¹®®®®\\\\\\\\ÄÀÇÇÇÇÇĄÇÇÀÀÀÀÀ €€€€€€€¹rrrrrGGGG€Ž€€€€€ÇǀŽŽŽŽ‡Ž‡ĀGGǬS‹S‹", "height": 363 }, "Arial Narrow": { "default": 117, "widths": "::Kuu»Œ(FFR{:F::uuuuuuuuuu::{{{uՌŒ˜˜Œ€£˜:iŒu¯˜£Œ£˜Œ€˜ŒÆŒŒ€:::cuFuuiuu:uu//i/¯uuuuFi:ui˜iiiF7F{Fuuuu7u›u{›fFF”qFMu€ŒŒŒŒŒŒÒ˜ŒŒŒŒ::::˜˜£££££Ò{£˜˜˜˜ŒŒ€uuuuuu»iuuuu::::uuuuuuuƍ€uuuuiuiÒ//JÒ(KS‹", "height": 294 }, "Gujarati Sangam MN": { "default": 143, "widths": "M/N¼Ø°*JEzœ4S1L14’œ’Êœ›µ©•Ž·¡A{¤†¾¡º“½›£žÚŒ’ 9J4p–3‘†††ŽUЁ6,6ʁŠŠO~Ns®tqN.IžQ¢¢¢¢`¢Å†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†Ì33SĀS‹S‹", "height": 396 }, "Wingdings": { "default": 162, "widths": "QQ`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 284 }, "Latin Modern Mono Light Cond": { "default": 90, "height": 336 }, "Gill Sans": { "default": 128, "widths": "GE[–‹­ 0SSk–8S8H€€€€€€€€€€8;–––U㫐µÀ€x¾»@@¨~ÈÈӃӛu›µ›ċµ›¥UHUxUm€pƒ{@m€88{8ŀ€€ecU€p¸€pkUCU•EpˆCh¾~–¾e–SS‹U^~U««««««åµ€€€€@@@@ÀÈÓÓÓÓÓë–Óµµµµ›ƒ€mmmmmm«p{{{{8888€Õ–€€€€p€pĀ88[ĀS‹S‹", "correction": 1.02, "height": 294 }, "Malayalam MN": { "default": 128, "widths": "KKcƒ‰ÝÍ.``g•CM9D””””””””””9F›œ›yø®ªËʚ‰Ñ¾Izºï¼Ý’Û¦¥¼­ð­§ªQDP{ŒP‡—™”ež~ª•ʵ¼•¼››¤«ºòœœ“rŠr««—ŽœŒš“dœ•BR¦6[+€²7§¶È¬¬•ª–->s~s¢Ċ£¢™ •ƒ›±7†yВ· ·°˜’˜À³œµ<€<\\•?tmtul]si-9z(£uupqWrWjq£vo\\$\\ŒC”­¨¼$rôj¦†_¦gqjŠ-af¢¦¦¦¦¦¦Ēœ••••??\\M¥±±±±±Ď¦±ˆˆˆˆœ †pppppp¾qnnnn/)/+urrrrrrÁ¦rggggvpsþ99K·ĀĀˆ", "height": 414 }, "Courier New": { "default": 154, "height": 290 }, "Bodoni 72 Smallcaps": { "default": 136, "widths": "03ˆˆˆ·…[NNˆˆBNB}Qxsxu}ly}BB‹ˆ‹Jýš’§·•‹ª¶PTªÁ¨¸¹—–ª¢é ›N}Nˆ…€pku„kgxƒ=?|jzc‚p]m{u¨sphN…N®3‚„€…ˆÎkˆÎˆˆ[[}ˆB\\kJššššššï§••••PPPP·¨¸¸¸¸¸ò¸ªªªª›‡¹pppppp¬ukkkk====„z¯ˆ{{{{€cp›CC±Ç?iS‹", "height": 307 }, "Marker Felt": { "default": 146, "widths": "@3O‘ˆ³)CCwq1_1…’’’’’’’’’’;1adaièŸ}s}nl}€=nls”’}{ŠŠóœ—šd†dM}6vnana\\lx36{3¸}inna_\\iq¸ix{d4dT3`‰£¡t_qz;qgn‡x1sqi€€~½t\\\\]]65J?}ppqprŎŠoqpošstttttt©`aaaa46XPk~jijjj¬_ljjjjxnxĀ:A¥šĀĀˆ", "height": 278 }, "STIXIntegralsUpSm": { "default": 162, "widths": "@Q`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 386 }, "Trebuchet MS": { "default": 134, "widths": "M^S††šµ)^^^†^^^†††††††††††^^†††^ŗ‘™‰†­¨Gz“‚¶£­­•{•¦–ڏ’^[^†††‡Œ_ŒI^LՌ‰dhfŒ}¿€~z^†^†^†††’†t·††·††ttŒ†^^†^——————Þ™‰‰‰‰GGGG£­­­­­þ†¨¦¦¦¦’ŽŒ‡‡‡‡‡‡àŒŒŒŒIIIIŒ‰‰‰‰‰í†ŒŒŒŒŒ~Ž~¼^^†¼)WS‹", "correction": 1.025, "height": 297 }, "Marion": { "default": 119, "widths": "MMP{ÃÀ'HHsŒAN<]e}v}w~k€€<=iia¶¨­§Â¥–ºÑcq¹—便¡Âª‹–Á§ê° ‹6p8lrKq~g€nLs’K=ƒK͎w~b]L‰s©yoe>L>ŽCm™®ŸLa„‘ÂSŒSP¢†<[„_¨¨¨¨¨¨ê§¥¥¥¥cccc¾¿¿¿¿¿ďŒ¿ÁÁÁÁ žˆqqqqqq±gnnnnKKKKwŽwwwwwŠw‰‰‰‰oo¿77JÙS‹S‹", "height": 306 }, "TeX Gyre Heros": { "default": 142, "widths": "GG[ŽŽä«1UUd–GUGGŽŽŽŽŽŽŽŽŽŽGG–––ŽĄ««¹¹«œÇ¹G€«ŽÕ¹Ç«Ç¹«œ¹«ò««œGGGxŽUŽŽ€ŽŽGŽŽ99€9ՎŽŽŽU€GŽ€¹€€€VCV–GŽŽŽŽC޽ޖ½f–UUމG]ŽŽ««««««Ā¹««««GGGG¹¹ÇÇÇÇÇĀ–Ç¹¹¹¹««œŽŽŽŽŽŽä€ŽŽŽŽGGGGŽŽŽŽŽŽŽò–ŽŽŽŽŽ€Ž€Ā99ZĀS‹S‹", "height": 367 }, "Skia": { "default": 177, "widths": "@w_±lý»6jj]±CWCUŸbƒv–{“|”“RR±±±æª¤”»›’°ÈO`¢ŠòÊ‘¢’ÁªČ”™jUj±€d„ŽtŽƒ^•DE†Dà•’ŽŽchd•ˆÎ‰zjKj±wu|–”Ktñƒ±ñi±iiœŽCiƒªªªªªªé”››››OOOO»ÊÂÂÂÂÂþ±ÂÁÁÁÁ”‘š„„„„„„ÏtƒƒƒƒDDDD•’’’’’ç±’••••‰Ž‰Ā66ŽĀS‹S‹", "height": 256 }, "Nadeem": { "default": 162, "widths": "B>:do«z#JJ{oQo>o¢¢¢¢¢¢¢¢¢¢>QHoHlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›JoJbݍ¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“J>J¢Q¢¢¢¢`¢ÛJÌÛoÌll ¢QxJl±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀoDZ±±± Ž™ÚƒJJJJšŸîoŸŸŸŸ†¡†ĀQQtS‹S‹", "height": 372 }, "Kaiti TC": { "default": 131, "widths": "@8h«sÓ»-KKm«8P8€xxxxzxxxxx88«««]믠¥Æ©’ÅÈ[U¾“âÇǓĤ|·°å´ª©E€E€Uƒ…l„nV{ˆ=;}EȈƒ†…XbW‡y«uoo{€{«8i“©­€ĀÃ]«Ã]­PTˆsJU]]¯¯¯¯¯¯ö£©©©©[[[[ÆÇÇÇÇÇÇð®Ç····ª€ƒƒƒƒƒƒ˜lnnnn====ƒ„ƒƒƒƒƒ³Äƒ‡‡‡‡oˆoĀ>C[ĀĀĀĀ‹", "height": 358 }, "STIXNonUnicode": { "default": 162, "widths": "@Q`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 521 }, "Impact": { "default": 140, "widths": "-E_ Œ±”/PPH‰+K/e‰bˆ€‰‹d‰‹44‰‰‰†Æ‚ŽŽjfŽJU‰a¸‹ŒŒŠ„vŒ†Ñ|yfHeH|U……ƒJ…†FHzFņƒ……\\yN†p«osZ_E_†E„‰ŒyE|É_‰ÉY‰QTy”UT_†‚‚‚‚‚‚·ŽjjjjJJJJ‹ŒŒŒŒŒ°‰ŒŒŒŒŒyÁƒƒƒƒFFFFƒ†ƒƒƒƒƒÄ‰ƒ††††s…sĀ++YŽ/cZ‘", "height": 312 }, "Snell Roundhand": { "default": 114, "widths": "@UU€€Çœ9ddUš@U@G€_€€€€€€€€@@šššrÍòä«ä«ÇœÕœœäŽĀ¹ÇǹäŽÇÕ¹òÕÇ«d9dš€9rrUrU9rr99r9«rrrrUUGrr«€rdd9dšU€¾€€9€ÍršÍfšMMrš@DrròòòòòòĀ«««««œœœœä¹ÇÇÇÇÇš¾ÕÕÕÕÇǎrrrrrr“UUUUU9999rrrrrrr“šrrrrrrrrĀ99€ĀĀĀˆ", "height": 312 }, "Lao Sangam MN": { "default": 143, "widths": "M/N¼Ø°*IEzœ4S1L14’œ’Éœ›µ©•Ž·¡A{¤†¾¡º“½›£žÚŒ’ 9J4p–3‘†††ŽU‰6,6ʁŠŠO~Ns®tqN.IžQ¢¢¢¢`¢Å†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†Ì33SĀS‹S‹", "height": 356 }, "STFangsong": { "default": 256, "widths": "@8h«sÓ»-KKm«8P8€xxxxxxxxxx88«««]뭝£Å¨ÅÃ[U½“ÕÅȐŠ{µ²çµ­¨E€E€ƒUrƒk€k]uƒ;;x;уƒ}U]K}|°umm{€{«8k“Ā­€ĀÃ]«ÃĀĀPPˆsĀU]]­­­­­­æ£¨¨¨¨[[[[ÅÅÈÈÈÈÈðĀȵµµµ®€rrrrrr•kllll;;GB…ƒƒƒƒƒƒ³Āƒ}}}}mƒmĀĀĀ[ĀĀĀĀ‹", "height": 256 }, "PT Sans Narrow": { "default": 115, "widths": "6?Iss£¨.<~j£Šs{nr…w¬€tp?Q@iVAfo[ojBoq87c<§poooFWFoc—i`\\H1Hj?ssss0h±cj—XjTTsg7Sc[yyyyyy¯unnnn<<<<ˆŠ¿j…………tt}ffffff¤[jjjj8888spooooo±jpoooo`o`©))]¨S‹S‹", "height": 332 }, "Latin Modern Sans Quotation": { "default": 160, "widths": "k[‚ċ ċðY|| ùYkY           YYÓùӗÕÎÒÍäǾÕÐR“×µąÐ÷É÷ѲåÏÎħÎÎÄZˆZŽÎ — Ž Žb  GP—Gù    k{s ŽÕŽŽŠˆLˆŽ[ŽÎùÎL­¯ ù¯xùqs ¦ù† —ÎÎÎÎÎÎĐÍÇÇÇÇRRRRäÐ÷÷÷÷÷ijùùÏÏÏÏη———————çŽŽŽŽŽGGGG       ùù     Ž ŽŀYYùÅS‹S‹", "height": 357 }, "Zapf Dingbats": { "default": 162, "widths": "GQ`¢¢«³;dd{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“V`V¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 253 }, "Al Nile": { "default": 177, "widths": "66/¢‘«z#QQ{lQlhZuOseuimmgmhQllܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢¡¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“SHS¢Q¢¢¢¢`¢ÛQÌÛoÌll ¢QxQl±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀlDZ±±± Ž™ÚƒJJJJšŸîlŸŸŸŸ†¡†ĀQQ˜S‹S‹", "height": 452 }, "RicpyHum": { "default": 150, "widths": "JgMœ޲(SS€ÕJWJG““““““““““JJÕÕÕ„Ā©“œ°‰z¶®E\\—wêµ¼ˆ¼’†±¢ò¢”ŠTGTĀ€€‡™w™„Z˜–@@@▖™™\\l^–zÐvzx€€€Õg““¢“`€ÕÕÕTÕaa€Jq„©©©©©©çœ‰‰‰‰EEEEÀµ¼¼¼¼¼î̼±±±± Ž—‡‡‡‡‡‡Õw„„„„@@@@š––––––êՖ––––†¡zĀJJ—ĀS‹S‹", "height": 316 }, "Damascus": { "default": 162, "widths": "BQ`¢¢«³;SS{ÌQ”>†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 389 }, "Arial Black": { "default": 171, "widths": "UU€©«ĀäGddŽ©UUUG««««««««««UU©©©œ¾ÇÇÇǹ«ÕÕd«Õ«òÕÕ¹Õǹ¹ÕÇĀÇǹdGd©€U«««««d««UU«UĀ««««rœr«œò«œŽdGd©U««©«G«Í«©Íf©ff«ÚUf«œÇÇÇÇÇÇĀǹ¹¹¹ddddÇÕÕÕÕÕÕĀ©ÕÕÕÕÕǹ«««««««Ā«««««UUUU«««««««Ā©«««««œ«œĀGG€ĀN†Z‘", "height": 361 }, "Hiragino Kaku Gothic StdN": { "default": 198, "widths": "U]uÉÇþîNrrl·NyN’ÆÆÆÆÆÆÆÆÆÆNN’·’­ùßÌØÛ»°×àX°Ü°ćàâÃãÒÄÆãÜĜéÖ¿w’w¦€€³Å­Å°‹¾¼Q^»PƼ·ÆÆ§{¼´ïµ¶–xyx­]ºÐ¢ÚxĀ’±·’ĀĀuu¼ĀN±­ßßßßßßłØ»»»»XXXXÝàâââââijĀâããããÖ±³³³³³³Ĝ­°°°°__fiü·····ĤĀ·¼¼¼¼¶Æ¶ĀNN–ĀĀĀZ‘", "height": 434 }, "Bangla Sangam MN": { "default": 177, "widths": "W6YÖ¤öÈ0TN‹±;_8W££££££££££8;¦±¦“å²°ÎÀª¡Ð·JŒº™Ø·Ô§×°²¹´¤øŸ¦¶AT;€ª:¥™™™¡aœ“>2“>撣ZY’ƒÆ„’X5S´Q¢¢¢¢`¢á†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†è::_ĀS‹S‹", "height": 383 }, "Palatino": { "default": 128, "widths": "@G_›€×Ç5UUd›@U@›€€€€€€€€€€@@›››r¿Çœ¶ÆœŽÃÕVUºœòÕɛɫ†Ç¹Ā«««U›U›€U€Žrœ{UŽ•K<ŽK╌šemTš‘Ö„Ž€U›U›G€€›€›€¿€›¿fMM”¡@U€rÇÇÇÇÇÇò¶œœœœVVVVÆÕÉÉÉÉÉĀ›ÕÇÇÇÇ«›Ž€€€€€€Âr{{{{JJJJŒ•ŒŒŒŒŒÔŽššššŽšŽĀGG›Ā8kS‹", "height": 301 }, "AppleGothic": { "default": 256, "widths": "R`f®®ĀÑf€€w¯H€X€®®®®®®®®®®€€€Å€ŸĀº¥¯¯”•¾´7‰¥éºÇšÉ¤¢©¼¶Ć·²¨€€€€€@‘…ŠW†)G„+叕ŽŒZXދ֋‡€€€»U¢¶Ü¯`|÷ĀĀÛĀĀZZ Ā:tĀt±±±±²±©±‹‹‹–JJJJ†½ÇÇÇÇũ±±±Á \\‰‡ˆ–ƒ‡JJJJšŸ”ĀsŸŸŸ‘†U‡Ā44ĀĀĀĀĀ‹", "height": 463 }, "Arial Rounded MT Bold": { "default": 152, "widths": "@U{˜ÛÃ>[[p•PUPH˜˜˜˜˜˜˜˜˜˜PP•••“û¸¸¾¾«›ËÃP“¾›ÕÃ˫˸« Ã°ð› ¥[H[•€U˜ ˜ ˜U ›EE“E㛛  p‹[›‹Ð…‹…cHc•U˜˜ H˜¾˜•¾e•[[žQc˜“¸¸¸¸¸¸þ¾««««PPPP¾ÃËËËËËĈ•ËÃÃÃ૘˜˜˜˜˜˜í˜˜˜˜˜EEEE›››››››õ•›››››‹ ‹ĀPP[ĀS‹S‹", "height": 296 }, "Lantinghei SC": { "default": 256, "widths": "GNV›¢ùÅ.]]r¨JžJKœ|š›œœŸIJ§¤¦›ĝ¼»ËÊ»«ÚÉAŒºœëÊÛ»Úʺ¬Ì¾ĉ¼»«MLM„¦Yœ››œJœ›37Œ2蜜››]‘M›ŒÊŒŒŒ]@]¤Q¢¢Ā¢`ĀۆÌÛĀĀll ¢Āx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀĀDZ±±± Ž™ĀĀÚƒĀĀĀĀĀJJšŸĀĀîĀĀĀŸĀ†¡†ĀĀĀĀĀ‹", "height": 356 }, "Myanmar Sangam MN": { "default": 143, "widths": "M/N¼Ø°*IEzœ4S1L14’œ’Éœ›µ©•Ž·¡A{¤†¾¡º“½›£žÚŒ’ 9J4p–3‘†††ŽU‰6,6ʁŠŠO~Ns®tqN.IžQ¢¢¢¢`¢Å†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†Ì33SĀS‹S‹", "height": 403 }, "Latin Modern Sans Demi Cond": { "default": 122, "widths": "QN}Ëzá¸D^^z½DQDzzzzzzzzzzzDDǽÇs¢££›°‘Š¢®Hsª„Ö®²œ²‡¥¨£æ££•K€KŽ£zt|l|pJ||8?u8¿|z||R]Y|n¤nni€G€ŽNl£½£G~²z½²[½VW|´½fzs££££££Ò›‘‘‘‘HHHH°®²²²²²í½½¨¨¨¨£Žxtttttt°lpppp8888z|zzzzz½½z||||n|nóDD½¥Z‘Z‘", "height": 361 }, "Kefa": { "default": 154, "widths": "BWkššď¶^^{Q‰5smmmmmmmmmmMQjjlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢ā¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“qHq¢Q¢¢¢¢`¢ÛVÌÛoÌll ¢QxLl±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÇ±±±± Ž™ÚƒJJJJšŸî^ŸŸŸŸ†¡†ĀQQ¥S‹S‹", "height": 481 }, "FZYingBiXingShu-S16S": { "default": 228, "height": 282 }, "Xingkai SC": { "default": 128, "widths": ">kMœ½±Œ8aV\\«UƒUH|O‘oˆ‘‡UU«ª«găÒ«¯±¬£«ËuxÀ´öª“¬±Ä´ª½¤Õ«¤œ¨Hªx€UniMO^keJknP“dU…dRVWk^„Seck‹k«€€€Ā€€Ā€€€€ĀĀ€€€€Ā€€€€€€€€€€€€€€€€€€€€€€€€€€äĀ€€€€€€€€mm€€€€€€OOP€FJ€€€€TT€€€¹Ā€gf€j€€€ĀĀĀZĀĀĀS‹", "height": 358 }, "Songti SC": { "default": 256, "widths": "@8h«sÓ»-KKm«8P8€xxxxxxxxxx88«««]뮞£Å¨‘ÅÄ[U¾“ÙÆÈ‘Å£zž¶®ã³©§E€E€€U…i„nTxˆE9yFɈ‚††]]S…x«ulm{€{«8k“±€ĀÃ]«ÃĀĀPPˆsĀU]]®®®®®®æ£¨¨¨¨[[[[ÅÆÈÈÈÈÈðĀȶ¶¶¶©‘€‚‚uuuu•ioook;;<<…ƒƒƒ‚‚‚³Āƒ„„~„n†lĀ;€?ؒ›‘“^|W’w¬u‚{U‚Uœ>v«•‚‘à™¬àQšop’Ž9s}      òªŠŠŠŠ;;;;ª±ÍÍÍÍÍù™Ï¸¸¸¸•‘““““““Ð†ŽŽŽŽ><<;—›››››ó‹–€‘‚à33hÇS‹S‹", "height": 348 }, "Latin Modern Roman Demi": { "default": 128, "widths": "UNqՀÕÇGdd€ÇGUG€€€€€€€€€€€GGåÇåyǶ¹Ä¨¡ÉÈf„ÈšóÈÀ¯ÀβÅÂĈœH“HŽÂ€|ŽrŽxN€ŽGN‡GՎ€Ž‡nedއ¹‡‡r“R“ŽNrÂÇÂR›°ŽÇ°`Ç\\\\Ž´ÇkŽyÂÂÂÂÂÂ蹨¨¨¨ffffÄÈÀÀÀÀÀĄÇÇÅÅÅÅ¡||||||¹rxxxxGGGG€Ž€€€€€ÇǀŽŽŽŽ‡Ž‡ĀGGÇÅZ‘Z‘", "height": 367 }, "Cochin": { "default": 128, "widths": "@dU€€Õ¾9dd€€@U@G€€€€€€€€€€@@€€€€Í´¯´Õ¯¡ÇÕd¡¾¦÷ÑÕ¡Õ¹˜¦Ì´Ā¾¹´dGd€€Bw‰r‰rQ€‰BB‰Blj€‰‰ddQ‰€¾…€rd9c€d€€€€9€Ír€Íf€MM‰š@Mr€´´´´´´÷´¯¯¯¯ddddÕÑÕÕÕÕÕĀ€ÕÌÌÌ̹¡Žwwwwww¯rrrrrBBBB€‰€€€€€Â€€‰‰‰‰€‰€Ā99€Ā?iS‹", "height": 294 }, "Hiragino Mincho Pro": { "default": 204, "widths": "UUW®¡ÔÎ0PPd®PTPd——————————PP€®€˜Ø½ªµÃ¢˜ÃÌZz³—îÌ̞̮¨Å»Ā¸¸©PdP€€€Œš‡›‰TˆžLJŽL枕™˜irV˜ËŠŒ~P€P€U¡¦Œ¦€¡·k®·[®dd¡™Pdk˜½½½½½½øµ¢¢¢¢ZZZZÃÌÌÌÌÌÌą®ÌÅÅÅŸ£˜ŒŒŒŒŒŒÓ‡‰‰‰‰LLLL•ž•••••⮕˜˜˜˜Œ™ŒĀKK¡Ā7\\S‹", "height": 397 }, "Latin Modern Mono": { "default": 134, "height": 341 }, "Braille": { "default": 213, "height": 270 }, "Lucida Grande": { "default": 162, "widths": "QQ`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 302 }, "Capitals": { "default": 171, "widths": "U€•Õ•ëÕ@kk••U•U•««««««««««UU€«€«ë«°«°˜”«¶f|şöË«ª«°‘¶«û««¢k•k¤«€«¯•¯–‡«¶a|°†ö¶«¨«°‹¶«û««¢kkk«€•«««k•Õ«•Õ€•••««U•««««««««ì«˜˜˜˜ffff°Ë«««««ì««¶¶¶¶«ª°««««««õ•––––aaaa¯¶«««««í•«¶¶¶¶«ª«ÕUU•Ā?iZ‘", "height": 341 }, "Big Caslon": { "default": 134, "widths": "6N[À{¦Û9PPc³>B>PˆQpeeƒr|ƒCC«³«bÞ´¿ÇÚ¶¨ÓæffĵþàϨÏÛ½Ö«þµ¦²IPIA€€sŠoŒoE††A?}?˅ƒ‰Œ[bS†q½ujoI?I†N‡®©…>xĀe³„h«D>¡ˆCHeb´´´´´´ĄÇ¶¶¶¶ffffÚàÏÏÏÏÏû³ÏÖÖÖÖ¦¨‰ssssss°oooooAAAA†ƒƒƒƒƒË³ƒ††††j‡jä ŽÁ\u001a?\u001a?", "height": 310 }, "Papyrus": { "default": 233, "widths": ":CSœ‰ÃÈ3oo`ƒGvG‡ªA‚z©~‡v‡GGv{œ¼¹¾Ô¹ž¼Ä>Œ·›Ý§é¦é¯§²Í•ɧ ªo‡oˆ€I‰š€›ˆeŠ‘65Œ8µ™”cy]q¤}ŠrA8ACx» ©8u郓éP‡\\WsAzƒ{¼¼¼¼¼¼Ģ¾¹¹¹¹>>>>Þ§éééééūƒéÍÍÍÍ ¦Š‰‰‰‰‰‰è€ˆˆˆˆ6666‡™™™™™Ā™ŠšŠđGGYĀS‹S‹", "height": 395 }, "Latin Modern Roman Unslanted": { "default": 142, "widths": "dUräŠäÕUrrŽÕUdUŽŽŽŽŽŽŽŽŽŽŽUUÖÕ֎ÕÎÄÇÒ¼µ×Îk’Õ®ùÎÕ¼Õ˜ÇÎÎĕÎΫUŠUÎŽŽ€€Ž€U€ŽUU€G䜎Ž€ur\\•€¹‚‡rŠMАU€²ÕÎMбœÕ±kÕkdލÕwœŽÎÎÎÎÎÎõǼ¼¼¼kkkkÒÎÕÕÕÕÕĒÕÕÎÎÎÎή•ŽŽŽŽŽŽÇ€€€€€UUUUŽœŽŽŽŽŽÇՎ••••‡Ž‡ĜUUÕ¾S‹S‹", "height": 363 }, "TeX Gyre Chorus": { "default": 113, "widths": "8H8qq®È)C8l…8H8WqqqqqqqqqqC=………a³Ÿš…³Ÿ”Ÿ®af©”×³šŠššv€½¤áŸ={R…€MllWqWRfq=8q=ŸvfqfMRRvq®lfq=…=…Hqqqq…l½W…½f…DDv€8CaaŸŸŸŸŸŸ½…ŸŸŸŸaaaa³³šššššÒ…©½½½½ŠlllllllŠWWWWW====fvfffff…qvvvvfqfĀ==šĀZ‘Z‘", "height": 337 }, "Latin Modern Roman Dunhill": { "default": 128, "widths": "UG€Õ€ÕÇGdd€ÇGUG€€€€€€€€€€€GGÇÇÇyÇÀµ¹Ä®§ÉÀ\\„Ç ëÀǮǼ޹ÀÀćÀÀœG€GœÀ€€ŽrŽrN€ŽGN‡GՎ€Ž‡dedއ¹‡‡r€G€œGr¤½À+|ĜŽ«ĜUÇrrŽœGrŽyÀÀÀÀÀÀç¹®®®®\\\\\\\\ÄÀÇÇÇÇÇĄÇÇÀÀÀÀÀ €€€€€€€¹rrrrrGGGG€Ž€€€€€ÇǀŽŽŽŽ‡Ž‡ĀGG€īS‹S‹", "height": 529 }, "Sana": { "default": 177, "widths": "&=9¢‘«z$MM{sQs/C^.]^q___^_;QÌsÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢ÿ¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“SHS¢Q¢¢¢¢`¢ÛMÌÛoÌll ¢QxMl±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀsDZ±±± Ž™ÚƒJJJJšŸîSŸŸŸŸ†¡†ĀQQåS‹S‹", "height": 384 }, "Latin Modern Mono Prop": { "default": 135, "widths": "ZKoà‡àÒKii‡ÒKZK‡‡‡‡‡‡‡‡‡‡‡KK‡Ò‡´Â»ÂÊ´¬ÒÂZ‡Ê¥ïÂÒ´Ò–ÂÂÂčÂÂ¥L‡L‡Â‡ƒ–x–xS‡–KSŽKà–‡–Žiji–ŽÂŽŽx‡‡‡‡KxÂÒ‡Œ–ҝeÒaa–Òq–ÂÂÂÂÂÂï´´´´ZZZZÊÂÒÒÒÒÒčÒÒÂÂÂÂÂ¥‡ƒƒƒƒƒƒÂxxxxxKKKK‡–‡‡‡‡‡Ò҇––––Ž–ŽčKKÒ®šššš", "height": 336 }, "Monaco": { "default": 154, "height": 341 }, "QXyingbixing": { "default": 162, "widths": "QQ`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 295 }, "STIXIntegralsD": { "default": 162, "widths": "@Q`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 677 }, "Mona Lisa Solid ITC TT": { "default": 119, "widths": ".Qwww‰wQ>>ww/U/owQmhwrwZmw//˜w˜{„¡ŽŽ«{w¡œLL“rǜ«…«“{d¡œò“œ“GoG˜˜€_dLhU>hh99r9˜h_hhLG>hZ…ZhZU˜UÊQwwww˜wìw˜ìwwMMg›/>dr0=m_hhhhhhœh____4444mrmmmmmœšmmmmmchd______’_____0000_d_____’š_ddddZdZĀ00€ĀZ‘Z‘", "height": 307 }, "TeX Gyre Pagella": { "default": 128, "widths": "@G_€€×Ç5UUd›@U@›€€€€€€€€€€@@›››r¿Çœ¶ÆœŽÃÕVUºœòÕɛɫ†Ç¹Ā«««U›U›€U€Žrœ{UŽ•J<ŽJ╌šemSš‘Ö„Ž€U›U›G€€€€›€¿€›¿f›MMš¡@U€rÇÇÇÇÇÇò¶œœœœVVVVÆÕÉÉÉÉÉÿ›ÕÇÇÇÇ«›Ž€€€€€€Âr{{{{IIIIŒ•ŒŒŒŒŒÔ›ŽššššŽšŽĀGG›ĀS‹S‹", "height": 353 }, "Yuppy SC": { "default": 256, "widths": "GG[Ž‹Ñ©1UUd–GUGM‹‹‹‹‹‹‹‹‹‹GG–––Œò°¡´¯¤º©G~¤ŒÉ«Ã¤Â¥¥¡¬­ó­« GMGxŽU‹‡‡ŒTˆ…9E{9ΆŽ‡‹X|Nƒ‚Å€ˆƒVCV–Q¢¢Ā€`ĀۆÌÛĀĀll ¢Āx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀĀDZ±±± Ž™ĀĀÚƒĀĀĀĀĀJJšŸĀĀîĀĀĀŸĀ†¡†ĀĀĀĀS‹", "height": 358 }, "Latin Modern Mono Slanted": { "default": 134, "height": 341 }, "STIXSizeThreeSym": { "default": 162, "widths": "@Q`¢¢«³;ÀÀ{ÌQ”QĚ¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›‚Ě‚¢Ȁ¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“è`è¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 805 }, "Wawati SC": { "default": 85, "widths": "UUV¸šÅŸ+UUqq1U4G³³³³³³³³³³67qqqº¸¥¦¬ Ÿ¯¦n¥¤†Á´Á§½¨®¿¾Ó§¨”UGU[€U…q‡}hƒ„=M{2´‡|vgi]~|scQ,QUUy¨š³,z›£q›Pqfhˆ’€]£ˆ······ÿ§¢¢¢¢llll°´ÁÁÁÁÁüqÁÄÄÄĨ»‰Ïu}}}}<yIIkyaíT[EW", "height": 336 }, "Kailasa": { "default": 162, "widths": "€Q`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 558 }, "Apple SD Gothic Neo": { "default": 177, "widths": "CBUƒÔ 4QQ_‹:o:W¡[ˆ‰Ž€Ž}“ŽII”†“s⛖’¦„¢«Bl–Ìª¯Ž¯”‹§šÚ™Ž‹WWWvVyŒsŒ€S‚‰::z:Ή‰Œ‹YjZˆ~ºz{s]H]ˆHoƒ¹`~ÉÝÝÛMVU NzÝj±±±±››ß±‹‹‹JJJB£½ÇÇÇǯꐬ±±±§ ŠŠyyя€JJJJ…Ÿ‰Ü…ŸŸŸˆ†ˆ{Ð00ÝÝnsEF", "height": 307 }, "New Peninim MT": { "default": 177, "widths": "@:^e˜³5FF[j8T0Rjjjjjjjjjj:Bjjj`ܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›BRB¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“B3B¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†€88µS‹S‹", "height": 262 }, "DIN Alternate": { "default": 128, "widths": "=BU{{…¦=GGZš=h=U{{{{{{{{{{==šššw͜œŽ¡ŽŠœ¦B{¡ŽÂ¯œ˜œœ“€¡ŽÕ‰€…GUGš€=€…r…€G…Š=B…Bڊ€……d{GŠr¹{rrG9GšB{{{{9{Í{šÍfšPPŠš¤S{wœœœœœœèŽŽŽŽŽBBBB¡¯œœœœœíšœ¡¡¡¡€˜Š€€€€€€Çr€€€€====€Š€€€€€Ñš€ŠŠŠŠr…rĀ==€ĀZ‘Z‘", "height": 298 }, "Wawati TC": { "default": 85, "widths": "UUV¸šÅŸ+UUqq1U4G³³³³³³³³³³67qqqº¸¥¦¬ Ÿ¯¦n¥¤†Á´Á§½¨®¿¾Ó§¨”UGU[€U…q‡}hƒ„=M{2´‡|vgi]~|scQ,QUUy¨š³,z›£q›Pqfhˆ’€]£ˆ······ÿ§¢¢¢¢llll°´ÁÁÁÁÁüqÁÄÄÄĨ»‰Ïu}}}}<uixfqfoqUUŽŽŽŒ±´©“º”ÇŽ«kl°¡Î³µ©š§«O¢šÐ¾¤¤f~fŽ€G^]PaHQ[[.-Z.ƒ[T^^HS?\\Wƒb[WY4YŽUVŽ£{4ŽÅsŽÅEŽ^^„Ž>Ls´´´´´´ö“””””kkkkسµµµµµāް¢¢¢¢¤i^^^^^^~PHHHH....x[TTTTT€ŽT\\\\\\\\[[ijUUZĀĀĀˆ", "height": 299 }, "ITF Devanagari": { "default": 185, "widths": "Efu¥\u0000ÍÇTVV~9cLy}Wli|mv`sFG˜ì¹««¹œŽ¹¹Ud¹œä¹¹Ž¹«Žœ¹¹ò¹¹œXy\\\u0000|Ur€r€rU€€GG€Gǀ€€€UdG€€¹€€r_P_’U\u0000\u0000€\u00003€\u0000ƒ^“\u0000\u0000”t`Or¹¹¹¹¹¹ä«œœœœUUUU¹¹¹¹¹¹¹ä†¹¹¹¹¹¹Ž€rrrrrr«rrrrrGGGG€€€€€€€¹ƒ€€€€€€€€ÙIIj½?iS‹", "height": 434 }, "JiaguRic B": { "default": 128, "widths": "@Ui€€ÕÇ.UU€@U@G€€€€€€€€€€GGr칫«¹œŽ¹¹Ud¹œä¹¹Ž¹«Žœ¹¹ò¹¹œUGUx€Ur€r€rU€€GG€Gǀ€€€UdG€€¹€€r{3{‹U€€€€3€Ã€ÃfMM”t@O€r¹¹¹¹¹¹ä«œœœœUUUU¹¹¹¹¹¹¹ä¹¹¹¹¹¹Ž€rrrrrr«rrrrrGGGG€€€€€€€¹€€€€€€€€ĀUUZĀ?iS‹", "height": 287 }, "Mishafi": { "default": 177, "widths": "\r)`‡Q«N;<<{RQD\"?Q6QFRJLLGL'QNDNlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›2?3¢E¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“6%<¢Q¢¢¢¢`¢Û2ÌÛoÌll ¢Qx2l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀADZ±±± Ž™ÚƒJJJJšŸîDŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 371 }, "FZHuangCao-S09S": { "default": 256, "widths": "10@e]V VSCW8Y/„gggggggggg87GEGj}®‚„™}u ‘¦’}cŠ‚Šq¬€z„vs´u|4–CvF€hlscSci?OkT­ubhifRmeX’U`k@8@Q¢¢Ā¢`ĀۆÌÛĀĀll ¢Āx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀĀDZ±±± Ž™ĀĀÚƒĀĀĀĀĀJJšŸĀĀîĀĀĀŸĀ†¡†ĀĀĀĀS‹", "height": 312 }, "TeX Gyre Cursor": { "default": 154, "height": 307 }, "Songti TC": { "default": 256, "widths": "@8h«sÓ»-KKm«8P8€xxxxxxxxxx88«««]뮞£Å¨‘ÅÄ[U¾“ÙÆÈ‘Å£zž¶®ã³©§E€E€€U…i„nTxˆE9yFɈ‚††]]S…x«ulm{€{«8k“±€ĀÃ]«ÃĀĀPPˆsĀU]]®®®®®®æ£¨¨¨¨[[[[ÅÆÈÈÈÈÈðĀȶ¶¶¶©‘€‚‚uuuu•ioook;;<<…ƒƒƒ‚‚‚³Āƒ„„~„n†lĀ@C[ĀĀĀĀ‹", "height": 358 }, "Hiragino Sans GB": { "default": 256, "widths": "UP[¨¦áÌ2[[Y¨=\\=y¨¨¨¨©¨¨¨¨¨==ƒ¨ƒ’èÀ¶ÂÂ¥šÁÇ@‚µšòÆÌ©Ìº«¦Æ¸Ă¹°«YyYˆ€€“¥”¥—k¤ž=D“;¦¥n‰dž‹Å‰ŽY`Y‹Q¢¢Ā±`Ā’†ÌÛĀĀll ¢Āx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀĀDZ±±± Ž™¥¥Úƒ———ONJJšŸ  îĀžžŸž†¡†ĀĀĀĀĀĀĀĀ‹", "height": 415 }, "Bodoni 72": { "default": 136, "widths": "0[ˆˆˆ·»[NNˆˆBNB}ˆˆˆˆˆˆˆˆˆˆBB‹ˆ‹jýš’§·•‹ª¶PTªÁ¨¸¹—–ª¢é ›N}Nˆ…€f{iznDxz@=x<´}{}wW]I}b™lgeN…N®[‚„€…ˆÎkˆÎˆˆ[[}ˆB\\kjššššššï§••••PPPP·¨¸¸¸¸¸òˆ¸ªªªª›‡ffffff¨innnn@@@@{}{{{{{¿ˆ{}}}}g}g›CC±Ç?iS‹", "height": 308 }, "QXyingbikai": { "default": 162, "widths": "AQ`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 287 }, "Sathu": { "default": 171, "widths": "1N€««èºNrr‚«NaNŠ««««««««««NN«««‘ߺ£§³“Ž­°>~ª•ʵ¼•¼››¤«ºòœœ“rŠr««—ŽœŒš“dœ•>>>³µ¼¼¼¼¼ė«¼««««œ•—ŽŽŽŽŽŽîŒ““““<<<<˜•›››››ÿ«›••••’š’ĈNN«ĈS‹S‹", "height": 312 }, "TeX Gyre Adventor": { "default": 142, "widths": "GLOŽŽÆÂ3^^m›GUGpŽŽŽŽŽŽŽŽŽŽGG›››—Þ½“о‰|߯:{—vë½Þ˜ß›m¨´öœ˜{Z›Z›€`¯¯¦¯¦P¬œ343𜨯¯McWœŽÕ{‰mZ¬Z›LŽŽŽŽ¬¿m›¿f›UUœG^m—½½½½½½þЉ‰‰‰::::ʽÞÞÞÞÞIJ›Þ¨¨¨¨˜˜Ž¯¯¯¯¯¯Ĩ¦¦¦¦¦3333¨œ¨¨¨¨¨ģ›§œœœœ‰¯‰ĀZZ›ĀS‹S‹", "height": 372 }, "Courier": { "default": 154, "height": 268 }, "Brush Script MT": { "default": 104, "widths": "HXMœx°~8UUS«UƒUHpEpƒmmkpsuUU«««Să£›¥ž¥p`¨˜Õ““ ¥¨¥“ m³›‹KH^x€UhSKkKEc^;H^;^Ph`KP@cS~K`Mk‹k«X[­« ‹‹¾h«¾e«hhŽ¥U@hS££££££ó›žžžžpppp¥““““““Û«“     hhhhhhhƒKKKKK;;;;P^PPPPP{«Pcccc`h`ĀUU[ĀĀĀˆ", "height": 315 }, "Arial Hebrew Scholar": { "default": 177, "widths": "G@kvµ³<>>W}7Y5P}}}}}}}}}}=>}}}qܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›GPG¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“MFM¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†€66ĀS‹S‹", "height": 290 }, "Symbol": { "default": 177, "widths": "@U`€¢ÕÇ;UU{@”@G€€€€€€€€€€GGrܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›U†U¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“{3{¢Q¢¢¢¢`¢Ê†·Êfll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÇ±±±± Ž™ÚƒJJJJšŸîŸŸŸŸ†¡†ĀQQvĀ?iS‹", "height": 289 }, "Plantagenet Cherokee": { "default": 192, "widths": "C7F……Õ»%WWw…7C/FŠalltnz}=Buˆuf弞°½šÀÔaa»œçÉÀ‘À¬†¥Ê®ï³¦’EFEM_<~Œu}Y”˜IIŒRꗉŒˆfeT˜ƒ¶‡„kG;GŽ7u…€…;|¾i¾9yRR•t/kif¼¼¼¼¼¼ó°ššššaaaa½ÉÀÀÀÀÀú€ÆÊÊÊʦ‘—~~~~~~·u}}}}IIIIŒ—‰‰‰‰‰Ð€˜˜˜˜„ŒƒĀ33UĀ?iS‹", "height": 304 }, "Avenir Next Condensed": { "default": 120, "widths": "/KXax¢…8@@lª8:8AxxxxxxxxxxCCªªª^¤w|{Ške‰“;a{[·’v“zoXuºtlf@A@ª€:gwVwl=wx89f8·xqwwFW@x`“a`U@9@ªKxxxx9{¤Yª{RªPPŽš8MY^wwwwww¡{kkkk;;;;А’’’’’¬ª’l|tgggggg¦Vllll8888qxqqqqq²ªqxxxx`w`v00€æS‹S‹", "height": 350 }, "STIXIntegralsSm": { "default": 162, "widths": "@Q`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 386 }, "Lao MN": { "default": 128, "widths": "KKcƒ‰ÝÍ.``g•CM9D””””””””””9F›œ›yø®ªËʚ‰Ñ¾Izºï¼Ý’Û¦¥¼­ð­§ªQDP{ŒP‡—™”ež:?^P=S9e}8lnmjgVcjADJ\\Jc–qtv|^]€}/pfZ“v‰m‹urgtg¡faqQ[RD^\u0014hZPgR?ga3.^;‘bUd_TM7gM}d`Z19NŽQZ[\\[9zKY‘MP^^gy\"L[^qqqqqq§v^^^^////|v‰‰‰‰‰¿F†ttttavihhhhhhŽORRRR3333ZbUUUUUŠPTgggg`d`yB;'}ĀĀˆ", "height": 248 }, "Simsun (Founder Extended)": { "default": 256, "widths": "GCh«u¸»-MMƒ«C«Cu}}}}}}}}}}CC«««[ë­£»Ã˜ˆ½ËUM³ëÐӐÓÍ}­Ë°ĀÀµ­MuMU€Ue€m€pEx…C@€=˅…€€UMK…{½}xh{ƒ{‹CmÃĀ€ƒĀĀ[«ĀUĀMM€sĀX[[­­­­­­à»˜˜˜˜UUUUÃÐÓÓÓÓÓýĀÓËËË˵…ĀĀeeee mĀpĀpĀCCC……Ā…………½Ā…Ā……Āx€xĀĀĀ[ĀĀĀš", "height": 288 }, "Party LET": { "default": 70, "widths": ",J9€wQ§!CCJa*E*QLsv‚k’ho**adì·É£¿¡Ž«ÄTµ£é¾¸£¬®Š¦«à²ž´CQCx€Ui¥œÑ¶7``y¥E`Exn‘‡‘™‘PP¥¥¥{¤À§™ºÑd…²›íÄ¿œ¿´žÂ«ú¶žš`x`¥¥€t“|S‚•KK‰I◊’ioX“½~rn`n¥UŽŸ’`€ñ•¥ñk¥€€”€H€•{¬¬¬¬¬¬ù¤§§§§ddddÀÄ¿¿¿¿¿Ā¥¿ÂžŒ½t||||KKKKˆ—ŠŠŠŠŠÑ¥Š““““~~Û::eÏR„S‹", "correction": 1.0, "height": 291 }, "American Typewriter": { "default": 163, "widths": "@kTš£ÍÒ0tt£€FOFf££££££££££FF€€€š±¿±£º¬ž¶ÈbžÈ£òĨ£¨ºž£¶ºîȺžp€p€€~ƒš‡YŒ£OJžOš~~fžšÍ£š‡€€€«k£º«¶€£ÊY€€€€ll€•#mYš¿¿¿¿¿¿ą£¬¬¬¬bbbbºÄ¨¨¨¨¨Ċ€¨¶¶¶¶º££Öƒ‡‡‡‡OOOOŒ£ŒŒŒŒŒä€ŒžžžžšššÄAB«Ñ?iS‹", "height": 295 }, "Yuppy TC": { "default": 256, "widths": "GG[Ž‹Ñ©1UUd–GUGM‹‹‹‹‹‹‹‹‹‹GG–––Œò°¡´¯¤º©G~¤ŒÉ«Ã¤Â¥¥¡¬­ó­« GMGxŽU‹‡‡ŒTˆ…9E{9ΆŽ‡‹X|Nƒ‚Å€ˆƒVCV–QĀĀ¢Ā`ĀۆĀÛĀĀll ¢Āx†l±±±±è±ĀĀĀ‹JJJJÀ½ĀĀÇÇÇĀĀDZ±±± Ž™ĀĀÚƒĀĀĀĀĀJJšŸĀĀĀĀĀĀĀŸĀ†¡†ĀĀĀĀĀ‹‹", "height": 358 }, "Weibei TC": { "default": 136, "widths": "€=X¹ ­0JJ„™>|>Rˆˆˆˆˆˆˆˆˆˆ>>™™™xÿ§”™«Œ‚£®LN¤xÓª®Š°’„‘ª£îœœGRG³€€‹Ž~Ž‚jENŠEЍŠckhŽ„Æ}ƒw€€€¥=~ˆ›œ€|ÕcÓÕW™VV‰Œ>dcx§§§§§§à™ŒŒŒŒOOb`°ª®®®®®ÛÓ®ªªªªœŠœ‹‹‹‹‹‹¹~‚‚‚‚JJb`‘ŽŠŠŠŠŠÛՊŽŽŽŽƒŽƒĀ>>hĀĀiĀ‘", "height": 358 }, "Tamil Sangam MN": { "default": 143, "widths": "@/N¼Ø°*IEzœ4S1L14’œ’É¡˜­©’Š´¥2uš¿ ¶“º–—Ÿ”ÝŒ—•9J4p–3‡…ƒ„ŠQ‰‚0/x/ȁ‹‹ˆNyNz³wx}N.IžQ¢¢¢¢`¢Å†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†Ì33SĀS‹S‹", "height": 276 }, "Notera Personal Use Only": { "default": 222, "widths": "K\u001d+Pye\\\u0001*2BV'W(bÞÞÞÞÞÞÞÞÞÞ((JQNA~»©€¬}d­j£Ä¥ý¾rŸ£Ã}n¾s©¿¤¾697:F2q_[g[GUvNAsK™tdiUaQLxe„fip-L5K\u001d[l?L?}eW}/VPGxL(dgP»»»»»»Ö€}}}}jjjj¬¾rrrrrÁ?r¾¾¾¾¤’sqqqqqq‹[[[[[NNNNdtdddddˆIdxxxxihi„++ByS‹S‹", "height": 256 }, "Khmer MN": { "default": 128, "widths": "@?Tpu»®'RRX9A19}}}}}}}}}}1;„…„gӔ‘­¬ƒu²¡>hžxË ¼|ºx “Ì“ŽD:DhwDrz‚~V‡z36p3ÈzŠ‚‚WgK|r°jxuG%GwU€€€€3€®€ÃfMM”t@O€r¹¹¹¹¹¹ä«œœœœUUUU¹¹¹¹¹¹¹ä¹¹¹¹¹¹Ž€rrrrrr«rrrrrGGGG€€€€€€€¹€€€€€€€€°33NĀ?iS‹", "height": 472 }, "Telugu Sangam MN": { "default": 139, "widths": "S3T˜é½.OJ„¨8Z5R››››››››››58¨‹Ù©§Ã¶¡™Æ­F„°‘ͮɞ̧©°ª›ë—¬>O8y¡7œ‘‘‘™\\”‹:/‹:ڋš••VˆT‹|»}z‹T2O«Q¢¢¢¢`¢Õ†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†Ü77ZĀS‹S‹", "height": 461 }, "HanWangShinSuMedium": { "default": 256, "widths": "€5V _¡…5u]m‘B‘##<#W>6<;/0&>;Q87-e/e…0:@€K8€¿+’¿55MM”Š\"6+=EEEEEE^5????))))¹DAAAAAZ5ACCCCDœ;;;;;;;M03333####€>66666QA6>>>>7Ž7Ď%%7Ā=O=O", "height": 326 }, "Hiragino Kaku Gothic ProN": { "default": 168, "widths": "UP[¨¦áÌ2[[Y¨=\\=y¨¨¨¨©¨¨¨¨¨==ƒ¨ƒ’èÀ¶ÂÂ¥šÁÇ@‚µšòÆÌ©Ìº«¦Æ¸Ă¹°«YyYˆ€€“¥”¥—k¤ž=D“;¦¥n‰dž‹Å‰ŽY`Y‹P–¨‘±_¤’w¨’M¨eež–=nw’ÀÀÀÀÀÀďÂ¥¥¥¥@@@@ÄÆÌÌÌÌÌĊ¨ÌÆÆÆÆ°¥¡““““““ø”————ONNF¥ž     ą¨ žžžžŽ¦ŽĀ=={Ā7_S‹", "height": 409 }, "PT Serif Caption": { "default": 154, "widths": "F^nššèú@dd†šRzPcšššššššššš^_šššˆĜÁ±³È¥ŸÆÒfa¶œüÒÔ«Ôµ™³ÃÅĎº¬YnYšvX’† ‘c˜©WS•Wù¬Ÿ£žo|k¦’Ò–‘dDdš^ššššD˜èˆšÉyš{{§˜E}ˆˆÁÁÁÁÁÁć³¥¥¥¥ffffÈÒÔÔÔÔÔКÔÃÃÃúª¸’’’’’’à†‘‘‘‘WWWWš¬ŸŸŸŸŸüšŸ¦¦¦¦‘£‘þTT„ÿ?iS‹", "height": 332 }, "Charter": { "default": 142, "widths": "GWU¿ŽÚ´,kk€ÕGRG{ŽŽŽŽŽŽŽŽŽŽRRÕÕÕ|ñ¤›¢±”в½Srœ…Þ·»»¥Ž™²žîš––l{lĀ€€‚Šr‘~R†HD„HؑŠˆbfV’Łx|€|ÕWŽŽŽŽ€€æqÕæTÕ^^Œ|Ggq|¤¤¤¤¤¤Þ¢””””SSSS±·»»»»»þÕ»²²²²–œ‚‚‚‚‚‚ºr~~~~HHHH‡‘ŠŠŠŠŠÑՊ’’’’Ā44—Ā?iS‹", "height": 313 }, "Hiragino Maru Gothic Pro": { "default": 147, "widths": "UM`¥¤äÅJ^^g«MrMy§§§§§§§§§§NNŒ«Œ”èú¾Å¦§ÄËE†±¡ôÇγθ¬­ÆºĈ¸µ®byb’€€“ Ž “fžš|>Rˆˆˆˆˆˆˆˆˆˆ>>™™™xÿ§”™«Œ‚£®LN¤xÓª®Š°’„‘ª£îœœGRG³€€‹Ž~Ž‚jENŠEЍŠckhŽ„Æ}ƒw€€€¥=~ˆ›œ€|ÕcÓÕW™VV‰Œ>dcx§§§§§§à™ŒŒŒŒOOb`°ª®®®®®ÛÓ®ªªªªœŠœ‹‹‹‹‹‹¹~‚‚‚‚JJb`‘ŽŠŠŠŠŠÛՊŽŽŽŽƒŽƒĀ>>hĀĀĀĀ‘", "height": 358 }, "PT Serif": { "default": 136, "widths": ">TaˆˆÏÞ9XXwˆHmGXˆˆˆˆˆˆˆˆˆˆTUˆˆˆyĐ¯¥µ•‹°ºZW¥Þ»½™½£‰ž¬¯ï¬¥œObO€iTŒwށX‡–MJ„MݘŽ‘Œbo_“‚º…}Y=YˆTˆˆˆˆ=‡Ïyˆ³mˆnn“ƒ=oyy¯¯¯¯¯¯ì¥••••ZZZZ¶»½½½½½ôˆ½¬¬¬¬¥˜£ÇwMMMM‰˜ŽŽŽŽŽàˆŽ““““‘ãKKtâ?iS‹", "height": 332 }, "STIXSizeTwoSym": { "default": 162, "widths": "@Q`¢¢«³;—œ{ÌQ”Q΢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›vÎv¢ƀ¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“¸`¸¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 647 }, "Tahoma": { "default": 140, "widths": "PUgºŒú­6bbŒºN]NbŒŒŒŒŒŒŒŒŒŒ[[ºººy隗𮐆«­`k—Å«µµŸ–¨™ç•”bbbºŒŒ†ŽvއRŽ;H€;׏‹ŽŽ\\rV€¾€r{b{ºUŒŒŒŒbŒî“ºîyº~~‘Œ[~“yššššššê𐐐````³«µµµµµúºµ¨¨¨¨”‘Œ††††††áv‡‡‡‡;;;;Œ‹‹‹‹‹éº‹€Ž€é66tÑKzS‹", "height": 309 }, "RicAldi": { "default": 128, "widths": "@VUÅ€ÆÆ,UU€Õ@U@G€€€€€€€€€€VVÕÕÕrü¸ª«Ï©©ÀÔdgÕ¸ñÍĠijŽ¸Æ¶üƸªWGWĀ€€r‹rŽrQ€”OHLٔ‚‹ˆddVŽ€µ€€r€€€ÕV€€€¶3€ÒoÕÒTÕTTŒ€@bor¸¸¸¸¸¸þ«©©©©dddd¹ÍÄÄÄÄÄďÄÆÆÆÆ¹Ž‘rrrrrrªrrrrrOOOO€”‚‚‚‚‚ÉՂŽŽŽŽ€€€Ā@@—Ā?iS‹", "height": 314 }, "HAN NOM B": { "default": 128, "height": 281 }, "HAN NOM A": { "default": 128, "height": 281 }, "Telugu MN": { "default": 128, "widths": "GF]{ÏÁ+[[aŒ?H6@‹‹‹‹‹‹‹‹‹‹6B’“’rꤠ¿¾Å²Es¯…à±Ð‰Íœ…œ±¢á£ŸL@Ls„K~އ‹_•‡9<|9݇˜arR‰~Ãu„O)O„U€€€€3€Á€ÃfMM”t@O€r¹¹¹¹¹¹ä«œœœœUUUU¹¹¹¹¹¹¹ä¹¹¹¹¹¹Ž€rrrrrr«rrrrrGGGG€€€€€€€¹€€€€€€€€Ã88WĀ?iS‹", "height": 431 }, "InaiMathi": { "default": 116, "widths": "M?R˜t¯‹+DDb|?L?9tttttttttt??|||sҋ‰”—„z “6gŠrª“‚ŸŒˆzƒ¼„8B8}h)~~y€~C€~/0t.Á~€€„IqB~t§rqtF4F•Q¢¢¢¢`¢É†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ą88XĀS‹S‹", "height": 322 }, "Blackmoor LET": { "default": 101, "widths": "3JW€Š–®5RRG’<^~ª•ʵ¼•¼››¤«ºòœœ“rŠr««—ŽœŒš“dœ•>>>³µ¼¼¼¼¼ė«¼««««œ•—ŽŽŽŽŽŽîŒ““““<<<<˜•›››››ÿ«›••••’š’ĈNN«Ĉ…¨…¨", "height": 341 }, "Sinhala MN": { "default": 128, "widths": "54E\\`š DCHh/6(/gggggggggg(1mmmU®zwŽŽl`’…3V‚c§„›f™tct„y¨yuw808Vb8^jekhGoe*,\\*¥dqkkHU=f^‘Wc`;\u001f;bU€€€€3€€ÃfMM”t@O€r¹¹¹¹¹¹ä«œœœœUUUU¹¹¹¹¹¹¹ä¹¹¹¹¹¹Ž€rrrrrr«rrrrrGGGG€€€€€€€¹€€€€€€€€‘**AĀ?iS‹", "height": 300 }, "Andale Mono": { "default": 154, "height": 288 }, "Latin Modern Roman": { "default": 128, "widths": "UG`ՀÕÇGdd€ÇGUG€€€€€€€€€€€GGÇÇÇyÇÀµ¹Ä®§ÉÀ\\„Ç ëÀǮǼ޹ÀÀćÀÀœG€GŽÀ€€ŽrŽrN€ŽGN‡GՎ€Ž‡dedއ¹‡‡r€G€ŽGrÀÇÀG|¯ŽÇ¯`Ç\\\\ŽœÇkŽyÀÀÀÀÀÀç¹®®®®\\\\\\\\ÄÀÇÇÇÇÇĄÇÇÀÀÀÀÀ €€€€€€€¹rrrrrGGGG€Ž€€€€€ÇǀŽŽŽŽ‡Ž‡ĀGGǬS‹S‹", "height": 363 }, "Muna": { "default": 177, "widths": "AA;¢‘«z\"JP{nQnGnyRxiymqqjqAQÌnÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢ø¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“SGS¢Q¢¢¢¢`¢ÛJÌÛoÌll ¢QxJl±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀnDZ±±± Ž™ÚƒJJJJšŸînŸŸŸŸ†¡†ĀQQtS‹S‹", "height": 399 }, "Avenir Next": { "default": 148, "widths": "@ThޔմCMMrªCRC_””””””””””MMªªª{ͳ£¸Â˜Ç¸C~¡ƒãÄڔؙ’¶Ÿù¦š’M_Mª€=‰£€£’L¢•@@ƒA╜££\\rQ•}¿|}qM9MªT””””9”Í{ªšfª__ŽšC\\{{³³³³³³ý¸˜˜˜˜CCCCÂÄÚÚÚÚÚþªÚ¶¶¶¶š”™‰‰‰‰‰‰ß€’’’’@@@@œ•œœœœœþªœ••••}¢}š99€ĀS‹S‹", "height": 350 }, "STKaiti": { "default": 131, "widths": "@8h«sÓ»-KKm«8P8€xxxxzxxxxx88«««]믠¥Æ©’ÅÈ[U¾“âÇǓĤ|·°å´ª©E€E€Uƒ…l„nV{ˆ=;}EȈƒ†…XbW‡y«uoo{€{«8i“©­€ĀÃ]«Ã]­PTˆsJU]]¯¯¯¯¯¯ö£©©©©[[[[ÆÇÇÇÇÇÇð®Ç····ª€ƒƒƒƒƒƒ˜lnnnn====ƒ„ƒƒƒƒƒ³Äƒ‡‡‡‡oˆoĀ?C[ĀĀĀĀ‹", "height": 261 }, "Latin Modern Mono Caps": { "default": 134, "height": 333 }, "Nanum Myeongjo": { "default": 243, "widths": "M_RžžáÜ-^^¤¤F¤FoŠŠŠŠŠŠŠŠŠŠiiŒ¤Œ†ÛººÊŽÊÙRaºąØØŽØº‚«º»ąÊºº^Ā^8wrTEEEؐccTÌr^^¸ó€€ó€3óóóóóóóó”óJóóó¹¹¹¹¹¹ó«œœœœUUUUó¹¹¹¹¹¹óóó¹¹¹¹¹óórrrrrrórrrrrGGGG󀀀€€€óó󀀀€€óóóJJóóóóóó", "height": 305 }, "Times New Roman": { "default": 128, "widths": "@Ui€€ÕÇ.UU€@U@G€€€€€€€€€€GGr칫«¹œŽ¹¹Ud¹œä¹¹Ž¹«Žœ¹¹ò¹¹œUGUx€Ur€r€rU€€GG€Gǀ€€€UdG€€¹€€r{3{‹U€€€€3€Ã€ÃfMM”tUO€r¹¹¹¹¹¹ä«œœœœUUUU¹¹¹¹¹¹¹ä¹¹¹¹¹¹Ž€rrrrrr«rrrrrGGGG€€€€€€€¹€€€€€€€€ĀUUZĀ8kS‹", "correction": 1.04, "height": 294 }, "Shree Devanagari 714": { "default": 151, "widths": "f9ZØ´ã¡8Y[‘ ;Ž;•—˜˜—˜——˜——<>‰Ž‰•Ěª¨±±“‹º´5|¥ˆÚ¹¼œ½ª˜‘®œë— •YuYœ‚Gˆ—ˆ—Ž>•55‚5ۏ–——Q}<z¼}|rs5s–;É«Æ5˜êœ¦ê[Œvy’µQyœ•ªªªªªªþ±““““5555²¹¼¼¼¼¼ĉ„¾®®®® ˜šˆˆˆˆˆˆé…ŽŽŽŽ5555—–––––Ā•—|—|þ;;p¯S‹S‹", "height": 380 }, "YuMincho": { "default": 128, "widths": "UFf˜€å¼;cch­CZCk€€€€€€€€€€HH­­­|Ú²±¸Õ¦šÍÖ^`·¥ùÏתײ¸Ð¶ú©¦§ckc€€€xsŽzUGFCْ‡ahWŽ|¹y}tcZc¤F€€§ZĀÙl­ˆĀĀ]]ĀC`l|²²²²²²ă¸¦¦¦¦^^^^ÕÏ×××××ėĀ×ÐÐÐЦ©—xxxxxx¸szzzzGGGGŠ’‡‡‡‡‡Ó‡ŽŽŽŽ}}ĀCCjĀĀĀZ‘", "height": 524 }, "TeX Gyre Bonum": { "default": 159, "widths": "RMaŸŸæÍ8MMqšRfRšŸŸŸŸŸŸŸŸŸŸRRšššŠÒ®½½Í¸¤ÍÍWš¸šì½ÍŸÒ¸©ŸÈ³ö¸¤¤MšMš€W”Ÿ…Ÿ…RŠ©MMŸMñ©Ÿ”q…a®…ȏŠ{HšHšMŸŸŸŸš…½\\š½fš__®šRl\\Š®®®®®®Ń½¸¸¸¸WWWWͽÍÍÍÍÍĽšÍÈÈÈȤŸ©””””””Ü……………MMMM©æš®®®®ŠŸŠĀ88vĀS‹S‹", "height": 343 }, "Bradley Hand": { "default": 213, "widths": "APS¡¬¶›7kkŽÕD\\DoŒ†Ÿš¡œ£¯‘ŒDDÕÕÕd¯º¯¡¥Ÿ‘ÀÂGGÁ£áȸ’½Ã¾½§ü§®èkokĀi€„|n‘aišˆTF Tç¬mxšdtZž~®{©Ži€iÕPž¬›€vEkÕE<Õif©yCDkdººººººě¡ŸŸŸŸGGGG£È¸¸¸¸¸ĨÕ¸½½½½®‘„„„„„„×naaaaTTTTj¬mmmmm¤ÕŽžžžž©v©è==HÐĀĀˆ", "height": 320 }, "Princetown LET": { "default": 140, "widths": "YG\\€ˆÂ1]\\R’BN?€ŒhŒŠŒŒvŒŒ?A’’’vŒ—–”Œ¡Z‚¤•Á«Œ–ŸŽšžªä®š’]€]•€D§–Œ—–”Œ¡Z‚¤•Á«Œ–ŸŽšžªä®š’e8e…G{Œ€™8€¿t’¿fMM”Š?Utv§§§§§§êŒ––––ZZZZ¹«ŒŒŒŒŒÏ’žžžž¹œŽ§§§§§§êŒ––––ZZZZ€«ŒŒŒŒŒÏžžžž€ŽšĐ77mĀ?iZ‘", "height": 287 }, "AppleMyungjo": { "default": 256, "widths": "f€f¤¤ÂÉf€€€¨f€f€¤¤¤¤¤¤¤¤¤¤X^€¥€wÔ±¥»§£»Æ^‰È«ñÌ·¤µµ¤ÍÄþ¶Á’€€€€€@‡ˆlŠuYw™LH”L՘xˆ„nf_•‰À‡ˆm€€€§ĀªµĀÄ3ĀËĀĀÃĀĀĀ”¹¹¹¹¹¹Ā«œœœœUUUU¹¹¹¹¹¹Ā¹¹¹¹¹ĀĀrrrrrrĀrrrrrGGGG€€€€€€€ĀĀĀ€€€€€Ā€ĀĀĀĀĀĀĀĀ‹", "height": 438 }, "LiSong Pro": { "default": 256, "widths": "UUG¤ŒÑ½>UU‰²WUWpˆˆˆˆˆˆˆˆˆˆWW|||v·¹ ¢­¡›º·U€»Ì¼°Ÿ°®‘¡ÁºÝ¼·UpUZˆU‡sˆycˆJNHהy‡ƒhf[…¼‚fB#BUĀĀ€Ā3ĀÀ€ÃĀĀMMĀtĀO€r¹¹¹¹ä«ĀĀœUUUU¹¹Ā¹¹¹ä¹¹¹¹¹¹Ž€ĀĀrrrr«rĀĀĀrĀĀGG€€ĀĀ€€€ĀĀĀĀ€€€€ĀĀĀĀ€ĀiĀ‹", "height": 264 }, "Type Embellishments One LET": { "default": 177, "widths": "8µÀßġÂįǹŚÊőƥÏƥĺĺËÚěěƋĽǹƔÕĭʤįĴÂŁĶĩ¤¤Ĵĵ¥¥¬¬~~””ĽŜǑǘÚ××××ÎĦĦ‹ĺ—ŁŚĬĴrįŐÆǸńƏŇŇƪãʼnŋÅ«ľľìÎÀĶě¬ÂĽƅßŷÞQ¢¢¢¢`¢ġ†ÌÛoÌll ¢Qx†l—±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ĽÚJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 316 }, "Lantinghei TC": { "default": 256, "widths": "GNV›¢ùÅ.]]r¨JžJKœ|š›œœŸIJ§¤¦›ĝ¼»ËÊ»«ÚÉAŒºœëÊÛ»Úʺ¬Ì¾ĉ¼»«MLM„¦Yœ››œJœ›37Œ2蜜››]‘M›ŒÊŒŒŒ]@]¤QĀĀ¢¢`ĀۆÌÛĀĀll ¢Āx†l±±±±è±ĀĀĀ‹JJJJÀ½ĀĀÇÇÇĀĀDZ±±± Ž™ĀĀÚƒĀĀĀĀĀJJšŸĀĀĀĀĀĀĀŸĀ†¡†ĀĀĀĀĀ‹‹", "height": 356 }, "pangzhonghua": { "default": 128, "height": 256 }, "PT Sans": { "default": 140, "widths": "DNVŒŒÆÐ8HHZ2\\7[ŒŒŒŒŒŒŒŒŒŒ8BpЖ–’§‰„¬JJœ„ʬ¯¯˜ˆŽ§‘ÔžŒNaN€hHŠsŠ‚R‰ŒED{KЌ‰Š‰WlWŠ{¼„wrY=YNŒŒŒŒ=Îz³mee~Dfzp––––––Ù’‰‰‰‰JJJJ¨¬¯¯¯¯¯ï¯§§§§˜Ìs‚‚‚‚EEEEŒ‰‰‰‰‰Ü‰ŠŠŠŠwŠwÒ22tÂS‹S‹", "height": 332 }, "Devanagari MT": { "default": 128, "widths": "RUoŸ€¿ÇAoo…CmDT…………………………XX………x칫«¹œŽ¹¹Ud¹œä¹¹Ž¹«Žœ¹¹ò¹¹œhTh…XUr€r€rU€€GG€Gǀ€€€UdG€€¹€€rkNk…U€€€€3€Ã€ÃfMM”t@O€r¹¹¹¹¹¹ä«œœœœUUUU¹¹¹¹¹¹¹ä…¹¹¹¹¹¹Ž€rrrrrr«rrrrrGGGG€€€€€€€¹€€€€€€€€õDDZõ?iS‹", "height": 412 }, "Menlo": { "default": 154, "height": 298 }, "Futura": { "default": 106, "widths": "5Gajj”ƒ4KKjj5.5sjjjjjjjjjj55‘j‘_¾hibw]Zwx8KlU’xzfzl`Tw_˜`caZsZ‘‘‘eeIe^?eh33d3œhceeDK9hW‚`^VM‘MÁGjjÁj‘jÛ^‘ÛnjIIhj5M^_hhhhhh”b]]]]8888wxzzzzz£jzwwwwcfjeeeeee’I^^^^3333chccccc˜jchhhh^e^;;Á¢S‹S‹", "correction": 1.5, "height": 331 }, "Yuanti SC": { "default": 62, "widths": "@>n¤‚íª\\\\\\k—0ŠAv‘S‹‡……HHŠ‡Š„ÝŒ£ ‹³­>esͪ²‹»“~¦šÍž„HvHn„\\ŠŽ}Ž€iŽŽ>Z;≏ŽŽbtjŽ|½|Œvg;gØ>}—È…6x؈«Ø<—^^ˆ4iˆ„Ø£‹‹‹‹>>>> ª²²²²²ïž²¦¦¦¦„‹ŠŠŠŠŠŠœ}€€€€>>>>„Ž¹—šŽŽŽŽŒ‹ŒĀ>>[ĀĀĀĀ‹", "height": 358 }, "Latin Modern Mono Light": { "default": 134, "height": 336 }, "Diwan Thuluth": { "default": 177, "widths": "\u0001)`‡Q«K;‚‚ZRQD\"?O4NDOGJJEJ'QNDNlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›2?3¢E¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“6%<¢Q¢¢¢¢`¢Û2ÌÛoÌll ¢Qx2l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀADZ±±± Ž™ÚƒJJJJšŸîDŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 439 }, "PilGi": { "default": 256, "widths": "€Mg¤vÒ¨Cmmx«CUC€xxxxxxxxxxCP€ø€Xþ¸žw«w‘ƒžƒ‹ Å‘žƒžžžžž‘ܵ‰‘{€{PĀC~ee~i_v~COyZ±}|reJ~q±‰wjPFPýĀĀĀĀĀ4ĀĀĀĀÅĀĀĀĀ„ĀĀĀĀ¸¸¸¸¸ĀŸ»»»»uuuuĀâ°°°°°ĀĀĀÛÛÛÛ¥Ā††††††Āb____LLLLx‘nnnnn‹Ā‹ĀĀĀĀĀĀĀˆ", "height": 259 }, "Hannotate TC": { "default": 155, "widths": "UZR¦“»Ð0LLs“F]D‹››››››››››DFä¢£š¡•„Ÿ¤>‰Ÿ~½ ª•¶§˜—˜–½œ‘¡T†Ts€L‹ƒ€…c~ƒEN‚@¬„„Žr~f|w£€‚x`KaUZ~±¯­G“¥‘–¥g—€€€e‘¢¢¢¢¢¢Óš••••ZZZZ¯ ªªªªªØ‚¬˜˜˜˜‘™˜‹‹‹‹‹‹­€ZZZZ„„„„„„µ–„||||‚…‚ĀAA€ĀRĀ€ˆ", "height": 358 }, "Oriya Sangam MN": { "default": 177, "widths": "8*F©‚Þ&B>nŒ/K,E,/ƒŒƒtµŒ£˜†€¥‘:o“y«‘¨„ªŒ“Ž‚Ä~ƒ4B/e‡.‚yyy€M|t1(t1¶t||GrFthhftF*BŽQ¢¢¢¢`¢²†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†¸..KĀS‹S‹", "height": 376 }, "PCMyungjo": { "default": 128, "height": 270 }, "STSong": { "default": 256, "widths": "@8h«sÓ»-KKm«8P8€xxxxxxxxxx88«««]뭝£Å¨ÅÃ[U½“ÕÅȐŠ{µ²çµ­¨E€E€ƒU‚†iƒo]zˆE9xFɈƒ‡†\\]R„x«ukm{€{«8k“Ā­€ĀÃ]«ÃĀĀPPˆsĀU]]­­­­­­æ£¨¨¨¨[[[[ÅÅÈÈÈÈÈðĀȵµµµ®€‚‚‚‚‚‚•koooo;;GB…ˆƒƒƒƒƒ³Āƒ„„„„mƒmĀ>B[ĀĀĀĀ‹", "height": 271 }, "SchoolHouse Printed A": { "default": 91, "widths": "?!9aYh%]]2h)^\u001d^~$ngzrltjw$*gafI[¿—¢œ‹²¡MˆŒŒÆ ®–®—ƒ“–{ߑ’‰>`A`VC[[N[WG[X!$S$–XU[[RJ?XMƒSQUN$B!…u£Ž4ŽÅgŽÅ.Ž^^„?2LfI¿¿¿¿¿¿á¢‹‹‹‹MMMMØ ®®®®®ýh®––––¥’[[[[[[ NXXXX!!!!xXUUUUUŸ]UXYXX‹QÜ%2ŽÜĀĀˆ", "height": 319 }, "Heiti TC": { "default": 142, "widths": "@LO¸‰ÆÂ3^^m›GUGpŽŽŽŽŽŽŽŽŽŽGG›››Þ½“о‰|߯:{—vë½Þ˜ß›m¨´öœ˜{Z›Z¬€a¯¯¦¯¦P¬œ343𜨯¯McWœŽÕ{‰mZzZ›LŽŽÍŽa’Õm›Ó¶UXœĀ^m½½½½½½þЉ‰‰‰::::ʽÞÞÞÞÞĐ·Þ¨¨¨¨˜¯Ž¯¯¯¯¯¯Ċ¦¦¦¦¦3333¨œ ¡¨¨¨ċƧœœœœ‰˜‰ĀĀĀ›ĀĀĀĀ‹", "height": 264 }, "HanziPen TC": { "default": 148, "widths": "X_gńµ©7XX|–DpF””””””””””CNzœq„×­—‰‘‰‹¢¥I—tȞ³¬Šnš”¦Ó’…WWz„Xr|aqtwkuDcw@ÀuhqtdX_|v¶vrXLXZ_d º­Nr³š³P–„„‘„b„­­­­­­ë‰‰‰‰‰OOOO¡ž³³³³³Ĉ„³””””’€‚rrrrrr«attttFFFFouhhhhh¤š|||||r}rĈJJ„ĈFĀ„ˆ", "height": 358 }, "Copperplate": { "default": 156, "widths": "NUUœœä¹9dd€šNUNGœœœœœœœœœœNNšššr𫹹¹«œ¹ÇU€«œÕÇǫǹ«œ¹«ò«œœdGdš€UŽœŽœŽŽœ«UrŽŽ¹«œŽœœŽ€«ŽÇŽŽŽd9dšUœœœœ9€š€ššfš^^«šN^€r««««««Ā¹««««UUUU¹ÇÇÇÇÇÇĀšÇ¹¹¹¹œ«ĝŽŽŽŽŽŽäŽŽŽŽŽUUUUœ«œœœœœòšœ««««ŽŽŽĀGG€Ā?iS‹", "height": 263 }, "Geeza Pro": { "default": 162, "widths": "PQ`¢¢«³;SS{ÌQ”K†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQ¸S‹S‹", "height": 465 }, "CNstrokeorder": { "default": 128, "height": 284 }, "Comic Sans MS": { "default": 204, "widths": "M=mزҨc^^ˆ{Gk@ƒœsœœœœœœœœMMbƒb†î»¡š¹ ›®ÅŒªœâÌ̅ࡲ®½¦Ċ¹£²``•¡Žƒ˜„–Œ‚ˆ”HgŠFdž‡‰…{}y…}¯—…Š^l^™= Ë£h£Ì”{Ìi{§§…²@s”†»»»»»»Ėš    ŒŒŒŒ¹ÌÌÌÌÌÌIJ{̽½½½£…rƒƒƒƒƒƒé„ŒŒŒŒHHHH‚†‡‡‡‡‡æ{‡……………‰nâ..c­.Uˆ", "height": 357 }, "Arial Hebrew": { "default": 177, "widths": "G@kvµ³<>>W}7Y5P}}}}}}}}}}=>}}}qܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›GPG¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“MFM¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†€66ĀS‹S‹", "height": 290 }, "STIXIntegralsUpD": { "default": 162, "widths": "@Q`¢¢«³;SS{ÌQ”Q†¢¢¢¢¢¢¢¢¢¢QQÌÌÌlܱ“±À‹‰¹¼JP§‰Ý½ÇŽÇ¢Š¢±§Û  ›S†S¢€¡ƒ¡^ ŸJN–J¡¡iƒ`Ÿ…ŝ†“S`S¢Q¢¢¢¢`¢Û†ÌÛoÌll ¢Qx†l±±±±±±è±‹‹‹‹JJJJÀ½ÇÇÇÇÇĀÌDZ±±± Ž™ÚƒJJJJšŸî̝ŸŸŸŸ†¡†ĀQQĀS‹S‹", "height": 677 }, "Nanum Brush Script": { "default": 74, "widths": "HCH½¦Ž?ZZaˆ;ˆ3W_>a^…a\\xT_NNsˆsf€viavnˆsBnˆ€’{isnˆx}sd¯€{…_Ï_fŒ@\\OORTWR]6Bi9gJZW\\WiWH…aTn]>V–Mu‘׌`YÛñññr‘if ›JŠñб±±±±±è±‹‹‹‹JJJJ˽ÇÇÇÇÇĀhȱ±±± ˆ¯ÚƒJJJJ˜Ÿî†ŸŸŸŸ†™†ÝHHññv{JJ", "height": 295 }, "Iowan Old Style": { "default": 142, "widths": "GVUŽŽáÕ,kktÕGZGvŽŽŽŽŽŽŽŽŽŽGGÕÕÕx¹ŸµÈ¢–¾ËWU±’éÏ͗ʹ‹¢Ê¸Ċ¶©«kvkĀ€€{Žr}R„–LFŒKᖍdq\\—~ƈ|yk€kÕVŽŽ›Ž€€ÒkÕÒTÕ^^ŒGjkx¹¹¹¹¹¹čµ¢¢¢¢WWWWÊÏÍÍÍÍÍĖÕÍÊÊÊÊ©•–{{{{{{Âr}}}}LLLL–áՍ————||ĀGG—Ā$JS‹", "height": 349 }, "Savoye LET": { "default": 128, "widths": ",D\u001f¼IoÀ\u0013UU)NP%\"J%ySKQM77.MAd=K;f>>>%%%%€SKKKKKoNKMMMMK€Kĝ;;@Ā?iS‹", "height": 304 }, "Gujarati MT": { "default": 128, "widths": "fHoŸ€¿ÇASSs…;d;T…………………………MM………h칫«¹œŽ¹¹Ud¹œä¹¹Ž¹«Žœ¹¹ò¹¹œkTk…XUr€r€rU€€GG€Gǀ€€€UdG€€¹€€rkNk…U€€€€3€Ã€Ãf``”t@O€r¹¹¹¹¹¹ä«œœœœUUUU¹¹¹¹¹¹¹ä…¹¹¹¹¹¹Ž€rrrrrr«rrrrrGGGG€€€€€€€¹€€€€€€€€õ;;Zâ?iS‹", "height": 395 }, "PortagoITC TT": { "default": 100, "widths": "#;Tu^•…:88mÌ/@*>dG[\\W_`R^_+.ÌÌÌS»rpjr_Wot7`lV¡€pgqof_skªkj`;N;©€€db\\dSMae2T_MŠob\\caYSd]“\\\\T=c=Ì;V]©o€gÎd̀cÕ??‹v7>dSrrrrrr–j____7777s€pppppœÌpssssjg±dddddcƒ\\SSSS2222dobbbbbŠÕhdddd•Z\\²%%lĀZ‘Z‘", "height": 312 }, "Times": { "default": 128, "widths": "@Ui€€ÕÇ.UU€@U@G€€€€€€€€€€GGr칫«¹œŽ¹¹Ud¹œä¹¹Ž¹«Žœ¹¹ò¹¹œUGUx€Ur€r€rU€€GG€Gǀ€€€UdG€€¹€€r{3{‹U€€€€3€Ã€ÃfMM”t@O€r¹¹¹¹¹¹ä«œœœœUUUU¹¹¹¹¹¹¹ä¹¹¹¹¹¹Ž€rrrrrr«rrrrrGGGG€€€€€€€¹€€€€€€€€ĀUUZĀ?iS‹", "height": 292 }, "Oriya MN": { "default": 128, "widths": "KKcƒ‰ÝÍ.``g•CM9D””””””””””9F›œ›yø®ªËʚ‰Ñ¾Izºï¼Ý’Û¦¥¼­ð­§ªQDP{ŒP‡—™”ež,:\n\\]*[^-\[\\\]=<>,:\s\\]/ typed_attr: (attr (LBRACKET datatype? RBRACKET)?)? datatype: note !box_def_prefix: "+" | "-" ATTR: /(?=\w)(?!_)[^\[\]>,\r\n!?]*[^\[\]>,\s]/ ?note: /[^\r\n\]]+/ start: (break_ | line)* BREAK.100: /^\n/ line: indent? (phantoms | comment | clause | NL) ?clause: assoc_clause | entity_clause | constraint_clause | inheritance_clause PHANTOMS: /( *:)+ */ NL indent: INDENT INDENT.10: / +|\t+/ comment: PERCENT /.*\r?\n/ // Associations assoc_clause: box_def_prefix? assoc_name_def COMMA seq{assoc_leg} (COLON seq{assoc_attr})? NL assoc_name_def: box_name assoc_leg: _assoc_card? leg_arrow? SP? (LBRACKET leg_note? RBRACKET)? entity_name_ref _assoc_card: card_hidden? card_prefix? card entity_name_ref: box_name !card_hidden: "-" !card_prefix: "_" | "/" CARD.2: /(?![-_\/])(\w|\?){2}(?=[ \t]*[^\w,\r\n:])/ LEG_ARROW: /[<>]/ leg_note: note assoc_attr: id_mark? typed_attr // Entities entity_clause: box_def_prefix? entity_name_def COLON seq{entity_or_table_attr}? NL entity_name_def: box_name entity_or_table_attr: _id_symbols? (typed_attr | HASHTAG foreign_reference) _id_symbols: id_groups? id_mark ID_GROUPS.10: /\d+(?=_)/ ID_MARK: "_" foreign_reference: this_table_attr MORETHAN that_table MORETHAN that_table_attr this_table_attr: attr that_table_attr: attr that_table: entity_name_ref MORETHAN: SP? ">" SP? // Constraints constraint_clause: CONSTRAINT_LPAREN constraint_name? CONSTRAINT_RPAREN (LBRACKET constraint_note? RBRACKET)? seq{constraint_target}? (COLON constraint_coords)? NL CONSTRAINT_NAME: /[^)]{1,3}/ constraint_note: note constraint_target: constraint_leg? SP* box_name_ref box_name_ref: box_name CONSTRAINT_LEG: /?/ constraint_coords: _constraint_coord COMMA _constraint_coord _constraint_coord: NUMBER | box_name_ref // Inheritance inheritance_clause: SLASH inheritance_name? BACKSLASH SP? inheritance_parent SP? inheritance_arrow SP? seq{inheritance_child} (COLON seq{typed_attr})? NL INHERITANCE_NAME: /(XT\d?|TX\d?|X\d?|T\d?|\d)/ inheritance_parent: entity_name_ref inheritance_child: entity_name_ref INHERITANCE_ARROW.-10: /<==?|<--?|--?>|==?>/ // Avoid anonymous tokens for some terminals constraint_leg: CONSTRAINT_LEG card: CARD box_name: BOX_NAME leg_arrow: LEG_ARROW phantoms: PHANTOMS break_: BREAK inheritance_name: INHERITANCE_NAME constraint_name: CONSTRAINT_NAME attr: ATTR inheritance_arrow: INHERITANCE_ARROW id_mark: ID_MARK id_groups: ID_GROUPS ================================================ FILE: mocodo/resources/i18n/messages_fr.po ================================================ # MOCODO # Copyright (C) 2023 Aristide Grange # # Translators: # Aristide Grange, 2023 msgid "" msgstr "Project-Id-Version: mocodo\nPOT-Creation-Date: 2023-09-23 13:41+0200\nPO-Revision-Date: 2015-08-16 07:00+0000\nLast-Translator: Aristide Grange , 2015-2016,2022-2023\nLanguage-Team: French (http://app.transifex.com/aristide/mocodo/language/fr/)\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nGenerated-By: pygettext.py 1.5\nLanguage: fr\nPlural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: mocodo/__main__.py:103 msgid "Unknown argument \"{k}\" for option --select." msgstr "Argument « {k} » inconnu pour l'option --select." #: mocodo/__main__.py:227 msgid "The file \"{path}\" is missing." msgstr "Le fichier « {path} » est manquant." #: mocodo/__main__.py:229 msgid "The file \"{path}\" is not a valid JSON file." msgstr "Le fichier « {path} » n'est pas un fichier JSON valide." #: mocodo/__main__.py:231 msgid "The file \"{path}\" could not be read:\n{err}" msgstr "Le fichier « {path} » n'a pas pu être lu :\n{err}" #: mocodo/__main__.py:235 msgid "No third-party rendering service for extension \"{extension}\". You may want to add one in \"{path}\"." msgstr "Aucun service de rendu tiers pour l'extension « {extension} ». Vous pouvez en ajouter un dans « {path} »." #: mocodo/__main__.py:255 msgid "The HTTP status code {code} was returned by:\n{url}" msgstr "Le code HTTP {code} a été retourné par :\n{url}" #: mocodo/__main__.py:257 msgid "Error while trying to retrieve the URL:\n{url}\n{e}" msgstr "Erreur lors de la tentative de récupération de l'URL :\n{url}\n{e}" #: mocodo/__main__.py:285 msgid " - Leg \"{b1} — {b2}\" overlaps \"{b3}\"." msgstr " - La patte « {b1} — {b2} » chevauche « {b3} »." #: mocodo/__main__.py:287 msgid " - Legs \"{b1} — {b2}\" and \"{b3} — {b4}\" overlap." msgstr " - Les pattes « {b1} — {b2} » et « {b3} — {b4} » se chevauchent." #: mocodo/__main__.py:289 msgid "Bad layout of boxes:\n{details}\nTo fix the problem, reorder and/or skip lines in the source text, either manually, or with the option -t arrange (chocolate bar under Mocodo online)." msgstr "Mauvaise disposition des boîtes :\n{details}\nPour corriger cela, réordonnez et/ou sautez des lignes dans le texte-source, soit à la main, soit à l'aide de l'option -t arrange (tablette de chocolat sous Mocodo online)." #: mocodo/argument_parser.py:401 msgid "OPTIONS ON MOCODO ITSELF" msgstr "OPTIONS SUR MOCODO LUI-MÊME " #: mocodo/argument_parser.py:402 msgid "INPUT/OUTPUT" msgstr "ENTRÉE/SORTIE " #: mocodo/argument_parser.py:404 msgid "ASPECT OF THE GRAPHICAL OUTPUT" msgstr "ASPECT DE LA SORTIE GRAPHIQUE " #: mocodo/argument_parser.py:406 msgid "" "\n" " NAME:\n" " Mocodo - An Entity-Relation Diagram Generator.\n" "\n" " DESCRIPTION:\n" " Mocodo is an open-source tool for designing and teaching relational databases.\n" " It takes as an input a textual description of both entities and associations\n" " of an entity-relationship diagram (ERD). It outputs a vectorial drawing in SVG\n" " and a relational schema in various formats (SQL, LaTeX, Markdown, etc.).\n" "\n" " NOTE:\n" " Each one of the following values is:\n" " - explicitely specified by the user as a command line option;\n" " - otherwise, retrieved from a file located at --params_path;\n" " - otherwise, retrieved from a file named 'params.json' in the input directory;\n" " - otherwise, calculated from a default value, possibly dependant of your system.\n" " " msgstr "" "\n" " NOM :\n" " Mocodo - Un générateur de diagrammes entité-association.\n" "\n" " DESCRIPTION :\n" " Mocodo est un outil libre destiné à l'enseignement des bases de données relationnelles.\n" " Il prend en entrée une description textuelle des entités et associations d'un diagramme\n" " entité-association (MCD). Il produit en sortie un dessin vectoriel en SVG et un schéma\n" " relationnel dans divers formats (SQL, LaTeX, Markdown, etc.).\n" "\n" " NOTE :\n" " Chacune des valeurs suivantes est :\n" " - spécifiée explicitement par l'utilisateur comme option de ligne de commande ;\n" " - sinon, récupérée depuis un fichier de chemin --params_path ;\n" " - sinon, récupérée depuis un fichier nommé « params.json » dans le répertoire d'entrée ;\n" " - sinon, calculée à partir d'une valeur par défaut, éventuellement dépendante de votre système.\n" " " #: mocodo/argument_parser.py:424 msgid "" "\n" " SEE ALSO:\n" " Online version https://mocodo.net\n" " Source code https://github.com/laowantong/mocodo\n" " Documentation https://laowantong.github.io/mocodo/doc/fr_refman.html\n" " Cheat sheet for -t https://github.com/laowantong/mocodo/blob/master/doc/fr_cheat_sheet.md\n" "\n" " LICENSE: MIT\n" "\n" " CONTACT:\n" " Author Aristide Grange\n" " Address Université de Lorraine\n" " Laboratoire LCOMS - UFR MIM\n" " 3 rue Augustin Fresnel\n" " 57070 METZ Technopôle\n" " France\n" " Mail @univ-lorraine.fr\n" " " msgstr "" "\n" " VOIR AUSSI :\n" " Version en ligne https://mocodo.net\n" " Code source https://github.com/laowantong/mocodo\n" " Documentation https://laowantong.github.io/mocodo/doc/fr_refman.html\n" " Aide-mémoire pour -t https://github.com/laowantong/mocodo/blob/master/doc/fr_cheat_sheet.md\n" "\n" " LICENCE : MIT\n" "\n" " CONTACT :\n" " Auteur Aristide Grange\n" " Adresse Université de Lorraine\n" " Laboratoire LCOMS - UFR MIM\n" " 3 rue Augustin Fresnel\n" " 57070 METZ Technopôle\n" " France\n" " Courriel @univ-lorraine.fr\n" " " #: mocodo/argument_parser.py:93 msgid "rearrange the layout with either a Branch & Bound or a Genetic Algorithm" msgstr "réarrange la disposition, soit par Branch & Bound, soit avec un algorithme génétique" #: mocodo/argument_parser.py:98 msgid "rewrite the given elements in ASCII" msgstr "réécrit les éléments donnés en ASCII" #: mocodo/argument_parser.py:104 msgid "dump the abstract syntax tree of the source text (for debugging purposes)" msgstr "crée l'arbre de syntaxe abstraite du texte source (pour le débogage)" #: mocodo/argument_parser.py:109 msgid "rewrite the given elements in camelCase" msgstr "réécrit les éléments donnés en camelCase" #: mocodo/argument_parser.py:115 msgid "rewrite the given elements by capitalizing the first letter of each word" msgstr "réécrit les éléments donnés en capitalisant la première lettre de chaque mot" #: mocodo/argument_parser.py:121 msgid "rewrite the given elements in lowercase, but more aggressively than 'lower'" msgstr "réécrit les éléments donnés en minuscules, mais plus agressivement que « lower »" #: mocodo/argument_parser.py:127 msgid "convert the conceptual model into a Chen's ERD" msgstr "convertit le modèle conceptuel dans la notation de Chen" #: mocodo/argument_parser.py:132 msgid "try to infer types, entities, CIFs or DF arrows from the existing elements" msgstr "essaie d'inférer les types, entités, CIFs ou flèches de DF à partir des éléments existants" #: mocodo/argument_parser.py:138 msgid "convert the conceptual model into a crow's foot ERD" msgstr "convertit le modèle conceptuel dans la notation crow's foot" #: mocodo/argument_parser.py:143 msgid "collect all the attributes of the MCD in a table" msgstr "extrait tous les attributs du MCD dans une table" #: mocodo/argument_parser.py:148 msgid "suppress the given elements whenever possible" msgstr "supprime les éléments donnés quand c'est possible" #: mocodo/argument_parser.py:154 msgid "move any (1,1) association attribute to the appropriate entity" msgstr "déplace tout attribut d'association (1,1) vers l'entité appropriée" #: mocodo/argument_parser.py:159 msgid "replace all element names by a numbered generic label" msgstr "remplace tous les noms d'éléments par un libellé générique numéroté" #: mocodo/argument_parser.py:164 msgid "rewrite the source text as is" msgstr "réécrit le texte source tel quel" #: mocodo/argument_parser.py:169 msgid "decompose any n-ary (*,N) associations into n binary ones" msgstr "décompose toute association n-aire (*,N) en n associations binaires" #: mocodo/argument_parser.py:174 msgid "try to fix common errors in the given elements" msgstr "essaie de corriger les erreurs courantes dans les éléments donnés" #: mocodo/argument_parser.py:180 msgid "apply a vertical, horizontal or diagonal symmetry to the diagram" msgstr "applique au diagramme une symétrie verticale, horizontale ou diagonale" #: mocodo/argument_parser.py:185 msgid "add random entities and associations (default: 10 new associations)" msgstr "ajoute des entités et associations aléatoires (par défaut : 10 nouvelles associations)" #: mocodo/argument_parser.py:190 msgid "rewrite the given elements in lowercase" msgstr "réécrit les éléments donnés en minuscules" #: mocodo/argument_parser.py:196 msgid "prefix the given elements with the given string" msgstr "préfixe les éléments donnés avec la chaîne donnée" #: mocodo/argument_parser.py:202 msgid "keep the stucture, but replace the given elements with random ones whenever possible" msgstr "garde la structure, mais randomise les éléments donnés quand c'est possible" #: mocodo/argument_parser.py:208 msgid "convert the conceptual model into a relational schema with the given template path" msgstr "convertit le modèle conceptuel en schéma relationnel avec le gabarit donné" #: mocodo/argument_parser.py:213 msgid "rewrite the given elements by applying the given 'search/repl' pattern" msgstr "réécrit les éléments donnés en appliquant le motif « recherche/remplacement » donné" #: mocodo/argument_parser.py:219 msgid "rewrite the given elements in snake_case" msgstr "réécrit les éléments donnés en snake_case" #: mocodo/argument_parser.py:225 msgid "decompose any n-ary (*,1) associations into n-1 binary ones" msgstr "décompose toute association n-aire (*,1) en n-1 associations binaires" #: mocodo/argument_parser.py:230 msgid "suffix the given elements with the given string" msgstr "suffixe les éléments donnés avec la chaîne donnée" #: mocodo/argument_parser.py:236 msgid "rewrite the given elements by swapping the case of each letter" msgstr "réécrit les éléments donnés en inversant la casse de chaque lettre" #: mocodo/argument_parser.py:238 msgid "The template '{stem}' doesn't have a help message in language of code '{language}' or 'en'." msgstr "Le gabarit « {stem} » n'a pas de message d'aide dans la langue de code « {language} » ou « en »." #: mocodo/argument_parser.py:242 msgid "rewrite the given elements in Title Case" msgstr "réécrit les éléments donnés en mettant la première lettre de chaque mot en majuscule" #: mocodo/argument_parser.py:254 msgid "convert the conceptual model into a UML class diagram" msgstr "convertit le modèle conceptuel en diagramme de classes UML" #: mocodo/argument_parser.py:259 msgid "encode the MCD into a URL for Mocodo online" msgstr "encode le MCD dans une URL pour Mocodo online" #: mocodo/argument_parser.py:264 msgid "rewrite the given elements in UPPERCASE" msgstr "réécrit les éléments donnés en majuscules" #: mocodo/argument_parser.py:288 msgid "The transformation '{subopt}' is not among the possible ones:\n{valid}." msgstr "La transformation « {subopt} » n'est pas parmi celles qui sont possibles :\n{valid}." #: mocodo/argument_parser.py:291 msgid "rewrite the given elements by keeping only a given slice" msgstr "réécrit les éléments donnés en n'en gardant qu'une tranche donnée" #: mocodo/argument_parser.py:324 msgid "truncate the given elements to the given length (default: {n})" msgstr "tronque les éléments donnés à la longueur donnée (par défaut : {n})" #: mocodo/argument_parser.py:412 msgid "override the automatic localization of the messages with the given language code (e.g., 'fr', 'en', ...)" msgstr "force la localisation des messages avec le code de langue donné (par exemple, « fr », « en », ...)" #: mocodo/argument_parser.py:417 msgid "the path of the parameter file. If omitted, use 'params.json' in the input directory. If non existent, use default parameters." msgstr "le chemin du fichier de paramètres. S'il est omis, utilise « params.json » dans le répertoire d'entrée. S'il n'existe pas, utilise les paramètres par défaut." #: mocodo/argument_parser.py:421 msgid "the path of the input file. By default, the output files will be generated in the same directory" msgstr "le chemin du fichier d'entrée. Par défaut, les fichiers de sortie seront générés dans le même répertoire" #: mocodo/argument_parser.py:443 msgid "show this help message, then exit" msgstr "affiche ce message d'aide, puis termine" #: mocodo/argument_parser.py:448 msgid "display the version number, then exit" msgstr "affiche le numéro de version, puis termine" #: mocodo/argument_parser.py:452 msgid "recreate a pristine version of the files 'sandbox.mcd' and 'params.json' in the input directory, then exit" msgstr "recrée une version vierge des fichiers « sandbox.mcd » et « params.json » dans le répertoire d'entrée, puis termine" #: mocodo/argument_parser.py:457 msgid "the directory of the output files" msgstr "le répertoire des fichiers de sortie" #: mocodo/argument_parser.py:462 msgid "one or several encodings to be tried successively when reading the input file" msgstr "un ou plusieurs encodages à essayer successivement lors de la lecture du fichier d'entrée" #: mocodo/argument_parser.py:468 msgid "generate a PNG or a PDF version of the SVG output (requires CairoSVG)" msgstr "génère une version PNG ou PDF de la sortie SVG (requiert CairoSVG)" #: mocodo/argument_parser.py:472 msgid "display the contents of the parameter file, then exit" msgstr "affiche le contenu du fichier de paramètres, puis termine" #: mocodo/argument_parser.py:476 msgid "reuse the geometry file of the previous execution" msgstr "réutilise le fichier de géométrie de l'exécution précédente" #: mocodo/argument_parser.py:482 msgid "discriminate between multiple SVG of the same interactive diagram" msgstr "ajoute un discriminateur à un SVG interactif" #: mocodo/argument_parser.py:488 msgid "under Jupyter Notebook, explicitely state the categories of results to display" msgstr "sous Jupyter Notebook, spécifie explicitement les catégories de résultats à afficher" #: mocodo/argument_parser.py:494 msgid "use an external web-service to further convert the conversion results into the given graphical formats" msgstr "utilise un service web externe pour convertir les résultats de la conversion dans les formats graphiques donnés" #: mocodo/argument_parser.py:498 msgid "backward compatibility alias for '-t' (with no arguments). Same as '-t markdown' but, under Jupyter Notebook, does not prevent the rendering of the conceptual diagram in the cell output" msgstr "alias de compatibilité ascendante pour « -t » (sans arguments). Équivalent à « -t markdown » mais, sous Jupyter Notebook, n'empêche pas le rendu du diagramme conceptuel dans la sortie de la cellule" #: mocodo/argument_parser.py:519 msgid "make a new version of the MCD by applying sequentially the given rewriting operations, and/or convert it into the given formats or languages. Under Jupyter Notebook, '-T' respectively replaces the current cell by the textual result, or copies it into the clipboard (pip3 install pyperclip)" msgstr "crée une nouvelle version du MCD en appliquant séquentiellement les opérations de réécriture données, et/ou le convertit dans les formats ou langages donnés. Sous Jupyter Notebook, « -T » remplace respectivement la cellule courante par le résultat textuel, ou le copie dans le presse-papier (pip3 install pyperclip)" #: mocodo/argument_parser.py:521 msgid "initial value for the random number generator" msgstr "valeur initiale pour le générateur de nombres aléatoires" #: mocodo/argument_parser.py:525 msgid "Untitled" msgstr "MCD" #: mocodo/argument_parser.py:527 msgid "name of the model, used at various places (file system, database, etc.)" msgstr "nom du modèle, utilisé à divers endroits (système de fichiers, base de données, etc.)" #: mocodo/argument_parser.py:534 msgid "the acronym to be circled in a functional dependency" msgstr "l'acronyme à entourer dans une dépendance fonctionnelle" #: mocodo/argument_parser.py:541 msgid "format string for minimal and maximal cardinalities" msgstr "chaîne de formatage pour les cardinalités minimales et maximales" #: mocodo/argument_parser.py:370 msgid "format string for foreign keys in relational diagram" msgstr "chaîne de formatage pour les clés étrangères dans le diagramme relationnel" #: mocodo/argument_parser.py:548 msgid "string for relative cardinalities" msgstr "chaîne pour les cardinalités relatives" #: mocodo/argument_parser.py:554 msgid "flex straight legs whose cardinalities may collide" msgstr "incurve les pattes rectilignes dont les cardinalités peuvent se chevaucher" #: mocodo/argument_parser.py:559 msgid "the color palette to use when generating the drawing. Name (without extension) of a file located in the directory 'colors', or path to a personal file" msgstr "la palette de couleurs à utiliser lors de la génération du dessin. Nom (sans extension) d'un fichier situé dans le répertoire « colors », ou chemin vers un fichier personnel" #: mocodo/argument_parser.py:563 msgid "specification of the fonts, dimensions, etc. Name (without extension) of a file located in the directory 'shapes', or path to a personal file" msgstr "spécification des polices, dimensions, etc. Nom (sans extension) d'un fichier situé dans le répertoire « shapes », ou chemin vers un fichier personnel" #: mocodo/argument_parser.py:569 msgid "scale the diagram by the given factor" msgstr "applique au diagramme le facteur de mise à l'échelle donné" #: mocodo/argument_parser.py:575 msgid "scale all calculated text widths by the given factor" msgstr "applique à tous les textes calculés le facteur de mise à l'échelle donné" #: mocodo/argument_parser.py:579 msgid "raise an error when horizontal or vertical legs overlap" msgstr "lève une erreur quand des pattes horizontales ou verticales se chevauchent" #: mocodo/argument_parser.py:424 msgid "forbid the use of identifiers in associations (according to the Merise standard)" msgstr "interdit l'utilisation d'identifiants dans les associations (conformément au standard Merise)" #: mocodo/argument_parser.py:586 msgid "set the visibility and the contents of the lateral gutters" msgstr "définit la visibilité et le contenu des gouttières latérales" #: mocodo/association.py:40 msgid "The association \"{name}\" cannot have an identifier." msgstr "L'association « {name} » ne peut pas avoir d'identifiant." #: mocodo/association.py:50 msgid "The association \"{name}\" should have at least 3 legs to become a cluster." msgstr "L'association « {name} » devrait avoir au moins 3 pattes pour devenir un agrégat." #: mocodo/association.py:77 msgid "An association named \"{df_label}\" must have at least one leg with a maximal cardinality of 1." msgstr "Une association nommée « {df_label} » doit avoir au moins une patte avec une cardinalité maximale de 1." #: mocodo/common.py:29 msgid "Output file \"{filename}\" successfully generated." msgstr "Fichier de sortie « {filename} » généré avec succès." #: mocodo/common.py:34 msgid "Source file \"{filename}\" successfully updated." msgstr "Fichier source « {filename} » mis à jour avec succès." #: mocodo/common.py:44 msgid "Unable to read \"{filename}\" with any of the following encodings: \"{encodings}\"." msgstr "Impossible de lire « {filename} » dans aucun des encodages suivants : \\\"{encodings}\\\"." #: mocodo/common.py:50 msgid "The file \"{input}\" doesn't exist." msgstr "Le fichier « {input} » n'existe pas." #: mocodo/common.py:60 mocodo/common.py:65 msgid "Problem with \"{name}\" file \"{path}\"." msgstr "Problème lors du chargement de « {name} » au bout du chemin « {path} »." #: mocodo/convert/_uml.py:22 msgid "{complete, disjoint}" msgstr "{complet, disjoint}" #: mocodo/convert/_uml.py:23 msgid "{incomplete, disjoint}" msgstr "{incomplet, disjoint}" #: mocodo/convert/_uml.py:24 msgid "{complete, overlapping}" msgstr "{complet, non disjoint}" #: mocodo/convert/_uml.py:25 msgid "{incomplete, overlapping}" msgstr "{incomplet, non disjoint}" #: mocodo/convert/read_template.py:16 msgid "Circular inheritance in template \"{name}.yaml\" of \"{folder}." msgstr "Héritage circulaire dans le gabarit « {name}.yaml » de « {folder} »." #: mocodo/convert/read_template.py:19 msgid "Template \"{name}.yaml\" not found in \"{folder}\"." msgstr "Gabarit « {name}.yaml » introuvable dans « {folder} »." #: mocodo/convert/read_template.py:23 msgid "Unable to decode template \"{name}.yaml\" of \"{folder}\"." msgstr "Impossible de décoder le gabarit « {name}.yaml » de « {folder} »." #: mocodo/convert/read_template.py:26 msgid "Template \"{name}.yaml\" of \"{folder}\" contains a YAML object as value of key \"{key}\"." msgstr "Le gabarit « {name}.yaml » de « {folder} » contient un objet YAML comme valeur de la clé « {key} »." #: mocodo/convert/read_template.py:32 msgid "Template \"{name}.yaml\" of \"{folder}\" contains a YAML array as value of key \"{key}\" which does not contain only YAML objects." msgstr "Le gabarit « {name}.yaml » de « {folder} » contient un tableau YAML comme valeur de la clé « {key} » qui ne contient pas uniquement des objets YAML." #: mocodo/convert/read_template.py:34 msgid "Template \"{name}.yaml\" of \"{folder}\" contains a YAML array as value of key \"{key}\" which does not contain only objects having an \"order\" key." msgstr "Le gabarit « {name}.yaml » de « {folder} » contient un tableau YAML comme valeur de la clé « {key} » qui ne contient pas uniquement des objets ayant une clé « order »." #: mocodo/convert/read_template.py:37 msgid "Template \"{name}.yaml\" of \"{folder}\" contains a YAML array as value of key \"{key}\" where the \"order\" key is not associated to a number." msgstr "Le gabarit « {name}.yaml » de « {folder} » contient un tableau YAML comme valeur de la clé « {key} » où la clé « order » n'est pas associée à un nombre." #: mocodo/convert/read_template.py:39 msgid "Template \"{name}.yaml\" of \"{folder}\" contains a YAML array as value of key \"{key}\" where the \"order\" keys are not sorted in ascending order." msgstr "Le gabarit « {name}.yaml » de « {folder} » contient un tableau YAML comme valeur de la clé « {key} » où les clés « order » ne sont pas triées par ordre croissant." #: mocodo/convert/relations.py:121 msgid "Cannot compile the regular expression \"{regex}\" or the remplacement string \"{replace}\" in a relation template producing \"*{stem_suffix}.{extension}\" files." msgstr "Impossible de compiler l'expression régulière « {regex} » ou la chaîne de remplacement « {replace} » dans un gabarit de relation produisant des fichiers « *{stem_suffix}.{extension} »." #: mocodo/convert/relations.py:244 msgid "Reciprocal relative identification around {association}." msgstr "Identification relative réciproque autour de {association}." #: mocodo/convert/relations.py:325 msgid "A weak entity (here, {entity}) cannot be strengthened by itself." msgstr "Une entité faible (ici, {entity}) ne peut pas être renforcée par elle-même." #: mocodo/convert/relations.py:328 msgid "Cycle of weak entities in {entities}." msgstr "Cycle d'entités faibles dans {entities}." #: mocodo/convert/relations.py:339 msgid "Totality (/T\\ or /XT\\) is mandatory for \"=>\" inheritance of parent \"{name}\"." msgstr "La totalité (/T\\ ou /XT\\) est obligatoire pour l'héritage « => » de l'entité-mère « {name} »." #: mocodo/convert/relations.py:600 msgid "is {name}" msgstr "est {name}" #: mocodo/diagram_link.py:11 mocodo/leg.py:443 msgid "Attribute \"{attribute}\" in entity \"{entity_1}\" references an unknown entity \"{entity_2}\"." msgstr "L'attribut « {attribute} » de l'entité « {entity_1} » fait référence à une entité « {entity_2} » inconnue." #: mocodo/diagram_link.py:17 mocodo/leg.py:449 msgid "Attribute \"{attribute_1}\" in entity \"{entity_1}\" references an unknown attribute \"{attribute_2}\" in entity \"{entity_2}\"." msgstr "L'attribute « {attribute_1} » de l'entité « {entity_1} » fait référence à un attribut « {attribute_2} » inconnu dans l'entité « {entity_2} »." #: mocodo/mcd.py:81 msgid "Duplicate association \"{name}\". If you want to make two associations appear with the same name, you must suffix it with a number." msgstr "Association « {name} » dupliquée. Si vous souhaitez faire apparaître deux associations sous le même nom, vous devez suffixer celui-ci avec un nombre." #: mocodo/mcd.py:87 msgid "Duplicate entity \"{name}\". If you want to make two entities appear with the same name, you must suffix it with a number." msgstr "Entité « {name} » dupliquée. Si vous souhaitez faire apparaître deux entités sous le même nom, vous devez suffixer celui-ci avec un nombre." #: mocodo/mcd.py:93 msgid "One entity and one association share the same name \"{name}\"." msgstr "Une entité et une association ont le même nom « {name} »." #: mocodo/mcd.py:97 msgid "The ERD \"{title}\" is empty." msgstr "Le MCD « {title} » est vide." #: mocodo/mcd.py:115 msgid "Association \"{association}\" linked to another association \"{entity}\"!" msgstr "Association « {association} » liée à une autre association « {entity} »." #: mocodo/mcd.py:117 msgid "Association \"{association}\" linked to an unknown entity \"{entity}\"!" msgstr "Association « {association} » liée à une entité inconnue « {entity} »." #: mocodo/mcd.py:124 msgid "Inheritance \"{inheritance}\" linked to an association \"{entity}\"!" msgstr "L'héritage « {inheritance} » est lié à une association « {entity} »." #: mocodo/mcd.py:126 msgid "Inheritance \"{inheritance}\" linked to an unknown entity \"{entity}\"!" msgstr "L'héritage « {inheritance} » est lié à une entité inconnue « {entity} »." #: mocodo/mcd.py:135 msgid "Constraint \"{constraint}\" linked to an unknown entity or association \"{box}\"!" msgstr "Une contrainte « {constraint} » est liée à une entité ou association inconnue « {box} »." #: mocodo/mcd.py:142 msgid "Constraint \"{constraint}\" aligned with an unknown entity or association \"{box}\"!" msgstr "Une contrainte « {constraint} » est alignée avec une entité ou association inconnue « {box} »." #: mocodo/mcd.py:175 msgid "The weak entity \"{entity}\" should have a discriminator." msgstr "L'entité faible « {entity} » devrait avoir un discriminateur." #: mocodo/mcd.py:399 msgid "Unable to reuse the geometry file \"{filename}\"." msgstr "Impossible de réutiliser le fichier de géométrie « {filename} »." #: mocodo/mcd.py:438 msgid "Unable to save geometry file \"{filename}\"." msgstr "Impossible de sauvegarder le fichier de géométrie « {filename} »." #: mocodo/mcd_to_svg.py:10 msgid "PNG and PDF generation requires cairosvg to be installed" msgstr "Générer un PNG ou un PDF requiert une installation fonctionnelle de CairoSVG." #: mocodo/mocodo_error.py:25 msgid "Invalid sub-argument: \"{subsubopt}={subsubarg}\"." msgstr "Sous-argument invalide : « {subsubopt}={subsubarg} »." #: mocodo/mocodo_error.py:28 msgid "Invalid sub-sub-option: \"{subsubopt}\"." msgstr "Sous-sous-option invalide : « {subsubopt} »." #: mocodo/mocodo_error.py:31 msgid "Unknown \"{opt}\" sub-option: \"{subopt}\"." msgstr "Sous-option « {opt} » inconnue : « {subopt} »." #: mocodo/rewrite/_drown.py:24 msgid "ENTITY" msgstr "ENTITÉ" #: mocodo/rewrite/_drown.py:25 msgid "ASSOC" msgstr "ASSOC" #: mocodo/rewrite/_drown.py:27 mocodo/rewrite/_grow.py:91 msgid "attr" msgstr "at" #: mocodo/rewrite/_drown.py:28 msgid "role" msgstr "rôle" #: mocodo/rewrite/_grow.py:88 msgid "Binary" msgstr "Binaire" #: mocodo/rewrite/_grow.py:88 msgid "Quaternary" msgstr "Quaternaire" #: mocodo/rewrite/_grow.py:88 msgid "Reflexive" msgstr "Réflexive" #: mocodo/rewrite/_grow.py:88 msgid "Ternary" msgstr "Ternaire" #: mocodo/rewrite/_grow.py:89 msgid "Entity" msgstr "Entité" #: mocodo/rewrite/_grow.py:90 msgid "Weak Entity" msgstr "Entité faible" #: mocodo/rewrite/_grow.py:92 msgid "id" msgstr "id" #: mocodo/rewrite/_grow.py:124 msgid "Cannot find a suitable combination of card schemes and arities." msgstr "Impossible de trouver une combinaison adéquate de schémas de cardinalités et d'arités." #: mocodo/rewrite/arrange_bb.py:111 msgid "Layout calculation time exceeded." msgstr "Temps de calcul du plongement dépassé." #: mocodo/rewrite/arrange_bb.py:234 msgid "Failed to calculate a non-constrained planar layout." msgstr "Impossible de calculer un plongement planaire." #: mocodo/rewrite/arrange_bb.py:236 msgid "Failed to calculate a planar layout satisfying the given constraint." msgstr "Impossible de calculer un plongement planaire satisfaisant la contrainte donnée." #: mocodo/rewrite/obfuscate.py:54 msgid "Obfuscation failed. Not enough substitution words in \"{filename}\"." msgstr "L'obfuscation a échoué. Pas assez de mots de substitution dans « {filename} »." #: mocodo/rewrite/op_tk.py:71 msgid "Operation \"{op_name}\" cannot be applied to \"{pre_token}\"." msgstr "L'opération « {op_name} » ne peut pas être appliquée à « {pre_token} »." #: mocodo/tools/parser_tools.py:11 msgid "Parsing error:\n{e}\n" msgstr "Erreur d'analyse syntaxique :\n{e}\n" #: mocodo/tools/parser_tools.py:22 mocodo/tools/parser_tools.py:24 msgid "{pin}\"{v}\" is not a valid line beginning." msgstr "{pin}« {v} » n'est pas un début de ligne valide." #: mocodo/tools/parser_tools.py:26 msgid "{pin}Malformed box name." msgstr "{pin}Nom de boîte mal formé." #: mocodo/tools/parser_tools.py:28 msgid "{pin}A valid box name starting a line must be followed by a colon or a comma." msgstr "{pin}Un nom de boîte valide en début de ligne doit être suivi d'un deux-points ou d'une virgule." #: mocodo/tools/parser_tools.py:30 msgid "{pin}Illegal comma after inheritance." msgstr "{pin}Virgule illégale après un héritage." #: mocodo/tools/parser_tools.py:32 mocodo/tools/parser_tools.py:34 #: mocodo/tools/parser_tools.py:36 msgid "{pin}Malformed cardinalities." msgstr "{pin}Cardinalités mal formées." #: mocodo/tools/parser_tools.py:38 msgid "{pin}An inheritance name must be \"\", \"X\", \"T\" or \"XT\"." msgstr "{pin}Un nom d'héritage doit être «  », « X », « T » ou « XT »." #: mocodo/tools/parser_tools.py:40 msgid "{pin}Only two coords are allowed." msgstr "{pin}Seulement deux coordonnées sont autorisées." #: mocodo/tools/parser_tools.py:42 msgid "{pin}An association leg cannot be empty." msgstr "{pin}Une patte d'association ne peut pas être vide." #: mocodo/tools/parser_tools.py:44 msgid "{pin}Expected a number or a box name." msgstr "{pin}Un nombre ou un nom de boîte est attendu." #: mocodo/tools/parser_tools.py:46 msgid "{pin}Only a box name is possible here." msgstr "{pin}Seul un nom de boîte est possible ici." #: mocodo/tools/parser_tools.py:48 msgid "{pin}Unclosed square bracket." msgstr "{pin}Crochet fermant manquant." #: mocodo/tools/parser_tools.py:52 msgid "{pin}A constraint name cannot contain more than three characters." msgstr "{pin}Un nom de contrainte ne peut contenir plus de trois caractères." #: mocodo/tools/parser_tools.py:54 msgid "{pin}Expected a box name or a constraint leg." msgstr "{pin}Un nom de boîte ou une patte de contrainte est attendu." #: mocodo/tools/parser_tools.py:56 msgid "{pin}Illegal character after a constraint name." msgstr "{pin}Caractère illégal après un nom de contrainte." #: mocodo/tools/parser_tools.py:58 msgid "{pin}A parent name must be followed by an inheritance arrow among \"<=\", \"<-\", \"->\", \"=>\"." msgstr "{pin}Un nom de parent doit être suivi d'une flèche d'héritage parmi « <= », « <- », « -> », « => »." #: mocodo/tools/parser_tools.py:60 msgid "{pin}Please change the old foreign key syntax (\"->\") by the new one (\">\")." msgstr "{pin}Veuillez changer l'ancienne syntaxe de clé étrangère (« -> ») par la nouvelle (« > »)." #: mocodo/tools/parser_tools.py:62 msgid "{pin}The constraint targets must be comma-separated." msgstr "{pin}Les cibles de la contrainte doivent être séparées par des virgules." #: mocodo/tools/parser_tools.py:64 mocodo/tools/parser_tools.py:66 msgid "{pin}An attribute label cannot start with \"{v[1]!r}\"." msgstr "{pin}Un nom d'attribut ne peut pas commencer par « {v[1]!r} »." #: mocodo/tools/parser_tools.py:68 msgid "{pin}An attribute label cannot contain \"{v}\"." msgstr "{pin}Un nom d'attribut ne peut pas contenir « {v} »." #: mocodo/tools/parser_tools.py:70 mocodo/tools/parser_tools.py:72 msgid "{pin}An attribute starting with \"#\" must contain two \">\"." msgstr "{pin}Vous semblez essayer de spécifier une clé étrangère. Au niveau du MCD (modèle conceptuel de données), ce sont les associations qui répondent à ce besoin. Les clés étrangères n'apparaîtront qu'après passage au relationnel, dans le MLD (modèle logique de données). À ce niveau, elles devront suivre la syntaxe : « #clé étrangère > table d'origine > clé primaire »." #: mocodo/tools/parser_tools.py:74 msgid "{pin}Expected an entity name." msgstr "{pin}Un nom d'entité est attendu." #: mocodo/tools/parser_tools.py:76 msgid "{pin}A box name cannot contain \"{v}\"." msgstr "{pin}Un nom de boîte ne peut pas contenir « {v} »." #: mocodo/tools/parser_tools.py:78 msgid "{pin}Expected a comma." msgstr "{pin}Une virgule est attendue." #: mocodo/tools/parser_tools.py:80 msgid "{pin}Malformed number." msgstr "{pin}Nombre mal formé." #: mocodo/tools/parser_tools.py:82 msgid "{pin}More than two coordinates." msgstr "{pin}Plus de deux coordonnées." #: mocodo/tools/parser_tools.py:84 msgid "{pin}An attribute label cannot have more than one optionality marker." msgstr "{pin}Un nom d'attribut ne peut pas avoir plus d'un marqueur d'optionalité." #: mocodo/tools/parser_tools.py:85 msgid "{pin}Token \"{t}\" encountered. Expected tokens: {expected}." msgstr "{pin}Token « {t} » rencontré. Tokens attendus : {expected}." ================================================ FILE: mocodo/resources/lorem/disparition.txt ================================================ aaaaaaah abaissa abandon abandonna abandonnai abandonnait abandonnant abandonnas abandons abasourdi abasourdir abasourdis abasourdissants abat abattait abattant abattis abattit abattoir abattra abattrait abattu abattus abbou abdallah abdication abdou abdul abhorrait abhorration abjuration abjurons ablation ablution ablutions aboli abolir abolira abolirait abolis abolissait abolissant abolit abolition abomination abominations abonda abondant abondants abord aborda abordai abordait abordant abords abouchant about abouti aboutir aboutirai aboutirait aboutissait aboutissants aboutit aboya abraca abracadabrant abraham abrasif abrasion abri abricot abritai abritait abrupt abruti abrutira abrutissant abrutit abscission abscons absolu absolution absolvant absorba absorbait absorbant abstint absurdo abus abusait abymant abyssal abyssin abyssum abyssus abâtardirai abîma abîmant acabit acacia acacias acajou acapulco accabla accablait accablant accablants accalmit acclama acclamation acclimata acclimatation accola accommodait accompagna accompagnait accompagnant accompli accomplir accomplira accomplirai accomplis accomplissait accomplissant accomplit accord accordait accordant accords accort accosta accostant accoucha accouchait accouda accoudant account accoupla accourais accourait accourir accourons accourrai accourrait accours accourt accouru accourus accourut accourût accroc accrochait accrochant accrocs accroupi accroupir accroupis accroupissant accroupit accu accumulait accumulation accusa accusait accusant accusatif accusation achab acharna acharnai acharnait acharnant achat aconit acoquina acqua acquis acquisition acquit acquitta acromion actif action actions activation actor actualisation adagio adam adamantin adaptait adaptation addition adduction adjoint adjoints adjudant adjuvants administra administrait administratif administration admira admirai admirais admirait admirant admiration admis admission admit admonition adolf adonaï adonis adonisant adonna adopta adoptait adoptif adoption adorait adorant adouba adoubla adoucir adoucira adoucirait adoucissait adoucissant adoucit adrar adrian adrianus adroit adulait advint affabulation affaibli affaiblir affaiblissait affaiblit affaira affairai affairait affairant affaissa affaissait affaissant affala affalant affamant affichant affichât affinait affirma affirmait affirmant affirmatif affirmation affirmât affliction afflua afflux affola affolait affolant affolants affolons affranchi affranchir affranchis affranchissant affront affrontait affrontant affronts affublait affublant affût affûta aficionado afin afraid africain africains afro agadir again agami agaçait agaçant agaçants agglutina agglutinatif aggrava aggravant agil agincourt agir agira agirai agirait agissais agissait agissant agit agita agitait agitant agitation agonir agonisa agonisai agonisait agonisant agostino agouti agrafait agrandi agrandir agrandira agricolas agrippai agrippait agrippant agrippants ahan ahanait ahanant ahiyohu ahmad ahuri ahurir ahuris ahurissant ahurissants ahurit aida aidant aiglon aignan aigri aigrir aigu aiguail aiguilla aiguillon aiguillons aiguisas aiguisoir aigus ailippopolis aillant aima aimais aimait aimant aimions aimons ainsi airain airlington airostat airs ajaccio ajourant ajours ajout ajouta ajoutai ajoutait ajoutant ajoutons ajouts ajustant akvavit alain alambic alangui alanguir alanguis alanguissait alanguissant alanguit alaric alarma alarmait alarmant alarmants alas alaska albanais albatros albi albin albinia albinoni albinos albion albugo album alcala alcalin alcool alfa alhambra alias alibi alibis alignant alita alitait alla allah allai allais allait allant allas allia alliant alligator allions allobarbital allons alloua alluma allumai allumait allumoir allusion allusions alluvion alluvions allât allô alma almanach almanachs alors alourdissait aloyau aloysius alpha alpins alto altra aluminium alunir amadou amadouant amaigrit amanda amant amants amaryllis amas amassa amassait amassant amati amaury ambiant ambigu ambigus ambition ambulant amical amicaux amici amidon aminci amincis amincissant amiral amis ammoniac amoindri amoindrir amoindrissait amoindrit amolli amollir amollissant amontillado amortissait amorçoir amour amouracha amours amphigouri amphitryon amplifiait amplification amputation amusait amusant amusants analogaux analysa analysait analysant analysons ananas anar anars anas anastasia anchilops anchois andiamo andilly anglais anglicans anglicisation anglisch anglo angoissa angoissait angoissant angoissants angora angström animait animal animaux animula anis anjou ankara ankarais anna annonça annonçait annonçant annonçât annotations annulant annulation anobli anoblir anoblit anodin anormal anormaux anschauung anschluss antan anthrax anti anticatarrhal anticipation anticipons anticlimax antico antidictatoriaux antinous anton antony antrim anudissant anything anônumos août apaisa apaisait apaisant apitoya apitoyant aplani aplanir aplanira aplaniront aplatir aplatis aplatissait aplatissant aplatit aplomb apocalypsis apollon apostat apostolorum apostropha apostrophait apparaissait apparaissant apparat apparaux apparaît apparaîtra apparaîtrai apparaîtrait appariait appariant apparition appartint apparut appauvris applaudi applaudir applaudirais applaudis applaudissais applaudit application appliqua appliquait appliquant apport apporta apportai apportait apportant apportas apports apposai apposition appris apprit apprivoisa approbation approcha approchai approchait approchant approchons approfondi approfondir approfondirons approfondissait approfondissant approuva approvisionnait approximatif approximatifs approximation appui appuya appuyait appuyant appât aquarium aquila aquilin aquilon aquilons arabisant arabisants arago aragon aramis arbin arcan archaïsant archi archibattit archibattu archiduc archifaux archimort archiviaux arcimboldo arcs ardu ardus argon argoud argousin argousins argua argus aria ariana arias arius arlington arma armagnac armait armand armons armorial armstrong arnaud arouan arousal arracha arrachait arrachant arrachons arras arriva arrivai arrivais arrivait arrivant arrivions arrogant arroi arrondi arrondir arrondissant arrosa arrosant arsonval artagnan arthur artichauts articulait articulant articulation articulations artisan artison arums ascot asimodo aska asphyxia asphyxiait asphyxiant asphyxiation aspic aspidistras aspira aspirai aspirant aspirants aspirons assagir assaillait assaillant assailli assaillir assaillira assaillit assainir assainirait assassin assassina assassinai assassinait assassinat assassinats assassins assaut assauts assimilait assimilant assimilation assis assista assistai assistais assistait assistanat assistant assistants assistas assit associa associait association associations assombri assombrir assombrissait assombrissant assombrit assomma assommait assommoir assorti assortir assortis assortissant assortit assoupi assoupir assoupirais assoupissait assoupit assourdir assourdis assourdissant assouvi assouvir assouvira assouviras assouvissait assouvit assura assurait assurant assurbanipal astaroth astic astral atarax ataturk atchoum athos atlas atoll atonal atrium atropos attabla attablant attacha attachait attachant attachât attaqua attaquai attaquait attaquant attardait attifant attifiaux attila attira attirail attirait attirant attisoir attraction attractions attrait attrapa attrapait attribua attribuait attribut attribution attrista attristait attristant attrition aubigny aubour aubusson auctor aucun aucuns audit auditif auditifs audition audruicq augsbourg augurait augural augurants augustin augustus aujourd aulnay auparavant aupick aura aurai aurais aurait auras auric aurillac aurions auroch aurochs aurons auront aurora auschwitz ausculta auscultation aussi aussitôt austro autan autant auto autobiographiait autobus autocar autochtons autocollant autocongratulait autojustifia autojustifiait automancipation automatisation automnal autonomat autonourrissant autoportrait autopropulsant autopsia autorisa autorisait autorisant autorisation autos autotrucida autour autrui avachi avachirait avachirons avachis avachissait avachit avais avait avala avalait avalant avancions avant avança avançai avançait avançant avaro avaros avatar avatars aviation avignon avilir avilis avilissait avilissant avilissants avillons avion avions aviron avis avisa avisaga avisai avisait avisant avision avivants avocal avocat avocats avogadro avoir avoisinait avons avorton avortons avoua avouait avouant avouas avril axial axiomatisation axolotls ayant ayants ayons azimuth azimuths azincourt aziz azor azoukh azur aïachi aïda baba babiroussa babouin baccara bach bachot back bacon bacs badaud badauds badin badoit baffrait bafouait bafouilla bafouillant bafouillis bahia bahut baignait baignant bail bain baisa baisant baisotant bakchich baladins balafrait balai balalaïka balam balandras balança balançait balançant balbutia balbutiait balbutiant balcon balcons baldaccini baldaquin balibar balkan balkanais balkans ballon ballons ballot balourd balthazar balzac balzar bambin bambinard bambins bambochait bambou banal banals banania banat banc bancal banco band bandit bandits bang banjo banjos bank banni bannir bannirait bannissant bantou baobab baobabs baour baptismaux baragouin barakalla baralipton barbara barbarisation barbatif barbillons barbon barbotait barbu barclay bardait barguigna baril barka barlach barmaid barman barnabooth baron barra barrait barrant barri barricadait bars baruch baryton bascula basculais basculait basculant basin bassin bassins bastia bastiannais bastion bataillon bataillons bath batifolait batifolant battait battant battants battit battrons battu battus baudoin baudruchon bavait bavard bavarda bavotant bazooka bhuj biafra biais bianca bianco bibs bichon bifur bigot bijou bijoux bilan bildungsroman billard billards billot biniou biplan biribi biscornu biscornus bismarck bison bisous bissac bissant bistouri bistrot bistrots bitch bivouaquait black blafard blalard blanc blanca blanchi blanchir blanchirait blanchis blanchissait blanchissant blanchissants blanchit blanchot blancs blandula blason blasonnait blasons bloc bloch block blockhaus blond blondi blondin bloody bloquant blotti blottir blottis blouson blousons blutoir bobinard bobo bocal bock bohm bohu bois boisson boissons boit bombant bombançait bombarda bombinait bona bonbon bonbons bond bondi bondir bondis bondissait bondissant bondit bonifacio bonjour bonnir bons book bootk booz bord bordj bords borg borgia borgias borkou born borniol borniols bororo bosco boss bossu boston botillons bottin bottins boubou bouc boucan boucla bouclât bouda bouddha boudin boudins boudiou boudoir bouffant bouffi bouffon bouffons bougnat bougnats bougon boui bouillait bouillant bouilli bouillir bouillon bouillonnant bouillons boukha boul boulingrins boulon boulons boulot boulottait boult bouquin bouquins bour bourbaki bourbon bourdalous bourdon bourdonnait bourdonnant bourdons bourg bourgois bourgs bourguiba bourguignon bourlinguant bourricot bourru bousculant bousillant bousins bout boutant boutants bouton boutons boutz bouvillon boxon boyard boyau boyaux brabançon brabham brahma brahms brailla braillait brain brama brancha brandi brandir brandissait brandissant brandissons brandit brando brandon brandons branlait branlant branlants branly bras brassard brassicourt bravant bravissimo bravo bravos brazza bricola bricolait bricolant brigand brigands brigantin brillait brillant brillat brimbala brimborion brimborions brin brindisi brins brio bris brisa brisant brisants bristol britannial broc broca brocard brocards broda brodait broquarts brou brouhaha brouillait brouillamini brouillard brouillards brouillon brouillons brout broutait broyait broyant brrr bruit bruits brulât brulôt brun brunissoir brut brutal bruyant bruyants brûla brûlait brûlant brûlants brûlis brûlot bubons bubula buccin bucco buck bugatti building buis buisson buissonnant buissons bull bungalow bunny burgraviat burin burnous burton bustos buta butait butin butoir butor buts buvait buvant buvons byron bâillant bâillon bâtard bâtards bâti bâtir bâtissant bâtit bâton bâtons böhm cabas cabin cabochon cabotait caboulot caboulots cabri caca cacha cachait cachalot cachalots cachant caciquant cadastral cadran cadratin caduc cafard cafards cagibi cagna cagoulard caha cahin caillot caillou cailloutis cailloux caius calcinant calcul calcula calculs caldarium calfats calicots califourchon caligula calissons calmar calmi calmir calmons calot calquait calus calvados calypso camail camard cambrai camion camp campa campagnard campagnol campagnols camping canada canal canalisait canalisation canard canards canasson canaux candis canif canin canon canons canot canots canta cantal cantaral canticorum canticum canton cantons cantorum canulard canyon caoutchouc caparaçon capharnaüm capisco capit capital capitalisation capitaux capitons capitula caporal capot capouan capricants caps captif captivant captura capuccino capuchons capucin carabins caracas caraco carafon carat carats caravaning caravanings carburant carcan carcopino cardan cardin cardinal cardinaux cardium cardons cargos cari caribou carignano carillon carissima carl carlin carmin carmins carnation carnavals caroli caroling carolus carpillon carquois carri carta carton cartons caruso cary casablanca casanova casbah casimir casino casoar cass cassa cassagnac cassant cassatas cassis casting castor castorp castration casus catamaran catarina catarrhait catarrhal catastropha catgut catimini catogans cattaro caulaincourt causa causait causant causons caviar cavour caïds caïman caïn chacal chacun chafaudant chafouins chagrin chagrina chagrinait chagrins chahut chair chairman chairs chaland chalazions chalgrin chaman chamans chambarda chamboulait chamois champ champi champignon champignons champion championnat champollion champs chandail chanson chansons chant chanta chantait chantant chantillon chantilly chants chantât chançard chaos chaouch chap chaqu char charabia charançon charbon charbons chardons chariff chariot charivari charlatan charlot charmant charmants charnu charognard charognards charria charrons chas chassa chassait chassant chat chatham chaton chatouillis chatoyant chats chauchat chaud chaudron chauds chaulin chaussons chauvin chaux chaînon chaînons chaïr chiala chiapas chiasma chibouk chic chicago chichi chichis chico chiffon chiffonnants chiffons chiffrait chilam child chinatown chinois chinook chiogga chipolata chivas chloroformant chloroformisation choc chocolat chocolats chocs choir choisi choisir choisis choisissait choisissant choisit choit choix chomsky choral chosification chotts chou chouchou choux christ christian chromos chrono chronos chuchota chuchotant chuchotis chuinta chuintant chuintis churchill chut chutait chutant châlit châssis châtain chômait ciao cicatrisation cigarillo cigarillos cingla cinglait cinglant cinglons cinq circinal circonlocutions circonstancia circonvallation circonvolution circuit circulait circulant circumnavigation cirro citadin citait citant citation citron city civil civilisation civium clac clafoutis clair clairon claironna claironnant clairvoyant clam clama clamait clan clapotis claqua claquant clark classant clawdia clic clifford clignancourt climat climats clin clochard clochards clodion cloison clopant clopin clos cloua clouait clouant clous clown clowns cloîtrai cloîtrait club cnrs coach coachs coagulait coagulant coalitif cobalt cochin cochon cocktail cocktails coco cocopinar cocos coda codifia codifiant cogito cogna coin coins colback colcotar coligram colimaçon colin colis colla collaborait collaboration collait collant collapsus collation collations collimation collision collodion collusion colombin colonial colonisation colorado colossal colportait cols columbarium columbia coma combat combattant combattu combinaison combinaisons combinant comblant combustion commanda commandait commandant commando commandos commis commissariat commissariats commission commotion commun communal communicants communication communications communion communions communs commutation comodoro compact compagnon compagnons compara comparait compas compassion compatir compatissant compatissons compilant compilation compilations complaisant complication complications complot complotait complots componction comportait comportant composa composition comprimant compris comprit compromis compromission compromit compta comptabilisant comptabilisation comptait comptant comptoir comput comtat conakry conciliant concis conclu concluait concluant conclusion conclut concoction concordants concourir concourront concours concourt concouru concussion condamna condamnait condamnant condamnation condition condor conduira conduiras conduirons conduisait conduisant conduisit conduisît conduit conduits confia confiait confiant configuration confin confins confirma confirmant confirmation confit confits conflagra conflagrant conflagration conflit conflits confluant confond confondant confondants confondu confondus confort confrontation confus confusion conginatal conglobation conglutinatif conjoint conjoints conjonction conjugal conjungo conjungos connais connaissait connaissant connaissons connard connaît connaîtra connaîtrais connaîtras connaîtrons connaîtront connu connus connut conquis consacra consanguin consanguins conscrit conscrits consigna consignation consistait consistorial consola consolant consolation consomma consommation consomption conson consortium conspirait conspiration conspuant constant constantin constants constata constatant constatation constats constitua constituait constituant constituants constitution constriction constrictor construction construisant construisit construit consul consulat consulats consulta consultai consultait consultant consultation contact contacta contactant contacts contant conti contigu continants continu continua continuait continuant continuation continuum contondant contour contournant contours contradiction contradictions contraignait contraignant contraint contraria contrariant contrariis contrario contrat contribuait contribution contristant contrit contrition contrôla contrôlait contrôlant contumax convaincant convaincu convainquit conviction convint convocation convoi convois convol convola convoqua convulsif convulsions conçoit conçu conçut coordonnons copains copur coqs coquin corail coran corbillard corbillards corbin corday cordial cordon cordons corfou corindon cormorans corna cornac cornalin corniaud cornichons corona coronat corporation corps corpus corridor corrobora corrompu corrompus corruption cors cortical cortico coruscants coruscation coryza cosa cosmic cosmos cosy cotation cotillon coton cotonnant cotons cotât couac couard couards coucha couchant couci coucou couguars couillon coula coulait coulant coulis couloir count country coup coupa coups cour courait courant courir courlis cournot courons courroux cours court courtaud courtauld courtois couru courus courut couscous cousin cousins cousit coussin coussins coustil coustou cousu couvait couvant couvrait couvrit couça coïncidant coït cracha crachant crachin crachotant crack crado craignait craignant craignit craignons craignît craint craintif cramant cramoisi cramoisis crampon cramponnant crapaud crapaudin crapauds crapouillot craquait crawford crayon crayonnant crazy cria criaillant criait criant criard criblait criblant cric crin crinolins cris crispait crispant crissant cristal cristallin critiquait critiquant crocs crocus croirait croirons croiront crois croisa croisait croisillon croisillons croissant croit croix croquant croquants croquis cross crossland croulant croup croupion croupir croupissait croupissant croupit croustillant croustillants croyais croyait croyant croyons croît croûton crubovin cruchon crucial crucifia crucifiait crucifix crut crypto cubain cuir cuisait cuisant cuisants cuisinait cuisit cuissard cuisson cuit culbutait culminant culmination culpabilisant culs cumin cumulus cunha cupidon curait curantur curarisant curatif curaçao curling curnonsky curricula curriculum customary cyclospiral cycloïdal cygnal cyprin cyprins cyrano cyrards câbla câlin câlinou côtissant côtoyait dabrant dactylo dactylos dada dadais dadas daggoo dahu daily daim daims damasquin damasquins damnant damnation damni dams dancing dancings danican danois dans dansa dansait danton danzig darby dard dardait dardant dards darmon darwinial darwisch dassault datait datant daunou dauphin dauphinat dauphinois dauphins dauzat davidoff davis davos dayan days diagnostic diamant diamants diamond diavolo dick dicta dictait dictatorial diction dicton dictons didon didot diffus diffusait diffusant diffusion dilapidation dilatation diminua diminutif dinard dindon dingo diongor dior dira dirait dirimant dirons diront disait disant discontinu discordant discourait discourir discours discouru discriminant discrimination discussion discutant disions disjoint disloquât disons disparaissait disparaissant disparaît disparaîtrons disparition disparu disparus disparut disposa disposait disposant disposas dispositif dispositifs disposition disposât disproportion disputant dissimula dissimulait dissimulant dissipant dissocia dissolus dissoluto dissous distant distillait distinct distinctif distinction distincts distingua distinguait distinguo distraction distrait distribution diurnal diva divagant divagation divaguait divaguant divan divans divin divinisant divins divisa division divisions divulgation divulguait diwan dixit djouf djougachvili docks doctorat doctrinal dodo dodonial dodu doigt doigts dois doit doktor dollar dollars dolly domaicq domanial dominait domination dominion dominus donald donation donc donjon donjons donna donnait donnant donnons donnât dont dori dorian doris dorlotant dormait dormant dormi dormir dormit dormitif dort dortoir dosant dossard dotait douait douaumont doubla doublon doublons doubrovnik doucin dougga dough douglas dourdan douta doutait doux down dracon dragon dragons dragua dramma drancy drap draps drogman droit droits drossant drouot drumont dubitatif dubitation dubitations dublin ducal ducaton ducats dudit dugland dugong dugongs dulcifiant dumping dumpty dunhill dupin duplicata duplication dura durait durandal durant durazzo durci durcissant durcit during duroc dutronc dvorak dynamo dîna dînait dînons fabius fabliau fabrication fabriqua fabriquait facs faction facto factotum factotums factum facultatif fada fagots faiblard faibli faiblir faiblissant faiblit failli faillir faillis faillit faim fair fairfax fais faisait faisant faisions faisons fait faits faix fakir falbalas fallacial fallait falloir fallu fallut falot falzar familial faminard faminards famulus fanal fanas fanatisait fanaux fanfaron fangio fanion fans fantassin fantassins fantômas faon faraud farci farcir farcis farfouilla farfouillant farouk fascina fascinait fascinant fascinants fascination fatal fatalitas fathom fatigant fatigua fatras fatum faubourg faubourgs fauchard faucillon faucon faudra faudrait faufilant faustillon faustina faustus faut fautif fautifs faux favori favoris favorisât façon façonna façonnait façons fiasco fiat ficha fichu fiction fidji fifty figaro figl fignola fignolant figon figurait figurant figuration figuris fila filait filant filial filiation filiations filin fill film films filon filou fils final finaud fini finir finira finirait finiras finis finissait finissant finissons finit finitif finlandais fins fisch fissah fission fissurant fiston fixa fixait fixant fixatif fixation fjord flacon flacons flagrant flair flairant flamand flamba flambait flamboyait flamboyant flanc flancs flanqua flanquait flanquant flançois flash flask flatta flattait flic flics flinguait flip flips flocons floctuation floraux florin florissant flot flots flotta flottait flottant flou flour fluctuation fluvial flux fluxion fluxionnait flânant foccard foch foin fois foison folds folichonnait folio folios folk folklorain folkoch folâtrait fonction fonctionna fonctionnait fond fonda fondait fondant fondants fondation fondations fondouk fondra fondrait fonds fondu fondus font fonts fonça fonçait fonçant football forain forains forban forclot ford forfait forfaits forhuir forma format formation formol formulant formulation forpaysant fort fortifiait fortifiant fortification fortin fortissimo forts fortuit fortunatos força fouaillant fouchtra foudroyant foui fouilla fouillait fouillant foulard fouloir foundation four fourbi fourbis fourbu fourbus fourgonnas fourgons fourguait fourni fournil fournir fournirait fourniront fournissait fournit fourra fourrant fourvoyant fous foutons foutrait foutu foutus frac fracas fracassa fracassant fractions fractura fragrant frais fraisil franc francfort franchi franchir franchirait franchis franchissant franchit franciscain franciscains francisco francs frangin frangination frangins franglais frank franz français françois frappa frappait frappant fraîchir fraîchira friand friands fricatif fricot fricsay friction frigo frigorifiant frimas fringant fripon frisait frisson frissonna frissonnait frissonnant frissons frit froid froidi froids froissa from frondaison frondaisons front frontal fronton fronts fronça fronçant frottait frottis froufroutait froufroutants fructifiant fructification frugal fruit fruits frusquin frustrait frustration frôla frôlait frôlant fugitif fugitifs fuir fuis fuit fulgurant fulguration full fulmicoton fulmina fulminant fulminants fulmination fuma fumait fumant fumants fumigation fumoir fumons fungus furibard furibond furin furlongs furtif fury fusa fusait fusant fusil fusils futur futurs fuyais fuyait fuyant fuyants fuyard fuyons gabarit gabo gabonais gadin gadsby gaffiot gafilt gaga gagna gagnai gagnait gagnant gagnants gagnons gaillard gain gainait gala galant galgala galimatias galion gallimard gallons galonnait galons galop galopait galopin galopins galuchat galvanisa galvanisant gambadant gamin gamins gamma gammon ganaith gandin gandourah gang ganglion gant gants gara garamond garanti garantir garantira garantirait garantirons garantissait garantissant garantit gard gardai gardais gardait gardant gargan gargantua gargouillis garni garnis garnison garros garrot gars garçon garçons gaston gatchi gaulant gaulois gavial gavoty gayac gazait gazon gazons gazouillis gaînait georges ghost ghôlan gibbons gibus gicla gifla gigli gigot gigotait gimond gina giocoso giovanni girardin girardon giraudoux girl giron gisait gisant giscard gitan glacial glaciaux glacis glanait gland glapi glapir glapissait glapissant glapit glas glass glaça glaçait glaçons glissa glissait glissant global gloria glorifiant glossanthrax gloussa gloutir glouton gloutonnant glow gluant glupf gnaptor gnard gnards gobbi godard godillot godrons gogaillait gogni golgotha golgothas goliath gombrich gombrowicz gonfalon gonfanon gonflait gonflant gong gonisait good gordon gorgonzola goriot gossipy gotha gottschalk goudron goujat goulot goulots goum goupillon goura gourbi gourd gourdin gourds gourmand gourou gouzis govinda goût goûta goûtait goûtons goûts grabat gracchus gradin gradins graduation gradus graf graffiti graffitial grain grains graissait gram grammatical grammaticaux granada grand grandi grandissait grandissant grandit grands granit grant granulait grapillon gratification gratin gratis grattant grattoir gratuit gravait gravant gravat gravats gravillon gravir gravissant gray gribaldi gribouillis grido griffonnant griffus grignotait grilla grimant grimaud grimaça grimaçait grimaçant grimpa grimpait grimpant grinling grinçait grippa gris grisi grisonnant grisou grivois grog groggy grogna grognant grognon groin gronda grondant groom grooms gros grossi grossir grossissant grosso groucho grouillant group groupait groupant guadalajara guaira guida guido guidon guignon guillain guillotin guillotina guimbard guindant guingois guirlands gunfight gunman gymnasium gyroscopal gyrus gîta habana habanas habilla habillait habillant habillas habillons habillât habit habita habitais habitait habitant habitants habitation habits habitua habsbourg hachant hachoir hadzushi hagard hagards haggada haidarabad haig haillon hainaut hainisch hajdu halifax hall hallali hallucinant hallucination hallucinations halo halvah hamac hamada hambourg hamid hamilton hammam hanap hanaps handicap hangar hangars hanouka hanoï hans hantait hantant happa hara harakiri harangua haranguant haras harassant harassants hardi hardit hardouin hari haricots harmonicas harmonisait harnois haro harpin harpo harpon harpons harry hartmann harwich hasard hasarda hasardant haschana haschich hassan haus haussait haut hautain hautbois hauts hawaii haydn hayworth haïkaï haïr haïssait haïssant hibou hidalgo hilarion himalaya hippy hirudination hispano hissa hissant historia histrion histrions hittorf hodja hoffmann hoggar hold holidays holland hollandais hollywood holà homard homatropini homicidal homini homo homunculus hongrois honni honnissait honnissant honolulu honora honorait honorant honorio honoris horatio horion horizon horizontal horizontaux hormis horn horrifiait horrifiant horrifiants horripilant hors hosanna hospodar hospodars hourdis houri hourvari houspillant hoyau hrivnas hugo huilait huis huissait huit hull humain humains humant humidifiant humour humpty humus huntingdon hurla hurlai hurlait hurlant huron hurons hurrah hussard hussards hutin hydro hydrobromidum hydrocution hypnotisant hypocras hypocrisif hâtif hôpital hôpitaux ibant ibnadir ibrahim icoglan icoglans idiosunkrasis idiot idiots idolâtrait iftikhar igli ignition ignora ignorais ignorait ignorant ignorions ignorons ignorât iguidi ilitch illico illumina illuminant illumination illusion illusions illustra illustrations imagina imaginai imaginait imaginant imaginatif imagination imaginations imago iman imbibait imbitation imbrication imbriquant imbroglio imbu imita imitait imitant imitation immaculant immaculation immatriculation immatriculations immisçait immisçant immobilisa immobilisait immola immoral immoraux immortalisa immortalisait immortalisant immortalisation immuno impact impair impairs imparfait imparfaits imparti impartial impartir implanta implantait implantant impliquait impliquant implora implorant implorants imploration implorations implosion impoli importait important importants importation importations importun imposa imposait imposant imposât imprima imprimait improbus improductif improductifs impromptu improuva improvisa improvisait improvisant impuissant impulsion impulsions impur impurs inadaptation inamical inanimait inanition inaugura inaugurai inaugurait inaugural inaugurant inauguration inca incapsulant incarnant incarnat incarnation inch incirconcis incisant incisif incisifs incision incitait incitation incivils inclama inclina inclinai inclinait inclinant inclusion incognito incommodait incommunicatif inconfort incongru incongrus inconnu inconnus inconsistant inconsistants inconstant incorporait incorporant incrusta incrustais incrustait incrustant incrustation incrustations incubation inculpa inculqua inculquât incurvait incurvant indian indiavolato indic indicatif indication indications indics indigna indignait indignants indignation indigo indiqua indiquait indiquant indisposa indistinct indistinction individu individus indivis indivision indivulgation indous induction induisit industrialisation infailli infamant infamants infant infanticidal infantil infantin infants infarcti infarctus infiltrations infini infinis infirma inflammation influx informa informait informant information informations infortun infraction infranchi infrastructural infusant infusion ingrat ingrid inguinal ingurgita ingurgitation inhabillant inhala inhibant inhuma inhumain inhumait inhumation inhumons initia initial initiatif initiation initiaux initiât injonction injuria injuriants injustifiait innovant innovation innsbruck inoculation inondant inopinant inopportun inopportuns inoriginal inoubli inouï inouïs input inquisition inquisitions inquisitorial insatisfaction insatisfaisant insatisfait inscription inscriptions inscrit inscrits inscrivait inscrivant inscrivit insignifiant insignifiants insinuai insinuant insinuants insinuation insista insistai insistait insistant insolant insolation insouciant insoumission inspirait inspirant inspiration installa installait installant installation installons instant instants instar instaura instaurait instauration instigation instilla instinct instinctif instinctifs instincts instituant institut instomation instructif instruction instructions instruit insu insubordination insuffisant insuffisants insultant insultants insupport insus intact intima intimidant intimidation intitulant intoxication intrados intransitif intrigant intrigants intrigua intriguait intriguant introductif introduction introduisait introduisant introduisis introduisit introduisons introduit introïbo intrus intuitif intuition inunguis invaincu invasion invita invitai invitait invitant invitasti invitation invocat invocation invocations involutif involutifs invoquait ions ipso irai irais irait iran iranian iridium irions iris irisait irisant irisations irlandais iron ironisa ironisait irons iront iroquois irradiait irradiant irraouaddi irriguons irrita irritait irritant irritants irritation irroration irruption irwin isard isards ischion isidro islam islandais ismaïl isola isolais isolait isolant isolibration isonzo ispahan issoudun issu issus istanboul italy ithos itou iturbi iulius ivan ivoirin ixil izumi jabot jack jacob jadis jaguar jailli jaillir jaillira jaillissait jaillissant jaillit jakobson jalon jalons jalousait jaloux jamais jamaïcain jambart jambon japon japonais jardin jardins jargon jarjack jarnacais jarnicoton jasmin jason jauni java jawol jazz joan jobard john johnson joignait joignant joignit joignons joigny joint jointif joli jolis jonas jonchant jonction josiah joua jouait jouant joufflu joug jouir jouissant jouissants jouissif joujoux jouons jour journal journaux jours jouxtait jouxtant jouât jovial joyau joyaux juan juba jubilation judas judaïsant judith judo jugurtha juif juifs juin julliard julot junior junot jupin jupon jura jurait jurant juridiction juro juron jurons jury jusant jusqu just justaucorps justifia justifiait justifiant justification justifiât justin juxtaposition kaakil kadams kafka kakari kakis kaku kalmouk kalpasoutra kamtchatka kana kangourou kant kaolin kapok karajan karamazov kari karl kasbah katoun katun kawa khamsin khan kidnappa kidnapping kill kilo kilog kilogs kilos kimono king kippour kiri kirk kirsch klan klux knock knout know kobold korsakov kotor kouglof koûppodoutourams krabs krach kraft krat krips kronprinz kudan kulturkampf kunimoto kuraki kurus labial labor labour labourant labrador labyrinthal lacan lacandon lacanial lacrymal lacunal lads lady lagonda laid laids laissa laissait laissant laissons lait laiton lamantin lamantins lambris laminoir lampant lampion lamplight lancinant landaux languido languir languissait languissant languissants languit lanzmann lança lançai lançait lançant lanças lapida lapidait lapidification lapin lapins lapis lappa laps lapsus laquais laranda larbaud larbin larcin larcins lard lardait largactyl larigot larmoyait larmoyant larvati larynx lascar lascars lascif lassait lasso latakia latin latinum latirostris latour laudanum lava lavabo lavait lavis layon lazuli lazzi lazzis lham lhomond liaison liait libation libations libidinal libitum library licol licou lilas lilial limaçon limina limitait limitatif limitation limon limonial limons limours linda ling lingots linon lins lion lions lipp lippman lippmann lippu liquidation lirons lisait lisant lisons lits littoral livarot living livius livorno livra livraison livrait livrant livry llord lloyd local locarno location locaux loch loco locus locutions loggia loggias logis logogriphiant logos loin lointain lointains loir loisif loisir lolita lollobrigida lombric lombrics london long longai longchamp longs loop lord lorgnon lorgnons lormian lors lorsqu loti lotir lotis lotus loua louai louait louant loucha louchant louis loukhoum loup loups lourant lourd lourdaud lourdauds lourds loustic louvain louvoyant lovait lowry loyal loyaux lucchino luciano lucky luisant lulling lumbago lumignon lumignons lunaisons lunch lundi lupanar lupin lupus luron lustral luth lutin lutrin luttait luxation lyndon lynx lyon lyrical lâchant macaroni mach machin machinal machination machinations mackintosh macquart macula macy madras madrid madrigal madrigaux maffia mafflu magasin magasins magistral magistrat magistrats magistraux magma magnani magnat magni magnifiait magnificat magnus magot maharadjah maharadjahs mahdi mahmoud mahon maigri maigrichon maigrichonnant maigrichons maigrir maigrissait maigrit mail maillard maillon maillons maillot maillotin maillots main mains maint maints mais maison maisons major majorat maladif maladroit malandrin malandrins malaria malaxa malcolm maldiction malfaisant malfaisants malin mallarmus malo malsain malstrom mamamouchi maman mamans mammouth manant manatus manchon manchot manchots mandarin mandat mandatant mandou mandrin manhattan maniait maniant manipula manitou mann manoir manoirs manqua manquait manquant manquants mansard mantic manu manuscrit manuscrits maquignon maquignons maquilla maquis marabout marais marasquin marat maraudait marc marcassin marcassins marcha marchai marchait marchal marchand marchands marchant marchions marchons marcillac mardi margina marginal marginalia mari marigot marigots marin marina marino marins marivaudant marlo marmara marmiton marmonna marmonnait marmorial marmot marocain marocains maroni maroquin marquait marquant marquis marquisats marri marron mars marshall marsouins marsyas martial martiaux martin marx mary mascaron mascarons masculin masqua masquait masquant massa massacra massacrât massif massifs mastabas mastic mastiff mastiquait mastoc masulipatam mata match mathias maths mathurin matifou matignon matin matinal matins matis matois matraquant matriarcal matrification maubourg maudis maudissant maudit maudits maupassant mauriac maurocordata mauss mauvais maux mavrocordato mavrokhordatos maxima maximin maximum maximus maya mayo mazout mazurka maçon maçons maîtrisant maïs mccormick mcdonald mcluhan miam miaulait mich michard michi michigan micro micron microsillon microvision midas midi mignardant mignon mignons migrain migrations mijotait mijotant mijotions mikado milady milan milita militant militari milliard milliards million milord milords mimait mimant mini minimal minimum minium minois minuit minus miraculant mirliton mirobolant miroir miroirs mirror miss mission mistral mitan mitard mithridatisation mitraillait mitron mitrons mitrovitsa mitzvah miyura moab mobilisa mobilisation moby moccolo modifiant modification modillons modo modulait modulant modus mogol mohican moignon moins mois moisi moisir moisson moka moku mokulu molasson mollir mollo monacal mondain mondial mondiaux monica monobloc monopolisait monotonous mons mont monta montait montant montanus montra montrait montrant montrât monts montsouris moonlight mops moraillon moral moralisation morand mordant mordaunt mordicus mordiou mordorait mordra mordu morfil morfond morfondait morfondu moribond moribonds moritz morland morpion mort morti mortifiant mortification mortis morts moss motard motif motifs motiva motivai moto mots motu motus moucha moucharab mouchard mouchoir mouchoirs mouilla mouillait mouillant moula moulait moulin moulins moult moulu mountain mourait mourant mourir mouron mourra mourrir mourrons mourut moussaillon mousson moussu moustachu mouton moutons mouvant mozart much mucus mugir mugis mugissait mugit muids mulligan mulon mulot mulots multi multipliait multipliant multiplication mundi muni munich munichois municipal municipaux mural murant murky murmura murmurait murmurant murmuras murs muscadin muscas music musical musigny mussipontins mussolini must mustang mustapha musulman mutandis mutant mutation mutatis mutilait mutilant mutilation myocardiaux myosotis mâchicoulis mâchonnant mâchu mâcon mâts mûri mûrir mûrit mûrs nabab nabot nacarat nadir nagasaki nagis nagra nahum nain nains naissain naissait naissant naka nana nanan nancy nantir nantis nantuckais napalm nappa naquis naquit nard narguant narihira narquois narra narrait narrant narratif narration narvals nasillards naso natal natif nation national nato natura navaja navigua naviguant navrait navrant naxos nazi nazin naît naîtrait naîtront naïf niais niait niant nichan nichant nicias nicolas nictation nictitant nidoral nids nigaud night nimbait nimbo nimbostratus nimium ninas ninipotch niolo nippon nirvâna nivial noam nobody nocif nocifs nocta nocturnal nocturnis nodal nodus noguchi noir noirci noircir noircis noircissait noircissant noircit noirs nois noix nomadisant nombril nominatif nomination nomma nommait nommant nommons noms nonchalant nonchalants nonnain nonobstant noor nord nords norfolk norint normal normand normatif norodom noroît nostalgical nostra nota notait notarial notariaux notations nothing notions nouant nougats nounou nounous nourri nourrir nourris nourrissait nourrissant nourrisson nourrit nous novak noya noyait noyau noyauta noyaux noyon noyons nuit nuits nunc nuptial nutri nutritif nutrition nylon oakham oakwood oaristys oasis oaxaca objurgua objurguant obligation obligations oblong oblongs obnubila obnubilant obradovitch obscur obscurci obscurcir obscurcissait obscurcissant obscurcit obscuri obscurs obstina obstinait obstinant obstination obstruction obtins obtint obtus obus ocarina occasion occipital occiput occis occlusion occultait occultations occupa occupait occupant occupas occupation occupations occupons octant octavo octavos octogonal octroi octroyait octuor odilon odorant odorants odorat officiait offrait offrant offrir offrira offrirai offrirait offris offrit offrons offs offusqua offusquait offusquant ogdan ogival oglio oignon oignons oindra oirrait oisif oisifs oisillon oisillons oisir olfactif olga olifant omar ombilic ombilical ombilicaux ombrait omicron omis omission omissions omnia omnibus onassinck onction onctions ondoyons ondulant ondulation onyx opacifiait opacifiant opalin opalins ophtalmo opina opinai opinant opinion opium opopanax opoponax opossum oppidum opportun opposa opposait opposant opposition opprimait optalidon optat optatif optatifs optima optimal options optât opus oraison oraisons oral orang oratorio ordinator ordo ordonna ordonnai ordonnait ordonnant organdi organisa organisait organisant organisation organisations orgon oribus origan original originant originaux orignals orillons orion orlando orly ornait ornans ornant orpin orrouy orsay orthographiait ortolans osai osais osait osaka osant oscars oscillant oscillation oscillo oshi oskar oslo osman ossia ossian osso oswald othon otoshi ottaviani ottavio otto ottoman ouais ouatant oubli oublia oubliais oubliait oubliant oublias oublions oublis oudinot oudry oufik oufkir ouistiti oulianov oulipo ouragan ouragans ourdi ourdir ourdira ourdis ourdissant ourdissoir ourdit ours oursin oustachis outang outangs outil outils ouvoir ouvrait ouvrant ouvrir ouvrira ouvrirai ouvrirais ouvrirait ouvris ouvrit ouvroir ouvrons ouïr ouïs ouït ovation ovibos ovins ovoïdal oxford oxydation oyant oyonnax oyons ozaru oïdipos pacha paddock padron pagination pagus paillard paillasson pain pair pairs paix pakçon paladin paladins palais palan palanquin palatin palatinat palissant pallas palliatifs palpa palpation palpitait palpitant palpitations palustris pamir pampa panama panard panards paniqua paniquant pannoir panofsky panorama pans pansas pantalon pantalons pantois papa papal paparazzi papillon papillotait papon papou paprika papyrus paraboom paradait paradis paradoxal paradoxaux paraissais paraissait paraissant paramont parana parangon paraphait paraphant paraphrasa paraît paraîtra paraîtrait parc parcourait parcourant parcourir parcours parcourt parcouru parcourut pardon parfait parfaits parfit parfois parfum parfumait parfums pari paria paris parkinson parla parlait parlant parlas parlons parlât parmi parnassum parodi paroi paroissial paroli paros parrain parsifal part partait partant partants parti partial participa participait participant participation partir partira partirai partirait partiras partirons partis partisan partit partitif partition partons partout parturiant parturition parturitions paru parut parvins parvint parvis pasionaria pass passa passai passait passant passants passas passation passif passion passionnant passons pastour patapouf patatration pataud pathos pathovocalisation patins patois patriarcal patrimonial patriotard patron patrons patrouilla patrouillait paturin paturins paty paul paulhan paulin pauma paumait paumant pavillon pavlov pavois pavor pavot pawlonias paya payai payait pays paysan paysans phantasma phantasmant pharaon pharynx phhhht philidor philip philippard philo philosophant photo photographia photos phtiriasis piaillant pianissimo piano pianos pianotait piaula piaulant pica picaillons pichu piganiol pignochait pignon pignons pilaf pilla pillait pillard pillards pilon pilori pilum pimpant pindarisants pingouin pingouins pinçard pion pipait piqua piquant piquants pissant pistant pistils piston pistons pitchu piton pityriasis pivot placard placards plafond plafonds plaid plaida plaidait plaignant plain plaintif plaintifs plaira plaisait plaisant plaisantait plaisir plaisirs plan planait planant planta plantait plantant planton plantons plaqua plasma plastic plastron plat platon platonisant plats play plaçait plaçant plaît pliant ploc plof plomb plombs ploucs plouf ployant plum plumial plumitifs plupart plus plut pluton plutôt pluvation plâtra pochard poco podrida pogrom poids poignant poignard poignards poignons poil poils poilu poindra poing point pointa pointait points pointu pointus poinçon poinçons poirautai poirota poirotait pois poison poissard poisson poissons poitrail poivra polarisants poli polir polisson politicards polka polo polochon polochons polonais poltron poltrons pommard pompait pompidou ponant ponction pond pondant pondu pons ponson pont pontchartrain pontifical pontificat ponton poor popa popol popotin population populi populo pornic port porta portail portait portant portants porthos portillon portillons portion portland porto portons portrait portraits ports portsmouth portât posa posai posant posas positif position post posta postal postaux postichard postillon postulait postulat potain potard potlatch potocki pots pouah pouf pouffant poula poulaga poulain pouls poumon poupard poupards poupon poupons pour pourchassa pourchassait pourim pourpoint pourquoi pourra pourrais pourrait pourras pourri pourrions pourrir pourrira pourriras pourris pourrissait pourrissant pourrissants pourrit pourrons pourront poursuit poursuivait poursuivant poursuivi poursuivit poursuivrait pourtant pourtour pourvoir pourvoirons pourvu pourvus poussa poussait poussant poussas poussif poussin poussât pouvait pouvant pouvoir pouvoirs pouvons poux pratiqua pratiquait pria priait priant prima primat primitif primitifs primonatif primordial principal principaux principium prions priori pris prison prit privait privatif privation prix probant probatif procalmadiol prochain proclama proconsul procurait procuration prodiguions productif productifs production produisait produisant produisit produit produits prof profana profilant profit profita profitant profond profonds profus profusion programmation prohibait prohibition promis promit promouvoir prompt prompto promu promulguant pronom pronominal prononciation prononça prononçait prononçant pronostic pronostics pronostiquait pronto propagation propitia proportion proportions propos proposa proposait proposant proposition proposons proprio propulsa propulsion prorata proscrit proscrivait proscrivant proscrivit prostration protagon protagons prototypal prou proutt prouva prouvait prouvant provincial provision provisions provocation provoqua provoquait provoquant provoquât proximal prurigo prurit prônait psalmodia psalmodiant psychologisation puait puant puants pubis publia public publication publics pudding pudibond pugilat puis puisait puisant puisard puisqu puissant puissants puits pulsion pulsions puma pumas punch puni punir punit punitif punition punito purifiant purification purifications purin puritain purotin purotins purpurin purpurins purs purânas putain putains putatif putatifs putsch pyjama pyroproductif python pâlir pâlissant pâlit pâlot pâmait pâmant pâmoison pâtir pâtira pâtis pâtissons pâtit pâtour quadri quadrigonal quadrupla quaggiu quai qualification quand quant quanti quantum quart quartaut quarto quarts quartz quasi quasia quasimodo quatuor quay quayno quidam quidams quignon quillon quillons quinault quintal quintaux quintinshill quintupla quipos quiproqui quiproquo quitta quittait quittant quittas quoaillant quoi quoiqu quorum rabais rabaud rabbin rabonnir rabougri rabougris rabougrit rabâcha rabâchait rabâchant raccord raccourci raccourcir raccourcit raccourir raccourt raccroc raccrocha raccusa rachis raciaux rack raclant raconta racontais racontait racontant racontar racontars racontas racorni racornissait radar radiation radin radio radioactif radis radotant radotis raffirma raffolait rafla rafraîchi rafraîchir rafraîchis rafraîchissant ragaillardi ragaillardira ragaillardirait raglan ragot ragoûtants rahat raid raidi raidis raidit raifort rail raincy raisin raisins raison raisonnait raisons raki ralluma ramadan ramas ramassa ramassis ramdam ramolli ramollis ramollit rampait rampant rampot rampotons ramsay ramun ranci randall randolph randonnait rang rank ranz rançon rançonnants rapatriait rapiat rapin rapointir rapparaissait rapparu rapparut rapport rapportai rapports rappris rapprochions rapt rara rasa rasoir raspail rassainir rassortir rassura rassurant rastignac ratatam ratichons ration ratissant rats rauqua ravaillac ravala ravi ravin ravir ravissant ravivant ravoir raya raymond rayon rayonnait rayonnant rayons razzias rhin rhino rhodopot rhomboïdal rhum rhumatisant rhumatismal riant ribotait ricain ricana ricanait ricanant riccoboni rich richard richmond rictus riflard riga rigaudons right rigolait rigolant rigolard rigolo rigor rimbaud rimini rimski rinuccinni rions ripaillait riposta riqui risquait risquant rita rital ritualisa ritualisation rivadavia rival rivalisation road robins robinson roblot roboratif robot rocamadour roch rocking rococo rodas rodin rogatons rognon roidi rois roland roll rollmops roma romain roman romanisation romanorum romans romantic romantisation romarin rompant rompit rompons rompu rompus romuald romulus rond rondos ronds ronflant ronron room rorqual rorquals rosa rosato rossignol rostand rota rotatif rothschild rotor rotto rouan roubaud roucoulant rougir rougissant rougit rougon roulait roulant roulis roupillon roussin rouvrir roux rowing royal ruban rubans rubican rubicond rubicons rubis rudolf ruffian ruffo rugby rugi rugir rugis rugissait rugissant rugit ruhlmann rullianus ruminant rural rutabaga ruth rutilant râlant rôdait rôdant rôti rôtir rôtissait sabayon sabbat sabin sabinial sabla sablons sabot sabotait sabots sachant sachions saclant sacra sacrifiant sacripants sacristain sacristains sacs safari safran saga sagas sahagun sahara sahib said saignait saillants saillir sain saindoux saint sais saisi saisir saisira saisirait saisiras saisirons saisis saisissait saisissant saisit saison saisons sait sala salah salaison salants salgidal sali salir salis salissant salivant salmigondis salomonis salon saloon salopards salpicon salsifis salua saluant salut salutations saluts salvatori samaritain sambin sampan samson sanatorium sancta sanction sandorf sandwich sandwichs sang sanglant sanglants sanglot sanglota sanglotait sanglotant sanglots sangs sanguin sano sans sanson santal santana saoul saoulait saphir saphirs sapin sarah sarbacan sardon sarrasin sarrasins sarrau sarraus sarrois sartinuloc sasso satan satchmo satiation satin satisfaction satisfactions satisfaisant satisfait saturant saturation saucisson sauf saupoudra saura saurais saurait sauras saurons saut sauta sautant sautoir sautoirs savais savait savant savants savarin savary savions savoir savoirs savons savorgnan savoura savourait savourant savoyards saxo saxon scala scandalisa scandalisant scapin scarborough scarifiant scarmouchis scazons schako scharf schizo schlussnig schnaps schola schonthan school schumann schupo sciait scilicat scintillait scintillant scintillations scions scission sclavons scolarisation scorbut scorpion scotch scotland scott scout scribouillard script scription scriptor scriptum scrivain scruta scrutait scrutant scrutin scrutins sculpta sculptural scutari shadows shah shako shampooing shandy shavouot shikibu shiraz shkodra shot shulhan shut siam siamois sibylla sibyllin siffla sifflait sifflant sifflota sifflotait sifflotant sigalas signa signait signal signalait signalant signalons signalât signaux signifiait signifiant significandi significatif signification significations signifying signons signor sihanouk sillon sillons silo silos simili similia similibus simonisait simoun simplicitas simulait simulant simulation sinatra sinciput sindon sinfonias sinon sint sinuant sinus sinusoïdal sion siphon sirdar sirocco sirop sirotait sisal situation sitôt sixain skiff slatim slips slivowitz slogan slogans sloop sloughi smalah small smaragdin smigard smith smoking snack sncf snob sobranvi sobranyi social sociaux soda sodomisant sodomisation sofa sofas sofia soif soigna soignait soin soins soir sois soit sokoro sola solda soldat soldats soliman sollicita solo solti solum solus solution solutions sombra sombrait sombrons sommant sommation sommations somnambulant somnolait somnolant sonda sondant sondas song sonna sonnai sonnait sonnant sonnons sono sons sont sopha sophistication sopor soprano sororal sors sort sortait sortant sorti sortir sortirons sortis sortit sortons sorts souchon souci soucis soudain soudanais soudoya soudoyait souffla soufflait soufflant soufflot souffrait souffrant souffrir souffrit souffrons souffrît souhait souhaitait souhaitons souhaits souk souligna soulignait soult soumis soumission sound soupait soupir soupira soupirait soupirant soupirants soupirs soupçon soupçonnait soupçons sourcil sourcils sourd sourdait souri souriait souriant souris sourit sournois sous souscription soustraction soustrait soustraits soutint souvaroff souvi souvint souzay soyons spahi spahis spalato sparadrap sphinx spica spinoza spiqua split sport sportif sportifs sports squadron squaw stabilisation staff staffs stagna stagnant stamboul stampillon standing stanislas staphylococcus star starbuck station stationnait stationnant stations statu statufiant statut statuts stich stick stigma stimula stimulant stimulus stipulant stirling stock stockait stockholm stocks stomacal stomato stomox stop stoppa stoppait stoppât stormings story stourbir stout stradivarius straggling stram strangula strangulant strangulation strapontin strato stravagant stravagants striait strict stricts strobo strontium structural stubb stuc studiantin studio studiorum studios stuffing stupid sturmi stylo styx suait suant subcortico subi subir subissant subissons subit subito subjonctif subjonctifs sublinguaux subscription subscrit subsistait substantif substantifs substitua substituai substituait substitut substitution substituts substratum substruction subtil subtils succinct succion succomba succombait such sucrins suffi suffira suffirait suffisait suffisant suffisants suffit suffocant suffoqua suffoquait suffoquant suicida suicidait suicidant suicidât suintait suis suit suivait suivant suivants suivi suivit suivons suivrais suivrons suiza sully sultan summa summum sunna sunt supplantait suppliait supplication suppliât support supportait supportant supposa supposant supposition suppositions suppositorial supposons supprimait supputant supra supranational suraigu surakarta surard surcroît surgi surgir surgirait surgissait surgissant surgit surhumain suri surin surmoi surnom surnomma surnommait suroît surplombait surplus surpris surprit surpuissant sursaut sursauta sursautait sursautant sursis surtout survint survivait survivant survivons survivrait survivront survola suscita suscitait suscitant susdit suspicion sussurait suturant suturation suys suât suçait suçotait swann swift swing symbolisait symbolisant symbolisation sympa symposium syncopal syndicat syndicats syndics synopsis syphilis tabac tabou tact taft tagadac taiba tailladant taillis tailor taira tairons tais taisait taisant taisons takagawa takamoku talc talion talisman talk tallipot talmud talon talonnait talons tamanoir tamazusa tambour tambourin tambours tamis tampon tandis tango tanguant tank tanka tankas tant tantôt taon taons tapant tapi tapinois tapir tapira tapis tapit tapota tapotait tapotant taquin taquoir tarabiscot taratata taraud tard tardjouman targui tari tarin tarir taris tarissant tarlatan tartinait tarzan tassili tatata tatillon tatou tatouait tatous taxi taxis taylor tchad tchouan thanatogramma thank thaon that third thirty tholon thomas thon thorah thorax thousand thugga thulin thuya thym tiflis tifs tillac tilman timariot timon timoration timour tindouf tinkling tins tint tintinnabulant tintouins tippoo tira tirailla tiraillant tirait tirana tirant tiras tiroir tiroirs tirons tison tissait tissant tissu titan titanic tito titra tituba titubant titubation titus toast tobolsk tocsin toffana tohu toisa toison toit toits told tomahawk tomahawks tomba tombais tombait tombant tombouctou tonitruant tonna tonnant tonton tontons tophus topinambour topons torchis torchon torchons tord tordait tordant tordu tordus torsion tort torticolis tortil tortilla tortillard tortillon torturait torturant toscan total totalisation toton touat toubib toubibs toucha touchait touchant touffu toujours toulon toundra tour tourbillon tourbillonna tourbillonnant tourbillons tourillon touring tourlourou tourna tournait tournant tournants tournis tournoi tournois tournons tournoya tournoyant tours tous toussa toussaint toussant toussota tout toutou town trabuco trac tracas tracassait tracassant tractions tractus tradition traditions traduction traductions traduisait traduisit traduit trafic traficants trafiquait trahi trahir trahison trahissait trahit train trait traitait traitant traits trajan tralala tralalas tramail tramait tramant tran tranchant tranquillisant transafricain transat transcription transcrit transcrits transcrivant transformation transfusion transfusions transi transistors transit transitif transmis transmission transparaissait transparaît transpassait transpira transpirait transpirant transport transporta transports transsubstantiation transvaal trapu trapus traquait traquant trauma travail travailla travaillait travaillons travaux trayant traça traçait traçant traînait traînant traînard triangulation trianon tribart tribu tribunal tribus tric tricot tricots trimard tringas trinqua trinquant trio triompha triomphait triomphal triomphant triomphants trip triparturition tripla tripota tripots trismus tristan tristou tristram tritons triturait trituration trivial triviaux trocart trognons trois troll tronc trop troquas trot trottoir trou troubadour troubla troublait troublant troublants trouffions troufignon troupiau trous troussa trouva trouvai trouvait trouvant trouvas trouvons trouvât troyat troïkas truands truc trucida trucidant trucs truly trusquin trust tryphiodorus trônait tsumori tuait tuba tubar tudor tumuli tumulus tunis tuons turbin turbot turc turf turin tutorial tutoyant tutoyons tutti tuyau tuyaux twilight tympan tympanon tympans typhon typhons typhus typo typos tyran tyrannisant tyrans tâchant tâchons tâta tâtonnant tâtons uaxactun ubac uicidant ulrich ultimal ultimatum ultimaux ultra ultradisant ultrafin ultramontains ungaro uninominal union unir unira unis unissait unissant unisson unit unto uomo upanishads upas upsala uranium urbain urbino ursins urubu urus usai usant uskub ustion usufruit usuzumi utilisa utilisai utilisait utilisant utilisation uttaïr uxorilocal vacant vacations vaccin vaccino vachards vacilla vacillant vacuum vagabond vagabonds vagi vagissais vagissait vagissant vagissons vagit vagula vaillant vain vaincrait vaincs vaincu vainquit vains vairons vais valait valant valdingua valdo valida validation vallon vallons valmont valoir valparaiso valu valut vamp vanish vantail vantait vaquait vaquons variorum vasavadatta vasistas vassal vassall vassaux vatican vaticination vaudra vaut vautour vautrin vaux vavin vaziluia vibra vibrait vibrant vibration vibrions vichnou victor victoria victorias vidait vidons vifs vigilant vigilants vignon viking vilain villa villacorta villacoublay vils vincit vindicatif vindication vingt vins vint viol violait violant violationis violin violon violons virginal virginia virginy viridans viridifiant viril virtualisation virtuosi virus visa visant visconti vision visita visitait visitant visitation vison vissa vital vitalium vitaux vitrail vitraux vivais vivait vivant vivants vivifiant vivions vivons vivrait vivrons vizir vladimir vobiscum vocabulariat vocalisait vocalisant vocalisation vocalisations vocation vocaux vogt voici voilà voir vois voisin voisinait voit voitot voix vola volait volant volants volapück volatil volcan volition volons volontariat volubilis vomi vomir vomissant vomitif vont voudrais voudrait voudras voudrions voudrons voulais voulait voulant vouloir vouloirs voulons voulu voulut voulût vous vouvoya voyagions voyais voyait voyal voyant voyants voyions voyl voyons voyou voyous vrai vrilla vrombir wagon wagons wagram waiting walpurgis wapiti warburg warm wartburg washington wasn wasqu what whig whipcord whisky whiskys whist widmark wigwam wilburg will william willigis willo wilson winding window windows winston wisconsin witold wittig wobisch woodrow woolf worms wright xiphidion xviii xxiii yacht yack yard yarn yatagan yazid yaùq yoga yoghourt yogi yolanda yori yorick york yotsu young your yours yoyo yucatan yvazoulay yvon zahir zain zakouskis zanzi zanzibar zarathoustra zazou zigouillait zigzaguait zigzaguant zinc zincs zingaro zinnia zinzin zinzins zinzolin zippo zohar zola zona zonal zoos zori zorilla zotanburg zoziau zozotant zurich zurichois zygoma ânon çatapathabrâhmana çruti îlot îlots ôtai ôtait ôtant ================================================ FILE: mocodo/resources/lorem/en4.txt ================================================ able acid aide AIDS ally also amid Arab area army atop aunt auto away baby back bake ball band bank bare barn base bath beam bean bear beat beef beer bell belt bend best bias bike bill bind bird bite blow blue boat body boil bold bolt bomb bond bone book boom boot born boss both bowl buck bulb bulk bull burn bury bush busy butt cage cake call calm camp card care cart case cash cast cave cell chef chew chin chip chop cite city clay clip club clue coal coat code coin cold come cook cool cope copy cord core corn cost coup crew crop cure cute damn dare dark data date dawn dead deal dear debt deck deem deep deer deny desk diet dirt disc dish disk dock doll door dose down drag draw drop drug drum duck dumb dump dust duty each earn ease east easy echo edge edit else even ever evil exam exit face fact fade fail fair fall fame fare farm fast fate fear feed feel file fill film find fine fire firm fish fist five flag flat flee flip flow fold folk food fool foot fork form four free from fuel full fund gain game gang gate gaze gear gene gift girl give glad goal goat gold golf good grab gray grin grip grow hair half hall hand hang hard harm hate haul have head heal hear heat heel hell help herb here hero hers hide high hike hill hint hire hold hole holy home hook hope horn host hour huge hunt hurt icon idea into iron item jail jazz join joke jump jury just keep kick kill kind king kiss knee know lack lady lake lamp land lane last late lawn lead leaf lean leap left lend lens less life lift like limb line link lion list live load loan lock long look loop lose loss lost lots loud love luck lung mail main make male mall many mark mask mass mate math meal mean meat meet melt menu mere mess mild milk mill mind mine miss mode mood Moon more most move much must myth nail name near neat neck need nest news next nice nine none noon norm nose note odds okay once only onto open oral ours oven over pace pack page pain pair pale palm pant park part pass past path peak peel peer pick pile pill pine pink pipe plan play plea plot plus poem poet poke pole poll pond pool poor pork port pose post pour pray pull pump pure push quit race rack rage raid rail rain rank rape rare rate read real rear rely rent rest rice rich ride ring riot rise risk road rock role roll roof room root rope rose ruin rule rush sack safe sail sake sale salt same sand save scan seal seat seed seek seem self sell send sexy shed ship shit shoe shop shot show shut sick side sigh sign silk sing sink site size skin skip slam slap slew slip slot slow snap snow soak soap soar sock sofa soft soil sole some song soon sort soul soup spin spit spot star stay stem step stir stop such suck suit sure swim tail take tale talk tall tank tape task team tear teen tell tend tent term test text than that them then they thin this thus tide tile till time tiny tire toll tone tool toss tour town trap tray tree trim trip true tube tuck tune turn twin type ugly unit upon urge used user vary vast very view vile vote wage wait wake walk wall want warm warn wash wave weak wear weed week well west what when whip whom wide wife wild will wind wine wing wipe wire wise wish with wolf wood word work wrap yard yeah year yell your zone ================================================ FILE: mocodo/resources/lorem/fr.txt ================================================ pour dans plus sont avec cette mais comme tout nous Mais fait été aussi leur bien peut deux encore marché Pour donc cours moins sans entre faire elle vous prix dont également Dans effet pays millions Belgique mois leurs taux années temps groupe ainsi toujours société depuis tous soit faut Bruxelles fois quelques sera entreprises contre francs Nous Cette dernier était chez monde alors sous actions autres reste trois notre doit nouveau milliards avant exemple compte belge premier nouvelle Elle terme avait produits cela niveau bénéfice toute travail partie trop hausse secteur part beaucoup valeur croissance rapport année base Bourse lors vers souvent autre peuvent surtout toutes nombre fonds point grande jour avoir quelque place grand personnes plusieurs certains permet politique chaque chiffre pourrait devrait produit rien mieux celui qualité France vente jamais production action baisse Avec résultats votre risque début banque voir avons elles moment question pouvoir titre doute long petit notamment droit heures cependant service Etats-Unis jours celle demande belges ceux services bonne seront économique raison situation Depuis entreprise nouvelles possible toutefois tant nouveaux selon parce seul sociétés vient jusqu quatre marchés mise seulement semble clients Tout Cela serait fort frais lieu gestion font quand capital gouvernement projet grands réseau données prendre plan points outre pourtant Ainsi type Europe pendant Comme mesure actuellement public dire important partir parfois veut présent passé forme autant développement mettre grandes investisseurs trouve maison moyen choix doivent direction simple période enfants dollars personnel assez programme général banques semaine président personne européenne moyenne tard petite certaines savoir loin explique plupart jeunes cinq contrat Banque valeurs seule rendement nombreux fonction offre client activités environ ministre cadre sens étaient sécurité recherche Paris sorte décembre suite davantage ensuite janvier donne vrai cause conditions suis juin peine certain septembre sommes famille pris laquelle directeur propose gens derniers étant chose portefeuille obligations afin différents technique ailleurs américain ventes Selon livre octobre vraiment sein dollar Enfin haut Plus petits porte durée domaine aurait jeune présente passe lorsque choses puis Vous aucun tandis coup existe propre carte crise importante atteint revenus montant forte Quant rapidement ville mars premiers marque véritable ligne longtemps propres devant passer départ total série quoi particulier concurrence élevé position connu principe tendance court pages évidemment résultat aura parmi Sans américaine face trouver durant femmes construction désormais distribution telle difficile autour européen pratique centre vendre juillet région sociale filiale film besoin mode représente réalité femme vaut Tél aucune hommes donner titres nombreuses différentes moyens formation chiffres Générale prochain genre bureau communication participation gros pourquoi estime devient réalisé création novembre pourra semaines consommation faible terrain site droits moitié puisque reprise compris projets avril vont call donné simplement firme perte Bien Philippe sait prend vite stratégie petites marketing presque Michel manque réaliser financiers Comment voiture chef constitue Internet enfin charge nature second payer actuel Elles investissements dispose financier membres date avaient gamme revanche comment décision tour actionnaires solution créer concerne belle lequel tél seconde version Pays-Bas cher chacun lire techniques décidé mouvement conseil nécessaire meilleur double sujet généralement restent celles politiques malgré confiance homme Certains ayant papier commerce Région Wallonie Windows termes contraire informations trimestre différence certaine formule voit programmes actuelle permis dossier Quand guerre acheter rendre février main voire bons technologie européens éléments unique venir générale courant suffit conserver maximum force largement milliard soient Pierre devenir franc minimum mort responsable possibilité presse affaires longue travers relativement Deux présence européennes devraient groupes ensemble santé pense bénéfices compagnie publique cœur revenu mesures table nettement questions permettre Chez retour majorité potentiel moindre récemment secteurs réduction large traitement perdu étrangers parents fond capacité vitesse activité quel tient taille éviter risques Jean Pourtant Allemagne parler propos quant signifie voie jouer prévoit blanc noir parti logiciel continue Notre bois meilleure perspectives développer celui-ci œuvre structure suivre tiers prise professionnels raisons néanmoins preuve social bénéficiaire couleurs mondial maintenant essentiellement prévu Japon prévisions centrale Alors international yeux bonnes opérations pied pourraient Londres juge devra uniquement corps divers Parmi numéro réduire Tous texte tenu budget pression style économiques Jacques montre population analystes processus placement classique dividende rester publics fortement plein wallonne Express faudra travailler Crédit directement prime Flandre crédit monnaie précise appel Autre travaux juste Chaque tableau terre permettent devenu rouge mémoire partenaires rapide travailleurs joue objectif salle parle musique milieu autorités chute régime liste opération bout performances électronique haute responsables lancé voitures patron Malgré affiche situe études Microsoft condition retrouve revient Belgacom route Ensuite Luxembourg campagne comptes hors culture Commission possibilités semestre actifs finalement internationale monétaire passage justice page tels poids celle-ci commercial entendu mondiale accord diverses totalement clair biens euro York parfaitement viennent division réseaux principal lancer supérieur atteindre référence téléphone management vins proche collection fiscale Ceci informatique investissement volume matériel publicité train coupon progression tenir protection couleur nouvel Lorsque change changement garantie somme Belge plaisir fils laisse importants privé besoins œuvres américains relations peau moteur augmentation suivi volonté beau bancaire laisser bureaux principalement intéressant logiciels sommet vivre élevés Robert contrats oublier performance réponse concept obtenir poste attendre lignes consiste augmenté vert figure développé magasins collaboration répondre holding livres convient fonctions fera pouvait million Paul britannique voix Grande-Bretagne disque affaire minutes quelle contexte limite mains commun réduit Pourquoi particuliers verre wallon allemand effets Chine meilleurs rend applications procédure devait profit méthode pose commence idée créé nuit Nord capitaux options consommateur cartes métier probablement aller facile International importantes Marc capitale devise prochaine transport Street demander utilisateurs image propriétaire facilement publiques croire disponible Louis veulent Charleroi consommateurs devises difficultés sort national machines annoncé choisi découvrir soutien avez perdre cuisine telles travaille ouvert phase certainement télévision pratiquement annuel bord paiement Bank institutions seuls arrive constate marques nationale regard représentent Belges état libre rachat Toutefois portes sortir commandes permettant manager fiscal cinéma histoire zone sauf avantages voici effectivement puisse réel puissance fixe Belgium contact époque rythme principaux vendu utilisé étude Leur sensible Bref rencontre spécialistes brut mauvais néerlandais supplémentaire mots reprises nécessaires soir Prix machine penser parts comprend fusion acquis totale voyage logique concurrents idées trouvé dette réellement financement disponibles vieux lance marge dirigeants avis changer conséquence sociales supérieure Certes faisant ordinateur partenaire warrant fabrication redressement suffisamment délégué pourront poursuit chemin emplois réalise évolution Cour automobile Premier ancien note parties pension professionnel assure garder Rien Actuellement est-il climat SICAV département sept partout immobilier lancement rating réussi patrimoine expérience Anvers anciens graphique Fortis faveur retrouver droite responsabilité commande Kredietbank direct utiliser tonnes connaissance acheté américaines clairement semblent biais futur neuf chance faillite équipe musée compagnies documents pertes sortie seraient choisir tellement industriel précompte immédiatement avantage constituent déchets sport demeure garde maisons Solvay conséquences active dépenses donnent employés sites élections détient obligation fruits véhicule Conseil investi mission profiter visite comprendre professionnelle affirme Wall charges privée rares succession liberté rentabilité suivant efficace assurer images agences impossible John enfant fournisseurs photo salaires Avant compter disposition formes bénéficiaires lesquels maintenir précisément couple enregistré recul offrir peur hauteur centres voulu industrielle positif administrateur intéressante commerciale interne pleine passant vision faits retard certes lundi Outre porter écrit cesse locaux délai trouvent classiques commencé réalisée Alain vigueur gagner Celui-ci Philips ceux-ci favorable pouvoirs participations annonce génération élément devenue touche conseils devoir souligne respectivement rapports vacances lieux naturellement statut ceci destiné défaut objectifs récente saison industriels Suisse catégorie complexe huit fisc obtenu repris occupe sérieux émis Quelques comportement limité vingt conjoncture gauche marche ordre mobilier parcours perspective normes recours Communauté annuelle lecteur objets fabricant niveaux Entre réalisation amateurs conséquent présenter Celle-ci vise types détail mauvaise professeur progressé signe passée approche Reste return jardin flamand Namur bilan sensiblement Trois utilise commune dimanche option partis analyse films surface warrants prises secret historique journée Pendant allemande André fille proposer avions augmenter parc Delhaize Lors limitée appareils villes au-dessus diminution prochaines servir Bernard commission faiblesse plus-value souhaite internationales producteur producteurs code belles cabinet fonctionnement gérer mouvements pratiques régions dossiers meilleures Parce entrée vendredi actif sociaux supplémentaires café message physique Société communes dizaine faute sélection source facteurs milliers soleil tirer concernant Bourses fallait sentiment bénéficier débat élevée ouvrage police pouvez attention a-t-il constructeurs contribuable moderne passion primes suit auquel dépasse spécialisée bruxellois déclaration multiples quartier vidéo dépend liquidités correction comité cherche filiales Sous signé leader calcul Rens artistes déficit cadres fédéral probable remboursement efforts restaurant Toutes couverture domicile soins devront luxe complet danger indispensable syndicats comporte faite juridique langue rendez-vous demandé respect continuer lesquelles local rare restructuration automatiquement plat boursier cotées décide Cependant Certaines matériaux ordinateurs tradition progressivement capable classe familiale réserve fonctionne solutions fabricants paie Finances réelle changé masse unités considéré auront noms riche Patrick proposé salon territoire fixé magasin candidats marges asiatique inférieur réaction fleurs record tribunal recettes poursuivre dessous portant Aussi Sabena acteurs dehors constructeur relation offrent spectaculaire produire confort familles investir reprend sert montrer mérite places Soit judiciaire textes quasi SNCB jeux permettra étudiants membre photos positions Cockerill lendemain cent gagné japonais mark pointe solide Voici anglais présentent décisions législation médias victimes écran nécessairement découverte club environnement noter crée exportations négociations répond entier business peinture voisins faibles location nord promotion technologies auraient caisse entend simples maladie menu chances commerciaux printemps Benelux poser Asie usage actionnaire prennent résistance surprise Etats mariage nécessité Puis cote Plusieurs beauté exclusivement lettre payé rendu software utile gestionnaires bénéficie procédé vaste crois normal Centre construire démarche emprunts naissance distance tourner Club attendant quantité tourne ajoute bancaires ajouter géant automatique faux attend litres présenté argent confirme indépendants énorme destinés véhicules ressources standard auparavant construit Quelle principales disposer global écoles Quel réputation fameux rappelle conseille heure veille difficulté limites commerciales samedi palais vend Tractebel connaissent reprendre village emploi amis budgétaire croit mises souci contient habitants Weekend bras beaux bruxelloise faisait introduit intérieur outils précis chercheurs taxe salaire transactions Christian chambre portée réflexion hasard matin assureurs réforme Beaucoup fournir recherches liés tenue proposent aide ferme secondes CGER contenu quotidien flamande centaines course billet critique naturel principale support week-end Dehaene Gand chargé économies augmente guide proposition laissé spécialiste francophones importance vent conception préférence spectacle avenir grave commencer diminuer chercher bonheur dizaines exactement outil scénario Jones coups émissions éventuellement Royale soumis lecture monter Grand central exigences assuré contacts consacré faut-il réussite échéance recevoir tableaux arriver évident Italie amélioration auteurs estimé quinze Russie demain précédent vendeur événements autrement experts fortes furent possibles circonstances placer publication réserves sauce venu Charles collaborateurs implique obligataire établi CD-Rom forcément remarquable Claude tourisme internationaux directe compétences conseiller facteur plastique rarement Royal affiché lutte relative actuels envie ministres secrétaire capitalisation langage positive circulation convaincre notion visage vouloir ajoutée caractéristiques Eric Union paix courrier disposent développe présentation barre comparaison déterminer firmes fournisseur informatiques luxembourgeois achats solde Serge globale propriété stratégique Renault partage porté sources Kong cour destinée absolument branche ouvre plans productivité Résultat améliorer joué Parlement dépit fichiers personnalité constitué gestionnaire né profession qualités conscience médecin celles-ci design décor faudrait participer appelle forces suisse appareil conduite longueur tarifs vérité lien locales francophone clubs correspond coupons estiment défi protéger réalisés méthodes revenir superbe volontiers document nommé tente financer scientifique Georges travaillent lié zones aime lettres ouverte Hong murs philosophie rappeler utilisés suivante représentant traduit remettre situé différente longs économie discours distributeur domaines régional faites italien restera usine Group personnage portent attendu Jean-Pierre articles changements fallu léger mener propriétaires spécifique récupérer voyages procéder locale médecins privés transmission concurrent courte quart baisser pieds publié Ford menace réunion transfert composé dimension personnages ralentissement conclusion agents parfum rémunération difficiles mettent pierre proches réglementation salles grimpé prochains prévue électrique dynamique exposition installé plancher distributeurs déclare connue préparation réalisées beurre opérateurs achat province spécifiques Albert renforcer téléphonique comptable effectuer trafic degré définitivement humain optique remarque talent appelé modifier définition peintre respecter stade statistiques certificats limiter livraison placements raconte volumes immobiliers anciennes chevaux médicaments Peter feuilles football identique pouvons remise structures tenter accords cotisations indice neutre constituer montrent placé loyer proximité voient épouse Canada entrer postes précision cité concours patrons populaire pétrole négatif allemands roman victime italienne ménages repas PetroFina langues tendances pire prudence savent Néanmoins conduit mille rénovation égard Américains exercice avance effectué fortune fournit lecteurs Morgan découvert différent emploie bleu royal technologique télécommunications Amsterdam fiscales indique information lourd signal Mieux aider ancienne apporte nette prestations publicitaires sensibles communauté volatilité étape assurance lancée résoudre garanti modification revue spéciale chacune différences messages priorité recommandation récent charme dividendes Olivier passent finale immeubles logement pourcentage rire stabilité difficilement défense magazine eaux jeunesse continuent révolution étonnant organisation constater emprunt éditions Daniel utilisée compartiment publicitaire article bande capacités centrales considérée milieux occasion quasiment pouvant Vermeulen-Raemdonck visiteurs chambres considérablement demi découvre essentiel broker dettes mardi reconnaissance salariés formules grosse heureux perd radio allait multimédia partiellement seules Gérard Securities toucher jugement considérer remplacer couvrir précieux segment dessins espace indices refuse chefs exemples rejoint spécialisé objet précédente rose versions destination Encore deviennent personnelle plats vingtaine virus Faut-il chasse longues Toute bases cotée final monnaies travaillé apporter aspects disparu David Management port racheter relever Celui catalogue centaine chaleur profil représentants conclu réside scientifiques Chambre secondaire serveur XIXe exige grimper immeuble montants paysage vendus assurances catégories dure décote soutenir édition dangereux agréable voulait combien disparition optimiste plus-values tomber erreur situations spécialisés subi suivent classement norme rentable sang socialiste tombe Justice attitude mines liée plantes vague General légumes Ceux-ci conflit excellent licence travailleur appris est-elle gagne mari préparer purement située vérifier Jean-Luc gain métal surfaces douze expliquer meubles chaussures créée institution solidarité Maastricht basée journal soin sourire Guerre bouteilles flexibilité maintient appartient moments rouges basé devons installations Bacob association format City Page disques modem mélange ordinaire vide chimique disent pharmaceutique numérique porteur répartition blanche composants future parvient évoque Durant calme Electrabel culturel grosses baissé lois moteurs principes trente éventuelle prévoir tours Pentium acheteur dimensions fonctionnaires organisé rencontré russe savoir-faire établissements Fédération Toujours créativité application dépasser importe jaune marqué mécanique socialistes tranche Quelles envisage traiter Surtout acheteurs chinois claire vécu Objectif bail demandes diversification montré renseignements souscription Tokyo entendre tests Siemens filles unité Bekaert composition resté sinon agence fini modifications Cash industrielles obtient permanence restaurants réels échange florins terrains émergents atouts offrant bouche champ chaud monte preneur présents quitte tarif facture fiscaux modeste processeur Fund avenue compétition relevé tenté Est-ce Musée bijoux différentiel déclaré institutionnels traité Intel traditionnels victoire connus correctement Dominique Tant accessible rencontrer stocks espérer jouent menée nécessite provenant utilisent affichent délais inférieure sent spécial Amérique acquérir album idéal véritables associé candidat connaissances signes cheveux conserve stress directeurs donnée endroit traditionnelle Martin ciel convention obligataires prouver Espagne Petit Source dessin humaine lait Seule Thierry boursiers continent destinées flamands néerlandaise pensions commencent considérable nationales conjoint crédits militaire morceaux privatisation repose sommeil traditionnel Seul capables combat finances puissant Bill Renseignements physiques Richard allant créations toile évidence convaincu excellente retraite théorie transformer Tour transaction visant Deutsche Mons attentes cycle détails Votre héros sérieusement Ceux considération impose propositions Autrement forts usines Afin Quels aisément ressemble risquent totalité imaginer originale intégré intéressantes loyers auxquels circuit indépendant intérieure maintien cotisation moyennes quitter stable Compaq galerie liens souffle apprendre concert liquide noire température transparence école champion diminué désir ressort voulons équipé alimentaire organisations présidence raisonnable ratio recommande utilisant accepter accepté cache chocolat chuté comparer courts figurent passagers prison viande associés esprit froid jeudi liées revu satisfaction satisfaire test tiennent vraie contrairement dépassé extérieur American Etat complémentaire déclarations réactions Fonds artiste conclure déduction remis déterminée fiscalité grand-chose humaines réponses équipes Michael Systems aspect commercialisation manger RTBF engagé obligé proportion signature étranger imposé silence vote Afrique Mobistar cible contemporain fondateur Jean-Claude communiquer existent majeure ouvrir électroniques compétitivité erreurs notation rang Apple accident certificat exceptionnel http proprement riches Barco Quoi violence adapté bénéficient récession sentir armes arrivé crainte garanties ménage officiellement ouvriers Autant discussion rejoindre époux citoyens concernés définir Paribas Telecom fabrique feront née oblige patients pensent responsabilités doublé fraude organise Henri conclut désire législateur écrans choc gratuit mobile naturelle dialogue révision familial lourde poche décider négociation tort Maison Trésor constante cotation déterminé managers opté transformation Life anniversaire compétence géographique mandat réservé établir Business fins richesse commente intermédiaire retrouvé sciences banquier former monté parfait veux René investit parvenir vieille collections dirige fonctionner mauvaises tapis venus Contrairement Suez piste pistes tensions campagnes investis proposés tabac bataille britanniques fine liégeois partenariat privées remplir supérieurs Beaux-Arts laser restauration Dutroux chimie rendent textile Brabant Colruyt James National Quatre préalable souvenir venue Communal avocat comparable consolidé critiques interdit mine quotidienne rigueur réduite tissu Invest pain participants procédures profondeur retrouvent rues taxation Mexique asiatiques conducteur demandent environs fermeture gris rumeurs accueille amoureux défendre pure souffre créneau journaux seuil Jeux Office auteur cash-flow fichier instruments quelles séance véritablement Yves attirer civil civile station courbe hectares influence ingénieurs tables vivent Exemple blancs couche cuir devenus extraordinaire patient peux aient animaux associations foie initiative poursuite survie Face apparemment consultant expansion séjour champagne commentaires complexes cylindres décennie rendements retenu sais sujets cuivre offert réagir varie Fondation artistique communications monétaires métaux permanente positifs électriques basse concentration investisseur provoqué doux stations coin modifié avocats estimations original souplesse Attention Frank Hainaut Suite annuels cellule clause exemplaires malheureusement minute normale Frédéric Sud-Est atout latine logements pilotes susceptibles Roger XVIIIe ordres remarquer actuelles bouteille constat opportunités prépare vendeurs accrue fruit jugé loisirs trentaine gendarmerie alimentaires coté modernes préciser réussir laissent parfaite spécialement évoluer Dewaay Désormais Groupe maladies négligeable tension Lion chansons dite festival négative préféré restant Cera adopté coopération distingue douceur retirer technologiques Editions Parfois bruit comptant démocratie exception mercredi offres sucre vedette évolue British Leurs compromis hauts élevées émission Faire attendue lourds quels soirée événement alternative chimiques conférence quitté serveurs Brésil CD-ROM correspondant locataire matériau périodes utilisées morale équilibre Sony fixer gratuitement trait Trop adultes consacrer normalement parole prochainement suscite verra clé mesurer notes potentiels relatives Flamands Francfort Palais Plan République transports Portugal couvert joueurs Malheureusement coupe dispositions effort endroits aides contribution insiste souhaitent communal impact progresser Sambre administrateurs deviendra dégager formations souscrire cellules facilité gras militaires passés quinzaine souvient automobiles bref confortable essentielle officiel vive vols Marcel combinaison distinction définitive japonaise liaison tissus cadeau canadien distribué existants ordinaires servi surveillance médecine revoir récentes voies Rappelons comptabilité fabriquer fasse intéressants peintures quartiers valable étapes bénéficié couvre diminue envers introduire missions Petrofina apparition coffre digne fibres initiatives littérature rembourser retrait Bundesbank Pascal Pologne consacre employeur favorables manquent assurée battre chantier conclusions consulter craindre vivant Chacun internes apprend liégeoise observe provenance sortes Marie cessé céder estimée marchandises Poste balance copie cuisson négocier spéciaux traite Bruges hollandais peut-on porteurs régler soutenue suivie Stanley accueillir médical notoriété provoquer sensibilité vocation impression séduit conflits imposable journalistes manifeste provoque wallons éditeurs canal fondamentale futurs graves mené pommes racheté remonte solides suffisante chargée chers discussions garantit indicateurs provient soutenu sportif systématiquement zéro comptent recette récit subir évolué Johan accorde faciliter hausses Macintosh Services débuts garantir portefeuilles susceptible universités Glaverbel Sotheby's actes brasserie caractéristique cherchent favoriser justement prudent stock échelle énormément Standard compose couronne exceptionnelle flux justifier réfugiés téléphoniques Monsieur Ville accepte inspiré pollution situent allemandes boissons douce gouvernements intervention motifs primaire World entrepreneurs représentation Thomas apparaissent complémentaires cycliques franchement instrument rayon Food conversion partager retenue simplicité Comité confirmé devaient expériences front jeter logistique reconnu Affaires Heureusement comédie historiques imposer obligatoire recourir références traces témoigne Java acte appliquer catastrophe conduire contribué fais intervenir mettant pilote plafond remplacement tire Berlin Vincent portable profonde refusé repos béton fermé juges parlementaires prévention Donc dispositif forment neige suffisant Louvain diffusion fédération lentement prenant souris contenter douleur intervient look manœuvre parquet poussé arguments billets consacrée dirigeant décoration holdings justifie levier majeur midi recyclage robe Entre-temps appels directive initial intéressés pousser pouvaient secrets surpris univers poisson spécialisées séduire verser générations nettoyage ouverts réductions vélo Anne Compagnie Souvent explique-t-il officielle résolution Service courses pari pousse revendre trace abonnés craint croissant juger régionale symbole touristes Rome actives communautaire contraintes journaliste traditionnelles variable amour atelier budgets budgétaires clef détriment nationaux paquet relatif Francis Rupo diesel gare parlent rapporte regarder éventuel Clabecq carrés psychologique rupture téléphonie Danemark Sauf citoyen four permettrait puissent rapides Marketing Tendances dit-il développements enregistre envoyé intermédiaires liquidité réagi Allemands Louise connues consolidation créateur idéale profité prévus résulte similaire Boeing Didier Dieu Willy agir coins constaté danse occidentale optimistes pensée professionnelles Computer Tournai appliquée chanson déroule franchir liquidation morts nouveauté prestigieux suppression Laurent Mercedes existantes pleinement simultanément établissement cercle corruption discipline familiales laboratoire livrer montée participe Personne adresse finance génie leasing versement bits concernées dents inclus maximale précédemment routes variations équipements Declerck chemins constituée globalement libres proposant souligner ambitions croissante décennies littéralement motivation rubrique souvenirs surprises vendue Celles-ci bébé plainte stockage écrire énergie Spector annonceurs débats ferait grain sont-ils séparation tournant vendues Compte Cools Volvo accessoires constitution consultants dommages occupé échanges Seconde adresses efficacité fixée frappe monopole panneaux restée sentiments terminé utiles Bruno Seuls appliqué donnant fondamentaux fréquemment métiers planche royale suppose Moins fourni japonaises payés profond programmation résolument golf poudre ================================================ FILE: mocodo/resources/lorem/fr5.txt ================================================ encore groupe depuis contre francs autres compte niveau partie hausse valeur Bourse toutes nombre grande permet chaque France jamais action baisse risque banque moment heures belges seront raison Depuis quatre semble serait projet grands points Europe mesure public partir autant mettre trouve maison simple petite savoir jeunes Banque client sommes ventes dollar petits aurait choses tandis existe propre marque devant passer durant femmes autour centre vendre besoin aucune hommes donner titres moyens bureau estime pourra faible droits Michel manque charge nature second actuel lequel chacun double celles papier termes permis guerre rendre unique suffit soient Pierre presse longue revenu retour taille parler propos suivre preuve social bonnes divers budget montre rester faudra Chaque devenu rapide milieu patron actifs accord lancer proche volume coupon nouvel change laisse moteur sommet Robert lignes figure livres disque quelle limite commun wallon effets devait profit cartes facile devise Street croire choisi perdre telles ouvert annuel arrive regard Belges rachat portes sortir fiscal puisse rythme penser fusion acquis totale voyage Certes chemin ancien assure garder climat rating Anvers Fortis faveur droite direct tonnes chance pertes sortie Solvay active fruits visite images enfant formes couple offrir pleine vision retard certes porter locaux gagner touche devoir statut saison Suisse obtenu repris occupe gauche marche normes objets return jardin option partis prises secret avions villes servir belles source soleil police pouvez primes auquel leader calcul cadres Toutes danger langue classe auront marges fleurs record Sabena dehors places textes membre photos pointe solide entier caisse entend lettre normal Centre tourne ajoute attend litres argent Quelle global fameux veille samedi palais emploi outils hasard course billet avenir Royale soumis monter Italie quinze Russie demain fortes furent placer Claude notion visage firmes achats design forces suisse tarifs faites locale courte menace agents parfum pierre salles connue beurre Albert trafic humain talent remise tenter indice neutre voient Canada entrer postes savent avance Morgan signal charme finale Daniel broker dettes grosse allait seules espace refuse Encore chasse profil conclu vendus tomber erreur appris Guerre rouges devons format disent future Durant trente claire filles agence atouts bouche quitte avenue bijoux connus stocks jouent signes stress Martin Source dessin repose combat allant visant impose usines loyers stable Compaq courts prison viande esprit aspect manger ouvrir riches sentir Autant feront oblige fraude mobile lourde Maison mandat former dirige pistes chimie Quatre avocat Invest Office auteur civile courbe tables vivent blancs couche survie retenu sujets cuivre offert clause minute latine XVIIIe ordres accrue Dewaay Groupe offres lourds morale parole Palais effort impact Sambre Marcel tissus cadeau revoir couvre envers coffre fibres Pascal battre vivant Chacun sortes traite Bruges suivie futurs graves pommes motifs Thomas traces pilote Berlin souris levier majeur appels verser pousse craint paquet diesel Louise Boeing Didier cercle livrer inclus routes libres vendue ferait Compte frappe utiles royale fourni poudre ================================================ FILE: mocodo/resources/lorem/lorem.txt ================================================ aberat abesset abest abiit abire abit ablatiuo absens absentis absit absolui absolutus abstinentia abstinere abstinuit abstulit absunt absurdum abunde accedat accedere accedit accedunt accendit acceperit acceperunt accepimus accepisset accepit acceptam acceptis accepto acceptum accesserit accessit acciderit accidisset accidit acciperent acciperet accipiat accipienda accipiendum accipiet accipimus accipitur accipiunt accius accusari accusationem accusationis accusatoris accusatus acerba acerbum acerrime aceto acetum achaia achillis acies acres acrior acris acriter acrius actae actiones actionibus actionis actis actoris actum acturus actus actutum acuto addere addidit addita additis addito additur adduci adductus addunt adduxit ademit adempta ademptum adeoque adeptus aderant aderit adesset adest adfectibus adfectus adferat adferre adfert adferunt adfici adfirmat adfuit adgressus adhibenda adhiberi adhibetur adhibita adhuc adicere adicies adicitur adiciunt adiecit adiecto adiectum adierit adiit adipiscing adire aditam aditus adiuuat adloquitur administrationem admirationem admisit admissum admittere admittit admodum admonet admota adoptionem adortus adposita adquirere adquiritur adsentior adsequi adsidue adsit adsunt aduenit aduentus aduerbium aduersariis aduersariorum aduersarius aduersis aduerso aduersus adulescens adulescentis adulteriis adulterio adulterium adulterum aedes aedibus aedificare aedificia aedificiis aedificiorum aedificium aedilis aedilium aedis aedium aeger aegre aegris aegritudinem aegritudo aegro aegrum aegypto aegyptum aelius aemilio aemilius aenean aeneas aequaliter aeque aequinoctium aequissimum aequitatem aequitatis aequius aequom aequore aequos aequum aerario aerarium aere aeris aestas aestatem aestatis aestimari aestimationem aestiuo aestus aetas aetatem aetatis aeterna aeternum aethere aetheris aetolis aetolorum aetolos aeuo aeuum affectus afranius africam africanus afuit agam agant agas agatur agebatur agedum agendo agendum agens agentem agerent ageretur agetur aggere agimus agis agitare agitat agite agitur agmen agmine agnoscere ago agricolae agrippa agris agrorum agros agrum aguntur aiax aiebat ait aiunt alas albam albano albis albo album alcides alere ales alexander alexandriam alexandro alexandrum alfenus aliam aliaque aliarum alias alibi alicuius alienam alienus aliisque alimenta alioquin aliorum alios aliquam aliquamdiu aliquanto aliquantum aliquas aliquem aliquet aliquos aliquotiens alis aliter aliubi alium aliunde alius alma alpes alpibus altae altaria alteram alterius alterna alternis altero alterum altior altis altitudinem altitudinis altitudo altius altus alueo alui aluminis aluo aluus amabo amans amantes amanti amari amas amator amauit ambiguum ambitione ambitus ambobus ament amet amicam amice amicis amicitias amicorum amicos amictus amicus amiserit amisit amissa amissis amisso amissum amittere amnis amoris ampla amplectitur amplexus amplissima amplissimis amplius analogia anceps ancillam ancipiti anguis angulo angusta angustiis angusto angustum animaduertere animaduertit animae animalibus animalium animis animorum animus annalibus anne annis annonae annorum annus ante antea antequam antiochus antiquam antiquitus antiquos antiquum antonii antoninus antonio antonius antris antro anulum anus anxia anxius aperire aperit aperte apertis aperto apertum apes apibus apii apium apollinem apollinis apollo apparatus apparent appareret apparet apparuit appellamus appellantur appellari appellata appellatione appellatus appellauit appello appio appius apte aptius aptus aput aquam aquarum aquas aquilae aquiliae aquis aras aratro arbiter arbitrabatur arbitrantur arbitratu arbitrium arbitror arbitrum arbores arboribus arboris arborum arbusta arcadiae arcana arcu arcus ardens ardentem ardet ardore ardua arduus area argento argentum argos argumenta argumentis argumento argumentum arida aries aristoteles armaque armata armatis armatorum armatos armatum armatura armatus armenta armis armorum ars artes artibus artifex artifices artificio artis artium artus arua aruis ascendit asiam aspectus aspera asperum aspexit aspice aspicit asse assidue astra astris astu athenas athenienses atheniensibus atheniensis atheniensium athenis atilius atque átque atqui atra atris atrox attalus attamen attico atticus attigit attinet attingere attulit auaritiam auarus aucta auctores auctoribus auctoritas auctoritatem auctoritatis audaciam audacter audax audeat audeo auderet audet audiat audiendum audierat audio audiret audiri audisset audistis audite auditis auditor auditus audiunt auem auersa auertere auertit aues auferre aufert augeri auget augue augures augurium augusto augustus auia auibus auide auidus auis auium aula auo auras auream aureis aurem aureos aures aureus auribus auris aurium aurora aurum ausi auspiciis auspicium auster ausus autem autumno auus auxilia auxiliis auxilio auxilium auxit axe axis baccho balbus barbam barbara barbaris barbarorum barbarus beatam beati beatus belle bellis bellorum bellum belua beneficia beneficiis beneficio beneficium beneuolentia benigne bestiis bibendum bibere bibitur biduo biduum biennio biennium binos bis blanda blandit bonam bonis bonorum bonus bos boues boum bracchia brachia breues breuibus breuior breuis breuitate breuiter breuius brumam brundisium bruto brutus bubus bus cacumine cadat cadauera cadere cadit cadunt caeca caecilius caecina caeco caecus caedes caedibus caedis caelestia caelestibus caelestis caelestium caelio caelius caeloque caelumque caerula caesarem caesaris caesus calamitas calamitatem calamitatis calce calida calidis calido calidum caligine callistratus calorem calumniae camillus campaniae campus cancer cancri candido candidus candore canem canere canes canibus canit cantus canum capax caperet capiat capillos capillum capite capitibus capitis capitolio capitolium capitur capiunt caprae captis captiua captiuis captiuorum captiuos captus capuam caput cara carcerem cardine carent carere caret carinae carissime caritas caritate carmen carmine carminibus carminis carnis caro carthaginem carthaginienses carthaginiensibus carthaginiensium carthaginis carthago carus casibus cassio cassius casta castellum castra castris castrorum casus catenis catilinam catonem catonis catulus caua caudam cauendum caueri cauetur cauit caule causam causarum causis cautionem cautum cccc ceciderunt cecidisse cecidit cedat cederet cedit cedo cedunt celeritatem celeriter celerius celerrime cella celso celsus cenam censet censores censoribus censuit censum censura census centum centuriones centurionibus ceperat ceperunt cepisset cepit cerae cerebrum cererem cereris ceres cernere cernimus cernis cernitur cerno certae certamen certamine certaminis certare certatim certe certiorem certis certius certus ceruicem ceruicibus ceruix cessare cessat cesserit cessit cestius ceterae ceteraque ceterarum ceteras ceterisque ceterorum ceteros ceterum ceu chrysippus cibaria cibus cicatrices ciceronem ciceronis ciet ciliciam cineres cinis cinna circa circiter circo circuitu circulo circulus circumdata circumire citius cito citra ciues ciuibus ciuilem ciuilia ciuilibus ciuilis ciuis ciuitas ciuitates ciuitatibus ciuitatis ciuitatium ciuitatum ciuium clades cladibus cladis clamat clamorem clamoribus clara clarissimi clarius claro clarus classes classibus classis claudio claudius clausa claustra clausum clementiam clipeo clipeum clodio clodius coacta coactus cocta coctum codicillos coegit coeperat coepere coeperit coeperunt coepisset coepit coepta coeptum coetus coeunt cogatur cogendus cogere cogetur cogitare cogitationes cogitationibus cogito cogitur cognita cognitionem cognitis cognito cognitum cognomen cognomine cognoscere cognoscite cognouit coguntur cohors cohortes cohortibus cohortium coire coit colere colitur colla collecta collegam colles collibus colligitur collis collo collum colonias colonus colores coloribus coloris columnis colunt comis comitatus comites comitia comitiis comitiorum comitum commeatus commemorat commendat commendo commentariis comminus commisit commissum committere committitur commode commodius commodo commodum commotus communes communibus communiter comparatione comperit competat competere competit complecti complexus complures compluribus componere composita compositis composito compositum composuit conatus concedere conceditur conceptus concessit concessum concidit concilium concordiam concurrere concurrunt concursus condemnari condemnatus condere condiciones condicionibus condicionis condictionem condidit conditum conducto confecit confectum conferre confert confessus confestim conficere conficitur confido confirmat confiteor confiteri confugit confusa congue coniecturam coniugium coniuncta coniunctionem coniunctum coniunx coniurationis conlatis conlocauit conloquium conscia conscientiam conscius conscripti consectetur consecuti consecutus consedit consensus consentire consequat consequatur consequens consequitur considerandum consiliis consiliorum consilium consistere consistit conspectum conspexit constabat constanter constantiam constaret constet constitit constituere constituitur constituta constitutione constitutionibus constitutus consuerunt consuetudinem consuetudinis consuetudo consueuit consulares consularibus consularis consulatus consulere consules consulibus consulis consultus consulum consumpta contemnere contemplatione contendere contendit contendunt contentione contentus contestatam contigit contineat continentia continentur contineri continetur contingat contingere contingit continuis continuo contionem contionibus contracta contractus contrahit contraque contrariam contrariis contrario contrarium contraxit controuersiam contulerunt contulit contumeliam conubia conuenerit conueniatur conueniens conueniet conueniri conuenisse conueniunt conuentus conuersus conuertere conuertit conuicium conuiuiis conuiuium convallis copiam copiarum copiis copiose coques coquitur coram cordi coriandrum corinthum cornelius cornibus cornua coronis corpore corporibus corporis corporum corpus correpta corruptum cortex cortice coss cotidiana cotidie cotta cottidie cras crassitudine crassitudo crasso crassus creati creatus creauit crebra crebris crebro credat credebant credebat credendum credens crederent crederet credibile crediderit credidit credimus credis credite creditis creditores creditoribus creditoris creditorum creditur credo credunt crescere crescit crescunt cretam creuit crimen crimine criminibus criminis criminum crines crinibus croci crucem cruciatus cruda crudelem crudelitas crudelitatem crudelitatis cruenta cruorem crura cruribus cubiculum cubile cubilia cubitorum cuicumque cuidam cuiquam cuique cuiuscumque cuiusdam cuiusquam cuiusque culpam culta cultum cultura cultus cum cúm cuminum cumque cuncta cunctos cupere cupide cupidine cupiditas cupiditates cupido cupidus cupiens cupit curabitur curae curam curandum curant curare curarum curas curationem curatores curauit cures curiam curis currere currit currus cursus cuspide custodes custodiam custodibus custos cutem cutis cxx cyathis cynthia dabant dabatur dabimus dabis dabitur dabo dabunt damnari damnata damnationem damnatus damnauit damno damnum damus danai danaum dandam dando dandum dantur dapes darem darent dares daretur dari das datas date datos datum daturus datus deam debeam debeant debeas debeatur debebit debebunt debemus debentur debeo deberent deberetur deberi debes debetis debetur debitam debiti debitoris debitum debuerit debuisse debuit deceat decedat decedere decem deceptus decernere decesserit decessisset decessit decet decidit decimo decimum declarat decocto decoctum decore decoris decorum decreta decretis decreto decretum decreuit decuit decumas decus dedecus dederat dedere dederit dederunt dedimus dedissent dedisset dedisti deditionem deditus deducere deducitur deducto deductus deduxit deerat deesset deest defectionem defendat defenderet defenditur defensionem defensionis defensor deferre defertur deficit definitio deformis defuisse defuit defunctus dehinc deiecit deiectus deinceps deinde deis delata delatus delectant delectat deliciis delictum delubra demens dementiae demetrius demissa democritus demonstrat demosthenes demus denarios denique densa dentes dentibus dentium denuo deorsum deorum deos deponere depositum deposuit deprehensus deque descendere descendit deserit deserta deseruit desiderant desiderare desideret desiderio desiderium desierit designatus desiit desinat desinere desinit desinunt desit desperatione destitit desunt deteriorem deterius detracto detrahere detrahit detraxit detrimento detulit detur deus dexteram dextras dextro dextrum diam dianam dicamus dicantur dicas dicatur dicebantur dicebatur dicemus dicendo dicendum dicens dicentem dicentur dicerem dicerentur diceres diceretur dices dicetur dicimus dicis dicitis dicitur dico dictam dictatorem dictatoris dictis dicto dictum dicturus dictus dicuntur didicit dido diebus diem dierum dies differentia differre differt differunt difficilem difficilis difficilius difficultas difficultatem difficulter digestorum digitis digitorum digitos digitum dignam dignissim dignitatem dignitatis dignus diis dilectus diligenter diligentiam diligentissime diligentius diligit dimicare dimidiam dimidio dimidium dimisit dimissa dimissis dimisso dimittere dionysius dirae dirum discedere discere discessit discessum disciplinam disciplinis discipulus discordia discrimen discrimine discutit dispositis disputationum dissensio disseruit dissimilis dissimulare distat distinctio ditis diua diuersis diuerso diuersum diues diuidere diuiditur diuidundo diuinam diuinis diuinitus diuino diuinum diuisa diuisione diuisit diuites diuitiarum diuitiis diuitis diuortium diuos diutius diuturna diuus dixerat dixere dixerit dixero dixerunt diximus dixisset dixisti dixit docent docere docet docti doctrinae doctus docuit dolabellam dolere dolet dolis dolor dolores doloribus doloris dolus domestica domesticis domibus domicilium dominam domine dominis dominium dominorum dominus domitio domitius domus donare donata donationem donationis donatus donauit donec donis dono donum dorso dos dotem dotis drachm druso drusus duabus duae duarum duas dubie dubiis dubio dubitari dubitas dubitatione dubitauit dubitet dubito dubius ducat ducebat ducem ducenta ducentos duceret duces ducibus ducis ducitur ducta ductor ductus ducum ducuntur dudum dui duis dulcedine dulcis dummodo dumque dumtaxat duobus duodecimo duorum duos duplex duplicem duplici duplum durare durat durior duris duritia durius duro durus duxerit duxit eademque eaedem eamque eamus eandem eaque earum easdem easque eat ebur ecastor ecce eccum ecquis edepol edere edicto edictum edidit edito editum edixit eduxit effecit effectus efficacius efficere efficiat efficitur efficiunt effigies effudit effugere effugit effuso egens egere egerit egerunt egestas eget egisset egit ego égo egomet egone egredi egregie egregius egressi egressus eho eidem eique eisdem eiusdem eiusmodi eiusque eleganter elegit eleifend elementa elementum elephanti elephantos eligere elit elocutio eloquentiam emancipato emancipatus emendat emere emerit eminet eminus emisit emisse emit emolumentum emplastrum empta emptionem emptorem emptoris emptum enim enimuero enixa ennius ensis eodemque eoque eorumque eosdem eosque ephesi epicurus epistulam epistularum epistulis epitheton epitomarum epitomatorum epulis equestris equidem equis equitatus equites equitibus equitumque equorum equus eram erant erat erciscundae erepta ereptum ergo erimus eripere eripi eripuit erit ero eros errant errare errat errorem erroris erumpit erunt erus esse ésse essemus essent esset est ést est” estis estne esto ét etenim etiamne etiamnunc etiamsi etruriam etsi euadere euasit euenire euenit euentus euismod eumenes eumque eundum eunt europam exacta excedere excedit excepit excepta exceptionem exceptis excepto exceptum excessit excidit excipere excipit excitat exclamat excusationem exeat exegit exemplar exemplis exemplo exemplum exercere exercet exercitatione exercitibus exercitus exercuit exeunt exheredatus exhibendum exhibuit exigat exigere exigit exiguo exiguum exilio exilium eximia exinde exire existere existimabat existimant existimari existimas existimationem existimatur existimauit existimes existimo exitio exitium exitus exoritur exorta exortus expectare expectat expediat expedire expedit experimentum experiri expers experti expertus explicare exploratum exponam exponere expositis exposuit expressit expressum exprimere exprimitur expulit exspectare exspecto exstat exstiterit exstitit extat extemplo externa externis extis extiterit extitit extraneo extremam extremis extremo extremum extrinsecus extulit exuit exul fabae fabio fabius fabulis facem facerem facerent faceret faces faciamus faciant faciat facibus faciebant faciebat faciem faciendam faciendum facient faciet facilis facilius facillime facimus facinoris facinus facis facito faciunt factis factorum factos factum facturus factus facultates facultatibus facundia falce falcidiam fallax fallere fallit falsa falsis falso falsum famam fames familiaris familiariter familiarum familias fanum farina fasces fastidium fastigium fata fateor fateri fatetur fatis fatorum fatus fauces faucibus fauorem faxo febris fecerant fecerat fecere fecerint fecerit fecerunt fecimus fecissent fecisset fecisti fecit fecunda fefellit felices felicior felicitas felicitatem feliciter felis felix felle feminam feminarum feminis feram ferant ferarum ferat ferebant ferebatur ferenda ferendum ferens ferentem ferit ferme fermentum ferocem ferox ferrea ferrent ferret ferri ferroque ferrum fertilis fertur feruntur ferus fessa fessus festa fetus feugiat fiant fiat fico ficta ficus fida fideicommissi fideicommissorum fideicommissum fideiussores fides fidius fiduciam fiebant fiebat fient fierent fieret fieri fiet figuris fila filiis filiorum filius fimo fimum fines fingere fingitur finibus finis finita finitimis finitur finium finxit firma firmum fisco fiscum fistula fit fiunt fixa flacco flaccus flagitium flammis flatus flectere flens flere fletus flores floribus flos fluctibus fluctus fluere fluit flumen flumine fluminibus fluminis fluminum fluuio fluuius focos foeda foedere foederis foedus folia foliis foliorum folium fons fontes fontibus fontis foramina foras forent foret foribus foris formas formidine formulam foro forsan forsitan fortassis fortes fortia fortibus fortior fortissimi fortissimus fortiter fortitudo fortius fortuito fortunis forum fossas fracta fractis fragor frangit frater fratres fratribus fratris fratrum fraudem fraudis fraus fregit frena frenis frequentes frequentia frequentius freto fretus frigida frigidus frigore frigoris frigus frondes frondibus frons frontem frontis fronto fructibus fructuario fructuarius fructus fructuum fruges frugibus frugum frui frumentum frustra fudit fuerant fuerat fuere fuerint fuerit fuero fuerunt fugam fugere fugiens fugientes fugisse fugitiuum fugiunt fuimus fuissent fuisset fuisti fuit fulmen fulmine fulminis fuluio fuluius fumo functus fundamenta fundis funditus fundus funere funeris fungi funus furem furens furere furiis furiosi furiosus furit furius furorem furoris furta furtim furto furtum fusce fusi fusus futuram futurus gaio gaius galbae galeam gallias gallis gallorum gallus gaudent gaudeo gaudere gaudet gaudio gaudium gelu geminos geminum gemitus gemmis genas generaliter genere generibus generis genero generum genetiuus genetrix genibus genitor genitus gens gentes gentibus gentis gentium genua genuit genus gerat gerebat gerens gereret gerit germaniae germanici germanorum germanos geruntur gesserit gessisse gessit gestae gestarum gestis gestorum gestus gignitur gignuntur gladiatores gladius gloriam gloriosum graccho gracchus gradibus gradus graecae graece graeciam graecis graecorum graecus gramine grana grande grandis grata grates gratias gratior gratissima gratissimum gratius gratus graues grauia grauiora grauissima grauissimum grauitatem grauiter grauius gravida greges gremio gressus gurgite gustu habeam habeant habeas habeatur habebam habebant habebatur habebit habebo habebunt habemus habenas habendum habenis habens habentes habenti habentur habeo haberemus haberent haberes haberetur haberi habes habetis habeto habetur habitant habitare habitat habito habitum habiturus habitus habuerat habuere habuerint habuerit habuerunt habuisset habuit hactenus hadrianus haec haeret haesit hanc hannibalem hannibalis harenis harum harundine haruspices hasdrubal hastis haudquaquam hausit haut hector hemina hendrerit herbam herbarum herbis hercle herculis heredes heredibus heredis hereditaria hereditas hereditatem hereditatis heredum heres heri hermogenianus heros heus hiatu hiberna hibernis hic hiemis hiems hinc hisce hispaniam historiam hoc hóc hodieque homero homerus homines hominibus hominis hominumque homo honeste honestius honesto honestum honores honoribus honoris honorum honos horas horatius hordeum horis horret horrida horror hortari hortatur hortensius hortos horum hospes hospita hospitium hostes hostia hostibus hostili hostis hostium huc huic huiuscemodi huiusmodi humanae humanarum humanis humanitas humanitate humanitatis humano humanum humilis humo humus hunc iacentem iacere iacet iactare iactat iactura iaculis iamdudum iamne iamque ianua iason iauolenus ibat ibidem ibique ibit ibo icta ictibus ictus idcirco idemque identidem ideoque idibus idonea idoneus idque idus iecur igitur ignari ignarus ignes ignibus ignis ignium ignominiam ignorante ignorantia ignorare ignorat ignorauit ignoro ignoscere ignota ignotis ignotum iidem iiii iisdem ilia ilico ilium illam illarum illas ille illic illinc illis illius illorum illos illum illyricum imagines imaginibus imago imber imbres imbribus imis imitari imitatus immortales immortalibus immortalis immortalium imo impedimento impedit imperare imperatores imperatoribus imperatoris imperatorum imperatum imperauit imperdiet imperia imperiis imperio imperium impetrare impetrauit impetus impia impius implere implet impleuit imponere impositum imposuit improbe improbus improuiso impuberi impubes impulit impune imum ín inanis incedit incendium incepto incerta incertis incerto incertus incessit inciderit incidit incipere incipiat incipit incipiunt inclusa inclusum incolae incolumis incommodo incommodum incredibile incredibilis incrementum index india indicant indicat indicio indicium indigna indignum indos inducere inducitur inductus induit indulgentia industria induxit inermes iners inertia inesse inest infamiam infamis infans infantes infecti infelix inferes inferiores inferos inferre infert infesto infestus infinitum infra ingenia ingeniis ingenio ingenium ingens ingentes ingentibus ingentis ingrati ingratus ingredi ingressus inibi iniecta inimica inimicitias inimicorum inimicus iniquo iniquum inire initia initiis initio initium iniuriarum iniuriis iniussu inlinitur inlita inlitum inmerito inmortales inmortalibus innocentem innocentiam innumerabiles innumerabilia inopiam inops inponitur inpositum inposuit inpune inquam inque inquies inquit inrita insaniam inscribitur insequenti insequitur insidiarum insidiis insignes insignibus insignis insita instare instat instituere instituerit instituit instituta institutionum institutis instituto institutus instructa instructus instrumento instrumentum instruxit insulis insuper integer integram integris integro integrum intellectum intellegamus intellegas intellegatur intellegendum intellegere intellegimus intellegis intellegitur intellego intelleguntur intellexit intendere intendit intentio intentus intercessit interdicto interdictum interdiu interdum interea interemit interemptus interesset interest interfecit interfectis interfecto interfectus interficere interfici interfuit interim interiore interitus interposito interque interrogatus intersit interualla interuallis interuallo interuallum interueniente interuenit intestato intestina intrare intrat intrauit intro intuens intueri intulit intus inuadit inuasit inuectus inuenerit inuenies inuenimus inuenio inueniri inuenisse inuenitur inueniuntur inuenta inuentus inuicem inuidiam inuisa inuisum inuita inuitis inuito inuitus inutilem inutiliter ioco iouem iouis ipsam ipsaque ipsarum ipsas ipse ipsis ipsius ipsorum ipsos ipsum ipsumque ipsus iracundiam iram irascitur irati iratus iret iri isdem isque isse istaec istas iste istis istius istoc istorum istos istum italiam itane itaque itemque iterumque itidem itinere itineribus itineris itur iuba iubeat iubebat iubent iubeo iuberet iubes iubetur iucunda iucundius iucundum iudex iudicari iudicatae iudicatus iudicauit iudices iudicia iudicibus iudiciis iudiciorum iudicis iudicium iudico iudicum iuga iugera iugerum iugis iugo iugum iugurtha iuliam iulianus iulio iulius iumenta iumentorum iuncta iunctis iungere iungitur iunguntur iunio iunius iunonem iunonis iunxit iuppiter iurando iurandum iurare iurauerit iurauit iure iurgia iuris iusiurandum iussa iusserit iusserunt iussisset iussit iussus iustam iuste iustis iustitiam iustius iusto iustus iuuant iuuare iuuat iuuenalis iuuenis iuuentae iuuentute iuuentutis iuuenum iuxta justo kalendis labeonis labitur laborare laborat labores laboribus laboris laborum labra labris lacedaemoniis lacedaemoniorum lacertis lacrimis lacte lactis lacus laelius laeta laetitiam laeto laetus laeuo laeuum lanae lapides lapidibus lapidis lapidum lapis lapsus lares lata latebras latenter latere lateribus lateris laterum latet latine latinis latinorum latinus latis latitudinem latitudo latius lato latrones latronum latus laudari laudatur laudes laudibus laudis lauro laurus laus lecta lectica lectis lecto lectulo lectus legari legatario legatarius legationis legatis legatorum legatus legauerit legauit legem legere leges legetur legibus legimus legiones legionibus legionis legionum legis legitimam legitimum legitur lego legum legunt lenis leniter leno lento lentulo lentulus lentus leo leonis lepido lepidus leporem leporis leto letum leuat leues leuia leuibus leuiora leuis leuiter leuius lex libeat libellos libellum libens libenter libentius liberae liberalis liberalitate liberam liberari liberatus liberauit liberetur liberis liberius libero liberorum liberos libertatis libertis libertorum libertus liberum libet libidinis libido libris librorum libros librum libyae liceat licebit licentiam liceret licet licinius licuit ligno lignum ligula ligures ligusticum limen limine limites limo linea linguam lino liquamen liquamine liquido liquor lis litis litore litoribus litoris litterarum litteris litus liuius lobortis locato locauit locis locorum locos locum locupletior locus locuti locutus longas longeque longinqua longior longis longitudinem longitudo longius longus loquar loquatur loquendi loqueretur loquimur loquitur loquor loquuntur lora lorem lubet lubrica lucanus lucet lucilio lucilius lucis lucius lucos lucretius lucro lucrum luctus lucus ludere ludis ludorum ludos ludum lumen lumine luminibus luminis lunam lupi lupus lustra lusus luto luxuriam lxxx lydia lyra macedones macedoniam macedonum macer maecenas maerore maesta maeuio maeuius magisque magister magistratibus magistratus magistratuum magistrum magna magnam magnaque magnas magne magnis magnitudinem magnitudinis magnitudo magnopere magnoque magnorum magnus mago maiestatem maiestatis maiora maiores maioribus maioris maiorum maius malam male malit mallet malorum malos maluit malum malunt malus mancipium mandare mandata mandatis mandatum mandauit maneat manebit manente maneret manet manibus manifesto manifestum manlio manlius mansit manumiserit manumissus manumittere manusque marcellus marcianus marcio marcius marco marcus mares maria marina mario marique maris maritimis marito maritus marius marmore mars martem martis masculino massa materiam materies matres matrimonio matrimonium matris matronae mature maturius matutino mauors mauris maximas maximeque maximus maxume mé meam mearum meas mecum medea medentur mederi medetur medias medicamenta medicamentis medicamento medicamentum medicinam medicis medico medicus mediis mediocris mediocriter medius medulla mehercules meis meliora melioris melius melle mellis membra membris membrorum membrum memento meminisse meminit memorabile memores memoriam mendacium menses mensibus mensis mensuram mentam mentes mentibus mentionem mentiri mentis meorum meos meque mercedem merces mercurius mereri meretricem meretrix meridiem merita meritis merito meritum mero meruisse meruit merum metello metellus metri metuens metuit metus meus mihi míhi mihique miles milia milibus militarem militaria militaribus militaris milites militiam militibus militis militum milium mille milonis minas minatur mineruam minimo minimum ministerio ministerium ministros minora minores minoribus minoris minucius minuit minus mirabile miraculo mirandum mirantur miraris miratur mire mirifice miror mirum miscentur miscere miscetur miscuit miserabile miserat miserere misericordiam miseros miserum miserunt misisset misit missae missus mitis mittam mitteret mittis mittitur mitto mittunt mixto mixtus moderatione modestinus modice modicis modico modicum modios modus moenia moenibus molem moleste molestia molestie molestus moliri molles mollia mollibus mollior mollis molliter mollius momento momentum moneo monere monet monstrat monstrum montes montibus montis montium monumenta monumentis monumentum moram morari moras moratus morbi morbis morborum morbus morem moreretur mores moriar moriatur moribus moriens morietur moris moritur moror morsus mortales mortalibus mortalium mortem mortis mortua mortuus morum mos mota motibus motus moueat mouentur moueri mouetur mouit mox mucius muliebri mulieres mulieribus mulieris mulierum mulso multam multaque multarum multas multisque multitudinem multitudinis multitudo multoque multorum multos multumque multus mundo mundus munere muneribus muneris munia munimenta munitiones munus muris murmure muros murrae murus musae musti mustum mutari mutata mutatio mutato mutatur mutauit mutuam mutuo nactus naeuius namque nanctus nares naribus narrare narratio nascatur nascens nascitur nascuntur naso natae natalis natam nate nationes nationibus natos natum naturae naturalis naturaliter naturam natus naualibus naues naufragio naufragium nauibus nauigia nauis nauita nauium nautae nautis nec necat necdum necem necessariam necessariis necessario necessarius necessest necessitas necessitatem necne nedum nefarium nefas negant negare negat negauit neget neglegentiam negotia negotiis negotiorum negotium neminem nemini nemora nemorum nempe nemus nepos nepotes nepotibus nepotis neptis neptuno neptunus nequam nequaquam nequeo nequiquam neratius neronem neronis nerua neruis neruorum neruos nesciat nescio nescire nescit nescius neue neuter neutro neutrum nibh niger nigrae nigris nigro nigrum nihilominus nihilque nilo nilus nimiam nimio nimirum nimis nimius nisl nisu nititur nitro niues nobilis nobilitas nobilitatem nobilitatis nobilium nobiscum noceat nocens nocentes nocere nocet noctemque noctesque noctibus noctis nocturnis nocturno nocuit nolite nolle nolo noluerit nolunt nomenque nominantur nominatim nominatiuo nominatiuus nominatur nominauit nomine nominibus nominis nominum non nón nondum nonne nonnulli nonnumquam nono nonummy noscere nosmet nosse noster nosti nostris nostrorum nostros nostrum notam notandum notat notis notitiam notus nouas nouem nouercam nouimus nouissime nouissimum nouitate nouus noxae noxia nubem nubere nubes nubibus nubila nuces nuda nudis nudo nudus nulla nullis nullius nullus numa numen numerare numerus numidae numine numinis numinum nummis nummorum nummos nummum numquam numquid nunc núnc nunquam nuntiatum nuntius nuper nupta nuptiae nuptiarum nuptiis nurus nusquam nutrix nutu nymphae obesse obicere obicitur obiecit obiecta obiectum obiit oblata obligari obligationem obligatus oblita oblitus obnoxia obruit obscuro obscurum obsecro obsequium obseruandum obseruari obseruatio obsides obsidionem obstare obstat obtinere obtinuit obtulit obuiam obuius occasionem occasus occidentem occiderat occiderit occidisset occidit occiso occisus occulto occultum occupare occupatus occupauit occurrere occurrit oceano oceanus ocius octauius octauo octoginta oculis oculorum oculos oculum odio odisse odit odium odoris offendit offerre officia officiis officiorum officium oleae oleo oleum olim oliuae olympo omen omine omissa omissis omisso omittam omitto omnemque omnesque omniaque omnibusque omnimodo omnino omnipotens omnisque omnium onere oneris onus opem operam operarum operas opere operibus operis operum opes opibus opiniones opinionum opinor opis oporteat oportebit oporteret oportet oportuit oppida oppidis oppido oppidum opportune opposita oppressit oppressus oppugnare optare optat optauit optime optimis optimo optimus optio opto optume opum opust oraculum oram orare oras orationes orationibus orationis oratores oratoribus oratoris oratorum orbis orci ordines ordinibus ordinis ordinum ordo ore oriens orientem orientis originem origo orion oris oritur oriuntur ornamenta ornamentis ornamentum ornare ornata ornatus oro orsus orta ortus oscula ossa ossibus ostendat ostenderet ostendimus ostenditur ostendunt ostia ostium otio otium oua ouis ouium pabulo pabulum pacem pacis pacto pactus paene paeniteat paenitentia paenitet palam palatio palatium pallas pallida palmis paludes palus pamphilum panem panis pansa papaueris papiam papinianus papirius parabat parant parati paratus parcere parcius parem parens parentes parentibus parentium parentum parere paret paria paribus parietes parietibus paris pariterque pariunt parricida parricidio parricidium pars parta partes parthis parthorum parthos partibus particeps participium particula partis partium partus paruae paruis parumper paruo paruum pascua passa passim passo passus passuum pastores patent paterentur pateretur paternae paternis paternum patet patiantur patiar patiatur patiens patientiam patietur patior patitur patiuntur patres patrias patribus patriis patrimonio patrimonium patrios patris patrium patronis patrono patronus patruo patruus pauca pauciores paucis paucorum paucos paulatim paulisper paulo paululum paulus pauore pauperem pauperis paupertas paupertatem pax peccare peccata peccatum pecore pecoris pecorum pectore pectoris pectus pecudes pecudum peculio peculium pecuniis pecus pede pedes pedibus pedis pedites peditum pedum peior peius pelago pelagus pelle pellentesque pellibus penates penatibus pendente pendere pendet penes penitus pennis peperit peracta percipi percussit percussus perdere perdidisse perdidit perdit perduxit pereat peregre peregrina pereunt perfectum perferre perfidia pergama perge pergit periculis periculorum periculosum periculum perierit perierunt periit perinde perire perisse peritus permisit permissum permittendum permittere permittitur permixta perniciem perperam perpeti perpetuam perpetuum perquam perque persae persarum persas persea persecutus persequitur perseus personarum personis perspicere perspicuum persuadere pertinacia pertinax pertineant pertineat pertinebit pertinent pertineret pertinet pertulit peruenerit peruenerunt perueniat perueniret peruenisset perueniunt peruentum pessimum pestem pestilentia pestis petam petatur petebant petebat petenda petendum petens petentes petentibus peterent peteretur petet petierit petiit petimus petisse petita petitionem petitur petiuit peto petunt pharetra philippus philosophiam philosophis philosophorum philosophus phoebo phoebus phrygiae pia pice picta picturae pietas pietatem pietatis piget pignore pignoris pignus pii pila pilis pingue pinguis pinnis pinus pio pipere piperis piratis pisces piscibus piscis piscium pisonem pisonis pius placeat placebat placent placerat placeret placet placido placuisse placuit plagis plano planum platonem platonis plautium plautus plebis plebs plenam plenis plenius pleno plenus pleraque plerique plerisque plerosque plerumque plinius plurali plures pluribus plurimos plurimum pluris plurium plus pluuiae pocula poenam poenarum poenis poenorum poenus poetam poetarum poetis polenta pollicetur pollicitus pollio polo poma pomis pompa pompeio pompeius pomponius ponam pondere ponderis pondo pondus ponere ponitur pontem pontifex pontifices pontificum ponto pontus ponuntur popularis populique populis populoque populorum populus porro portat porticus portionem porttitor portus poscere poscit positis posito positus possedit possemus possent possessionis possessorem posset possideat possideret possidet possimus possint possit possumus possunt posteaquam postera posteriore posterioribus posterius posteros posterum posthac postliminio postquam postremum postridie postulabat postulare postulauit postulo postumius postumus posuere posuerunt posuisse posuit pota potens potentem potentiam poteram poterant poterit potero poterunt potestatem potestatis potestis potionem potior potissimum potitus potius potuerint potuerit potuerunt potuissent potuisset potuisti potuit potus praebent praebere praebet praebuit praecedit praecepit praecepta praeceptis praeceptum praecipere praecipites praecipiti praecipue praecipuum praeclare praeclarum praecordia praedam praedia praediis praediorum praedium praedonum praeerat praefecti praefectus praeiudicium praemia praemiis praemio praemium praepositio praepositus praesent praesentes praesentia praesentibus praesentis praesertim praeses praesides praesidia praesidiis praesidis praesidium praestabit praestanda praestandum praestant praestaret praestari praestat praestetur praestiterit praestitit praesto praeterea praeterito praeteritum praeterquam praetores praetoria praetoribus praetoris praetorium praetorum praeturam praua precario preces precibus precor premit premunt pressa pressit pretio pretium priami pridem pridie primis primordia primores primoribus primus princeps principatum principes principia principibus principiis principis principium principum priora priores prioribus prioris priorum prisco pristinum priuata priuatis priuatorum priuatus priuilegium priusquam probandum probant probari probata probatio probatur probetur probo probus procedere procedit proceres processit proconsulis proculus procuratorem proderit prodesse prodest prodidit prodigia prodita proditionis proditum producitur producta proelia proeliis proelio proelium profectus proferre professus proficere proficiscitur proficit profugit profuit profundum progenies progredi progressus prohiberi prohibetur prohibitus prohibuit proin proinde proles promiserit promisit promissum promittere promittit promptu promunturium prona pronepos pronuntiatum pronuntiauit propemodum propere propinqua propinquos propinquum propior propius proponerentur proponitur proposito propositum proposuit proprias proprietas proprietatem proprietatis propriis proprio proprium propterea prorsus prosit prospere prosunt protinus protulit prouidentia prouinciae prouinciale prouinciam prouinciarum prouinciis prout proximam proxime proximus prudens prudenter prudentiam pubertatem pubes publicas publice publicis publicorum publicus pudeat pudet pudicitiam pudorem pudoris puellis pueris pueritia puerorum pueros puerum pugnam pugnandi pugnare pugnas pugnatum pugnauit pulcherrima pulcherrimum pulchre pulchritudinem pulchrum pulli pullos pulsi pulsus puluerem puluis pulvinar punico punicum pupillo pupillus puppis pure purgare purgat puro purpura purpureo purum purus pus putabam putabant putabat putamus putant putaret putas putatis putatur putauerunt putauit putem putent putes putetis puto quacumque quadam quadragensimo quadragesimo quadraginta quadrantem quadrato quaecumque quaedam quaenam quaeque quaeramus quaeras quaeratur quaerebatur quaerenda quaerendum quaereretur quaerimus quaeris quaeritis quaeritur quaero quaerunt quaesitum quaesiuit quaeso quaestiones quaestionibus quaestionis quaestionum quaestores quaestus quales qualia qualibet qualis qualitate qualiter quam quám quamdiu quamlibet quamquam quamque quamuis quandam quandoque quanquam quantis quantitatem quanto quantumque quantus quapropter quaque quare quartam quarto quartus quarum quasdam quasi quasque quatenus quater quatit quattuordecim queat quemadmodum quemcumque quemquam quemque quendam quercus queritur queror questus quí quia quibusdam quibusque quicquam quicquid quicumque quid quíd quidam quiddam quidem quidnam quidni quidque quidquid quidue quiduis quies quieta quietem quietis quinariae quinctius quindecim quinquagensimo quinquaginta quinquennium quinto quintus quippe quique quirites quiritium quis quisnam quispiam quisquam quisque quisquis quiuis quoad quocirca quocumque quod quód quodammodo quodcumque quoddam quodque quodsi quoius quolibet quominus quomodo quonam quondam quoniam quoquam quoquo quorum quorundam quosdam quosque quotannis quotienscumque quouis quousque rabies radices radicibus radicis radios radix ramos rapere rapido rapit rapta raptim raptor raptus rapuit raro rarum ratam rates rationes rationibus rationis ratus rebus recens recentes recentibus receperunt recepit recepto receptus recessit recipere recipiat recipit recipiunt recte rectissime rectius rector rectum recuperatores recusare reddam reddatur redderet reddidit reddita redditur reddunt redeat redegit redemit redeo redeunt redierunt rediit rediret redisse reditus reduxit referat referens referri refertur referuntur reficere refugit regem regere reges regiam regibus regiis regina regiones regionibus regionis regionum regit regium regnare regnauit regnis regno regnum regulam regularum regulus regum reis relata relatum relicta relictis relicto relictus religionis religiosum relinquatur relinqueret relinquitur relinquunt reliquas reliquerit reliquerunt reliquias reliquisset reliquit reliquorum reliquos reliquum remanere remanet remansit remedia remediis remedio remedium remisit remissa remittere remittit remota remotis remoto reos repente repentino reperiri reperiuntur reperta repertum repetere repetita repetundarum repperit rerumque rescripserunt rescripsit rescripto rescriptum resinae resistere respexit respice respicit respondent respondere responderit responderunt respondet respondisse respondit responsorum responsum restat restitit restituat restitueret restituit restituta restitutionem restitutus retia retinent retineri retinet retro rettulit reuerentia reuersus reuertitur reum reuocari reuocauit reus rex rhenum rhodiis rhoncus ridiculum ripis risus rite ritus robore roboris robur rogare rogas rogationem rogatus rogauit rogo rogum romam romanis romanorum romanus romulo romulus rore rosaceo rosae roscius rostra rostris rostro rota rudis ruere rufo rufus ruinis ruit rumore rumpere rumpit rupes rupit rupta ruptis rure ruris rursum rursusque rustica rusticus rutam rutrum ruunt sabinis sabino sabinus sacerdotes sacrae sacramento sacras sacrificio sacrificium sacris sacrorum sacros sacrum saecula saeculis saeculo saepe saepissime saepius saepta saeua saeuis saeuitiam saeuo saeuus sagittis salem salis sallustius salsa saltem saltus saluam salue saluos salus salutare salutem salutis saluum samnites samnitibus samnitium sanari sanat sancta sanctum sane sanguinem sanguinis sanguis sanitatem sanus sapere sapien sapiens sapientes sapientiam sapientis saporem sardiniam sata satin satisdare satius saturnia saucius saxa saxis saxo saxum scaenam scaeuola scaurus scelere sceleribus sceleris scelerisque scelerum scelus sceptra schema sciamus sciant sciat sciebat sciendum sciens sciente scientiam sciet scilicet scimus scio scipionem scipionis scirent sciret scis scitis scito sciunt scopulos scribas scribendo scribendum scriberet scribis scribitur scribo scribunt scripserit scripserunt scripsisset scripsisti scripsit scriptam scriptis scriptores scriptura scriptus scuta secat secreto secretum secum secundis secundo secundus secuntur secura securitatem securus secus secuti secutus sed sedecim sedem sedens sedere sedet sedibus sedis seditionem seditionis sedulo seges segetes segnis segnius seio seius sella sem semet semine seminibus seminis semper semperque sempronio sempronius senatores senatorum senatus seneca senectae senectutem senectutis senex senibus senior senis senserit sensibus sensim sensisse sensit sensus sententiam sententiarum sententiis sentiat sentio sentiret sentit sentiunt separatim septem septentrionem septimo septimum septuagensimo septuaginta sepulchrum sepulcrum sequantur sequar sequatur sequebantur sequebatur sequens sequentem sequentia sequentibus sequentis sequerentur sequeretur sequetur sequitur sequor sequuntur sera sereno serito seritur serius sermones sermonibus sermonis sero serpens serpentes serpentibus serpentium serta seruandum seruant seruari seruata seruatur seruauit seruet seruiat seruilius seruio seruire seruis seruitutem seruitutis seruius serum seruom seruorum seruus sese sestertium set seuere seueritas seueritatem seuero seuerus sexagensimo sexaginta sextarios sexto sextus sexus sí sibimet sicca siccis sicco siccum siciliam siculis sicuti sidere sideribus siderum sidus siet signa significant significari significationem significatur significet signis signorum signum silentium siluas siluestris siluis similes similia similibus similis similiter similitudinem similitudo simplex simpliciter simulacra simulacrum simulatione simulque simus sine singularem singularis singulis singulorum singulos sinistrum sinit sint sinus siqua siquidem siquis sistit sit sita situs siue socero socerum societatem societatis sociis sociorum socius socrates sodales solacia solacium solam soleant soleat solebant solebat solemus solent soleo solere solet solido solidum solis soliti solitos solitudines solitus sollemnia sollertia sollicitudin sollicitudinem sollicitus solos solstitium soluatur soluendo soluerit soluitur solus soluta solutis soluto solutus somnus sonant sonat sonitus sono sonus sopor sordes sordibus sordida sororis sororum sors sortis sortitus spargere sparsa spatia spatiis spatio spatium specialiter species specimen spectaculo spectaculum spectant spectari spectet speculum specus spem sperare sperat spero spes spicula spina spiritus splendore spolia spoliis spondes sponte spuma stabat stabulis stadia stagna stantem staret statimque stationes statius statuas statuere statuit status stellae stellarum stellis stercus sterilis stetit sticho stichus stilo stipendia stipendium stipulari stipulationem stipulationis stipulatus stipuletur stirpem stirpis stoici stomachum strage strepitu studia studiis studiorum studiose studium stulte stultitia stultus stupet stupro stuprum suadere suadet suam suaque suarum suas subegit subest subeunt subiecit subiecta subinde subire subito sublata sublatis sublato sublatum sublime sublimis subsidium substituto substitutus subter subtiliter succedere succedit successit successorem succurrere succurrit suco sucus sudore sues sufficere sufficiat sufficit suffragiis suffragium suis sullam sulpicius sumere sumitur summas summe summus sumpsit sumpta sumptibus sumptus sumus sunto suom suorum suos superare superauit superba superbiam superbo superbus superesse superest superiora superiores superioribus superioris superiorum superis superius superne superos superque supersit superstes supersunt superuacua superuacuum superum supplementum supplex supplicatio supplicio supplicium supra suprema supremum surgere surgit sursum suscepit susceptum suscipere suscipit suspectam suspectus suspendisse suspensa suspicari suspicionem suspicor sustineri sustinet sustinuit sustulit suus syllabam syllabis syracusis syriam tabellas tabulam tabularum tabulis tacere tacito tacitus tactu taedio talem talenta tales talia talibus talis talium tamdiu tamen tametsi tamquam tamque tandem tangere tangit tantam tantaque tantos tantummodo tantumque tantundem tantus tarde tardius tarentum tarquinius tartara tauri taurus té tecta tectis tecto tectum tecum tegit tela telis tellure telluris tellus telorum telum temere temeritatem temperantia temperat tempestas tempestates tempestatibus templa templis templorum templum tempor tempore temporibus temporis temporum temptare temptat tempus tendere tendit teneant teneatur tenebant tenebat tenebitur tenebris tenens tenentur teneo tenera teneretur teneris tenero tenes tenetur tenuem tenuere tenuerunt tenues tenuisse tenuit tenus teque terentius teres tergo tergum teritur terminos terna ternos terraeque terram terraque terrarum terras terret terribilis terris terrorem tertiam tertio tertius testamento testamentum testatorem testatoris testatus testes testibus testimonio testimonium testis testium testor tetigit teucros texit thalamos theatro theatrum thebis theophrastus theseus thessaliae thraciae tiberis tiberius tibia tigris timendum timent timeo timeri timet timorem timoris timuit tincidunt tiro titan titio titius titulo titulus togam tolerare tollere tollitur tollunt toris tormentis toros torqueri torquet tortor torum totas totidemque totiens totis totius totos totumque totus trabes tractari tractatus tractus tradere tradiderit tradiderunt tradidit tradita traditus traduntur trahens trahere trahit trahunt traiano traianus traiecit transeat transeunt transferri transgressus transiit transire transisse transitus translata translatum transtulit traxit trebatius trecentos trepida tres tria tribuit tribunali tribunatum tribunis tribunorum tribunus tribus tributa tributum triduo triduum triennium trigensimo trigesimo triginta triplex tristique tristis tristitia trita tritici triticum tritum triumphauit triumphus troiam troianis troianorum troianos truces trunco tú tuam tuarum tuas tuba tubero tuenda tueri tuetur tuis tulerit tulerunt tulisset tulit tullio tullius tumor tumulo tumultus tumulum tune tunicam tuom tuorum tuos tuque tura turbam turbata turbidus turbine ture turis turmas turno turnus turpem turpis turpiter turpius turres turribus turris tusculano tusculanum tussim tuta tutelam tutius tutores tutoribus tutoris tutus tuus tyranno tyrannus ubera ubicumque ubique ulcera ulceribus ulcus ulixes ullam ullamcorper ullis ullius ullo ullus ulpianus ulterius ultimam ultimis ultimo ultimus ultionem ultor ultrices ultro umbris umeros umida umorem umoris umquam unam uncia undas undecimo undique undis ungues unguibus unicum uniuersam uniuersos uniuersum unius uno unquam unum unusquisque urbana urbanis urbes urbibus urbis urbium urbs urget urinam urit urna usibus usquam usque usucapere usum usurae usurarum usuris usurum usus út utamur utar utatur utcumque utebantur utebatur utendum uterentur uteretur utero uterque utetur utiles utilia utilior utilissimum utilitatis utiliter utilius utimur utinam utique utitur utor utpote utque utramque utraque utrimque utrique utrisque utriusque utroque utrumne utrumque utuntur uxoris vacare vacat vacuam vacuo vacuum vada vadis vado vae vaga vagus valde valeant valeat valebit valent valeret valerio valerius valetudinem valetudinis valetudo valido vallo vallum valuit vana vapore varias varietate varius varronem varus vasa vasis vasto vatis vatum vectigalia vectigalibus vectus vehementer vehementius vehicula veios vel vela velimus velint velit vellem vellent vellere vellet velox veluti venas vendere vendiderit vendidit venditionem venditorem venditoris venena venenis veneno venenum venerant venerat venerem venerint venerit venerunt veniamus veniant veniat veniebat veniens venientem veniet venimus venio venirent veniret venissent venisset venit veniunt venter ventis ventorum ventos ventrem ventris ventum ventura venturum ventus venus veram verba verbera verberibus verbis verborum verbum verecundiam vereor vereri vergilius verisimile veritas veritatem veritatis veritus verius verno vero verres versantur versari versatus versibus verso versus vertere verticem vertitur verus vesicae vesperi vestae vestes vestibulo vestibulum vestibus vestigiis vestigium vestimenta vestis vestros vestrum vetant vetat vetera veteres veteribus veteris veterum vetuit vetustate viam viarum vias vicem vicensimo vicesimo vicina vicinis vicino vicinus vicisse vicissim vicit victa victima victis victores victoriam victoriati victoribus victoris victus videamus videantur videas videatur videbam videbantur videbatur videbimus videbis videbitur videbo videbuntur videlicet videmus videndum videntur videor viderat viderem viderentur videres videretur viderit videro viderunt vides videte videtis videtur vidimus vidisset vidisti vidit vigilia viginti vigor viiii vilis villam vim vina vincat vincere vincitur vincla vinclis vinctum vincula vinculis vinculum vindicari vindicat vindicta vineis vino vinum violentia vires virga virginis virgis virgo virgulta viribus viridis virilem virilis viris viritim virium virorum viros virtus virtutes virtutibus virtutis virtutum virus visam viscera visceribus visere viso visus vitae vitam vitare vitellius vites vitia vitibus vitiis vitiorum vitiosa vitiosum vitis vitium viuat viuendi viuendum viueret viuit viuos viuum viuunt viuus vivamus vixdum vixisse vixit vixque vobiscum vocabant vocabatur vocabulis vocabulum vocalis vocamus vocantur vocari vocas vocatus vocauit voces vocibus vocis voco volat volebant volebat volens volentem volentibus volet volgo volgus volnere volneribus volnus volo voltus volucres volucrum voluerit voluerunt voluisset voluisti voluit volumine volumus voluntatem voluntatis voluptates voluptatibus voluptatis volutpat vomere vos vota votis voto votum vox vuas vulgo vulgus vulnere vulneribus vulneris vulnus vultus ================================================ FILE: mocodo/resources/pristine_sandbox.mcd ================================================ AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise PIÈCE: réf. pièce, libellé pièce COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, _11 AYANT-DROIT, 0N EMPLOYÉ EMPLOYÉ: matricule, nom employé PROJET: num. projet, nom projet FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT TRAVAILLER, 0N EMPLOYÉ, 1N PROJET SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ ================================================ FILE: mocodo/resources/prompts/chat/cards_examples/1_input.mcd ================================================ Bureau: num. bureau Rattacher, 11 [?] Bureau, 1N [?] Département Département: num. département, budget département Dépendre, 11 [?] Projet, 1N [?] Département Projet: num. projet, nom projet, budget projet Se Situer, 11 [?] Téléphone, 1N [?] Bureau : Diriger, 11 [?] Département, 01 [?] Employé Travailler, 1N [?] Projet, 01 [?] Employé Téléphone: num. téléphone Joindre, 1N [?] Téléphone, 11 [?] Employé Employé: matricule, nom employé, adr. employé Assumer, 1N [?] Employé, 1N [?] Fonction: date début, date fin Fonction: num. fonction, salaire ================================================ FILE: mocodo/resources/prompts/chat/cards_examples/1_output.mcd ================================================ Bureau: num. bureau Rattacher, 11 [Tout bureau est rattaché à un département.] Bureau, 1N [Tout département compte au moins un bureau.] Département Département: num. département, budget département Dépendre, 11 [Un projet dépend d'un et un seul département.] Projet, 1N [Il y a au moins un projet par département.] Département Projet: num. projet, nom projet, budget projet Se Situer, 11 [Le téléphone d'un employé se situe dans son bureau.] Téléphone, 1N [Il y a un ou plusieurs téléphones par bureau.] Bureau : Diriger, 11 [Tout département a un directeur.] Département, 01 [Un employé peut être le directeur d'au plus un département.] Employé Travailler, 1N [Plusieurs employés peuvent travailler sur un même projet.] Projet, 01 [Un employé travaille sur au plus un projet.] Employé Téléphone: num. téléphone Joindre, 1N [Un téléphone peut être attribué à plusieurs employés du même bureau.] Téléphone, 11 [Un employé n'a qu'un seul téléphone.] Employé Employé: matricule, nom employé, adr. employé Assumer, 1N [Un employé peut avoir assumé différentes fonctions.] Employé, 1N [Une même fonction peut avoir été remplie par différents employés.] Fonction: date début, date fin Fonction: num. fonction, salaire ================================================ FILE: mocodo/resources/prompts/chat/cards_examples/2_input.mcd ================================================ Rejoindre, 01 [?] Rivière, 0N [?] Rivière Rivière: pos. source, nom rivière, longueur, position fin Se Jeter, 01 [?] Rivière, 1N [?] Mer Mer: nom mer, surface mer : Crue, 1N [?] Date, 0N [?] Ville, 0N [?] Rivière: durée crue, hauteur atteinte Traverser, 0N [?] Rivière, 0N [?] Ville: ordre traversée Arroser, 1N [?] Rivière, 0N [?] Pays Baigner, 1N [?] Mer, 0N [?] Pays: longueur côte : Date: date Ville: pos. ville, nom ville DF, 11 [?] Ville, 1N [?] Pays Pays: nom pays, surface pays Toucher, 0N [?] Pays, 0N [?] Pays ================================================ FILE: mocodo/resources/prompts/chat/cards_examples/2_output.mcd ================================================ Rejoindre, 01 [Une rivière peut rejoindre au plus une autre rivière.] Rivière, 0N [Une rivière a un nombre quelconque d'affluents.] Rivière Rivière: pos. source, nom rivière, longueur, position fin Se Jeter, 01 [Une rivière peut se jeter dans au plus une mer.] Rivière, 1N [Une mer reçoit au moins un fleuve.] Mer Mer: nom mer, surface mer : Crue, 1N [À toute date répertoriée dans la base, il y a au moins une crue.] Date, 0N [Une ville peut subir un nombre quelconque de crues.] Ville, 0N [Une rivière peut avoir des crues.] Rivière: durée crue, hauteur atteinte Traverser, 0N [Une rivière peut traverser un nombre quelconque de villes.] Rivière, 0N [Une ville peut être traversée par un nombre quelconque de rivières.] Ville: ordre traversée Arroser, 1N [Une rivière arrose au moins un pays.] Rivière, 0N [Un pays peut être arrosé par un nombre quelconque de rivières.] Pays Baigner, 1N [Une mer baigne au moins un pays.] Mer, 0N [Un pays peut avoir des côtes.] Pays: longueur côte : Date: date Ville: pos. ville, nom ville DF, 11 [Une ville appartient à exactement un pays.] Ville, 1N [Un pays compte au moins une ville.] Pays Pays: nom pays, surface pays Toucher, 0N [Un pays peut être limitrophe à un nombre quelconque d'autres pays.] Pays, 0N [Un pays peut être limitrophe à un nombre quelconque d'autres pays.] Pays ================================================ FILE: mocodo/resources/prompts/chat/cards_examples/3_input.mcd ================================================ : : DF2, 11 Ville, 0N Pays Ville: code ville, nom ville, population, code postal DF1, 0N Ville, 11 Producteur Langue: code_langue, nom_langue, famille Parler, 0N Langue, 1N Pays Pays: code pays, nom pays, capitale, population, monnaie DF3, 0N Ville, 11 Distributeur Producteur: id producteur, nom commercial, date de création, site web Aka, 0N Langue, 1N Film: titre localisé, statut titre DF6, 01 Film, 0N Langue Distribuer, 0N Pays, 0N Distributeur, 0N Film: date de sortie, nombre de copies, nombre d'entrées Distributeur: id distributeur, nom distributeur, type, site web Produire, 1N Producteur, 1N Film: budget, date de début, date de fin Genre: id genre, nom genre, descriptif DF4, 1N Genre, 11 Film Film: num. d'exploitation, titre original, durée, procédé couleur, procédé son, synopsis, restriction âge Participer, 0N Fonction, 1N Film, 1N Personne: rang dans la fonction, salaire Fonction: id fonction, intitulé, département Être sous-genre, 0N Genre, 01 Genre DF5, 1N Prise de Vues, 11 Film : Jouer, 0N Film, 0N Personne, 11 Personnage: importance du rôle, cachet Personne: id personne, nom, prénom, genre, date de naissance, biographie, photo, téléphone, adresse : Prise de Vues: procédé, format, largeur, définition Être en lien avec, 0N Personnage, 0N Personnage: nature de la relation Personnage: id personnage, nom, âge, genre, description : ================================================ FILE: mocodo/resources/prompts/chat/cards_examples/3_output.mcd ================================================ : : DF2, 11 [Une ville appartient à un et un seul pays.] Ville, 0N [Un pays peut avoir un nombre quelconque de villes.] Pays Ville: code ville, nom ville, population, code postal DF1, 0N [Une ville peut accueillir un nombre quelconque de producteurs.] Ville, 11 [Un producteur est basé dans une et une seule ville.] Producteur Langue: code_langue, nom_langue, famille Parler, 0N [Une langue est parlée dans zéro (langue morte), un ou plusieurs pays.] Langue, 1N [Un pays parle au moins une langue.] Pays Pays: code pays, nom pays, capitale, population, monnaie DF3, 0N [Une ville peut accueillir un nombre quelconque de distributeurs.] Ville, 11 [Un distributeur est basé dans une et une seule ville.] Distributeur Producteur: id producteur, nom commercial, date de création, site web Aka, 0N [Une langue peut être associée à un nombre quelconque de films.] Langue, 1N [Un film peut avoir des titres dans plusieurs langues.] Film: titre localisé, statut titre DF6, 01 [Un film a une langue originale ou pas (film muet).] Film, 0N [Une langue peut être la langue originale de plusieurs films.] Langue Distribuer, 0N [Un pays peut être concerné par un nombre quelconque de distributions.] Pays, 0N [Un distributeur peut distribuer un nombre quelconque de films dans différents pays.] Distributeur, 0N [Un film peut être distribué dans un nombre quelconque de pays.] Film: date de sortie, nombre de copies, nombre d'entrées Distributeur: id distributeur, nom distributeur, type, site web Produire, 1N [Un producteur produit au moins un film.] Producteur, 1N [Un film est produit par au moins un producteur.] Film: budget, date de début, date de fin Genre: id genre, nom genre, descriptif DF4, 1N [Un genre peut caractériser plusieurs films.] Genre, 11 [Tout film appartient à un et un seul genre.] Film Film: num. d'exploitation, titre original, durée, procédé couleur, procédé son, synopsis, restriction âge Participer, 0N [Une fonction peut être remplie sur plusieurs films.] Fonction, 1N [Un film ne peut se faire sans participants.] Film, 1N [Une personne peut participer à plusieurs films.] Personne: rang dans la fonction, salaire Fonction: id fonction, intitulé, département Être sous-genre, 0N [Un genre peut avoir zéro ou plusieurs sous-genres.] Genre, 01 [Un genre peut être sous-genre d'au plus un autre genre.] Genre DF5, 1N [Un procédé de prise de vues peut être utilisé pour plusieurs films.] Prise de Vues, 11 [Un film utilise une et une seule prise de vues.] Film : Jouer, 0N [Un film peut comporter plusieurs personnages.] Film, 0N [Une personne peut jouer dans plusieurs films.] Personne, 11 [Un personnage est incarné par un seul acteur dans un seul film.] Personnage: importance du rôle, cachet Personne: id personne, nom, prénom, genre, date de naissance, biographie, photo, téléphone, adresse : Prise de Vues: procédé, format, largeur, définition Être en lien avec, 0N [Un personnage peut être en relation avec plusieurs autres personnages.] Personnage, 0N [Un personnage peut être en relation avec plusieurs autres personnages.] Personnage: nature de la relation Personnage: id personnage, nom, âge, genre, description : ================================================ FILE: mocodo/resources/prompts/chat/cards_fr.md ================================================ Tu es un enseignant spécialiste des Modèles Conceptuels de Données (MCD) de la méthode MERISE. # Syntaxe de Mocodo Pour décrire les MCD, tu utilises le langage Mocodo : - Chaque ligne définit une entité ou une association. - L'ordre des lignes, ainsi que les sauts de ligne, sont importants pour le plongement. - Une entité E avec les attributs a1, ..., an est définie par la ligne : ```mocodo E: a1, ..., an ``` - Une association A entre les entités E1, ..., Em avec les attributs a1, ..., an est définie par la ligne: ```mocodo A, XX E1, ..., XX EM: a1, ... am ``` ... où les XX sont des couples de cardinalités minimale et maximale en notation _look here_. Ils peuvent être: `01`, `0N`, `11` et `1N`. Ils peuvent être suivis d'un chevron `>` ou `<` pour indiquer une flèche. - Les cardinalités sont en notation _look here_, c'est-à-dire que `A, 01 E1, 1N E2` se lira : pour une occurrence de E1, il peut y avoir 0 ou 1 occurrence de E2 ; pour une occurrence de E2, il peut y avoir 1 ou plusieurs occurrences de E1. - Entre une cardinalité et l'entité qu'elle distingue, on peut insérer entre crochets droits une courte explication de la cardinalité, p. ex.: ```mocodo A, 01 [Pour une occurrence de E1, il y au plus une occcurence de E2.] E1, 1N [Pour une occurrence de E2, il y au moins une occurrence de E1.] E2 ``` - Si les cardinalités sont erronées, fais comme si elles étaient correctes : une explication absurde rendra évident le problème. - Les associations ont pour nom, en général un verbe, mais parfois un substantif et parfois « DF » pour « dépendance fonctionnelle ». # Instructions - Remplace les `[?]` par de courtes explications de cardinalités. - Utilise la langue du MCD. - Renvoie-le comme un code Markdown. - Ne modifie en aucun cas le reste du code. - En particulier, respecte les sauts de ligne. - N'écris rien avant le code complété. - N'écris rien après le code complété. # Exemples de données et de résultats attendus {examples} # MCD à compléter {question} ================================================ FILE: mocodo/resources/prompts/chat/types_examples/1_input.mcd ================================================ Recevoir, 01 Rivière, 0N Rivière Rivière: pos. source, nom rivière, longueur, position fin Se Jeter, 01 Rivière, 1N Mer Mer: nom mer, surface mer : Crue, 1N Date, 0N Ville, 0N Rivière: durée crue, hauteur atteinte Traverser, 0N Rivière, 0N Ville: ordre traversée Arroser, 1N Rivière, 0N Pays Baigner, 1N Mer, 0N Pays: longueur côte : Date: date Ville: pos. ville, nom ville DF, 11 Ville, 1N Pays Pays: nom pays, surface pays Toucher, 0N Pays, 0N Pays ================================================ FILE: mocodo/resources/prompts/chat/types_examples/1_output.mcd ================================================ Recevoir, 01 Rivière, 0N Rivière Rivière: pos. source [POINT], nom rivière [VARCHAR(255)], longueur [INTEGER], position fin [POINT] Se Jeter, 01 Rivière, 1N Mer Mer: nom mer [VARCHAR(255)], surface mer [DECIMAL(15,2)] : Crue, 1N Date, 0N Ville, 0N Rivière: durée crue [INTEGER], hauteur atteinte [DECIMAL(5,2)] Traverser, 0N Rivière, 0N Ville: ordre traversée [INTEGER] Arroser, 1N Rivière, 0N Pays Baigner, 1N Mer, 0N Pays: longueur côte [INTEGER] : Date: date [DATE] Ville: pos. ville [POINT], nom ville [VARCHAR(255)] DF, 11 Ville, 1N Pays Pays: nom pays [VARCHAR(255)], surface pays [DECIMAL(15,2)] Toucher, 0N Pays, 0N Pays ================================================ FILE: mocodo/resources/prompts/chat/types_examples/2_input.mcd ================================================ : : DF2, 11 Ville, 0N Pays Ville: code ville, nom ville, population, code postal DF1, 0N Ville, 11 Producteur Langue: code_langue, nom_langue, famille Parler, 0N Langue, 1N Pays Pays: code pays, nom pays, capitale, population, monnaie DF3, 0N Ville, 11 Distributeur Producteur: id producteur, nom commercial, date de création, site web Aka, 0N Langue, 1N Film: titre localisé, statut titre DF6, 01 Film, 0N Langue Distribuer, 0N Pays, 0N Distributeur, 0N Film: date de sortie, nombre de copies, nombre d'entrées Distributeur: id distributeur, nom distributeur, type, site web Produire, 1N Producteur, 1N Film: budget, date de début, date de fin Genre: id genre, nom genre, descriptif DF4, 1N Genre, 11 Film Film: num. d'exploitation, titre original, durée, procédé couleur, procédé son, synopsis, restriction âge Participer, 0N Fonction, 1N Film, 1N Personne: rang dans la fonction, salaire Fonction: id fonction, intitulé, département Être sous-genre, 0N Genre, 01 Genre DF5, 1N Prise de Vues, 11 Film : Jouer, 0N Film, 0N Personne, 11 Personnage: importance du rôle, cachet Personne: id personne, nom, prénom, genre, date de naissance, biographie, photo, téléphone, adresse : Prise de Vues: procédé, format, largeur, définition Être en lien avec, 0N Personnage, 0N Personnage: nature de la relation Personnage: id personnage, nom, âge, genre, description : ================================================ FILE: mocodo/resources/prompts/chat/types_examples/2_output.mcd ================================================ : : DF2, 11 Ville, 0N Pays Ville: code ville [VARCHAR(10)], nom ville [VARCHAR(255)], population [INTEGER], code postal [VARCHAR(10)] DF1, 0N Ville, 11 Producteur Langue: code_langue [CHAR(2)], nom_langue [VARCHAR(50)], famille [VARCHAR(50)] Parler, 0N Langue, 1N Pays Pays: code pays [CHAR(2)], nom pays [VARCHAR(100)], capitale [VARCHAR(100)], population [INTEGER], monnaie [VARCHAR(50)] DF3, 0N Ville, 11 Distributeur Producteur: id producteur [VARCHAR(20)], nom commercial [VARCHAR(255)], date de création [DATE], site web [VARCHAR(255)] Aka, 0N Langue, 1N Film: titre localisé [VARCHAR(255)], statut titre [VARCHAR(50)] DF6, 01 Film, 0N Langue Distribuer, 0N Pays, 0N Distributeur, 0N Film: date de sortie [DATE], nombre de copies [INTEGER], nombre d'entrées [INTEGER] Distributeur: id distributeur [VARCHAR(20)], nom distributeur [VARCHAR(255)], type [VARCHAR(50)], site web [VARCHAR(255)] Produire, 1N Producteur, 1N Film: budget [DECIMAL(15,2)], date de début [DATE], date de fin [DATE] Genre: id genre [VARCHAR(20)], nom genre [VARCHAR(100)], descriptif [TEXT] DF4, 1N Genre, 11 Film Film: num. d'exploitation [VARCHAR(20)], titre original [VARCHAR(255)], durée [INTEGER], procédé couleur [VARCHAR(50)], procédé son [VARCHAR(50)], synopsis [TEXT], restriction âge [VARCHAR(10)] Participer, 0N Fonction, 1N Film, 1N Personne: rang dans la fonction [INTEGER], salaire [DECIMAL(12,2)] Fonction: id fonction [VARCHAR(20)], intitulé [VARCHAR(100)], département [VARCHAR(100)] Être sous-genre, 0N Genre, 01 Genre DF5, 1N Prise de Vues, 11 Film : Jouer, 0N Film, 0N Personne, 11 Personnage: importance du rôle [VARCHAR(50)], cachet [DECIMAL(12,2)] Personne: id personne [VARCHAR(20)], nom [VARCHAR(100)], prénom [VARCHAR(100)], genre [CHAR(1)], date de naissance [DATE], biographie [TEXT], photo [BLOB], téléphone [VARCHAR(20)], adresse [VARCHAR(255)] : Prise de Vues: procédé [VARCHAR(100)], format [VARCHAR(50)], largeur [DECIMAL(5,2)], définition [INTEGER] Être en lien avec, 0N Personnage, 0N Personnage: nature de la relation [VARCHAR(100)] Personnage: id personnage [VARCHAR(20)], nom [VARCHAR(100)], âge [INTEGER], genre [CHAR(1)], description [TEXT] : ================================================ FILE: mocodo/resources/prompts/chat/types_fr.md ================================================ Tu es un enseignant spécialiste des Modèles Conceptuels de Données (MCD) de la méthode MERISE. # Syntaxe de Mocodo Pour décrire les MCD, tu utilises le langage Mocodo : - Chaque ligne définit une entité ou une association. - L'ordre des lignes, ainsi que les sauts de ligne, sont importants pour le plongement. - Une entité E avec les attributs a1, ..., an est définie par la ligne : ```mocodo E: a1, ..., an ``` - Une association A entre les entités E1, ..., Em avec les attributs a1, ..., an est définie par la ligne: ```mocodo A, XX E1, ..., XX EM: a1, ... am ``` ... où les XX définissent les cardinalités minimale et maximale. - Après chaque attribut, on peut insérer entre crochets droits son type de données SQL, principalement : BINARY(n), BLOB, BOOLEAN, CHAR(n), DATE, DATETIME, DECIMAL(m,n), INTEGER, JSON, POINT, SMALLINT, TEXT, TIME, TIMESTAMP, VARCHAR(n). # Instructions - Remplace les `[?]` par le type approprié. - Renvoie le MCD complété comme un code Markdown. - Ne modifie en aucun cas le reste du code. - En particulier, respecte les sauts de ligne. - N'écris rien avant le code complété. - N'écris rien après le code complété. # Exemples de données et de résultats attendus {examples} # MCD à compléter {question} ================================================ FILE: mocodo/resources/relation_templates/_index.json ================================================ { "d2": { "category": "cv", "help_en": "convert the conceptual model into a relational schema in D2 format", "help_fr": "convertit le modèle conceptuel en un schéma relationnel au format D2", "help_zh": "将概念模型转换为 D2 格式的关系模式", "aliases": [] }, "dbml": { "category": "cv", "help_en": "convert the conceptual model into a relational schema in DBML format", "help_fr": "convertit le modèle conceptuel en un schéma relationnel au format DBML", "help_zh": "将概念模型转换为 DBML 格式的关系模式", "aliases": [], "fr_examples": { "dbml": "version de base", "dbml:b": "avec _boilerplate_" } }, "sql": { "category": "cv", "help_en": "convert the conceptual model into a physical model for SQL", "help_fr": "convertit le modèle conceptuel en un modèle physique pour SQL", "help_zh": "将概念模型转换为 SQL 的物理模型", "aliases": ["ddl"] }, "debug": { "category": "cv", "help_en": "list internal informations relative to the conversion into relational schema", "help_fr": "liste des informations internes relatives à la conversion en schéma relationnel", "help_zh": "列出与转换为关系模式相关的内部信息", "aliases": [] }, "dependencies": { "category": "cv", "help_en": "convert the conceptual model into a dependency graph", "help_fr": "convertit le modèle conceptuel en un graphe de dépendances", "help_zh": "将概念模型转换为依赖图", "aliases": [] }, "diagram": { "category": "cv", "help_en": "convert the conceptual model into a relational diagram in Mocodo syntax", "help_fr": "convertit le modèle conceptuel en un diagramme relationnel au format Mocodo", "help_zh": "将概念模型转换为 Mocodo 格式的关系图", "aliases": [], "fr_examples": { "diagram": "version de base", "diagram:c": "avec contraintes d'unicité et d'optionalité" } }, "html": { "category": "cv", "help_en": "convert the conceptual model into a relational schema in HTML format", "help_fr": "convertit le modèle conceptuel en un schéma relationnel au format HTML", "help_zh": "将概念模型转换为 HTML 格式的关系模式", "aliases": [], "fr_examples": { "html": "version de base", "html:b": "avec _boilerplate_", "html:c": "avec contraintes d'unicité et d'optionalité", "html:e": "avec explications", "html:bce": "avec _boilerplate_, contraintes et explications" } }, "latex": { "category": "cv", "help_en": "convert the conceptual model into a relational schema in LaTeX format", "help_fr": "convertit le modèle conceptuel en un schéma relationnel au format LaTeX", "help_zh": "将概念模型转换为 LaTeX 格式的关系模式", "aliases": ["tex"], "fr_examples": { "latex": "version de base", "latex:b": "avec _boilerplate_", "latex:c": "avec contraintes d'unicité et d'optionalité", "latex:e": "avec explications", "latex:bce": "avec _boilerplate_, contraintes et explications" } }, "markdown": { "category": "cv", "help_en": "convert the conceptual model into a relational schema in Markdown format", "help_fr": "convertit le modèle conceptuel en un schéma relationnel au format Markdown", "help_zh": "将概念模型转换为 Markdown 格式的关系模式", "aliases": ["md", "mld"], "fr_examples": { "markdown": "version de base", "markdown:c": "avec contraintes d'unicité et d'optionalité", "markdown:e": "avec explications", "markdown:ce": "avec contraintes et explications" } }, "mssql": { "category": "cv", "help_en": "convert the conceptual model into a physical model for Microsoft SQL Server", "help_fr": "convertit le modèle conceptuel en un modèle physique pour Microsoft SQL Server", "help_zh": "将概念模型转换为 Microsoft SQL Server 的物理模型", "aliases": ["ms_sql", "sql_server", "sqlserver"], "fr_examples": { "mssql": "version de base", "mssql:b": "avec _boilerplate_" } }, "mysql": { "category": "cv", "help_en": "convert the conceptual model into a physical model for MySQL", "help_fr": "convertit le modèle conceptuel en un modèle physique pour MySQL", "help_zh": "将概念模型转换为 MySQL 物理模型", "aliases": [], "fr_examples": { "mysql": "version de base", "mysql:b": "avec _boilerplate_" } }, "oracle": { "category": "cv", "help_en": "convert the conceptual model into a physical model for Oracle DB", "help_fr": "convertit le modèle conceptuel en un modèle physique pour Oracle DB", "help_zh": "将概念模型转换为 Oracle DB 的物理模型", "aliases": ["oracle_db"], "fr_examples": { "oracle": "version de base", "oracle:b": "avec _boilerplate_" } }, "postgresql": { "category": "cv", "help_en": "convert the conceptual model into a physical model for PostgreSQL", "help_fr": "convertit le modèle conceptuel en un modèle physique pour PostgreSQL", "help_zh": "将概念模型转换为 PostgreSQL 的物理模型", "aliases": ["postgres"], "fr_examples": { "postgresql": "version de base", "postgresql:b": "avec _boilerplate_" } }, "sqlite": { "category": "cv", "help_en": "convert the conceptual model into a physical model for SQLite", "help_fr": "convertit le modèle conceptuel en un modèle physique pour SQLite", "help_zh": "将概念模型转换为 SQLite 的物理模型", "aliases": [], "fr_examples": { "sqlite": "version de base", "sqlite:b": "avec _boilerplate_" } }, "text": { "category": "cv", "help_en": "convert the conceptual model into a relational schema in text format", "help_fr": "convertit le modèle conceptuel en un schéma relationnel au format texte", "help_zh": "将概念模型转换为文本格式的关系模式", "aliases": ["txt"], "fr_examples": { "text": "version de base", "text:c": "avec contraintes d'unicité et d'optionalité", "html:e": "avec explications", "html:ce": "avec contraintes et explications" } } } ================================================ FILE: mocodo/resources/relation_templates/d2.yaml ================================================ help_en: 'convert the conceptual model into a relational schema in D2 format' help_fr: 'convertit le modèle conceptuel en un schéma relationnel au format D2' help_zh: '将概念模型转换为 D2 格式的关系模式' stem_suffix: '_ddl' extension: 'd2' to_defer: true highlight: 'text' transform_datatype: - order: 100 comment: 'Fill in empty data types with the default one.' search: '^\s*$' replace: 'VARCHAR(42)' - order: 300 search: 'UNSIGNED_INT_PLACEHOLDER' replace: 'UNSIGNED INT' - order: 400 search: 'BOOLEAN_PLACEHOLDER' replace: 'BOOLEAN' transform_optionality: - order: 100 comment: 'Replace BANG with NOT NULL.' search: '^!$' replace: 'NOT NULL' - order: 200 comment: 'Replace QMARK with NULL. NB: without the double quotes, NULL is not rendered (https://github.com/terrastruct/d2/issues/1655)' search: '^\?$' replace: '"NULL"' compose_primary_key: ' "{label}":{filler}{datatype} OPENING_BRACE constraint: [PK] CLOSING_BRACE' compose_normal_attribute: ' "{label}":{filler}{datatype} OPENING_BRACE constraint: [{optionality}] CLOSING_BRACE' compose_primary_foreign_key: ' "{label}":{filler}{datatype} OPENING_BRACE constraint: [PK; FK] CLOSING_BRACE\nADD_FOREIGN_KEY "{this_relation_name}".("{label}") -> "{outer_source}".("{non_disambiguated_label}")' compose_foreign_key: ' "{label}":{filler}{datatype} OPENING_BRACE constraint: [FK; {optionality}] CLOSING_BRACE\nADD_FOREIGN_KEY "{this_relation_name}".("{label}") -> "{outer_source}".("{non_disambiguated_label}")' add_unicity_constraints: - order: 100 search: '( .+)$' replace: '\1; UNQ{unicities}' column_separator: '\n' compose_relation: '"{this_relation_name}": {{ shape: sql_table\n{columns}\n}}' transform_relation: - order: 100 comment: 'Replace brace placeholders' search: 'OPENING_BRACE (.+?) CLOSING_BRACE' replace: '{\1}' - order: 200 search: '(UNQ\d)(\d+)' replace: '\1; UNQ\2' iterated: true - order: 300 search: '(\]\})((?:; UNQ\d)+)' replace: '\2\1' - order: 400 comment: 'Suppress useless UNQ constraint separator' search: '(?m)({constraint: \[); ' replace: '\1' - order: 500 comment: 'Suppress brackets with no semi-colon inside.' search: '\[([^\];\n]*)\]' replace: '\1' - order: 600 comment: 'Suppress empty constraints' search: '(?m){constraint: }$' replace: '' relation_separator: '\n\n' compose_relational_schema: '{relations}\n' transform_relational_schema: - order: 100 comment: 'Move the foreign keys to the end of the document.' search: '(?sm)^ADD_FOREIGN_KEY ([^\n]+\n)(.+)' replace: '\2\nRef:\1' iterated: true - order: 250 comment: 'Suppress multiple newlines between Ref lines.' search: '(?m)^(Ref:.+)\n+(?=Ref:)' replace: '\1\n' - order: 300 comment: 'Clean up the useless parenthesis (without comma inside).' search: '(?m)^(Ref:.+)\.\((.+?)\) -> (.+)\.\((.+?)\)' replace: '\1.\2 -> \3.\4' - order: 400 comment: 'Suppress Ref:' search: '(?m)^Ref:' replace: '' ================================================ FILE: mocodo/resources/relation_templates/dbml-b.yaml ================================================ parent: 'dbml' compose_relational_schema: 'Project "{title}" {{\n Note: ''Generated by Mocodo {version}''\n}}\n\n{relations}\n' ================================================ FILE: mocodo/resources/relation_templates/dbml.yaml ================================================ help_en: 'convert the conceptual model into a relational schema in DBML format' help_fr: 'convertit le modèle conceptuel en un schéma relationnel au format DBML' help_zh: '将概念模型转换为 DBML 格式的关系模式' fr_examples: - order: 1 example: 'dbml' explanation: 'version de base' - order: 2 example: 'dbml:b' explanation: 'avec _boilerplate_' stem_suffix: '_ddl' extension: 'dbml' to_defer: false highlight: 'text' transform_datatype: - order: 100 comment: 'Fill in empty data types with the default one.' search: '^\s*$' replace: 'VARCHAR(42)' - order: 200 comment: 'Remove all spaces in the data type.' search: ' +' replace: '_' - order: 300 search: 'UNSIGNED_INT_PLACEHOLDER' replace: 'UNSIGNED_INT' - order: 400 search: 'BOOLEAN_PLACEHOLDER' replace: 'BOOLEAN' transform_optionality: - order: 100 comment: 'Replace BANG with NOT NULL.' search: '^!$' replace: 'NOT NULL' - order: 200 comment: 'Replace QMARK with NULL.' search: '^\?$' replace: 'NULL' compose_primary_key: ' "{label}"{filler}{datatype} [{optionality}] [pk]\nADD_PRIMARY_KEY "{label}"' compose_normal_attribute: ' "{label}"{filler}{datatype} [{optionality}]' compose_primary_foreign_key: ' "{label}"{filler}{datatype} [{optionality}] [pk]\nADD_PRIMARY_KEY "{label}"\nADD_FOREIGN_KEY "{this_relation_name}".("{label}") > "{outer_source}".("{non_disambiguated_label}")' compose_foreign_key: ' "{label}"{filler}{datatype} [{optionality}]\nADD_FOREIGN_KEY "{this_relation_name}".("{label}") > "{outer_source}".("{non_disambiguated_label}")' add_unicity_constraints: - order: 100 search: '$' replace: '\nADD_CONSTRAINT u{unicities} "{label}"' column_separator: '\n' compose_relation: 'Table "{this_relation_name}" {{\n{columns}\n Indexes {{\n [pk] \n }}\n}}' transform_relation: - order: 400 comment: 'Move the primary keys inside ''Indexes'' clause.' search: '(?sm)^ADD_PRIMARY_KEY ([^\n]+)\n(.+?^ \[pk\] [^\n]*)\n' replace: '\2\1, \n' iterated: true - order: 500 comment: 'Accumulate the composite unique constraints inside ''Indexes'' clause.' search: '(?sm)(^ADD_CONSTRAINT u\d+)(\d)( [^\n]+)(\n.+?)(^ \})' replace: '\1\3\4 [unique] u\2\3\n\5' iterated: true - order: 600 comment: 'Accumulate the single unique constraints inside ''Indexes'' clause.' search: '(?sm)^ADD_CONSTRAINT (u\d [^\n]+\n)(.+?)(^ \})' replace: '\2 [unique] \1\3' iterated: true - order: 700 comment: 'Group the constraints having the same number.' search: '(?sm)^( \[unique\] u\d )([^\n]+)(\n.*?)\1([^\n]+)\n' replace: '\1\2, \4\3' iterated: true - order: 800 comment: 'Remove the pk lines which contain no comma, except a trailing comma.' search: '(?m)^ \[pk\] [^\n,]+, \n' replace: '' - order: 900 comment: 'Remove the [pk] suffixes when it still exists a [pk] line.' search: '(?ms) \[pk\](.+?^ \[pk\] )' replace: '\1' iterated: true - order: 1000 comment: 'Finalize Indexes'' line format.' search: '(?m)^ (\[\w+\]) (?:u\d )?(.+")(?:, )?' replace: ' (\2) \1' - order: 1100 comment: 'Clean up the useless parenthesis (without comma inside).' search: '(?sm)^ \(([^,\n]+)\) (\[\w+\])' replace: ' \1 \2' - order: 1200 comment: 'Clean up the empty Indexes.' search: '(?sm)^ Indexes \{\n \}\n' replace: '' - order: 1300 comment: 'Merge brackets and reverse their contents.' search: '\[(.+?)\] \[(.+?)\]' replace: '[\2, \1]' iterated: true - order: 1400 comment: 'Suppress empty brackets.' search: ' \[\]' replace: '' relation_separator: '\n\n' compose_relational_schema: '{relations}\n' transform_relational_schema: - order: 100 comment: 'Move the foreign keys to the end of the document.' search: '(?sm)^ADD_FOREIGN_KEY ([^\n]+\n)(.+)' replace: '\2\nRef:\1' iterated: true - order: 200 comment: 'Group the foreign keys having the same origin and target.' search: '(?m)^(Ref:.+)\.\((.+)\) > (.+)\.\((.+)\)\n((?:.*\n)*)^\1\.\((.+)\) > \3\.\((.+)\)' replace: '\1.(\2, \6) > \3.(\4, \7)\5' iterated: true - order: 250 comment: 'Suppress multiple newlines between Ref lines.' search: '(?m)^(Ref:.+)\n+(?=Ref:)' replace: '\1\n' - order: 300 comment: 'Clean up the useless parenthesis (without comma inside).' search: '(?m)^(Ref:.+)\.\(([^,\n]+)\) > (.+)\.\(([^,\n]+)\)' replace: '\1.\2 > \3.\4' ================================================ FILE: mocodo/resources/relation_templates/ddl.yaml ================================================ parent: 'sql' ================================================ FILE: mocodo/resources/relation_templates/debug.yaml ================================================ help_en: 'list internal informations relative to the conversion into relational schema' help_fr: 'liste des informations internes relatives à la conversion en schéma relationnel' help_zh: '列出与转换为关系模式相关的内部信息' stem_suffix: '_debug' extension: 'tsv' to_defer: false highlight: 'tsv' column_separator: '' compose_relation: '{columns}' relation_separator: '' transform_relation: - order: 100 search: '\tNone' replace: '\t' compose_relational_schema: 'this relation name\tattribute\toptionality\tunicities\tnature\tis primary\tadjacent source\touter source\tassociation name\tdatatype\tleg note\n{relations}' compose_foreign_key: '{this_relation_name}\t{attribute}\t{optionality}\t{unicities}\t{nature}\t{is_primary}\t{adjacent_source}\t{outer_source}\t{association_name}\t{datatype}\t{leg_note}\n' compose_normal_attribute: '{this_relation_name}\t{attribute}\t{optionality}\t{unicities}\t{nature}\t{is_primary}\t{adjacent_source}\t{outer_source}\t{association_name}\t{datatype}\t{leg_note}\n' compose_primary_foreign_key: '{this_relation_name}\t{attribute}\t{optionality}\t{unicities}\t{nature}\t{is_primary}\t{adjacent_source}\t{outer_source}\t{association_name}\t{datatype}\t{leg_note}\n' compose_primary_key: '{this_relation_name}\t{attribute}\t{optionality}\t{unicities}\t{nature}\t{is_primary}\t{adjacent_source}\t{outer_source}\t{association_name}\t{datatype}\t{leg_note}\n' ================================================ FILE: mocodo/resources/relation_templates/dependencies.yaml ================================================ help_en: 'convert the conceptual model into a dependency graph' help_fr: 'convertit le modèle conceptuel en un graphe de dépendances' help_zh: '将概念模型转换为依赖图' stem_suffix: '_dependencies' extension: 'gv' to_defer: true highlight: 'graphviz' compose_primary_key: '' compose_normal_attribute: '' compose_foreign_key: '<-"{outer_source}"' compose_primary_foreign_key: '<-"{outer_source}"' column_separator: '' compose_relation: '"{this_relation_name}"{columns}' relation_separator: '\n' transform_relation: - order: 100 comment: 'Present all the dependencies of the same relation on multiple lines' search: '(".+?")<-(".+?")(.*)' replace: '\1\3\n \2 -> \1' iterated: true compose_relational_schema: '// Generated by Mocodo {version}digraph {{\n node [shape=box]\n\n{relations}\n}}' transform_relational_schema: - order: 100 comment: 'Remove the independant relations' search: '(?m)^".+' replace: '' - order: 200 comment: 'Remove the empty lines' search: '\n+' replace: '\n' - order: 300 comment: 'Remove the duplicated consecutive lines' search: '(?m)^(.+\n)\1+' replace: '\1' - order: 400 search: '' replace: '\n\n' ================================================ FILE: mocodo/resources/relation_templates/diagram-c.yaml ================================================ stem_suffix: '_mld' extension: 'mcd' to_defer: false highlight: 'plain' compose_primary_key: '_{label}' compose_foreign_key: '#{label} > {outer_source} > {non_disambiguated_label}' compose_normal_attribute: '{label}' compose_primary_foreign_key: '_#{label} > {outer_source} > {non_disambiguated_label}' compose_relation: '{this_relation_name}: {columns}' add_optionality_constraints: - order: 100 search: '^([^_][^>]+)$' replace: '\1{optionality}' - order: 200 search: '^([^_].+?)( >)' replace: '\1{optionality}\2' transform_relation: - order: 100 comment: 'By default, the first attribute is an identifier' search: ': _' replace: ': ' compose_relational_schema: '%%mocodo\n{relations}' transform_relational_schema: - order: 100 search: '(?m)^:\n(?=:)' replace: ':' - order: 200 search: '\n\n:+\n\n' replace: '\n\n' iterated: true - order: 300 search: '^%%mocodo\n\n?:+\n\n' replace: '' - order: 400 search: '\n\n:+\n?$' replace: '' ================================================ FILE: mocodo/resources/relation_templates/diagram.yaml ================================================ help_en: 'convert the conceptual model into a relational diagram in Mocodo syntax' help_fr: 'convertit le modèle conceptuel en un diagramme relationnel au format Mocodo' help_zh: '将概念模型转换为 Mocodo 格式的关系图' fr_examples: - order: 1 example: 'diagram' explanation: 'version de base' - order: 2 example: 'diagram:c' explanation: 'avec contraintes d''unicité et d''optionalité' parent: 'diagram-c' add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/html-b.yaml ================================================ parent: 'html-bc' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/html-bc.yaml ================================================ parent: 'html-c' compose_relational_schema: '\n\n\n\n\n\n\n\n\n

Conversion en relationnel

Générée par Mocodo

\n
\n
MCD {title}
\n
\n{relations}{deleted_relations}\n
\n
\n\n' ================================================ FILE: mocodo/resources/relation_templates/html-bce.yaml ================================================ parent: 'html-ce' compose_relational_schema: '\n\n\n\n\n\n\n\n\n

Conversion en relationnel

Générée par Mocodo

\n
\n
MCD {title}
\n
\n{relations}{deleted_relations}\n
\n
\n\n' ================================================ FILE: mocodo/resources/relation_templates/html-be.yaml ================================================ parent: 'html-bce' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/html-c.yaml ================================================ stem_suffix: '_mld' extension: 'html' to_defer: false highlight: 'html' transform_attribute: - order: 100 search: '&' replace: '&' - order: 200 search: '>' replace: '>' - order: 300 search: '<' replace: '<' transform_relation_name: - order: 100 search: '&' replace: '&' - order: 200 search: '>' replace: '>' - order: 300 search: '<' replace: '<' compose_association_attribute: '{label}' compose_association_primary_key: '{label}' compose_deleted_child_attribute: '{label}' compose_deleted_child_discriminator_: '{label}' compose_deleted_child_discriminator_T: '{label}' compose_deleted_child_discriminator_X: '{label}' compose_deleted_child_discriminator_XT: '{label}' compose_deleted_child_entity_name: '{label}' compose_deleted_child_foreign_key: '#{label}' compose_deleted_child_ex_foreign_key: '{label}' compose_deleted_parent_attribute: '{label}' compose_deleted_parent_discriminator_: '{label}' compose_deleted_parent_discriminator_T: '{label}' compose_deleted_parent_discriminator_X: '{label}' compose_deleted_parent_discriminator_XT: '{label}' compose_deleted_parent_foreign_key: '#{label}' compose_deleted_parent_ex_foreign_key: '{label}' compose_deleted_parent_primary_key: '{label}' compose_foreign_key: '#{label}' compose_ex_foreign_key: '{label}' compose_normal_attribute: '{label}' compose_outer_attribute: '{label}' compose_outer_primary_key: '{label}' compose_parent_primary_key: '#{label}' compose_primary_foreign_key: '#{label}' compose_primary_key: '{label}' compose_primary_ex_foreign_key: '{label}' compose_stopped_foreign_key: '#{label}' compose_stopped_ex_foreign_key: '{label}' compose_strengthening_primary_foreign_key: '#{label}' compose_strengthening_primary_ex_foreign_key: '{label}' compose_unsourced_foreign_key: '{label}' compose_unsourced_ex_foreign_key: '{label}' compose_unsourced_primary_foreign_key: '{label}' compose_unsourced_primary_ex_foreign_key: '{label}' add_unicity_constraints: - order: 100 search: '' replace: ' u{unicities}' add_optionality_constraints: - order: 100 search: '^(.+)()' replace: '\1{optionality}\2' column_separator: ',\n ' compose_relation: '
\n {this_relation_name} (\n {columns}\n )\n
' transform_relation: - order: 100 search: '(u\d+)(\d)' replace: '\1 u\2' iterated: true compose_relational_schema: '\n\n
\n{relations}\n
' transform_relational_schema: - order: 10000 search: '\s+$' replace: '' ================================================ FILE: mocodo/resources/relation_templates/html-ce.yaml ================================================ parent: 'html-c' compose_association_attribute: '{label}Le champ {label} était déjà un simple attribut de l''association {this_relation_name}.' compose_association_primary_key: '{label}Le champ {label} fait partie de la clé primaire de la table. C''était déjà un « identifiant » de l''association {this_relation_name}. Attention : la notion d''identifiant d''association n''existe pas en Merise ; il s''agit d''une commodité introduite par Mocodo pour éviter d''inclure dans l''association une entité réduite à cet identifiant.' compose_deleted_child_attribute: '{label}Le champ {label} a migré à partir de l''entité-fille {adjacent_source} (supprimée).' compose_deleted_child_discriminator_: '{label}Un discriminateur {label} est ajouté pour indiquer la nature de la spécialisation. Peut être vide, du fait de l''absence de contrainte de totalité.' compose_deleted_child_discriminator_T: '{label}Un discriminateur {label} est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité.' compose_deleted_child_discriminator_X: '{label}Un discriminateur {label} est ajouté pour indiquer la nature de la spécialisation. Peut être vide, du fait de l''absence de contrainte de totalité.' compose_deleted_child_discriminator_XT: '{label}Un discriminateur {label} est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité.' compose_deleted_child_entity_name: '{label}Un champ booléen {label} est ajouté pour indiquer si on a affaire ou pas à la spécialisation de même nom.' compose_deleted_child_foreign_key: '#{label}Le champ {label} est une clé étrangère. Il a migré à partir de l''entité-fille {adjacent_source} (supprimée) dans laquelle il avait déjà migré à partir de l''entité {outer_source}.' compose_deleted_child_ex_foreign_key: '{label}Le champ {label} a migré à travers l''entité-fille {adjacent_source} (supprimée) dans laquelle il avait déjà migré à partir de l''entité {outer_source} (également supprimée).' compose_deleted_parent_attribute: '{label}Le champ {label} est un simple attribut. Il était simple attribut de l''entité-mère {adjacent_source} (supprimée).' compose_deleted_parent_discriminator_: '{label}Un discriminateur {label} est ajouté pour indiquer la nature de la spécialisation. Peut être vide, du fait de l''absence de contrainte de totalité.' compose_deleted_parent_discriminator_T: '{label}Un discriminateur {label} est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité.' compose_deleted_parent_discriminator_X: '{label}Un discriminateur {label} est ajouté pour indiquer la nature de la spécialisation. Peut être vide, du fait de l''absence de contrainte de totalité.' compose_deleted_parent_discriminator_XT: '{label}Un discriminateur {label} est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité.' compose_deleted_parent_foreign_key: '#{label}Le champ {label} est une clé étrangère. Il a migré à travers l''entité-mère {adjacent_source} (supprimée), et réfère maintenant directement à l''entité {outer_source}.' compose_deleted_parent_ex_foreign_key: '{label}Le champ {label} a migré à travers l''entité-mère {adjacent_source} (supprimée) dans laquelle il avait déjà migré à partir de l''entité {outer_source} (également supprimée).' compose_deleted_parent_primary_key: '{label}Le champ {label} fait partie de la clé primaire de la table. Il était clé primaire de l''entité-mère {adjacent_source} (supprimée).' compose_foreign_key: '#{label}Le champ {label} est une clé étrangère. Il a migré par l''association de dépendance fonctionnelle {association_name} à partir de l''entité {outer_source} en perdant son caractère identifiant.' compose_ex_foreign_key: '{label}Le champ {label} est un simple attribut. Il a migré par l''association de dépendance fonctionnelle {association_name} à partir de l''entité {outer_source} en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n''est pas considéré comme clé étrangère.' compose_normal_attribute: '{label}Le champ {label} était déjà un simple attribut de l''entité {this_relation_name}.' compose_outer_attribute: '{label}Le champ {label} a migré à partir de l''association de dépendance fonctionnelle {association_name}.' compose_outer_primary_key: '{label}Le champ {label} fait partie de la clé primaire de la table. Il a migré à partir de l''association de dépendance fonctionnelle {association_name} dont il était « identifiant ». Attention : la notion d''identifiant d''association n''existe pas en Merise ; il s''agit d''une tolérance introduite par Mocodo, mais vous devriez vraisemblablement placer cet identifiant dans l''entité .' compose_parent_primary_key: '#{label}Le champ {label} fait partie de la clé primaire de la table. C''est une clé étrangère qui a migré à partir de l''entité-mère {outer_source}.' compose_primary_foreign_key: '#{label}Le champ {label} fait partie de la clé primaire de la table. C''est une clé étrangère qui a migré directement à partir de l''entité {outer_source}.' compose_primary_key: '{label}Le champ {label} fait partie de la clé primaire de la table. C''était déjà un identifiant de l''entité {this_relation_name}.' compose_primary_ex_foreign_key: '{label}Le champ {label} fait partie de la clé primaire de la table. Sa table d''origine ({outer_source}) ayant été supprimée, il n''est pas considéré comme clé étrangère.' compose_stopped_foreign_key: '#{label}Le champ {label} est une clé étrangère. Il a migré directement à partir de l''entité {outer_source} en perdant son caractère identifiant.' compose_stopped_ex_foreign_key: '{label}Le champ {label} est un simple attribut. Il a migré directement à partir de l''entité {outer_source} en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n''est pas considéré comme clé étrangère.' compose_strengthening_primary_foreign_key: '#{label}Le champ {label} fait partie de la clé primaire de la table. C''est une clé étrangère qui a migré à partir de l''entité {outer_source} pour renforcer l''identifiant.' compose_strengthening_primary_ex_foreign_key: '{label}Le champ {label} fait partie de la clé primaire de la table. Il a migré à partir de l''entité {outer_source} pour renforcer l''identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n''est pas considéré comme clé étrangère.' compose_unsourced_foreign_key: '{label}Le champ {label} a migré par l''association de dépendance fonctionnelle {association_name} à partir de l''entité {adjacent_source} (supprimée). Attention : aucune contrainte d''intégrité référentielle n''est plus assurée.' compose_unsourced_ex_foreign_key: '{label}Le champ {label} a migré par l''association de dépendance fonctionnelle {association_name} à partir de l''entité {adjacent_source} (supprimée). Attention : aucune contrainte d''intégrité référentielle n''est plus assurée.' compose_unsourced_primary_foreign_key: '{label}Le champ {label} fait partie de la clé primaire de la table. Il a migré par l''association de dépendance fonctionnelle {association_name} à partir de l''entité {adjacent_source} (supprimée). Attention : aucune contrainte d''intégrité référentielle n''est plus assurée.' compose_unsourced_primary_ex_foreign_key: '{label}Le champ {label} fait partie de la clé primaire de la table. Il a migré par l''association de dépendance fonctionnelle {association_name} à partir de l''entité {adjacent_source} (supprimée). Attention : aucune contrainte d''intégrité référentielle n''est plus assurée.' compose_relation: '
\n
{this_relation_name} (\n {columns}\n )\n
' compose_deleted_relation: 'La table {this_relation_name} a été supprimée car elle était réduite à la clé primaire de son entité d''origine. Pour conserver de telles tables, préfixez d''un « + » la définition des entités d''origine.' add_unicity_constraints: - order: 100 search: '' replace: ' u{unicities}' - order: 200 search: '' replace: ' Il obéit en outre à la contrainte d''unicité {unicities}.' - order: 300 search: '(déjà un simple attribut.+) en outre' replace: '\1' - order: 400 search: '(pas considéré comme clé.+) en outre' replace: '\1 par contre' add_optionality_constraints: - order: 100 search: '^(.+?)(.+? (?:champ .*?|discriminateur ))()' replace: '\1{optionality}\2OPTIONALITY_SYMBOL''{optionality}''\3' transform_forced_relation: - order: 100 search: '(?s)(.+)$' replace: '\1\n
  • Avertissement. Table résultant de la conversion forcée d''une association DF.
  • ' transform_relation: - order: 800 search: 'OPTIONALITY_SYMBOL''\?''' replace: 'à saisie facultative ' - order: 900 search: 'OPTIONALITY_SYMBOL''!''' replace: 'à saisie obligatoire ' - order: 1000 comment: 'All header attributes are followed by their description. We must gather all the descriptions after the header.' search: '(?s)(.+?)(.+)' replace: '\2\n
  • \1
  • ' iterated: true - order: 1050 comment: 'Suppress the numerical suffixes of the DF associations.' search: '(association de dépendance fonctionnelle [^<]+)\d+()' replace: '\1\2' - order: 1100 search: '(
  • Le champ .*?)(.+?)( était déjà un simple attribut de .+?.)\n\1(.+?)\3' replace: '\1\2, \4\3' iterated: true - order: 1150 search: '
  • Le champ (.+), (.+?) était déjà un simple attribut( de .+?.)' replace: '
  • Les champs \1 et \2 étaient déjà de simples attributs\3' - order: 1200 search: '(
  • Le champ .*?)(.+?)( est un simple attribut\. Il a migré .+? pas considéré comme clé étrangère.+)\n\1(.+?)\3' replace: '\1\2, \4\3' iterated: true - order: 1250 search: '
  • Le champ (.+), (.+?) est un simple attribut\. Il a migré (.+?) en perdant son caractère identifiant. (.+?), il n''est pas considéré comme clé étrangère.' replace: '
  • Les champs \1 et \2 sont de simples attributs. Ils ont migré \3 en perdant leur caractère identifiant. \4, ils ne sont pas considérés comme clés étrangères.' - order: 1300 search: '(
  • Le champ .*?)(.+?)( est une clé étrangère\. Il a migré .+? l''association de dépendance fonctionnelle .+? en perdant son caractère identifiant.+)\n\1(.+?)\3' replace: '\1\2, \4\3' iterated: true - order: 1350 search: '
  • Le champ (.+), (.+?) est une clé étrangère\. Il a (migré .+ l''association de dépendance fonctionnelle .+ en perdant) son (caractère identifiant)\. Il obéit' replace: '
  • Les champs \1 et \2 sont des clés étrangères. Ils ont \3 leur \4. Ils obéissent' - order: 1400 search: '
  • Le champ (.+), (.+?) est une clé étrangère\. Il a (migré .+ l''association de dépendance fonctionnelle .+ en perdant) son (caractère identifiant)' replace: '
  • Les champs \1 et \2 sont des clés étrangères. Ils ont \3 leur \4' - order: 1500 search: '(
  • Le champ .*?)(.+?)( fait partie de la clé primaire de la table. Sa table d''origine .+?)\n\1(.+?)\3' replace: '\1\2, \4\3' iterated: true - order: 1600 search: '
  • Le champ (.+), (.+?) fait partie de la clé primaire de la table. Sa table d''origine (.+?) ayant été supprimée, il n''est pas considéré comme clé étrangère.' replace: '
  • Les champs \1 et \2 font partie de la clé primaire de la table. Leur table d''origine \3 ayant été supprimée, ils ne sont pas considérés comme clés étrangères.' - order: 1700 search: '(
  • Le champ .*?)(.+?)( fait partie de la clé primaire de la table.+?)\n\1(.+?)\3' replace: '\1\2, \4\3' iterated: true - order: 1800 search: '
  • Le champ (.+), (.+?) fait partie de la clé primaire de la table. C''est une clé étrangère qui a (migré directement à partir de l''entité .+?)' replace: '
  • Les champs \1 et \2 font partie de la clé primaire de la table. Ce sont des clés étrangères qui ont \3' - order: 1900 search: '
  • Le champ (.+), (.+?) fait partie de la clé primaire de la table. C''était déjà un identifiant (de l''entité .+?)' replace: '
  • Les champs \1 et \2 font partie de la clé primaire de la table. C''étaient déjà des identifiants \3' - order: 2000 search: '
  • Le champ (.+), (.+?) fait partie de la clé primaire de la table. Il a (migré .+?)' replace: '
  • Les champs \1 et \2 font partie de la clé primaire de la table. Ils ont \3' - order: 2100 search: '
  • Les champs (.+). Il fait partie' replace: '
  • Les champs \1. Ils font partie' - order: 2200 search: '$' replace: '' - order: 2300 search: '(?s)(.+)' replace: '\1I' iterated: true - order: 2400 search: '(?s)fait partie de( la clé primaire de la table.+)I' replace: 'constitue\1' - order: 2500 search: '(?s)font partie de( la clé primaire de la table.+)I' replace: 'constituent\1' - order: 2600 search: 'I+' replace: '' - order: 2700 search: '(?s)(\n
  • .+)' replace: '
      \1\n
    \n' - order: 2800 search: '$' replace: '\n' - order: 2900 comment: 'Develop the multiple unicity constraints' search: '(contrainte d''unicité) (\d)(\d)' replace: '\1 \2 \1 \3' iterated: true - order: 3000 comment: 'Mark any duplicated unicity constraint, except the first one.' search: '(?s)(contrainte d''unicité )(\d)(.+)\1\2' replace: '\1\2\3\1DUPLICATED_ALT_GROUP:\2' iterated: true - order: 3100 comment: 'Mark the remaining duplicated unicity constraints' search: '(?s)(contrainte d''unicité )(\d+)(.+\1DUPLICATED_ALT_GROUP:\2)' replace: '\1DUPLICATED_ALT_GROUP:\2\3' iterated: true - order: 3400 comment: 'Remove the mark' search: 'DUPLICATED_ALT_GROUP:' replace: '' - order: 3500 comment: 'Remove the repetitions' search: '(contrainte d''unicité) (\d+) \1 (\d)' replace: '\1 \2\3' iterated: true - order: 3600 search: '(contrainte d''unicité \d+)(\d)' replace: '\1, \2' iterated: true - order: 3700 search: 'à la contrainte d''unicité (.+), (\d)' replace: 'aux contraintes d''unicité \1 et \2' iterated: true compose_deleted_relations: '

    \n\n
    \n NB. {deleted_relation_lines}\n
    ' compose_relational_schema: '\n\n
    \n{relations}\n{deleted_relations}\n
    ' transform_relational_schema: - order: 100 search: '(La table )(.+?)( a été supprimée .+)\1(.+?)\3' replace: '\1\2, \4\3' iterated: true - order: 200 search: 'La table (.+), (.+?) a été supprimée car elle était réduite à la clé primaire de son entité d''origine' replace: 'Les tables \1 et \2 ont été supprimées car elles étaient réduites à la clé primaire de leur entité d''origine' ================================================ FILE: mocodo/resources/relation_templates/html-e.yaml ================================================ parent: 'html-ce' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/html.yaml ================================================ help_en: 'convert the conceptual model into a relational schema in HTML format' help_fr: 'convertit le modèle conceptuel en un schéma relationnel au format HTML' help_zh: '将概念模型转换为 HTML 格式的关系模式' fr_examples: - order: 1 example: 'html' explanation: 'version de base' - order: 2 example: 'html:b' explanation: 'avec _boilerplate_' - order: 3 example: 'html:c' explanation: 'avec contraintes d''unicité et d''optionalité' - order: 4 example: 'html:e' explanation: 'avec explications' - order: 5 example: 'html:bce' explanation: 'avec _boilerplate_, contraintes et explications' parent: 'html-c' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/latex-b.yaml ================================================ parent: 'latex-bc' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/latex-bc.yaml ================================================ parent: 'latex-c' compose_relational_schema: '% Generated by Mocodo {version}\n\n\documentclass[a4paper]{{article}}\n\usepackage[normalem]{{ulem}}\n\usepackage[T1]{{fontenc}}\n\usepackage[french]{{babel}}\n\frenchsetup{{StandardLayout=true}}\n\n\\newcommand{{\relat}}[1]{{\\textsc{{#1}}}}\n\\newcommand{{\attr}}[1]{{#1}}\n\\newcommand{{\prim}}[1]{{\uline{{#1}}}}\n\\newcommand{{\foreign}}[1]{{\#\\textsl{{#1}}}}\n\n\\title{{Conversion en relationnel\\du MCD \emph{{{title}}}}}\n\author{{\emph{{Généré par Mocodo}}}}\n\n\begin{{document}}\n\maketitle\n\n\begin{{itemize}}\n{relations}\n\end{{itemize}}\n\n\end{{document}}\n' ================================================ FILE: mocodo/resources/relation_templates/latex-bce.yaml ================================================ parent: 'latex-ce' compose_relational_schema: '% Generated by Mocodo {version}\n\n\documentclass[a4paper]{{article}}\n\usepackage[normalem]{{ulem}}\n\usepackage[T1]{{fontenc}}\n\usepackage[french]{{babel}}\n\frenchsetup{{StandardLayout=true}}\n\n\\newcommand{{\relat}}[1]{{\\textsc{{#1}}}}\n\\newcommand{{\attr}}[1]{{#1}}\n\\newcommand{{\prim}}[1]{{\uline{{#1}}}}\n\\newcommand{{\foreign}}[1]{{\#\\textsl{{#1}}}}\n\n\\title{{Conversion en relationnel\\du MCD \emph{{{title}}}}}\n\author{{\emph{{Généré par Mocodo}}}}\n\n\begin{{document}}\n\maketitle\n\n\begin{{itemize}}\n{relations}\n\end{{itemize}}\n\n\end{{document}}\n' ================================================ FILE: mocodo/resources/relation_templates/latex-be.yaml ================================================ parent: 'latex-bce' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/latex-c.yaml ================================================ parent: 'html-c' extension: 'tex' highlight: 'latex' column_separator: ', ' compose_relation: ' \item \relat{{{this_relation_name}}} ({columns})' compose_relational_schema: '% Generated by Mocodo {version}\n\n\begin{{itemize}}\n{relations}\n\end{{itemize}}\n' transform_relation: - order: 1000 comment: 'Compose normal attributes' search: '(.+?)' replace: '\\attr{\1}' - order: 1100 comment: 'Compose primary keys' search: '(.+?)' replace: '\\prim{\1}' - order: 1200 comment: 'Compose foreign primary keys' search: '#(.+?)' replace: '\\foreign{\\prim{\1}}' - order: 1300 comment: 'Compose foreign attributes' search: '#(.+?)' replace: '\\foreign{\1}' - order: 1400 comment: 'Exponents' search: '(.*u)(\d)(.*)' replace: '\1_\2\3' iterated: true - order: 1500 comment: 'Exponents' search: ' (.+?)' replace: '$^{\1}$' - order: 1600 comment: 'Escape underlines' search: '_' replace: '\_' ================================================ FILE: mocodo/resources/relation_templates/latex-ce.yaml ================================================ parent: 'html-ce' extension: 'tex' highlight: 'latex' column_separator: ', ' compose_relation: ' \item \relat{{{this_relation_name}}} ({columns})' compose_relational_schema: '% Generated by Mocodo {version}\n\n\begin{{itemize}}\n{relations}\n\end{{itemize}}{deleted_relations}\n' transform_relation: - order: 10000 comment: 'Compose normal attributes' search: '(.+?)' replace: '\\attr{\1}' - order: 10100 comment: 'Compose primary keys' search: '(.+?)' replace: '\\prim{\1}' - order: 10200 comment: 'Compose foreign primary keys' search: '#(.+?)' replace: '\\foreign{\\prim{\1}}' - order: 10300 comment: 'Compose foreign attributes' search: '#(.+?)' replace: '\\foreign{\1}' - order: 10400 comment: 'Exponents' search: '(.*u)(\d)(.*)' replace: '\1_\2\3' iterated: true - order: 10500 comment: 'Exponents' search: ' (.+?)' replace: '$^{\1}$' transform_relational_schema: - order: 1000 comment: 'Process italics' search: '(.+?)' replace: '\\emph{\1}' - order: 1100 comment: 'Process strong' search: '(.+?)' replace: '\\paragraph{\1}' - order: 1200 comment: 'Format lists' search: '
  • (.+?)
  • ' replace: ' \\item \1' - order: 1300 search: '
    ' replace: '\n\n' - order: 1400 search: '
    \n (.+?)\n
    ' replace: '\1' - order: 1500 search: '(( \\item .+\n)+)' replace: ' \\begin{itemize}\n\1 \\end{itemize}\n' - order: 1600 comment: 'Escape underlines' search: '_' replace: '\_' ================================================ FILE: mocodo/resources/relation_templates/latex-e.yaml ================================================ parent: 'latex-ce' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/latex.yaml ================================================ help_en: 'convert the conceptual model into a relational schema in LaTeX format' help_fr: 'convertit le modèle conceptuel en un schéma relationnel au format LaTeX' help_zh: '将概念模型转换为 LaTeX 格式的关系模式' fr_examples: - order: 1 example: 'latex' explanation: 'version de base' - order: 2 example: 'latex:b' explanation: 'avec _boilerplate_' - order: 3 example: 'latex:c' explanation: 'avec contraintes d''unicité et d''optionalité' - order: 4 example: 'latex:e' explanation: 'avec explications' - order: 5 example: 'latex:bce' explanation: 'avec _boilerplate_, contraintes et explications' parent: 'latex-c' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/markdown-b.yaml ================================================ parent: 'markdown-bc' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/markdown-bc.yaml ================================================ parent: 'markdown-c' ================================================ FILE: mocodo/resources/relation_templates/markdown-bce.yaml ================================================ parent: 'markdown-ce' ================================================ FILE: mocodo/resources/relation_templates/markdown-be.yaml ================================================ parent: 'markdown-bce' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/markdown-c.yaml ================================================ parent: 'html-c' extension: 'md' highlight: 'markdown' column_separator: ', ' compose_relation: '- **{this_relation_name}** ({columns})' compose_relational_schema: '\n\n{relations}' transform_relation: - order: 1000 comment: 'Compose normal attributes' search: '(.+?)' replace: '\1' - order: 1100 comment: 'Compose primary keys' search: '(.+?)' replace: '\1' - order: 1200 comment: 'Compose foreign primary keys' search: '(.+?)' replace: '_\1_' - order: 1300 comment: 'Compose foreign attributes' search: '(.+?)' replace: '_\1_' ================================================ FILE: mocodo/resources/relation_templates/markdown-ce.yaml ================================================ parent: 'html-ce' extension: 'md' highlight: 'markdown' column_separator: ', ' compose_relation: '- **{this_relation_name}** ({columns})' compose_relational_schema: '\n\n{relations}{deleted_relations}' transform_relation: - order: 10000 comment: 'Compose normal attributes' search: '(.+?)' replace: '\1' - order: 10100 comment: 'Compose primary keys' search: '(.+?)' replace: '\1' - order: 10200 comment: 'Compose foreign primary keys' search: '(.+?)' replace: '_\1_' - order: 10300 comment: 'Compose foreign attributes' search: '(.+?)' replace: '_\1_' transform_relational_schema: - order: 1000 comment: 'Process italics' search: '' replace: '_' - order: 1100 comment: 'Process strong' search: '' replace: '**' - order: 1200 comment: 'Format lists' search: '
  • (.+?)
  • ' replace: ' - \1' - order: 1300 search: '
    ' replace: '\n----\n' - order: 1400 search: '
    \n (.+?)\n
    ' replace: '\1' ================================================ FILE: mocodo/resources/relation_templates/markdown-e.yaml ================================================ parent: 'markdown-ce' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/markdown.yaml ================================================ help_en: 'convert the conceptual model into a relational schema in Markdown format' help_fr: 'convertit le modèle conceptuel en un schéma relationnel au format Markdown' help_zh: '将概念模型转换为 Markdown 格式的关系模式' fr_examples: - order: 1 example: 'markdown' explanation: 'version de base' - order: 2 example: 'markdown:c' explanation: 'avec contraintes d''unicité et d''optionalité' - order: 3 example: 'markdown:e' explanation: 'avec explications' - order: 5 example: 'markdown:ce' explanation: 'avec contraintes et explications' parent: 'markdown-c' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/md-b.yaml ================================================ parent: 'markdown-b' ================================================ FILE: mocodo/resources/relation_templates/md-bc.yaml ================================================ parent: 'markdown-bc' ================================================ FILE: mocodo/resources/relation_templates/md-bce.yaml ================================================ parent: 'markdown-bce' ================================================ FILE: mocodo/resources/relation_templates/md-be.yaml ================================================ parent: 'markdown-be' ================================================ FILE: mocodo/resources/relation_templates/md-c.yaml ================================================ parent: 'markdown-c' ================================================ FILE: mocodo/resources/relation_templates/md-ce.yaml ================================================ parent: 'markdown-ce' ================================================ FILE: mocodo/resources/relation_templates/md-e.yaml ================================================ parent: 'markdown-e' ================================================ FILE: mocodo/resources/relation_templates/md.yaml ================================================ parent: 'markdown' ================================================ FILE: mocodo/resources/relation_templates/mld-b.yaml ================================================ parent: 'markdown-b' ================================================ FILE: mocodo/resources/relation_templates/mld-bc.yaml ================================================ parent: 'markdown-bc' ================================================ FILE: mocodo/resources/relation_templates/mld-bce.yaml ================================================ parent: 'markdown-bce' ================================================ FILE: mocodo/resources/relation_templates/mld-be.yaml ================================================ parent: 'markdown-be' ================================================ FILE: mocodo/resources/relation_templates/mld-c.yaml ================================================ parent: 'markdown-c' ================================================ FILE: mocodo/resources/relation_templates/mld-ce.yaml ================================================ parent: 'markdown-ce' ================================================ FILE: mocodo/resources/relation_templates/mld-e.yaml ================================================ parent: 'markdown-e' ================================================ FILE: mocodo/resources/relation_templates/mld.yaml ================================================ parent: 'markdown' ================================================ FILE: mocodo/resources/relation_templates/ms_sql-b.yaml ================================================ parent: 'mssql-b' ================================================ FILE: mocodo/resources/relation_templates/ms_sql.yaml ================================================ parent: 'mssql' ================================================ FILE: mocodo/resources/relation_templates/mssql-b.yaml ================================================ parent: 'mssql' compose_relational_schema: '-- Generated by Mocodo {version}\n\nCREATE DATABASE [{title}]\nCONTAINMENT = NONE\nON PRIMARY (\nNAME = ''{title}'',\nFILENAME = ''C:\path\\to\{title}.mdf'',\nSIZE = 100MB,\nMAXSIZE = UNLIMITED,\nFILEGROWTH = 10MB\n)\nLOG ON (\nNAME = ''{title}_log'',\nFILENAME = ''C:\path\\to\{title}_log.ldf'',\nSIZE = 50MB,\nMAXSIZE = 2048GB,\nFILEGROWTH = 5MB\n)\nGO\nUSE [{title}]\nGO\n\n{relations}\n' transform_relational_schema: - order: 30000 comment: 'The indent placeholders in the _boilerplate_ where used to protect it from the addition of trailing commas. Transform them into double spaces.' search: '' replace: ' ' ================================================ FILE: mocodo/resources/relation_templates/mssql.yaml ================================================ help_en: 'convert the conceptual model into a physical model for Microsoft SQL Server' help_fr: 'convertit le modèle conceptuel en un modèle physique pour Microsoft SQL Server' help_zh: '将概念模型转换为 Microsoft SQL Server 的物理模型' fr_examples: - order: 1 example: 'mssql' explanation: 'version de base' - order: 4 example: 'mssql:b' explanation: 'avec _boilerplate_' parent: 'sql' stem_suffix: '_ddl_mssql' transform_label: - order: 1100 comment: 'Protect reserved keywords' search: '(?i)^(ACCESS|ACCESSIBLE|ALLOCATE|ANALYSE|ANALYZE|ARE|ARRAY|ASENSITIVE|ASSERTION|ASYMMETRIC|AT|AUDIT|AUTOINCREMENT|AVG|BEFORE|BIGINT|BINARY|BIT|BLOB|BOOLEAN|BOTH|CALL|CASCADED|CAST|CHANGE|CHAR|CHARACTER|CHARACTER_LENGTH|CLUSTER|COLLATION|COMMENT|COMPRESS|CONCAT|CONCURRENTLY|CONDITION|CONNECT|CONNECTION|CONSTRAINTS|CORRESPONDING|COUNT|CUBE|CUME_DIST|CURRENT_CATALOG|CURRENT_PATH|CURRENT_ROLE|CURRENT_SCHEMA|DATABASES|DATE|DAY|DAYS|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DEC|DECIMAL|DEFERRABLE|DEFERRED|DEFINE|DEFINER|DELAYED|DENSE_RANK|DESCRIBE|DETERMINISTIC|DIAGNOSTICS|DISCONNECT|DISTINCTROW|DIV|DO|DUAL|EACH|ELSEIF|EMPTY|ENCLOSED|ENUM|ESCAPED|EXCEPTION|EXCLUDE|EXCLUSIVE|EXPLAIN|EXTRACT|FALSE|FILTER|FIRST|FIRST_VALUE|FLOAT|FLOAT4|FLOAT8|FOLLOWING|FORCE|FOUND|FREEZE|FULLTEXT|GENERATED|GET|GLOB|GLOBAL|GO|GROUPING|GROUPS|HASH|HIGH_PRIORITY|HOUR|HOURS|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IDENTIFIED|IGNORE|ILIKE|IMMEDIATE|INCREMENT|INDEXED|INDICATOR|INFILE|INITIAL|INITIALLY|INOUT|INPUT|INSENSITIVE|INT|INT1|INT2|INT3|INT4|INT8|INTEGER|INTEVAL|INVOKER|IO_AFTER_GTIDS|IO_BEFORE_GTIDS|ISNULL|ISOLATION|ITERATE|JSON_ARRAYAGG|JSON_EXISTS|JSON_OBJECTAGG|JSON_TABLE|KEYS|LAG|LAST|LAST_VALUE|LATERAL|LEAD|LEADING|LEAVE|LEVEL|LIMIT|LINEAR|LINES|LOAD|LOCALTIME|LOCALTIMESTAMP|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOWER|LOW_PRIORITY|LTRIM|MASTER_BIND|MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAX|MAXEXTENTS|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MEMBER|MICROSECOND|MICROSECONDS|MIDDLEINT|MIN|MINUS|MINUTE|MINUTES|MINUTE_MICROSECOND|MINUTE_SECOND|MLSLABEL|MOD|MODE|MODIFIES|MODIFY|MONTH|MONTHS|NATURAL|NCHAR|NEW|NEXT|NO|NOAUDIT|NOCOMPRESS|NONE|NOTHING|NOTNULL|NOWAIT|NO_WRITE_TO_BINLOG|NTH_VALUE|NTILE|NULLS|NUMBER|NUMERIC|NVARCHAR|OFFLINE|OFFSET|ONLINE|ONLY|OPTIMIZE|OPTIMIZER_COSTS|OPTIONALLY|OUT|OUTFILE|OUTPUT|OVERLAPS|PAD|PARTIAL|PARTITION|PCTFREE|PERCENTILE_CONT|PERCENTILE_DISC|PERCENT_RANK|PLACING|PORTION|POSITION|PRECEDING|PRECISION|PREPARE|PRESERVE|PRIOR|PRIVILEGES|PURGE|RAISE|RANGE|RANK|RAW|READS|READ_WRITE|REAL|RECURSIVE|REGEXP|RELATIVE|RELEASE|RENAME|REPEAT|REPLACE|REQUIRE|RESIGNAL|RESOURCE|RESPECT|RETURNING|RLIKE|ROLLUP|ROW|ROWID|ROWNUM|ROWS|ROW_NUMBER|RTRIM|SCHEMAS|SCROLL|SECOND|SECONDS|SECOND_MICROSECOND|SENSITIVE|SEPARATOR|SESSION|SHARE|SHOW|SIGNAL|SIMILAR|SIZE|SMALLINT|SOUNDS|SPACE|SPATIAL|SPECIFIC|SQL|SQLCODE|SQLERROR|SQLEXCEPTION|SQLID|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_BUFFER_RESULT|SQL_CACHE|SQL_CALC_FOUND_ROWS|SQL_NO_CACHE|SQL_SMALL_RESULT|SSL|START|STARTING|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES|STDDEV_POP|STDDEV_SAMP|STORED|STRAIGHT_JOIN|SUBSTRING|SUCCESSFUL|SUM|SYMMETRIC|SYNONYM|SYSDATE|SYSTEM|TEMPORARY|TERMINATED|TIMEZONE_HOUR|TIMEZONE_MINUTE|TINYBLOB|TINYINT|TINYTEXT|TRAILING|TRANSLATE|TRANSLATION|TREAT|TRIM|TRUE|UESCAPE|UID|UNBOUNDED|UNDO|UNKNOWN|UNLOCK|UNNEST|UNSIGNED|UPPER|USAGE|USING|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALIDATE|VALUE|VARBINARY|VARCHAR|VARCHAR2|VARCHARACTER|VARIADIC|VAR_POP|VAR_SAMP|VERBOSE|VIRTUAL|WHENEVER|WINDOW|WITHIN|WITHOUT|WORK|WRITE|XML|XMLCAST|XMLEXISTS|XMLFOREST|XMLPARSE|XMLQUERY|XMLROOT|XMLSERIALIZE|XOR|YEAR|YEARS|YEAR_MONTH|ZEROFILL)$' replace: '[\1]' transform_relation_name: - order: 1000 comment: 'Protect reserved keywords' search: '(?i)^(ACCESS|ACCESSIBLE|ALLOCATE|ANALYSE|ANALYZE|ARE|ARRAY|ASENSITIVE|ASSERTION|ASYMMETRIC|AT|AUDIT|AUTOINCREMENT|AVG|BEFORE|BIGINT|BINARY|BIT|BLOB|BOOLEAN|BOTH|CALL|CASCADED|CAST|CHANGE|CHAR|CHARACTER|CHARACTER_LENGTH|CLUSTER|COLLATION|COMMENT|COMPRESS|CONCAT|CONCURRENTLY|CONDITION|CONNECT|CONNECTION|CONSTRAINTS|CORRESPONDING|COUNT|CUBE|CUME_DIST|CURRENT_CATALOG|CURRENT_PATH|CURRENT_ROLE|CURRENT_SCHEMA|DATABASES|DATE|DAY|DAYS|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DEC|DECIMAL|DEFERRABLE|DEFERRED|DEFINE|DEFINER|DELAYED|DENSE_RANK|DESCRIBE|DETERMINISTIC|DIAGNOSTICS|DISCONNECT|DISTINCTROW|DIV|DO|DUAL|EACH|ELSEIF|EMPTY|ENCLOSED|ENUM|ESCAPED|EXCEPTION|EXCLUDE|EXCLUSIVE|EXPLAIN|EXTRACT|FALSE|FILTER|FIRST|FIRST_VALUE|FLOAT|FLOAT4|FLOAT8|FOLLOWING|FORCE|FOUND|FREEZE|FULLTEXT|GENERATED|GET|GLOB|GLOBAL|GO|GROUPING|GROUPS|HASH|HIGH_PRIORITY|HOUR|HOURS|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IDENTIFIED|IGNORE|ILIKE|IMMEDIATE|INCREMENT|INDEXED|INDICATOR|INFILE|INITIAL|INITIALLY|INOUT|INPUT|INSENSITIVE|INT|INT1|INT2|INT3|INT4|INT8|INTEGER|INTERVAL|INVOKER|IO_AFTER_GTIDS|IO_BEFORE_GTIDS|ISNULL|ISOLATION|ITERATE|JSON_ARRAYAGG|JSON_EXISTS|JSON_OBJECTAGG|JSON_TABLE|KEYS|LAG|LAST|LAST_VALUE|LATERAL|LEAD|LEADING|LEAVE|LEVEL|LIMIT|LINEAR|LINES|LOAD|LOCALTIME|LOCALTIMESTAMP|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOWER|LOW_PRIORITY|LTRIM|MASTER_BIND|MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAX|MAXEXTENTS|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MEMBER|MICROSECOND|MICROSECONDS|MIDDLEINT|MIN|MINUS|MINUTE|MINUTES|MINUTE_MICROSECOND|MINUTE_SECOND|MLSLABEL|MOD|MODE|MODIFIES|MODIFY|MONTH|MONTHS|NATURAL|NCHAR|NEW|NEXT|NO|NOAUDIT|NOCOMPRESS|NONE|NOTHING|NOTNULL|NOWAIT|NO_WRITE_TO_BINLOG|NTH_VALUE|NTILE|NULLS|NUMBER|NUMERIC|NVARCHAR|OFFLINE|OFFSET|ONLINE|ONLY|OPTIMIZE|OPTIMIZER_COSTS|OPTIONALLY|OUT|OUTFILE|OUTPUT|OVERLAPS|PAD|PARTIAL|PARTITION|PCTFREE|PERCENTILE_CONT|PERCENTILE_DISC|PERCENT_RANK|PLACING|PORTION|POSITION|PRECEDING|PRECISION|PREPARE|PRESERVE|PRIOR|PRIVILEGES|PURGE|RAISE|RANGE|RANK|RAW|READS|READ_WRITE|REAL|RECURSIVE|REGEXP|RELATIVE|RELEASE|RENAME|REPEAT|REPLACE|REQUIRE|RESIGNAL|RESOURCE|RESPECT|RETURNING|RLIKE|ROLLUP|ROW|ROWID|ROWNUM|ROWS|ROW_NUMBER|RTRIM|SCHEMAS|SCROLL|SECOND|SECONDS|SECOND_MICROSECOND|SENSITIVE|SEPARATOR|SESSION|SHARE|SHOW|SIGNAL|SIMILAR|SIZE|SMALLINT|SOUNDS|SPACE|SPATIAL|SPECIFIC|SQL|SQLCODE|SQLERROR|SQLEXCEPTION|SQLID|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_BUFFER_RESULT|SQL_CACHE|SQL_CALC_FOUND_ROWS|SQL_NO_CACHE|SQL_SMALL_RESULT|SSL|START|STARTING|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES|STDDEV_POP|STDDEV_SAMP|STORED|STRAIGHT_JOIN|SUBSTRING|SUCCESSFUL|SUM|SYMMETRIC|SYNONYM|SYSDATE|SYSTEM|TEMPORARY|TERMINATED|TIMEZONE_HOUR|TIMEZONE_MINUTE|TINYBLOB|TINYINT|TINYTEXT|TRAILING|TRANSLATE|TRANSLATION|TREAT|TRIM|TRUE|UESCAPE|UID|UNBOUNDED|UNDO|UNKNOWN|UNLOCK|UNNEST|UNSIGNED|UPPER|USAGE|USING|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALIDATE|VALUE|VARBINARY|VARCHAR|VARCHAR2|VARCHARACTER|VARIADIC|VAR_POP|VAR_SAMP|VERBOSE|VIRTUAL|WHENEVER|WINDOW|WITHIN|WITHOUT|WORK|WRITE|XML|XMLCAST|XMLEXISTS|XMLFOREST|XMLPARSE|XMLQUERY|XMLROOT|XMLSERIALIZE|XOR|YEAR|YEARS|YEAR_MONTH|ZEROFILL)$' replace: '[\1]' transform_datatype: - order: 200 search: 'BOOLEAN_PLACEHOLDER' replace: 'BIT' - order: 300 comment: '(Meant to replace the parent''s transformation)' search: 'UNSIGNED_INT_PLACEHOLDER' replace: 'TINYINT' ================================================ FILE: mocodo/resources/relation_templates/mysql-b.yaml ================================================ parent: 'mysql' compose_relational_schema: '-- Generated by Mocodo {version}\n\nCREATE DATABASE IF NOT EXISTS `{title}`\nCHARACTER SET utf8mb4\nCOLLATE utf8mb4_general_ci\n;\nUSE `{title}`;\n\n{relations}\n' transform_relational_schema: - order: 20000 comment: 'The indent placeholders in the _boilerplate_ where used to protect it from the addition of trailing commas. Transform them into double spaces.' search: '' replace: ' ' ================================================ FILE: mocodo/resources/relation_templates/mysql.yaml ================================================ help_en: 'convert the conceptual model into a physical model for MySQL' help_fr: 'convertit le modèle conceptuel en un modèle physique pour MySQL' help_zh: '将概念模型转换为 MySQL 物理模型' fr_examples: - order: 1 example: 'mysql' explanation: 'version de base' - order: 4 example: 'mysql:b' explanation: 'avec _boilerplate_' parent: 'sql' stem_suffix: '_ddl_mysql' transform_label: - order: 1100 comment: 'Protect reserved keywords' search: '(?i)^(ACCESS|ALLOCATE|ANALYSE|ANY|ARE|ARRAY|ASSERTION|ASYMMETRIC|AT|AUDIT|AUTHORIZATION|AUTOINCREMENT|AVG|BACKUP|BEGIN|BIT|BOOLEAN|BREAK|BROWSE|BULK|CASCADED|CAST|CHARACTER_LENGTH|CHECKPOINT|CLOSE|CLUSTER|CLUSTERED|COALESCE|COLLATION|COMMENT|COMMIT|COMPRESS|COMPUTE|CONCAT|CONCURRENTLY|CONNECT|CONNECTION|CONSTRAINTS|CONTAINS|CONTAINSTABLE|CORRESPONDING|COUNT|CURRENT|CURRENT_CATALOG|CURRENT_PATH|CURRENT_ROLE|CURRENT_SCHEMA|DATE|DAY|DAYS|DBCC|DEALLOCATE|DEFERRABLE|DEFERRED|DEFINE|DEFINER|DENY|DIAGNOSTICS|DISCONNECT|DISTRIBUTED|DO|END|ENUM|ERRLVL|ESCAPE|EXCEPTION|EXCLUDE|EXCLUSIVE|EXEC|EXECUTE|EXTERNAL|EXTRACT|FILE|FILLFACTOR|FILTER|FIRST|FOLLOWING|FOUND|FREETEXT|FREETEXTTABLE|FREEZE|FULL|GLOB|GLOBAL|GO|GOTO|HASH|HOLDLOCK|HOUR|HOURS|IDENTIFIED|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|ILIKE|IMMEDIATE|INCREMENT|INDEXED|INDICATOR|INITIAL|INITIALLY|INPUT|INVOKER|ISNULL|ISOLATION|JSON_ARRAYAGG|JSON_EXISTS|JSON_OBJECTAGG|LAST|LEVEL|LINENO|LOWER|LTRIM|MAX|MAXEXTENTS|MEMBER|MERGE|MICROSECOND|MICROSECONDS|MIN|MINUS|MINUTE|MINUTES|MLSLABEL|MODE|MODIFY|MONTH|MONTHS|NATIONAL|NCHAR|NEW|NEXT|NO|NOAUDIT|NOCHECK|NOCOMPRESS|NONCLUSTERED|NONE|NOTHING|NOTNULL|NOWAIT|NULLIF|NULLS|NUMBER|NVARCHAR|OFF|OFFLINE|OFFSET|OFFSETS|ONLINE|ONLY|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OUTPUT|OVERLAPS|PAD|PARTIAL|PCTFREE|PERCENT|PERCENTILE_CONT|PERCENTILE_DISC|PIVOT|PLACING|PLAN|PORTION|POSITION|PRECEDING|PREPARE|PRESERVE|PRINT|PRIOR|PRIVILEGES|PROC|PUBLIC|RAISE|RAISERROR|RAW|READTEXT|RECONFIGURE|RELATIVE|REPLICATION|RESOURCE|RESPECT|RESTORE|RETURNING|REVERT|ROLLBACK|ROLLUP|ROWCOUNT|ROWGUIDCOL|ROWID|ROWNUM|RTRIM|RULE|SAVE|SCROLL|SECOND|SECONDS|SEMANTICKEYPHRASETABLE|SEMANTICSIMILARITYDETAILSTABLE|SEMANTICSIMILARITYTABLE|SESSION|SESSION_USER|SETUSER|SHARE|SHUTDOWN|SIMILAR|SIZE|SOME|SOUNDS|SPACE|SQLCODE|SQLERROR|SQLID|SQL_BUFFER_RESULT|SQL_CACHE|SQL_NO_CACHE|START|STATISTICS|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES|STDDEV_POP|STDDEV_SAMP|SUBSTRING|SUCCESSFUL|SUM|SYMMETRIC|SYNONYM|SYSDATE|SYSTEM_USER|TABLESAMPLE|TEMPORARY|TEXTSIZE|TIMEZONE_HOUR|TIMEZONE_MINUTE|TOP|TRAN|TRANSACTION|TRANSLATE|TRANSLATION|TREAT|TRIM|TRUNCATE|TRY_CONVERT|TSEQUAL|UESCAPE|UID|UNBOUNDED|UNKNOWN|UNNEST|UNPIVOT|UPDATETEXT|UPPER|USER|VALIDATE|VALUE|VARCHAR2|VARIADIC|VAR_POP|VAR_SAMP|VERBOSE|VIEW|WAITFOR|WHENEVER|WITHIN|WITHOUT|WORK|WRITETEXT|XML|XMLCAST|XMLEXISTS|XMLFOREST|XMLPARSE|XMLQUERY|XMLROOT|XMLSERIALIZE|YEAR|YEARS)$' replace: '`\1`' transform_relation_name: - order: 1000 comment: 'Protect reserved keywords' search: '(?i)^(ACCESS|ALLOCATE|ANALYSE|ANY|ARE|ARRAY|ASSERTION|ASYMMETRIC|AT|AUDIT|AUTHORIZATION|AUTOINCREMENT|AVG|BACKUP|BEGIN|BIT|BOOLEAN|BREAK|BROWSE|BULK|CASCADED|CAST|CHARACTER_LENGTH|CHECKPOINT|CLOSE|CLUSTER|CLUSTERED|COALESCE|COLLATION|COMMENT|COMMIT|COMPRESS|COMPUTE|CONCAT|CONCURRENTLY|CONNECT|CONNECTION|CONSTRAINTS|CONTAINS|CONTAINSTABLE|CORRESPONDING|COUNT|CURRENT|CURRENT_CATALOG|CURRENT_PATH|CURRENT_ROLE|CURRENT_SCHEMA|DATE|DAY|DAYS|DBCC|DEALLOCATE|DEFERRABLE|DEFERRED|DEFINE|DEFINER|DENY|DIAGNOSTICS|DISCONNECT|DISTRIBUTED|DO|END|ENUM|ERRLVL|ESCAPE|EXCEPTION|EXCLUDE|EXCLUSIVE|EXEC|EXECUTE|EXTERNAL|EXTRACT|FILE|FILLFACTOR|FILTER|FIRST|FOLLOWING|FOUND|FREETEXT|FREETEXTTABLE|FREEZE|FULL|GLOB|GLOBAL|GO|GOTO|HASH|HOLDLOCK|HOUR|HOURS|IDENTIFIED|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|ILIKE|IMMEDIATE|INCREMENT|INDEXED|INDICATOR|INITIAL|INITIALLY|INPUT|INVOKER|ISNULL|ISOLATION|JSON_ARRAYAGG|JSON_EXISTS|JSON_OBJECTAGG|LAST|LEVEL|LINENO|LOWER|LTRIM|MAX|MAXEXTENTS|MEMBER|MERGE|MICROSECOND|MICROSECONDS|MIN|MINUS|MINUTE|MINUTES|MLSLABEL|MODE|MODIFY|MONTH|MONTHS|NATIONAL|NCHAR|NEW|NEXT|NO|NOAUDIT|NOCHECK|NOCOMPRESS|NONCLUSTERED|NONE|NOTHING|NOTNULL|NOWAIT|NULLIF|NULLS|NUMBER|NVARCHAR|OFF|OFFLINE|OFFSET|OFFSETS|ONLINE|ONLY|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OUTPUT|OVERLAPS|PAD|PARTIAL|PCTFREE|PERCENT|PERCENTILE_CONT|PERCENTILE_DISC|PIVOT|PLACING|PLAN|PORTION|POSITION|PRECEDING|PREPARE|PRESERVE|PRINT|PRIOR|PRIVILEGES|PROC|PUBLIC|RAISE|RAISERROR|RAW|READTEXT|RECONFIGURE|RELATIVE|REPLICATION|RESOURCE|RESPECT|RESTORE|RETURNING|REVERT|ROLLBACK|ROLLUP|ROWCOUNT|ROWGUIDCOL|ROWID|ROWNUM|RTRIM|RULE|SAVE|SCROLL|SECOND|SECONDS|SEMANTICKEYPHRASETABLE|SEMANTICSIMILARITYDETAILSTABLE|SEMANTICSIMILARITYTABLE|SESSION|SESSION_USER|SETUSER|SHARE|SHUTDOWN|SIMILAR|SIZE|SOME|SOUNDS|SPACE|SQLCODE|SQLERROR|SQLID|SQL_BUFFER_RESULT|SQL_CACHE|SQL_NO_CACHE|START|STATISTICS|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES|STDDEV_POP|STDDEV_SAMP|SUBSTRING|SUCCESSFUL|SUM|SYMMETRIC|SYNONYM|SYSDATE|SYSTEM_USER|TABLESAMPLE|TEMPORARY|TEXTSIZE|TIMEZONE_HOUR|TIMEZONE_MINUTE|TOP|TRAN|TRANSACTION|TRANSLATE|TRANSLATION|TREAT|TRIM|TRUNCATE|TRY_CONVERT|TSEQUAL|UESCAPE|UID|UNBOUNDED|UNKNOWN|UNNEST|UNPIVOT|UPDATETEXT|UPPER|USER|VALIDATE|VALUE|VARCHAR2|VARIADIC|VAR_POP|VAR_SAMP|VERBOSE|VIEW|WAITFOR|WHENEVER|WITHIN|WITHOUT|WORK|WRITETEXT|XML|XMLCAST|XMLEXISTS|XMLFOREST|XMLPARSE|XMLQUERY|XMLROOT|XMLSERIALIZE|YEAR|YEARS)$' replace: '`\1`' transform_datatype: - order: 300 comment: '(Meant to replace the parent''s transformation)' search: 'UNSIGNED_INT_PLACEHOLDER' replace: 'TINYINT UNSIGNED' ================================================ FILE: mocodo/resources/relation_templates/oracle-b.yaml ================================================ parent: 'oracle' compose_relational_schema: '-- Generated by Mocodo {version}\n\nCREATE DATABASE "{title}"\nUSER SYS IDENTIFIED BY your_sys_password\nUSER SYSTEM IDENTIFIED BY your_system_password\nMAXLOGFILES 5\nMAXLOGMEMBERS 5\nMAXDATAFILES 100\nMAXINSTANCES 1\nMAXLOGHISTORY 100\nCHARACTER SET AL32UTF8\nNATIONAL CHARACTER SET AL16UTF16\nDATAFILE ''/path/to/{{title}}.dbf'' SIZE 100M\nEXTENT MANAGEMENT LOCAL\nDEFAULT TABLESPACE your_tablespace\nDEFAULT TEMPORARY TABLESPACE your_temp_tablespace\n;\nCONNECT "{title}";\n\n{relations}\n' transform_relational_schema: - order: 20000 comment: 'The indent placeholders in the _boilerplate_ where used to protect it from the addition of trailing commas. Transform them into double spaces.' search: '' replace: ' ' ================================================ FILE: mocodo/resources/relation_templates/oracle.yaml ================================================ help_en: 'convert the conceptual model into a physical model for Oracle DB' help_fr: 'convertit le modèle conceptuel en un modèle physique pour Oracle DB' help_zh: '将概念模型转换为 Oracle DB 的物理模型' fr_examples: - order: 1 example: 'oracle' explanation: 'version de base' - order: 4 example: 'oracle:b' explanation: 'avec _boilerplate_' parent: 'sql' stem_suffix: '_ddl_oracle' transform_label: - order: 1100 comment: 'Protect reserved keywords' search: '(?i)^(ACCESSIBLE|ALLOCATE|ANALYSE|ANALYZE|ARE|ARRAY|ASENSITIVE|ASSERTION|ASYMMETRIC|AT|AUTHORIZATION|AUTOINCREMENT|AVG|BACKUP|BEFORE|BEGIN|BIGINT|BINARY|BIT|BLOB|BOOLEAN|BOTH|BREAK|BROWSE|BULK|CALL|CASCADE|CASCADED|CASE|CAST|CHANGE|CHARACTER|CHARACTER_LENGTH|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLLATION|COMMIT|COMPUTE|CONCAT|CONCURRENTLY|CONDITION|CONNECTION|CONSTRAINT|CONSTRAINTS|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CORRESPONDING|COUNT|CROSS|CUBE|CUME_DIST|CURRENT_CATALOG|CURRENT_DATE|CURRENT_PATH|CURRENT_ROLE|CURRENT_SCHEMA|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DATABASES|DAY|DAYS|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DBCC|DEALLOCATE|DEC|DECLARE|DEFERRABLE|DEFERRED|DEFINE|DEFINER|DELAYED|DENSE_RANK|DENY|DESCRIBE|DETERMINISTIC|DIAGNOSTICS|DISCONNECT|DISTINCTROW|DISTRIBUTED|DIV|DO|DOUBLE|DUAL|EACH|ELSEIF|EMPTY|ENCLOSED|END|ENUM|ERRLVL|ESCAPE|ESCAPED|EXCEPTION|EXCLUDE|EXEC|EXECUTE|EXIT|EXPLAIN|EXTERNAL|EXTRACT|FALSE|FETCH|FILLFACTOR|FILTER|FIRST|FIRST_VALUE|FLOAT4|FLOAT8|FOLLOWING|FORCE|FOREIGN|FOUND|FREETEXT|FREETEXTTABLE|FREEZE|FULL|FULLTEXT|FUNCTION|GENERATED|GET|GLOB|GLOBAL|GO|GOTO|GROUPING|GROUPS|HASH|HIGH_PRIORITY|HOLDLOCK|HOUR|HOURS|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IGNORE|ILIKE|INDEXED|INDICATOR|INFILE|INITIALLY|INNER|INOUT|INPUT|INSENSITIVE|INT|INT1|INT2|INT3|INT4|INT8|INTERVAL|INVOKER|IO_AFTER_GTIDS|IO_BEFORE_GTIDS|ISNULL|ISOLATION|ITERATE|JOIN|JSON_ARRAYAGG|JSON_EXISTS|JSON_OBJECTAGG|JSON_TABLE|KEY|KEYS|KILL|LAG|LAST|LAST_VALUE|LATERAL|LEAD|LEADING|LEAVE|LEFT|LIMIT|LINEAR|LINENO|LINES|LOAD|LOCALTIME|LOCALTIMESTAMP|LONGBLOB|LONGTEXT|LOOP|LOWER|LOW_PRIORITY|LTRIM|MASTER_BIND|MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAX|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MEMBER|MERGE|MICROSECOND|MICROSECONDS|MIDDLEINT|MIN|MINUTE|MINUTES|MINUTE_MICROSECOND|MINUTE_SECOND|MOD|MODIFIES|MONTH|MONTHS|NATIONAL|NATURAL|NCHAR|NEW|NEXT|NO|NOCHECK|NONCLUSTERED|NONE|NOTHING|NOTNULL|NO_WRITE_TO_BINLOG|NTH_VALUE|NTILE|NULLIF|NULLS|NUMERIC|NVARCHAR|OFF|OFFSET|OFFSETS|ONLY|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTIMIZE|OPTIMIZER_COSTS|OPTIONALLY|OUT|OUTER|OUTFILE|OUTPUT|OVER|OVERLAPS|PAD|PARALLEL|PARTIAL|PARTITION|PERCENT|PERCENTILE_CONT|PERCENTILE_DISC|PERCENT_RANK|PIVOT|PLACING|PLAN|PORTION|POSITION|PRECEDING|PRECISION|PREPARE|PRESERVE|PRIMARY|PRINT|PRIVILEGES|PROC|PROCEDURE|PURGE|RAISE|RAISERROR|RANGE|RANK|READ|READS|READTEXT|READ_WRITE|REAL|RECONFIGURE|RECURSIVE|REFERENCES|REGEXP|RELATIVE|RELEASE|REPEAT|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESPECT|RESTORE|RESTRICT|RETURN|RETURNING|REVERT|RIGHT|RLIKE|ROLLBACK|ROLLUP|ROWCOUNT|ROWGUIDCOL|ROW_NUMBER|RTRIM|RULE|SAVE|SCHEMA|SCHEMAS|SCROLL|SECOND|SECONDS|SECOND_MICROSECOND|SEMANTICKEYPHRASETABLE|SEMANTICSIMILARITYDETAILSTABLE|SEMANTICSIMILARITYTABLE|SENSITIVE|SEPARATOR|SESSION_USER|SETUSER|SHOW|SHUTDOWN|SIGNAL|SIMILAR|SOME|SOUNDS|SPACE|SPATIAL|SPECIFIC|SQL|SQLCODE|SQLERROR|SQLEXCEPTION|SQLID|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_BUFFER_RESULT|SQL_CACHE|SQL_CALC_FOUND_ROWS|SQL_NO_CACHE|SQL_SMALL_RESULT|SSL|STARTING|STATISTICS|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES|STDDEV_POP|STDDEV_SAMP|STORED|STRAIGHT_JOIN|SUBSTRING|SUM|SYMMETRIC|SYSTEM|SYSTEM_USER|TABLESAMPLE|TEMPORARY|TERMINATED|TEXTSIZE|TIMEZONE_HOUR|TIMEZONE_MINUTE|TINYBLOB|TINYINT|TINYTEXT|TOP|TRAILING|TRAN|TRANSACTION|TRANSLATE|TRANSLATION|TREAT|TRIM|TRUE|TRUNCATE|TRY_CONVERT|TSEQUAL|UESCAPE|UNBOUNDED|UNDO|UNKNOWN|UNLOCK|UNNEST|UNPIVOT|UNSIGNED|UPDATETEXT|UPPER|USAGE|USE|USING|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALUE|VARBINARY|VARCHARACTER|VARIADIC|VARYING|VAR_POP|VAR_SAMP|VERBOSE|VIRTUAL|WAITFOR|WHEN|WHILE|WINDOW|WITHIN|WITHOUT|WORK|WRITE|WRITETEXT|XML|XMLCAST|XMLEXISTS|XMLFOREST|XMLPARSE|XMLQUERY|XMLROOT|XMLSERIALIZE|XOR|YEAR|YEARS|YEAR_MONTH|ZEROFILL)$' replace: '"\1"' transform_relation_name: - order: 1000 comment: 'Protect reserved keywords' search: '(?i)^(ACCESSIBLE|ALLOCATE|ANALYSE|ANALYZE|ARE|ARRAY|ASENSITIVE|ASSERTION|ASYMMETRIC|AT|AUTHORIZATION|AUTOINCREMENT|AVG|BACKUP|BEFORE|BEGIN|BIGINT|BINARY|BIT|BLOB|BOOLEAN|BOTH|BREAK|BROWSE|BULK|CALL|CASCADE|CASCADED|CASE|CAST|CHANGE|CHARACTER|CHARACTER_LENGTH|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLLATION|COMMIT|COMPUTE|CONCAT|CONCURRENTLY|CONDITION|CONNECTION|CONSTRAINT|CONSTRAINTS|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CORRESPONDING|COUNT|CROSS|CUBE|CUME_DIST|CURRENT_CATALOG|CURRENT_DATE|CURRENT_PATH|CURRENT_ROLE|CURRENT_SCHEMA|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DATABASES|DAY|DAYS|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DBCC|DEALLOCATE|DEC|DECLARE|DEFERRABLE|DEFERRED|DEFINE|DEFINER|DELAYED|DENSE_RANK|DENY|DESCRIBE|DETERMINISTIC|DIAGNOSTICS|DISCONNECT|DISTINCTROW|DISTRIBUTED|DIV|DO|DOUBLE|DUAL|EACH|ELSEIF|EMPTY|ENCLOSED|END|ENUM|ERRLVL|ESCAPE|ESCAPED|EXCEPTION|EXCLUDE|EXEC|EXECUTE|EXIT|EXPLAIN|EXTERNAL|EXTRACT|FALSE|FETCH|FILLFACTOR|FILTER|FIRST|FIRST_VALUE|FLOAT4|FLOAT8|FOLLOWING|FORCE|FOREIGN|FOUND|FREETEXT|FREETEXTTABLE|FREEZE|FULL|FULLTEXT|FUNCTION|GENERATED|GET|GLOB|GLOBAL|GO|GOTO|GROUPING|GROUPS|HASH|HIGH_PRIORITY|HOLDLOCK|HOUR|HOURS|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IGNORE|ILIKE|INDEXED|INDICATOR|INFILE|INITIALLY|INNER|INOUT|INPUT|INSENSITIVE|INT|INT1|INT2|INT3|INT4|INT8|INTERVAL|INVOKER|IO_AFTER_GTIDS|IO_BEFORE_GTIDS|ISNULL|ISOLATION|ITERATE|JOIN|JSON_ARRAYAGG|JSON_EXISTS|JSON_OBJECTAGG|JSON_TABLE|KEY|KEYS|KILL|LAG|LAST|LAST_VALUE|LATERAL|LEAD|LEADING|LEAVE|LEFT|LIMIT|LINEAR|LINENO|LINES|LOAD|LOCALTIME|LOCALTIMESTAMP|LONGBLOB|LONGTEXT|LOOP|LOWER|LOW_PRIORITY|LTRIM|MASTER_BIND|MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAX|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MEMBER|MERGE|MICROSECOND|MICROSECONDS|MIDDLEINT|MIN|MINUTE|MINUTES|MINUTE_MICROSECOND|MINUTE_SECOND|MOD|MODIFIES|MONTH|MONTHS|NATIONAL|NATURAL|NCHAR|NEW|NEXT|NO|NOCHECK|NONCLUSTERED|NONE|NOTHING|NOTNULL|NO_WRITE_TO_BINLOG|NTH_VALUE|NTILE|NULLIF|NULLS|NUMERIC|NVARCHAR|OFF|OFFSET|OFFSETS|ONLY|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTIMIZE|OPTIMIZER_COSTS|OPTIONALLY|OUT|OUTER|OUTFILE|OUTPUT|OVER|OVERLAPS|PAD|PARALLEL|PARTIAL|PARTITION|PERCENT|PERCENTILE_CONT|PERCENTILE_DISC|PERCENT_RANK|PIVOT|PLACING|PLAN|PORTION|POSITION|PRECEDING|PRECISION|PREPARE|PRESERVE|PRIMARY|PRINT|PRIVILEGES|PROC|PROCEDURE|PURGE|RAISE|RAISERROR|RANGE|RANK|READ|READS|READTEXT|READ_WRITE|REAL|RECONFIGURE|RECURSIVE|REFERENCES|REGEXP|RELATIVE|RELEASE|REPEAT|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESPECT|RESTORE|RESTRICT|RETURN|RETURNING|REVERT|RIGHT|RLIKE|ROLLBACK|ROLLUP|ROWCOUNT|ROWGUIDCOL|ROW_NUMBER|RTRIM|RULE|SAVE|SCHEMA|SCHEMAS|SCROLL|SECOND|SECONDS|SECOND_MICROSECOND|SEMANTICKEYPHRASETABLE|SEMANTICSIMILARITYDETAILSTABLE|SEMANTICSIMILARITYTABLE|SENSITIVE|SEPARATOR|SESSION_USER|SETUSER|SHOW|SHUTDOWN|SIGNAL|SIMILAR|SOME|SOUNDS|SPACE|SPATIAL|SPECIFIC|SQL|SQLCODE|SQLERROR|SQLEXCEPTION|SQLID|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_BUFFER_RESULT|SQL_CACHE|SQL_CALC_FOUND_ROWS|SQL_NO_CACHE|SQL_SMALL_RESULT|SSL|STARTING|STATISTICS|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES|STDDEV_POP|STDDEV_SAMP|STORED|STRAIGHT_JOIN|SUBSTRING|SUM|SYMMETRIC|SYSTEM|SYSTEM_USER|TABLESAMPLE|TEMPORARY|TERMINATED|TEXTSIZE|TIMEZONE_HOUR|TIMEZONE_MINUTE|TINYBLOB|TINYINT|TINYTEXT|TOP|TRAILING|TRAN|TRANSACTION|TRANSLATE|TRANSLATION|TREAT|TRIM|TRUE|TRUNCATE|TRY_CONVERT|TSEQUAL|UESCAPE|UNBOUNDED|UNDO|UNKNOWN|UNLOCK|UNNEST|UNPIVOT|UNSIGNED|UPDATETEXT|UPPER|USAGE|USE|USING|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALUE|VARBINARY|VARCHARACTER|VARIADIC|VARYING|VAR_POP|VAR_SAMP|VERBOSE|VIRTUAL|WAITFOR|WHEN|WHILE|WINDOW|WITHIN|WITHOUT|WORK|WRITE|WRITETEXT|XML|XMLCAST|XMLEXISTS|XMLFOREST|XMLPARSE|XMLQUERY|XMLROOT|XMLSERIALIZE|XOR|YEAR|YEARS|YEAR_MONTH|ZEROFILL)$' replace: '"\1"' transform_datatype: - order: 100 comment: '(Meant to replace the parent''s transformation)' search: '^\s*$' replace: 'VARCHAR2(42)' - order: 200 comment: '(Meant to replace the parent''s transformation)' search: 'UNSIGNED_INT_PLACEHOLDER' replace: 'NUMBER(1) UNSIGNED' - order: 300 comment: '(Meant to replace the parent''s transformation)' search: 'BOOLEAN_PLACEHOLDER' replace: 'NUMBER(1) DEFAULT 0' ================================================ FILE: mocodo/resources/relation_templates/oracle_db-b.yaml ================================================ parent: 'oracle-b' ================================================ FILE: mocodo/resources/relation_templates/oracle_db.yaml ================================================ parent: 'oracle' ================================================ FILE: mocodo/resources/relation_templates/postgres-b.yaml ================================================ parent: 'postgresql-b' ================================================ FILE: mocodo/resources/relation_templates/postgres.yaml ================================================ parent: 'postgresql' ================================================ FILE: mocodo/resources/relation_templates/postgresql-b.yaml ================================================ parent: 'postgresql' compose_relational_schema: '-- Generated by Mocodo {version}\n\nDROP DATABASE IF EXISTS ''{title}'';\nCREATE DATABASE ''{title}''\nENCODING ''UTF8''\nLC_COLLATE ''fr_FR.utf8''\nLC_CTYPE ''fr_FR.utf8''\n;\nCONNECT ''{title}'';\n\n{relations}\n' transform_relational_schema: - order: 20000 comment: 'The indent placeholders in the _boilerplate_ where used to protect it from the addition of trailing commas. Transform them into double spaces.' search: '' replace: ' ' ================================================ FILE: mocodo/resources/relation_templates/postgresql.yaml ================================================ help_en: 'convert the conceptual model into a physical model for PostgreSQL' help_fr: 'convertit le modèle conceptuel en un modèle physique pour PostgreSQL' help_zh: '将概念模型转换为 PostgreSQL 的物理模型' fr_examples: - order: 1 example: 'postgresql' explanation: 'version de base' - order: 4 example: 'postgresql:b' explanation: 'avec _boilerplate_' parent: 'sql' stem_suffix: '_ddl_postgresql' transform_label: - order: 1100 comment: 'Protect reserved keywords' search: '(?i)^(ACCESS|ACCESSIBLE|ADD|ALLOCATE|ALTER|ARE|ASENSITIVE|ASSERTION|AT|AUDIT|AUTOINCREMENT|AVG|BACKUP|BEFORE|BEGIN|BETWEEN|BIGINT|BIT|BLOB|BOOLEAN|BREAK|BROWSE|BULK|BY|CALL|CASCADE|CASCADED|CHANGE|CHAR|CHARACTER|CHARACTER_LENGTH|CHECKPOINT|CLOSE|CLUSTER|CLUSTERED|COALESCE|COMMENT|COMMIT|COMPRESS|COMPUTE|CONCAT|CONDITION|CONNECT|CONNECTION|CONSTRAINTS|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CORRESPONDING|COUNT|CUBE|CUME_DIST|CURRENT|CURRENT_PATH|CURSOR|DATABASE|DATABASES|DATE|DAY|DAYS|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFERRED|DEFINE|DEFINER|DELAYED|DELETE|DENSE_RANK|DENY|DESCRIBE|DETERMINISTIC|DIAGNOSTICS|DISCONNECT|DISTINCTROW|DISTRIBUTED|DIV|DOUBLE|DROP|DUAL|EACH|ELSEIF|EMPTY|ENCLOSED|ENUM|ERRLVL|ESCAPE|ESCAPED|EXCEPTION|EXCLUDE|EXCLUSIVE|EXEC|EXECUTE|EXISTS|EXIT|EXPLAIN|EXTERNAL|EXTRACT|FILE|FILLFACTOR|FILTER|FIRST|FIRST_VALUE|FLOAT|FLOAT4|FLOAT8|FOLLOWING|FORCE|FOUND|FREETEXT|FREETEXTTABLE|FULLTEXT|FUNCTION|GENERATED|GET|GLOB|GLOBAL|GO|GOTO|GROUPING|GROUPS|HASH|HIGH_PRIORITY|HOLDLOCK|HOUR|HOURS|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IDENTIFIED|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IGNORE|IMMEDIATE|INCREMENT|INDEX|INDEXED|INDICATOR|INFILE|INITIAL|INOUT|INPUT|INSENSITIVE|INSERT|INT|INT1|INT2|INT3|INT4|INT8|INTEGER|INTERVAL|INVOKER|IO_AFTER_GTIDS|IO_BEFORE_GTIDS|ISOLATION|ITERATE|JSON_ARRAYAGG|JSON_EXISTS|JSON_OBJECTAGG|JSON_TABLE|KEY|KEYS|KILL|LAG|LAST|LAST_VALUE|LEAD|LEAVE|LEVEL|LINEAR|LINENO|LINES|LOAD|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOWER|LOW_PRIORITY|LTRIM|MASTER_BIND|MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAX|MAXEXTENTS|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MEMBER|MERGE|MICROSECOND|MICROSECONDS|MIDDLEINT|MIN|MINUS|MINUTE|MINUTES|MINUTE_MICROSECOND|MINUTE_SECOND|MLSLABEL|MOD|MODE|MODIFIES|MODIFY|MONTH|MONTHS|NATIONAL|NCHAR|NEW|NEXT|NO|NOAUDIT|NOCHECK|NOCOMPRESS|NONCLUSTERED|NONE|NOTHING|NOWAIT|NO_WRITE_TO_BINLOG|NTH_VALUE|NTILE|NULLIF|NULLS|NUMBER|NUMERIC|NVARCHAR|OF|OFF|OFFLINE|OFFSETS|ONLINE|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTIMIZE|OPTIMIZER_COSTS|OPTION|OPTIONALLY|OUT|OUTFILE|OUTPUT|OVER|PAD|PARALLEL|PARTIAL|PARTITION|PCTFREE|PERCENT|PERCENTILE_CONT|PERCENTILE_DISC|PERCENT_RANK|PIVOT|PLAN|PORTION|POSITION|PRECEDING|PRECISION|PREPARE|PRESERVE|PRINT|PRIOR|PRIVILEGES|PROC|PROCEDURE|PUBLIC|PURGE|RAISE|RAISERROR|RANGE|RANK|RAW|READ|READS|READTEXT|READ_WRITE|REAL|RECONFIGURE|RECURSIVE|REGEXP|RELATIVE|RELEASE|RENAME|REPEAT|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESOURCE|RESPECT|RESTORE|RESTRICT|RETURN|REVERT|REVOKE|RLIKE|ROLLBACK|ROLLUP|ROW|ROWCOUNT|ROWGUIDCOL|ROWID|ROWNUM|ROWS|ROW_NUMBER|RTRIM|RULE|SAVE|SCHEMA|SCHEMAS|SCROLL|SECOND|SECONDS|SECOND_MICROSECOND|SEMANTICKEYPHRASETABLE|SEMANTICSIMILARITYDETAILSTABLE|SEMANTICSIMILARITYTABLE|SENSITIVE|SEPARATOR|SESSION|SET|SETUSER|SHARE|SHOW|SHUTDOWN|SIGNAL|SIZE|SMALLINT|SOUNDS|SPACE|SPATIAL|SPECIFIC|SQL|SQLCODE|SQLERROR|SQLEXCEPTION|SQLID|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_BUFFER_RESULT|SQL_CACHE|SQL_CALC_FOUND_ROWS|SQL_NO_CACHE|SQL_SMALL_RESULT|SSL|START|STARTING|STATISTICS|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES|STDDEV_POP|STDDEV_SAMP|STORED|STRAIGHT_JOIN|SUBSTRING|SUCCESSFUL|SUM|SYNONYM|SYSDATE|SYSTEM|TEMPORARY|TERMINATED|TEXTSIZE|TIMEZONE_HOUR|TIMEZONE_MINUTE|TINYBLOB|TINYINT|TINYTEXT|TOP|TRAN|TRANSACTION|TRANSLATE|TRANSLATION|TREAT|TRIGGER|TRIM|TRUNCATE|TRY_CONVERT|TSEQUAL|UESCAPE|UID|UNBOUNDED|UNDO|UNKNOWN|UNLOCK|UNNEST|UNPIVOT|UNSIGNED|UPDATE|UPDATETEXT|UPPER|USAGE|USE|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALIDATE|VALUE|VALUES|VARBINARY|VARCHAR|VARCHAR2|VARCHARACTER|VARYING|VAR_POP|VAR_SAMP|VIEW|VIRTUAL|WAITFOR|WHENEVER|WHILE|WITHIN|WITHOUT|WORK|WRITE|WRITETEXT|XML|XMLCAST|XMLEXISTS|XMLFOREST|XMLPARSE|XMLQUERY|XMLROOT|XMLSERIALIZE|XOR|YEAR|YEARS|YEAR_MONTH|ZEROFILL)$' replace: '"\1"' transform_relation_name: - order: 1000 comment: 'Protect reserved keywords' search: '(?i)^(ACCESS|ACCESSIBLE|ADD|ALLOCATE|ALTER|ARE|ASENSITIVE|ASSERTION|AT|AUDIT|AUTOINCREMENT|AVG|BACKUP|BEFORE|BEGIN|BETWEEN|BIGINT|BIT|BLOB|BOOLEAN|BREAK|BROWSE|BULK|BY|CALL|CASCADE|CASCADED|CHANGE|CHAR|CHARACTER|CHARACTER_LENGTH|CHECKPOINT|CLOSE|CLUSTER|CLUSTERED|COALESCE|COMMENT|COMMIT|COMPRESS|COMPUTE|CONCAT|CONDITION|CONNECT|CONNECTION|CONSTRAINTS|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CORRESPONDING|COUNT|CUBE|CUME_DIST|CURRENT|CURRENT_PATH|CURSOR|DATABASE|DATABASES|DATE|DAY|DAYS|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFERRED|DEFINE|DEFINER|DELAYED|DELETE|DENSE_RANK|DENY|DESCRIBE|DETERMINISTIC|DIAGNOSTICS|DISCONNECT|DISTINCTROW|DISTRIBUTED|DIV|DOUBLE|DROP|DUAL|EACH|ELSEIF|EMPTY|ENCLOSED|ENUM|ERRLVL|ESCAPE|ESCAPED|EXCEPTION|EXCLUDE|EXCLUSIVE|EXEC|EXECUTE|EXISTS|EXIT|EXPLAIN|EXTERNAL|EXTRACT|FILE|FILLFACTOR|FILTER|FIRST|FIRST_VALUE|FLOAT|FLOAT4|FLOAT8|FOLLOWING|FORCE|FOUND|FREETEXT|FREETEXTTABLE|FULLTEXT|FUNCTION|GENERATED|GET|GLOB|GLOBAL|GO|GOTO|GROUPING|GROUPS|HASH|HIGH_PRIORITY|HOLDLOCK|HOUR|HOURS|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IDENTIFIED|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IGNORE|IMMEDIATE|INCREMENT|INDEX|INDEXED|INDICATOR|INFILE|INITIAL|INOUT|INPUT|INSENSITIVE|INSERT|INT|INT1|INT2|INT3|INT4|INT8|INTEGER|INTERVAL|INVOKER|IO_AFTER_GTIDS|IO_BEFORE_GTIDS|ISOLATION|ITERATE|JSON_ARRAYAGG|JSON_EXISTS|JSON_OBJECTAGG|JSON_TABLE|KEY|KEYS|KILL|LAG|LAST|LAST_VALUE|LEAD|LEAVE|LEVEL|LINEAR|LINENO|LINES|LOAD|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOWER|LOW_PRIORITY|LTRIM|MASTER_BIND|MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAX|MAXEXTENTS|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MEMBER|MERGE|MICROSECOND|MICROSECONDS|MIDDLEINT|MIN|MINUS|MINUTE|MINUTES|MINUTE_MICROSECOND|MINUTE_SECOND|MLSLABEL|MOD|MODE|MODIFIES|MODIFY|MONTH|MONTHS|NATIONAL|NCHAR|NEW|NEXT|NO|NOAUDIT|NOCHECK|NOCOMPRESS|NONCLUSTERED|NONE|NOTHING|NOWAIT|NO_WRITE_TO_BINLOG|NTH_VALUE|NTILE|NULLIF|NULLS|NUMBER|NUMERIC|NVARCHAR|OF|OFF|OFFLINE|OFFSETS|ONLINE|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTIMIZE|OPTIMIZER_COSTS|OPTION|OPTIONALLY|OUT|OUTFILE|OUTPUT|OVER|PAD|PARALLEL|PARTIAL|PARTITION|PCTFREE|PERCENT|PERCENTILE_CONT|PERCENTILE_DISC|PERCENT_RANK|PIVOT|PLAN|PORTION|POSITION|PRECEDING|PRECISION|PREPARE|PRESERVE|PRINT|PRIOR|PRIVILEGES|PROC|PROCEDURE|PUBLIC|PURGE|RAISE|RAISERROR|RANGE|RANK|RAW|READ|READS|READTEXT|READ_WRITE|REAL|RECONFIGURE|RECURSIVE|REGEXP|RELATIVE|RELEASE|RENAME|REPEAT|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESOURCE|RESPECT|RESTORE|RESTRICT|RETURN|REVERT|REVOKE|RLIKE|ROLLBACK|ROLLUP|ROW|ROWCOUNT|ROWGUIDCOL|ROWID|ROWNUM|ROWS|ROW_NUMBER|RTRIM|RULE|SAVE|SCHEMA|SCHEMAS|SCROLL|SECOND|SECONDS|SECOND_MICROSECOND|SEMANTICKEYPHRASETABLE|SEMANTICSIMILARITYDETAILSTABLE|SEMANTICSIMILARITYTABLE|SENSITIVE|SEPARATOR|SESSION|SET|SETUSER|SHARE|SHOW|SHUTDOWN|SIGNAL|SIZE|SMALLINT|SOUNDS|SPACE|SPATIAL|SPECIFIC|SQL|SQLCODE|SQLERROR|SQLEXCEPTION|SQLID|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_BUFFER_RESULT|SQL_CACHE|SQL_CALC_FOUND_ROWS|SQL_NO_CACHE|SQL_SMALL_RESULT|SSL|START|STARTING|STATISTICS|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES|STDDEV_POP|STDDEV_SAMP|STORED|STRAIGHT_JOIN|SUBSTRING|SUCCESSFUL|SUM|SYNONYM|SYSDATE|SYSTEM|TEMPORARY|TERMINATED|TEXTSIZE|TIMEZONE_HOUR|TIMEZONE_MINUTE|TINYBLOB|TINYINT|TINYTEXT|TOP|TRAN|TRANSACTION|TRANSLATE|TRANSLATION|TREAT|TRIGGER|TRIM|TRUNCATE|TRY_CONVERT|TSEQUAL|UESCAPE|UID|UNBOUNDED|UNDO|UNKNOWN|UNLOCK|UNNEST|UNPIVOT|UNSIGNED|UPDATE|UPDATETEXT|UPPER|USAGE|USE|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALIDATE|VALUE|VALUES|VARBINARY|VARCHAR|VARCHAR2|VARCHARACTER|VARYING|VAR_POP|VAR_SAMP|VIEW|VIRTUAL|WAITFOR|WHENEVER|WHILE|WITHIN|WITHOUT|WORK|WRITE|WRITETEXT|XML|XMLCAST|XMLEXISTS|XMLFOREST|XMLPARSE|XMLQUERY|XMLROOT|XMLSERIALIZE|XOR|YEAR|YEARS|YEAR_MONTH|ZEROFILL)$' replace: '"\1"' transform_datatype: - order: 300 comment: '(Meant to replace the parent''s transformation)' search: 'UNSIGNED_INT_PLACEHOLDER' replace: 'SMALLINT' ================================================ FILE: mocodo/resources/relation_templates/sql.yaml ================================================ help_en: 'convert the conceptual model into a physical model for SQL' help_fr: 'convertit le modèle conceptuel en un modèle physique pour SQL' help_zh: '将概念模型转换为 SQL 的物理模型' stem_suffix: '_ddl' extension: 'sql' to_defer: false highlight: 'sql' transform_attribute: - order: 100 comment: 'Ensure there are only letters, digits and underscores in the label.' search: '(?u)\W+' replace: '_' transform_relation_name: - order: 100 comment: 'Ensure there are only letters, digits and underscores in the label.' search: '(?u)\W+' replace: '_' transform_datatype: - order: 100 comment: 'Fill in empty data types with the default one.' search: '^\s*$' replace: 'VARCHAR(42)' - order: 200 search: 'BOOLEAN_PLACEHOLDER' replace: 'BOOLEAN' - order: 300 search: 'UNSIGNED_INT_PLACEHOLDER' replace: 'UNSIGNED INT' transform_optionality: - order: 100 comment: 'Replace BANG with NOT NULL.' search: '^!$' replace: ' NOT NULL' - order: 200 comment: 'Replace QMARK with NULL.' search: '^\?$' replace: ' NULL' label_role_separator: '_' compose_primary_key: '{label}{filler}{datatype}{optionality}\nPRIMARY KEY ({label})' compose_normal_attribute: '{label}{filler}{datatype}{optionality}' compose_foreign_key: '{label}{filler}{datatype}{optionality}\nFOREIGN KEY ({label}) REFERENCES {outer_source} ({non_disambiguated_label})' compose_primary_foreign_key: '{label}{filler}{datatype}{optionality}\nPRIMARY KEY ({label})\nFOREIGN KEY ({label}) REFERENCES {outer_source} ({non_disambiguated_label})' add_unicity_constraints: - order: 100 search: '$' replace: '\nCONSTRAINT {this_relation_name}_u{unicities} UNIQUE ({label})' column_separator: '\n ' compose_relation: 'CREATE TABLE {this_relation_name} (\n {columns}\n);\n' transform_relation: - order: 100 comment: 'Move the primary keys to the end of the table.' search: '(?sm)^(PRIMARY KEY [^\n]+)\n(.*)^\)' replace: '\2 \1\n)' iterated: true - order: 200 comment: 'Concatenate them.' search: '(?m)^( PRIMARY KEY )\((.+)\)\n\1\((.+)\)' replace: '\1(\2, \3)' iterated: true - order: 300 comment: 'Move the resulting clause to the beginning of the table.' search: '(?m)^(CREATE TABLE .+\n)((?:.+\n)*)( PRIMARY KEY .+\n)' replace: '\1\3\2' - order: 1100 comment: 'Move the foreign keys to the end of the table.' search: '(?sm)^(FOREIGN KEY [^\n]+)\n(.*)^\)' replace: '\2 \1\n)' iterated: true - order: 1200 comment: 'Concatenate them when the reference tables are the same and the foreign column are distinct.' search: '(?sm)^( FOREIGN KEY )\(([^\n]+)\) (REFERENCES [^\n]+ )\(([^\n]+)\)\n(.*?)^\1\(([^\n]+)\) \3\((?!\4)([^\n]+)\)\n' replace: '\1(\2, \6) \3(\4, \7)\n\5' iterated: true - order: 2100 comment: 'Move the unique constraints to the end of the table.' search: '(?sm)^(CONSTRAINT [^\n]+ UNIQUE [^\n]+)\n(.*)^\)' replace: '\2 \1\n)' iterated: true - order: 2200 comment: 'Explode the composite constraints, e.g. _u123.' search: '(?m)^( CONSTRAINT .+)_u(\d)(\d+)( UNIQUE .+\n)' replace: '\1_u\2\4\1_u\3\4' iterated: true - order: 2300 comment: 'Concatenate them.' search: '(?sm)^( CONSTRAINT [^\n]+ UNIQUE )\(([^\n]+)\)\n(.*?)^\1\(([^\n]+)\)\n' replace: '\1(\2, \4)\n\3' iterated: true - order: 2400 comment: 'Suppress their number, let the system name them.' search: '(?m)^ CONSTRAINT [^\n]+ (UNIQUE .+)' replace: ' \1' compose_relational_schema: '-- Generated by Mocodo {version}\n\n{relations}\n' transform_relational_schema: - order: 1000 comment: 'Move all foreign keys constraints to the end of the document.' search: '(?m)(^CREATE TABLE ([^\n]+) \(\n(?: .+\n)*) (FOREIGN KEY .+)\n((?:.*\n)*)' replace: '\1\4ALTER TABLE \2 ADD \3;\n' iterated: true - order: 1100 comment: 'Group the foreign key constraints by table' search: '(?m)^((ALTER TABLE .+?) ADD .+\n)(?!\2)' replace: '\1\n' - order: 10000 comment: 'Add a newline after the last "^);" followed by a non-empty line.' search: '(?ms)(.+^\);\n)(?!\n)' replace: '\1\n' - order: 10100 comment: 'Add a trailing comma to every line starting with two spaces and followed by two spaces.' search: '(\n \S.+)(?=\n \S)' replace: '\1,' ================================================ FILE: mocodo/resources/relation_templates/sql_server-b.yaml ================================================ parent: 'mssql-b' ================================================ FILE: mocodo/resources/relation_templates/sql_server.yaml ================================================ parent: 'mssql' ================================================ FILE: mocodo/resources/relation_templates/sqlite-b.yaml ================================================ parent: 'sqlite' compose_relational_schema: '-- Generated by Mocodo {version}\n\nCREATE DATABASE IF NOT EXISTS "{title}";\nATTACH DATABASE "{title}";\n\n{relations}\n' ================================================ FILE: mocodo/resources/relation_templates/sqlite.yaml ================================================ help_en: 'convert the conceptual model into a physical model for SQLite' help_fr: 'convertit le modèle conceptuel en un modèle physique pour SQLite' help_zh: '将概念模型转换为 SQLite 的物理模型' fr_examples: - order: 1 example: 'sqlite' explanation: 'version de base' - order: 4 example: 'sqlite:b' explanation: 'avec _boilerplate_' parent: 'sql' stem_suffix: '_ddl_sqlite' transform_label: - order: 1100 comment: 'Protect reserved keywords' search: '(?i)^(ACCESS|ACCESSIBLE|ALLOCATE|ANALYSE|ANALYZE|ANY|ARE|ARRAY|ASC|ASENSITIVE|ASSERTION|ASYMMETRIC|AT|AUDIT|AUTHORIZATION|AVG|BACKUP|BEFORE|BEGIN|BIGINT|BINARY|BIT|BLOB|BOOLEAN|BOTH|BREAK|BROWSE|BULK|BY|CALL|CASCADE|CASCADED|CAST|CHANGE|CHAR|CHARACTER|CHARACTER_LENGTH|CHECKPOINT|CLOSE|CLUSTER|CLUSTERED|COALESCE|COLLATION|COLUMN|COMMENT|COMPRESS|COMPUTE|CONCAT|CONCURRENTLY|CONDITION|CONNECT|CONNECTION|CONSTRAINTS|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CORRESPONDING|COUNT|CROSS|CUBE|CUME_DIST|CURRENT|CURRENT_CATALOG|CURRENT_DATE|CURRENT_PATH|CURRENT_ROLE|CURRENT_SCHEMA|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DATABASES|DATE|DAY|DAYS|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFERRED|DEFINE|DEFINER|DELAYED|DENSE_RANK|DENY|DESC|DESCRIBE|DETERMINISTIC|DIAGNOSTICS|DISCONNECT|DISTINCTROW|DISTRIBUTED|DIV|DO|DOUBLE|DUAL|EACH|ELSEIF|EMPTY|ENCLOSED|END|ENUM|ERRLVL|ESCAPED|EXCEPTION|EXCLUDE|EXCLUSIVE|EXEC|EXECUTE|EXIT|EXPLAIN|EXTERNAL|EXTRACT|FALSE|FETCH|FILE|FILLFACTOR|FILTER|FIRST|FIRST_VALUE|FLOAT|FLOAT4|FLOAT8|FOLLOWING|FOR|FORCE|FOUND|FREETEXT|FREETEXTTABLE|FREEZE|FULL|FULLTEXT|FUNCTION|GENERATED|GET|GLOB|GLOBAL|GO|GOTO|GRANT|GROUPING|GROUPS|HASH|HIGH_PRIORITY|HOLDLOCK|HOUR|HOURS|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IDENTIFIED|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IGNORE|ILIKE|IMMEDIATE|INCREMENT|INDEXED|INDICATOR|INFILE|INITIAL|INITIALLY|INNER|INOUT|INPUT|INSENSITIVE|INT|INT1|INT2|INT3|INT4|INT8|INTEGER|INTERVAL|INVOKER|IO_AFTER_GTIDS|IO_BEFORE_GTIDS|ISOLATION|ITERATE|JSON_ARRAYAGG|JSON_EXISTS|JSON_OBJECTAGG|JSON_TABLE|KEY|KEYS|KILL|LAG|LAST|LAST_VALUE|LATERAL|LEAD|LEADING|LEAVE|LEFT|LEVEL|LIKE|LINEAR|LINENO|LINES|LOAD|LOCALTIME|LOCALTIMESTAMP|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOWER|LOW_PRIORITY|LTRIM|MASTER_BIND|MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAX|MAXEXTENTS|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MEMBER|MERGE|MICROSECOND|MICROSECONDS|MIDDLEINT|MIN|MINUS|MINUTE|MINUTES|MINUTE_MICROSECOND|MINUTE_SECOND|MLSLABEL|MOD|MODE|MODIFIES|MODIFY|MONTH|MONTHS|NATIONAL|NATURAL|NCHAR|NEW|NEXT|NO|NOAUDIT|NOCHECK|NOCOMPRESS|NONCLUSTERED|NONE|NOWAIT|NO_WRITE_TO_BINLOG|NTH_VALUE|NTILE|NULLIF|NULLS|NUMBER|NUMERIC|NVARCHAR|OF|OFF|OFFLINE|OFFSET|OFFSETS|ONLINE|ONLY|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTIMIZE|OPTIMIZER_COSTS|OPTION|OPTIONALLY|OUT|OUTER|OUTFILE|OUTPUT|OVER|OVERLAPS|PAD|PARALLEL|PARTIAL|PARTITION|PCTFREE|PERCENT|PERCENTILE_CONT|PERCENTILE_DISC|PERCENT_RANK|PIVOT|PLACING|PLAN|PORTION|POSITION|PRECEDING|PRECISION|PREPARE|PRESERVE|PRINT|PRIOR|PRIVILEGES|PROC|PROCEDURE|PUBLIC|PURGE|RAISE|RAISERROR|RANGE|RANK|RAW|READ|READS|READTEXT|READ_WRITE|REAL|RECONFIGURE|RECURSIVE|REGEXP|RELATIVE|RELEASE|RENAME|REPEAT|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESOURCE|RESPECT|RESTORE|RESTRICT|RETURN|REVERT|REVOKE|RIGHT|RLIKE|ROLLBACK|ROLLUP|ROW|ROWCOUNT|ROWGUIDCOL|ROWID|ROWNUM|ROWS|ROW_NUMBER|RTRIM|RULE|SAVE|SCHEMA|SCHEMAS|SCROLL|SECOND|SECONDS|SECOND_MICROSECOND|SEMANTICKEYPHRASETABLE|SEMANTICSIMILARITYDETAILSTABLE|SEMANTICSIMILARITYTABLE|SENSITIVE|SEPARATOR|SESSION|SESSION_USER|SETUSER|SHARE|SHOW|SHUTDOWN|SIGNAL|SIMILAR|SIZE|SMALLINT|SOME|SOUNDS|SPACE|SPATIAL|SPECIFIC|SQL|SQLCODE|SQLERROR|SQLEXCEPTION|SQLID|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_BUFFER_RESULT|SQL_CACHE|SQL_CALC_FOUND_ROWS|SQL_NO_CACHE|SQL_SMALL_RESULT|SSL|START|STARTING|STATISTICS|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES|STDDEV_POP|STDDEV_SAMP|STORED|STRAIGHT_JOIN|SUBSTRING|SUCCESSFUL|SUM|SYMMETRIC|SYNONYM|SYSDATE|SYSTEM|SYSTEM_USER|TABLESAMPLE|TEMPORARY|TERMINATED|TEXTSIZE|TIMEZONE_HOUR|TIMEZONE_MINUTE|TINYBLOB|TINYINT|TINYTEXT|TOP|TRAILING|TRAN|TRANSLATE|TRANSLATION|TREAT|TRIGGER|TRIM|TRUE|TRUNCATE|TRY_CONVERT|TSEQUAL|UESCAPE|UID|UNBOUNDED|UNDO|UNKNOWN|UNLOCK|UNNEST|UNPIVOT|UNSIGNED|UPDATETEXT|UPPER|USAGE|USE|USER|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALIDATE|VALUE|VARBINARY|VARCHAR|VARCHAR2|VARCHARACTER|VARIADIC|VARYING|VAR_POP|VAR_SAMP|VERBOSE|VIEW|VIRTUAL|WAITFOR|WHENEVER|WHILE|WINDOW|WITH|WITHIN|WITHOUT|WORK|WRITE|WRITETEXT|XML|XMLCAST|XMLEXISTS|XMLFOREST|XMLPARSE|XMLQUERY|XMLROOT|XMLSERIALIZE|XOR|YEAR|YEARS|YEAR_MONTH|ZEROFILL)$' replace: '"\1"' transform_relation_name: - order: 1000 comment: 'Protect reserved keywords' search: '(?i)^(ACCESS|ACCESSIBLE|ALLOCATE|ANALYSE|ANALYZE|ANY|ARE|ARRAY|ASC|ASENSITIVE|ASSERTION|ASYMMETRIC|AT|AUDIT|AUTHORIZATION|AVG|BACKUP|BEFORE|BEGIN|BIGINT|BINARY|BIT|BLOB|BOOLEAN|BOTH|BREAK|BROWSE|BULK|BY|CALL|CASCADE|CASCADED|CAST|CHANGE|CHAR|CHARACTER|CHARACTER_LENGTH|CHECKPOINT|CLOSE|CLUSTER|CLUSTERED|COALESCE|COLLATION|COLUMN|COMMENT|COMPRESS|COMPUTE|CONCAT|CONCURRENTLY|CONDITION|CONNECT|CONNECTION|CONSTRAINTS|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CORRESPONDING|COUNT|CROSS|CUBE|CUME_DIST|CURRENT|CURRENT_CATALOG|CURRENT_DATE|CURRENT_PATH|CURRENT_ROLE|CURRENT_SCHEMA|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DATABASES|DATE|DAY|DAYS|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFERRED|DEFINE|DEFINER|DELAYED|DENSE_RANK|DENY|DESC|DESCRIBE|DETERMINISTIC|DIAGNOSTICS|DISCONNECT|DISTINCTROW|DISTRIBUTED|DIV|DO|DOUBLE|DUAL|EACH|ELSEIF|EMPTY|ENCLOSED|END|ENUM|ERRLVL|ESCAPED|EXCEPTION|EXCLUDE|EXCLUSIVE|EXEC|EXECUTE|EXIT|EXPLAIN|EXTERNAL|EXTRACT|FALSE|FETCH|FILE|FILLFACTOR|FILTER|FIRST|FIRST_VALUE|FLOAT|FLOAT4|FLOAT8|FOLLOWING|FOR|FORCE|FOUND|FREETEXT|FREETEXTTABLE|FREEZE|FULL|FULLTEXT|FUNCTION|GENERATED|GET|GLOB|GLOBAL|GO|GOTO|GRANT|GROUPING|GROUPS|HASH|HIGH_PRIORITY|HOLDLOCK|HOUR|HOURS|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IDENTIFIED|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IGNORE|ILIKE|IMMEDIATE|INCREMENT|INDEXED|INDICATOR|INFILE|INITIAL|INITIALLY|INNER|INOUT|INPUT|INSENSITIVE|INT|INT1|INT2|INT3|INT4|INT8|INTEGER|INTERVAL|INVOKER|IO_AFTER_GTIDS|IO_BEFORE_GTIDS|ISOLATION|ITERATE|JSON_ARRAYAGG|JSON_EXISTS|JSON_OBJECTAGG|JSON_TABLE|KEY|KEYS|KILL|LAG|LAST|LAST_VALUE|LATERAL|LEAD|LEADING|LEAVE|LEFT|LEVEL|LIKE|LINEAR|LINENO|LINES|LOAD|LOCALTIME|LOCALTIMESTAMP|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOWER|LOW_PRIORITY|LTRIM|MASTER_BIND|MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAX|MAXEXTENTS|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MEMBER|MERGE|MICROSECOND|MICROSECONDS|MIDDLEINT|MIN|MINUS|MINUTE|MINUTES|MINUTE_MICROSECOND|MINUTE_SECOND|MLSLABEL|MOD|MODE|MODIFIES|MODIFY|MONTH|MONTHS|NATIONAL|NATURAL|NCHAR|NEW|NEXT|NO|NOAUDIT|NOCHECK|NOCOMPRESS|NONCLUSTERED|NONE|NOWAIT|NO_WRITE_TO_BINLOG|NTH_VALUE|NTILE|NULLIF|NULLS|NUMBER|NUMERIC|NVARCHAR|OF|OFF|OFFLINE|OFFSET|OFFSETS|ONLINE|ONLY|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTIMIZE|OPTIMIZER_COSTS|OPTION|OPTIONALLY|OUT|OUTER|OUTFILE|OUTPUT|OVER|OVERLAPS|PAD|PARALLEL|PARTIAL|PARTITION|PCTFREE|PERCENT|PERCENTILE_CONT|PERCENTILE_DISC|PERCENT_RANK|PIVOT|PLACING|PLAN|PORTION|POSITION|PRECEDING|PRECISION|PREPARE|PRESERVE|PRINT|PRIOR|PRIVILEGES|PROC|PROCEDURE|PUBLIC|PURGE|RAISE|RAISERROR|RANGE|RANK|RAW|READ|READS|READTEXT|READ_WRITE|REAL|RECONFIGURE|RECURSIVE|REGEXP|RELATIVE|RELEASE|RENAME|REPEAT|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESOURCE|RESPECT|RESTORE|RESTRICT|RETURN|REVERT|REVOKE|RIGHT|RLIKE|ROLLBACK|ROLLUP|ROW|ROWCOUNT|ROWGUIDCOL|ROWID|ROWNUM|ROWS|ROW_NUMBER|RTRIM|RULE|SAVE|SCHEMA|SCHEMAS|SCROLL|SECOND|SECONDS|SECOND_MICROSECOND|SEMANTICKEYPHRASETABLE|SEMANTICSIMILARITYDETAILSTABLE|SEMANTICSIMILARITYTABLE|SENSITIVE|SEPARATOR|SESSION|SESSION_USER|SETUSER|SHARE|SHOW|SHUTDOWN|SIGNAL|SIMILAR|SIZE|SMALLINT|SOME|SOUNDS|SPACE|SPATIAL|SPECIFIC|SQL|SQLCODE|SQLERROR|SQLEXCEPTION|SQLID|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_BUFFER_RESULT|SQL_CACHE|SQL_CALC_FOUND_ROWS|SQL_NO_CACHE|SQL_SMALL_RESULT|SSL|START|STARTING|STATISTICS|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES|STDDEV_POP|STDDEV_SAMP|STORED|STRAIGHT_JOIN|SUBSTRING|SUCCESSFUL|SUM|SYMMETRIC|SYNONYM|SYSDATE|SYSTEM|SYSTEM_USER|TABLESAMPLE|TEMPORARY|TERMINATED|TEXTSIZE|TIMEZONE_HOUR|TIMEZONE_MINUTE|TINYBLOB|TINYINT|TINYTEXT|TOP|TRAILING|TRAN|TRANSLATE|TRANSLATION|TREAT|TRIGGER|TRIM|TRUE|TRUNCATE|TRY_CONVERT|TSEQUAL|UESCAPE|UID|UNBOUNDED|UNDO|UNKNOWN|UNLOCK|UNNEST|UNPIVOT|UNSIGNED|UPDATETEXT|UPPER|USAGE|USE|USER|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALIDATE|VALUE|VARBINARY|VARCHAR|VARCHAR2|VARCHARACTER|VARIADIC|VARYING|VAR_POP|VAR_SAMP|VERBOSE|VIEW|VIRTUAL|WAITFOR|WHENEVER|WHILE|WINDOW|WITH|WITHIN|WITHOUT|WORK|WRITE|WRITETEXT|XML|XMLCAST|XMLEXISTS|XMLFOREST|XMLPARSE|XMLQUERY|XMLROOT|XMLSERIALIZE|XOR|YEAR|YEARS|YEAR_MONTH|ZEROFILL)$' replace: '"\1"' transform_relation: - order: 300 comment: 'Prevent moving primary key constraints to the beginning of the table (cf. parent template).' transform_relational_schema: - order: 1000 comment: 'Prevent moving foreign key constraints to the end of the table (cf. parent template).' - order: 1100 comment: 'Prevent grouping foreign key constraints (cf. parent template).' ================================================ FILE: mocodo/resources/relation_templates/sqlserver-b.yaml ================================================ parent: 'mssql-b' ================================================ FILE: mocodo/resources/relation_templates/sqlserver.yaml ================================================ parent: 'mssql' ================================================ FILE: mocodo/resources/relation_templates/tex-b.yaml ================================================ parent: 'latex-b' ================================================ FILE: mocodo/resources/relation_templates/tex-bc.yaml ================================================ parent: 'latex-bc' ================================================ FILE: mocodo/resources/relation_templates/tex-bce.yaml ================================================ parent: 'latex-bce' ================================================ FILE: mocodo/resources/relation_templates/tex-be.yaml ================================================ parent: 'latex-be' ================================================ FILE: mocodo/resources/relation_templates/tex-c.yaml ================================================ parent: 'latex-c' ================================================ FILE: mocodo/resources/relation_templates/tex-ce.yaml ================================================ parent: 'latex-ce' ================================================ FILE: mocodo/resources/relation_templates/tex-e.yaml ================================================ parent: 'latex-e' ================================================ FILE: mocodo/resources/relation_templates/tex.yaml ================================================ parent: 'latex' ================================================ FILE: mocodo/resources/relation_templates/text-b.yaml ================================================ parent: 'text-c' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/text-bc.yaml ================================================ parent: 'text-c' ================================================ FILE: mocodo/resources/relation_templates/text-bce.yaml ================================================ parent: 'text-ce' ================================================ FILE: mocodo/resources/relation_templates/text-be.yaml ================================================ parent: 'text-bce' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/text-c.yaml ================================================ parent: 'html-c' extension: 'txt' highlight: 'text' transform_attribute: transform_relation_name: column_separator: ', ' compose_relation: '- {this_relation_name} ({columns})' transform_relation: - order: 10000 comment: 'Compose normal attributes' search: '(.+?)' replace: '\1' - order: 10100 comment: 'Compose primary keys' search: '(.+?)' replace: '_\1_' - order: 10200 comment: 'Compose foreign primary keys' search: '(.+?)' replace: '_\1_' - order: 10300 comment: 'Compose foreign attributes' search: '(.+?)' replace: '\1' compose_relational_schema: '# Generated by Mocodo {version}\n\n{relations}' transform_relational_schema: - order: 10000 search: '([^<]*)u0 ?([^<]*)' replace: '\1⁰\2' - order: 10100 search: '([^<]*)u1 ?([^<]*)' replace: '\1¹\2' - order: 10200 search: '([^<]*)u2 ?([^<]*)' replace: '\1²\2' - order: 10300 search: '([^<]*)u3 ?([^<]*)' replace: '\1³\2' - order: 10400 search: '([^<]*)u4 ?([^<]*)' replace: '\1⁴\2' - order: 10500 search: '([^<]*)u5 ?([^<]*)' replace: '\1⁵\2' - order: 10600 search: '([^<]*)u6 ?([^<]*)' replace: '\1⁶\2' - order: 10700 search: '([^<]*)u7 ?([^<]*)' replace: '\1⁷\2' - order: 10800 search: '([^<]*)u8 ?([^<]*)' replace: '\1⁸\2' - order: 10900 search: '([^<]*)u9 ?([^<]*)' replace: '\1⁹\2' - order: 11000 search: ' |' replace: '' ================================================ FILE: mocodo/resources/relation_templates/text-ce.yaml ================================================ parent: 'html-ce' extension: 'txt' highlight: 'text' column_separator: ', ' compose_relation: '- {this_relation_name} ({columns})' transform_relation: - order: 10000 comment: 'Compose normal attributes' search: '(.+?)' replace: '\1' - order: 10100 comment: 'Compose primary keys' search: '(.+?)' replace: '_\1_' - order: 10200 comment: 'Compose foreign primary keys' search: '(.+?)' replace: '_\1_' - order: 10300 comment: 'Compose foreign attributes' search: '(.+?)' replace: '\1' compose_relational_schema: '# Generated by Mocodo {version}\n\n{relations}{deleted_relations}' transform_relational_schema: - order: 1000 comment: 'Process italics' search: '(.+?)' replace: '«\xa0\1\xa0»' - order: 1100 comment: 'Process strong' search: '' replace: '' - order: 1200 comment: 'Format lists' search: '
  • (.+?)
  • ' replace: ' - \1' - order: 1300 search: '
    ' replace: '\n--------------------------------------------------------------------------------' - order: 1400 search: '
    \n (.+?)\n
    ' replace: '\1' - order: 10000 search: '([^<]*)u0 ?([^<]*)' replace: '\1⁰\2' - order: 10100 search: '([^<]*)u1 ?([^<]*)' replace: '\1¹\2' - order: 10200 search: '([^<]*)u2 ?([^<]*)' replace: '\1²\2' - order: 10300 search: '([^<]*)u3 ?([^<]*)' replace: '\1³\2' - order: 10400 search: '([^<]*)u4 ?([^<]*)' replace: '\1⁴\2' - order: 10500 search: '([^<]*)u5 ?([^<]*)' replace: '\1⁵\2' - order: 10600 search: '([^<]*)u6 ?([^<]*)' replace: '\1⁶\2' - order: 10700 search: '([^<]*)u7 ?([^<]*)' replace: '\1⁷\2' - order: 10800 search: '([^<]*)u8 ?([^<]*)' replace: '\1⁸\2' - order: 10900 search: '([^<]*)u9 ?([^<]*)' replace: '\1⁹\2' - order: 11000 search: ' |' replace: '' ================================================ FILE: mocodo/resources/relation_templates/text-e.yaml ================================================ parent: 'text-ce' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/text.yaml ================================================ help_en: 'convert the conceptual model into a relational schema in text format' help_fr: 'convertit le modèle conceptuel en un schéma relationnel au format texte' help_zh: '将概念模型转换为文本格式的关系模式' parent: 'text-c' fr_examples: - order: 1 example: 'text' explanation: 'version de base' - order: 2 example: 'text:c' explanation: 'avec contraintes d''unicité et d''optionalité' - order: 3 example: 'html:e' explanation: 'avec explications' - order: 5 example: 'html:ce' explanation: 'avec contraintes et explications' add_unicity_constraints: add_optionality_constraints: ================================================ FILE: mocodo/resources/relation_templates/txt-b.yaml ================================================ parent: 'text-b' ================================================ FILE: mocodo/resources/relation_templates/txt-bc.yaml ================================================ parent: 'text-bc' ================================================ FILE: mocodo/resources/relation_templates/txt-bce.yaml ================================================ parent: 'text-bce' ================================================ FILE: mocodo/resources/relation_templates/txt-be.yaml ================================================ parent: 'text-be' ================================================ FILE: mocodo/resources/relation_templates/txt-c.yaml ================================================ parent: 'text-c' ================================================ FILE: mocodo/resources/relation_templates/txt-ce.yaml ================================================ parent: 'text-ce' ================================================ FILE: mocodo/resources/relation_templates/txt-e.yaml ================================================ parent: 'text-e' ================================================ FILE: mocodo/resources/relation_templates/txt.yaml ================================================ parent: 'text' ================================================ FILE: mocodo/resources/rendering_services.json ================================================ { "d2": { "preprocessing": ["urlsafe_encoding"], "url": "https://kroki.io/d2/svg/{data}" }, "gv": { "preprocessing": ["minify_graphviz", "urlsafe_encoding"], "url": "https://kroki.io/graphviz/svg/{data}" }, "mmd": { "preprocessing": ["urlsafe_encoding"], "url": "https://kroki.io/mermaid/svg/{data}" }, "puml": { "preprocessing": ["urlsafe_encoding"], "url": "https://kroki.io/plantuml/svg/{data}" }, "url": { "preprocessing": ["encode_prefix"], "url": "https://api.qrserver.com/v1/create-qr-code/?format=svg&size=1000x1000&qzone=1&data={data}" } } ================================================ FILE: mocodo/resources/shapes/arial.json ================================================ { "arrow_axis": 8, "arrow_half_height": 6, "arrow_width": 12, "association_attribute_font": { "family": "Arial", "size": 14 }, "association_cartouche_font": { "family": "Arial Black", "size": 13 }, "attribute_text_height_ratio": 0.7, "box_stroke_depth": 3, "card_baseline": 3, "card_font": { "family": "Arial", "size": 12 }, "card_margin": 6, "card_text_height_ratio": 0.85, "card_underline_depth": 1.2, "card_underline_skip_height": -3, "cartouche_text_height_ratio": 0.85, "constraint_dot_gap_width": 5.0, "constraint_dot_stroke_depth": 2.5, "constraint_font": { "family": "Arial", "size": 12 }, "constraint_margin": 5, "constraint_stroke_depth": 1.5, "constraint_text_height_tweak": 0.3, "dash_width": 4, "df_text_height_ratio": 1.0, "entity_attribute_font": { "family": "Arial", "size": 14 }, "entity_cartouche_font": { "family": "Arial Black", "size": 13 }, "inner_stroke_depth": 1.5, "label_font": { "family": "Arial", "size": 12 }, "leg_stroke_depth": 1.5, "line_skip_height": 2, "margin": 9, "note_baseline": 24, "note_font": { "family": "Arial Black", "size": 13 }, "note_overlay_height": 40, "rect_margin_height": 5, "rect_margin_width": 8, "round_corner_radius": 14, "round_rect_margin_height": 6, "round_rect_margin_width": 9, "underline_depth": 1.2, "underline_skip_height": -2 } ================================================ FILE: mocodo/resources/shapes/copperplate.json ================================================ { "arrow_axis": 8, "arrow_half_height": 6, "arrow_width": 12, "association_attribute_font": { "family": "Gill Sans", "size": 15 }, "association_cartouche_font": { "family": "Copperplate", "size": 18 }, "attribute_text_height_ratio": 0.65, "box_stroke_depth": 1.5, "card_baseline": 3, "card_font": { "family": "Futura", "size": 11 }, "card_margin": 5, "card_text_height_ratio": 0.85, "card_underline_depth": 1, "card_underline_skip_height": -2, "cartouche_text_height_ratio": 0.85, "constraint_dot_gap_width": 5.0, "constraint_dot_stroke_depth": 2, "constraint_font": { "family": "Futura", "size": 11 }, "constraint_margin": 5, "constraint_stroke_depth": 1, "constraint_text_height_tweak": 0.3, "dash_width": 4, "df_text_height_ratio": 1.1, "entity_attribute_font": { "family": "Gill Sans", "size": 15 }, "entity_cartouche_font": { "family": "Copperplate", "size": 18 }, "inner_stroke_depth": 1.5, "label_font": { "family": "Futura", "size": 11 }, "leg_stroke_depth": 1, "line_skip_height": 0, "margin": 9, "note_baseline": 24, "note_font": { "family": "Futura", "size": 16 }, "note_overlay_height": 40, "rect_margin_height": 6, "rect_margin_width": 8, "round_corner_radius": 14, "round_rect_margin_height": 5, "round_rect_margin_width": 7, "underline_depth": 1, "underline_skip_height": -3 } ================================================ FILE: mocodo/resources/shapes/georgia.json ================================================ { "arrow_axis": 8, "arrow_half_height": 6, "arrow_width": 12, "association_attribute_font": { "family": "Georgia", "size": 14 }, "association_cartouche_font": { "family": "Georgia", "size": 14 }, "attribute_text_height_ratio": 0.85, "box_stroke_depth": 1.5, "card_baseline": 3, "card_font": { "family": "Verdana", "size": 12 }, "card_margin": 5, "card_text_height_ratio": 0.85, "card_underline_depth": 1, "card_underline_skip_height": -2, "cartouche_text_height_ratio": 0.85, "constraint_dot_gap_width": 5.0, "constraint_dot_stroke_depth": 2, "constraint_font": { "family": "Verdana", "size": 12 }, "constraint_margin": 5, "constraint_stroke_depth": 1, "constraint_text_height_tweak": 0.3, "dash_width": 4, "df_text_height_ratio": 1.0, "entity_attribute_font": { "family": "Georgia", "size": 14 }, "entity_cartouche_font": { "family": "Georgia", "size": 14 }, "inner_stroke_depth": 1, "label_font": { "family": "Verdana", "size": 12 }, "leg_stroke_depth": 1, "line_skip_height": 2, "margin": 9, "note_baseline": 24, "note_font": { "family": "Georgia", "size": 15 }, "note_overlay_height": 40, "rect_margin_height": 5, "rect_margin_width": 5, "round_corner_radius": 14, "round_rect_margin_height": 5, "round_rect_margin_width": 7, "underline_depth": 1, "underline_skip_height": 0.0 } ================================================ FILE: mocodo/resources/shapes/mondrian.json ================================================ { "arrow_axis": 12, "arrow_half_height": 9, "arrow_width": 18, "association_attribute_font": { "family": "Skia", "size": 18, "weight": "bold" }, "association_cartouche_font": { "family": "Skia", "size": 20, "weight": "bold" }, "attribute_text_height_ratio": 0.85, "box_stroke_depth": 5, "card_baseline": 4, "card_font": { "family": "Skia", "size": 18, "weight": "bold" }, "card_margin": 8, "card_text_height_ratio": 0.85, "card_underline_depth": 1.2, "card_underline_skip_height": -3, "cartouche_text_height_ratio": 0.85, "constraint_dot_gap_width": 10, "constraint_dot_stroke_depth": 7, "constraint_font": { "family": "Skia", "size": 18, "weight": "bold" }, "constraint_margin": 8, "constraint_stroke_depth": 5, "constraint_text_height_tweak": 0.3, "dash_width": 2, "df_text_height_ratio": 1.0, "entity_attribute_font": { "family": "Skia", "size": 18, "weight": "bold" }, "entity_cartouche_font": { "family": "Skia", "size": 20, "weight": "bold" }, "inner_stroke_depth": 5, "label_font": { "family": "Skia", "size": 18, "weight": "bold" }, "leg_stroke_depth": 5, "line_skip_height": 1, "margin": 25, "note_baseline": 32, "note_font": { "family": "Skia", "size": 20, "weight": "bold" }, "note_overlay_height": 50, "rect_margin_height": 10, "rect_margin_width": 10, "round_corner_radius": 2, "round_rect_margin_height": 10, "round_rect_margin_width": 10, "underline_depth": 1.2, "underline_skip_height": 0.0 } ================================================ FILE: mocodo/resources/shapes/sans.json ================================================ { "arrow_axis": 8, "arrow_half_height": 6, "arrow_width": 12, "association_attribute_font": { "family": "Liberation Sans", "size": 14 }, "association_cartouche_font": { "family": "Liberation Sans", "size": 14 }, "attribute_text_height_ratio": 0.85, "box_stroke_depth": 1.5, "card_baseline": 3, "card_font": { "family": "Liberation Sans", "size": 12 }, "card_margin": 5, "card_text_height_ratio": 0.85, "card_underline_depth": 1, "card_underline_skip_height": 1, "cartouche_text_height_ratio": 0.85, "constraint_dot_gap_width": 5.0, "constraint_dot_stroke_depth": 2, "constraint_font": { "family": "Liberation Sans", "size": 12 }, "constraint_margin": 2.5, "constraint_stroke_depth": 1, "constraint_text_height_tweak": 0.3, "dash_width": 4, "df_text_height_ratio": 1.0, "entity_attribute_font": { "family": "Liberation Sans", "size": 14 }, "entity_cartouche_font": { "family": "Liberation Sans", "size": 14 }, "inner_stroke_depth": 1, "label_font": { "family": "Liberation Sans", "size": 12 }, "leg_stroke_depth": 1, "line_skip_height": 2, "margin": 9, "note_baseline": 24, "note_font": { "family": "Liberation Sans", "size": 14 }, "note_overlay_height": 40, "rect_margin_height": 5, "rect_margin_width": 5, "round_corner_radius": 14, "round_rect_margin_height": 5, "round_rect_margin_width": 7, "underline_depth": 1, "underline_skip_height": 0.0 } ================================================ FILE: mocodo/resources/shapes/serif.json ================================================ { "arrow_axis": 8, "arrow_half_height": 6, "arrow_width": 12, "association_attribute_font": { "family": "Liberation Serif", "size": 14 }, "association_cartouche_font": { "family": "Liberation Serif", "size": 14 }, "attribute_text_height_ratio": 0.85, "box_stroke_depth": 1.5, "card_baseline": 3, "card_font": { "family": "Liberation Serif", "size": 12 }, "card_margin": 5, "card_text_height_ratio": 0.85, "card_underline_depth": 1, "card_underline_skip_height": 1, "cartouche_text_height_ratio": 0.85, "constraint_dot_gap_width": 5.0, "constraint_dot_stroke_depth": 2, "constraint_font": { "family": "Liberation Serif", "size": 12 }, "constraint_margin": 2.5, "constraint_stroke_depth": 1, "constraint_text_height_tweak": 0.3, "dash_width": 4, "df_text_height_ratio": 1.0, "entity_attribute_font": { "family": "Liberation Serif", "size": 14 }, "entity_cartouche_font": { "family": "Liberation Serif", "size": 14 }, "inner_stroke_depth": 1, "label_font": { "family": "Liberation Serif", "size": 12 }, "leg_stroke_depth": 1, "line_skip_height": 2, "margin": 9, "note_baseline": 24, "note_font": { "family": "Liberation Serif", "size": 14 }, "note_overlay_height": 40, "rect_margin_height": 5, "rect_margin_width": 5, "round_corner_radius": 14, "round_rect_margin_height": 5, "round_rect_margin_width": 7, "underline_depth": 1, "underline_skip_height": 0.0 } ================================================ FILE: mocodo/resources/shapes/times.json ================================================ { "arrow_axis": 8, "arrow_half_height": 6, "arrow_width": 12, "association_attribute_font": { "family": "Times New Roman", "size": 14 }, "association_cartouche_font": { "family": "Times New Roman", "size": 14 }, "attribute_text_height_ratio": 0.85, "box_stroke_depth": 1.5, "card_baseline": 3, "card_font": { "family": "Verdana", "size": 12 }, "card_margin": 5, "card_text_height_ratio": 0.85, "card_underline_depth": 1, "card_underline_skip_height": -2, "cartouche_text_height_ratio": 0.85, "constraint_dot_gap_width": 5.0, "constraint_dot_stroke_depth": 2, "constraint_font": { "family": "Verdana", "size": 12 }, "constraint_margin": 5, "constraint_stroke_depth": 1, "constraint_text_height_tweak": 0.3, "dash_width": 4, "df_text_height_ratio": 1.1, "entity_attribute_font": { "family": "Times New Roman", "size": 14 }, "entity_cartouche_font": { "family": "Times New Roman", "size": 14 }, "inner_stroke_depth": 1, "label_font": { "family": "Verdana", "size": 12 }, "leg_stroke_depth": 1, "line_skip_height": 2, "margin": 9, "note_baseline": 24, "note_font": { "family": "Times New Roman", "size": 16 }, "note_overlay_height": 40, "rect_margin_height": 5, "rect_margin_width": 5, "round_corner_radius": 14, "round_rect_margin_height": 5, "round_rect_margin_width": 7, "underline_depth": 1, "underline_skip_height": 0.0 } ================================================ FILE: mocodo/resources/shapes/trebuchet.json ================================================ { "arrow_axis": 8, "arrow_half_height": 6, "arrow_width": 12, "association_attribute_font": { "family": "Trebuchet MS", "size": 14 }, "association_cartouche_font": { "family": "Trebuchet MS", "size": 14 }, "attribute_text_height_ratio": 0.85, "box_stroke_depth": 1.5, "card_baseline": 3, "card_font": { "family": "Verdana", "size": 12 }, "card_margin": 5, "card_text_height_ratio": 0.85, "card_underline_depth": 1, "card_underline_skip_height": -2, "cartouche_text_height_ratio": 0.85, "constraint_dot_gap_width": 5.0, "constraint_dot_stroke_depth": 2, "constraint_font": { "family": "Verdana", "size": 12 }, "constraint_margin": 5, "constraint_stroke_depth": 1, "constraint_text_height_tweak": 0.3, "dash_width": 4, "df_text_height_ratio": 0.9, "entity_attribute_font": { "family": "Trebuchet MS", "size": 14 }, "entity_cartouche_font": { "family": "Trebuchet MS", "size": 14 }, "inner_stroke_depth": 1, "label_font": { "family": "Verdana", "size": 12 }, "leg_stroke_depth": 1, "line_skip_height": 2, "margin": 9, "note_baseline": 24, "note_font": { "family": "Trebuchet MS", "size": 14 }, "note_overlay_height": 40, "rect_margin_height": 5, "rect_margin_width": 5, "round_corner_radius": 14, "round_rect_margin_height": 5, "round_rect_margin_width": 7, "underline_depth": 1, "underline_skip_height": 0.0 } ================================================ FILE: mocodo/resources/shapes/verdana.json ================================================ { "arrow_axis": 8, "arrow_half_height": 6, "arrow_width": 12, "association_attribute_font": { "family": "Verdana", "size": 12, "weight": "bold" }, "association_cartouche_font": { "family": "Verdana", "size": 12, "weight": "bold" }, "attribute_text_height_ratio": 0.85, "box_stroke_depth": 2, "card_baseline": 3, "card_font": { "family": "Verdana", "size": 12, "weight": "bold" }, "card_margin": 5, "card_text_height_ratio": 0.85, "card_underline_depth": 1, "card_underline_skip_height": -2, "cartouche_text_height_ratio": 0.85, "constraint_dot_gap_width": 6, "constraint_dot_stroke_depth": 3, "constraint_font": { "family": "Verdana", "size": 12, "weight": "bold" }, "constraint_margin": 5, "constraint_stroke_depth": 1.5, "constraint_text_height_tweak": 0.3, "dash_width": 4, "df_text_height_ratio": 1.0, "entity_attribute_font": { "family": "Verdana", "size": 12, "weight": "bold" }, "entity_cartouche_font": { "family": "Verdana", "size": 12, "weight": "bold" }, "inner_stroke_depth": 1, "label_font": { "family": "Verdana", "size": 12, "weight": "bold" }, "leg_stroke_depth": 2, "line_skip_height": 2, "margin": 9, "note_baseline": 24, "note_font": { "family": "Verdana", "size": 14 }, "note_overlay_height": 40, "rect_margin_height": 5, "rect_margin_width": 5, "round_corner_radius": 14, "round_rect_margin_height": 5, "round_rect_margin_width": 7, "underline_depth": 1, "underline_skip_height": 0 } ================================================ FILE: mocodo/resources/shapes/xinnian.json ================================================ { "arrow_axis": 12, "arrow_half_height": 9, "arrow_width": 18, "association_attribute_font": { "family": "Arial Black", "size": 14 }, "association_cartouche_font": { "family": "Arial Black", "size": 14 }, "attribute_text_height_ratio": 0.5, "box_stroke_depth": 5, "card_baseline": 5, "card_font": { "family": "Comic Sans MS", "size": 14 }, "card_margin": 10, "card_text_height_ratio": 0.85, "card_underline_depth": 1, "card_underline_skip_height": -4.0, "cartouche_text_height_ratio": 0.85, "constraint_dot_gap_width": 8, "constraint_dot_stroke_depth": 4, "constraint_font": { "family": "Comic Sans MS", "size": 14 }, "constraint_margin": 5.0, "constraint_stroke_depth": 2, "constraint_text_height_tweak": 0.3, "dash_width": 4, "df_text_height_ratio": 1.0, "entity_attribute_font": { "family": "Arial Black", "size": 14 }, "entity_cartouche_font": { "family": "Arial Black", "size": 14 }, "inner_stroke_depth": 0.5, "label_font": { "family": "Comic Sans MS", "size": 14 }, "leg_stroke_depth": 2, "line_skip_height": 2, "margin": 20, "note_baseline": 24, "note_font": { "family": "Arial Black", "size": 16 }, "note_overlay_height": 40, "rect_margin_height": 9, "rect_margin_width": 14, "round_corner_radius": 30, "round_rect_margin_height": 10, "round_rect_margin_width": 14, "underline_depth": 2, "underline_skip_height": -5.0 } ================================================ FILE: mocodo/resources/transformations.json ================================================ { "arrange": { "category": "rw", "help": "réarrange la disposition, soit par Branch & Bound, soit avec un algorithme génétique", "fr_examples": { "arrange": "B&B sans contraintes", "arrange:timeout=60": "B&B limité à une minute", "arrange:wide": "B&B privilégiant la largeur", "arrange:current": "B&B sur la grille courante", "arrange:balanced=0": "B&B sur la plus petite grille équilibrée", "arrange:balanced=1": "B&B sur la seconde plus petite grille équilibrée", "arrange:algo=ga": "algorithme génétique" }, "aliases": [] }, "ascii": { "category": "rw", "help": "réécrit les éléments donnés en ASCII", "fr_examples": { "ascii:roles,labels": "rôles, libellés des boîtes et des attributs en ASCII" }, "aliases": [], "op_tk": true }, "ast": { "category": "cv", "help": "crée l'arbre de syntaxe abstraite du texte source (pour le débogage)", "aliases": [] }, "camel": { "category": "rw", "help": "réécrit les éléments donnés en camelCase", "aliases": [ "camelcase", "camel_case" ], "op_tk": true }, "capitalize": { "category": "rw", "help": "réécrit les éléments donnés en capitalisant la première lettre de chaque mot", "aliases": [], "op_tk": true }, "casefold": { "category": "rw", "help": "réécrit les éléments donnés en minuscules, mais plus agressivement que « lower »", "aliases": [ "case_fold" ], "op_tk": true }, "prompt": { "category": "cv", "help": "génère un prompt pour demander à une IA de compléter le MCD", "fr_examples": { "prompt:cards": "avec les explications des cardinalités", "prompt:types": "avec les types des attributs" }, "aliases": [ "chat", "chatbot", "ia", "ai" ] }, "chen": { "category": "cv", "help": "convertit le modèle conceptuel dans la notation de Chen", "fr_examples": { "chen": "sans attributs", "chen:attrs": "avec attributs", "chen:attrs --defer": "calcule le rendu graphique via un service web", "chen:layout=circo,mindist=2,scale=0.6": "ajoute des options arbitraires pour Graphviz" }, "aliases": [] }, "create": { "category": "rw", "help": "essaie d'inférer les types, entités, CIFs ou flèches de DF à partir des éléments existants", "fr_examples": { "guess:types": "deviner les types manquants", "create:types=": "remplacer les types manquants par `[]`", "create:types=TODO": "remplacer les types manquants par `[TODO]`", "make:entities": "réparer l'oubli d'entités référencées dans des associations", "create:dfs": "mettre des DF partout où c'est possible", "add:df_arrows": "ajouter des flèches aux DF 11", "add:cifs": "ajouter les CIF correspondant aux agrégats", "add:cifs=light": "même chose en visualisation allégée", "add:roles": "mettre comme rôles le nom des associations partout où c'est utile" }, "aliases": [ "add", "insert", "make", "guess", "infer", "complete", "new" ], "op_tk": true }, "crow": { "category": "cv", "help": "convertit le modèle conceptuel dans la notation crow's foot", "fr_examples": { "crow": "format Graphviz", "crow --defer": "calcule le rendu graphique via un service web", "crow:mmd": "format Mermaid", "crow:mermaid": "idem" }, "aliases": [ "crowfoot", "crowsfoot" ] }, "data_dict": { "category": "cv", "help": "extrait tous les attributs du MCD dans une table", "aliases": [ "data_dictionary" ], "fr_examples": { "data_dict": "tableau Markdown, trois colonnes", "data_dict:label": "liste Markdown, une colonne", "data_dict:label,type='Description'": "deux colonnes, un libellé personnalisé", "data_dict:label='Attribut',type='Description'": "deux colonnes, deux libellés personnalisés", "data_dict:**box**='Entité ou
    association',label,`type`=`'Type de données'`": "mise en forme de certains libellés", "data_dict:tsv": "tableau TSV, trois colonnes", "data_dict:tsv,label": "liste des attributs séparés par des retours à la ligne" } }, "delete": { "category": "rw", "help": "supprime les éléments donnés quand c'est possible", "fr_examples": { "empty": "ne garde que la structure et le nom des boîtes", "delete:types,notes,attrs,cards": "idem", "delete:cards": "remplace les cardinalités par `XX`", "delete:card_prefixes": "supprime les marqueurs d'entités faibles et d'agrégats", "delete:dfs": "supprime les entités indépendantes dont tous les attributs sont identifiants (et les DF qui les relient)" }, "aliases": [ "del", "suppress", "erase", "remove", "hide", "empty" ], "op_tk": true }, "drain": { "category": "rw", "help": "déplace tout attribut d'association (1,1) vers l'entité appropriée", "aliases": [] }, "drown": { "category": "rw", "help": "remplace tous les noms d'éléments par un libellé générique numéroté", "aliases": [ "drown_by_numbers", "anonymize", "anonymise" ] }, "echo": { "category": "rw", "help": "réécrit le texte source tel quel", "aliases": [] }, "explode": { "category": "rw", "help": "décompose toute association n-aire (*,N) en n associations binaires", "fr_examples": { "explode arrange": "décomposer les non-DF ternaires et plus, puis réarranger", "explode:arity=3 arrange": "idem", "explode:weak arrange": "idem, avec création d'entités faibles", "explode:arity=2.5 arrange": "étendre aux non-DF binaires porteuses d'attributs", "explode:arity=2 arrange": "étendre à toutes les non-DF binaires" }, "aliases": [] }, "fix": { "category": "rw", "help": "essaie de corriger les erreurs courantes dans les éléments donnés", "fr_examples": { "fix:cards": "normaliser les cardinalités en 01, 11, 0N et 1N" }, "aliases": [], "op_tk": true }, "flip": { "category": "rw", "help": "applique au diagramme une symétrie verticale, horizontale ou diagonale", "fr_examples": { "flip:v": "symétrie verticale", "flip:h": "symétrie horizontale", "flip:d": "symétrie selon la seconde diagonale", "flip:vhd": "symétrie selon la première diagonale", "flip:dhv": "idem (ordre indifférent)" }, "aliases": [ "mirror", "reflect" ] }, "grow": { "category": "rw", "help": "ajoute des entités et associations aléatoires (par défaut : 10 nouvelles associations)", "fr_examples": { "grow arrange": "ajouter des éléments avec les paramètres par défaut, puis réarranger", "grow:n=10": "nombre total d'associations à ajouter (défaut)", "grow:arity_1=2": "nombre d'associations réflexives (défaut)", "grow:arity_3=2": "nombre d'associations ternaires (défaut)", "grow:arity_4=0": "nombre d'associations quaternaires (défaut)", "grow:doubles=1": "nombre d'associations liant deux mêmes entités (défaut)", "grow:composite_ids=1": "nombre d'identifiants composites (défaut)", "grow:ent_attrs=4": "nombre maximal d'attributs par entité (défaut)", "grow:assoc_attrs=2": "nombre maximal d'attributs par association (défaut)", "grow:'*1-*N'=3": "nombre d'associations `*1-*N` (défaut)", "grow:'01-11'=1": "nombre d'associations `01-11` (défaut)", "grow:'_11-*N'=1": "une entité faible (zéro par défaut)", "grow:'/1N-*N'=1": "un agrégat (zéro par défaut)", "grow:from_scratch arrange": "à partir d'un MCD vide", "grow:grow:n=9,from_scratch,ent_attrs=3 obfuscate:labels=en4 create:roles lower:roles arrange": "créer un MCD d'entraînement à la conversion en relationnel" }, "aliases": [] }, "lower": { "category": "rw", "help": "réécrit les éléments donnés en minuscules", "fr_examples": { "lower:attrs,roles": "attributs et rôles en minuscules" }, "aliases": [ "lowercase", "lower_case" ], "op_tk": true }, "pascal": { "category": "rw", "help": "réécrit les élements donnés en PascalCase", "aliases": [ "pascalcase", "pascal_case" ], "op_tk": true }, "prefix": { "category": "rw", "help": "préfixe les éléments donnés avec la chaîne donnée", "fr_examples": { "prefix:roles='-'": "force les rôles à remplacer le nom des clés étrangères lors du passage au relationnel" }, "aliases": [ "prepend" ], "op_tk": true }, "randomize": { "category": "rw", "help": "garde la structure, mais randomise les éléments donnés quand c'est possible", "fr_examples": { "obfuscate": "libellés remplacés par du Lorem Ipsum", "obfuscate:labels=lorem": "idem", "obfuscate:labels=disparition": "idem, lexique du roman de Perec", "obfuscate:labels=en4": "idem, mots anglais de 4 lettres (SFW)", "obfuscate:attrs=fr,boxes=fr5": "idem, mots français de longueur quelconque pour les attributs, de 5 lettres pour les boîtes", "randomize:types": "types randomisés avec les fréquences de `default_datatypes_fr.tsv`." }, "aliases": [ "rand", "random", "randomise", "obfuscate", "obscure" ], "op_tk": true }, "relation": { "category": "cv", "help": "convertit le modèle conceptuel en schéma relationnel avec le gabarit donné", "fr_examples": { "relation:path/to/my_template.yaml": "chemin relatif, extension obligatoire" }, "aliases": [ "template", "relation_template" ] }, "replace": { "category": "rw", "help": "réécrit les éléments donnés en appliquant le motif « recherche/remplacement » donné", "fr_examples": { "replace:boxes='DIRIGER/RÉPONDRE DE'": "renomme une boîte", "replace:texts='personel/personnel'": "corrige une faute d'orthographe", "replace:replace:texts='_/ '": "remplace les tirets bas par des espaces", "replace:types='VARCHAR/VARCHAR2'": "modifie un nom de type", "replace:cards=0N/1N": "remplace toutes les cardinalités 0N par 1N", "replace:cards=1N//1N": "crée des agrégats un peu partout", "replace:cards='0/X' replace:cards='11/X1' replace:cards='1N/XN'": "masque les cardinalités minimales", "delete:card_prefixes replace:cards=11/_11": "ajoute des marqueurs d'entités faibles" }, "aliases": [ "substitute", "sub", "repl" ], "op_tk": true }, "slice": { "category": "rw", "help": "réécrit les éléments donnés en n'en gardant qu'une tranche donnée", "fr_examples": { "slice:boxes=5:10": "de l'indice 5 (inclus) à l'indice 10 (exclu)", "slice:boxes=5:": "supprime les 5 premiers caractères", "slice:boxes=:5": "ne garde que les 5 premiers caractères", "slice:boxes=:-5": "supprime les 5 derniers caractères", "slice:boxes=:": "équivalent de `echo`", "slice:boxes=": "idem", "slice:boxes": "idem" }, "aliases": [ "cut", "interval" ], "op_tk": true }, "snake": { "category": "rw", "help": "réécrit les éléments donnés en snake_case", "aliases": [ "snakecase", "snake_case" ], "op_tk": true }, "split": { "category": "rw", "help": "décompose toute association n-aire (*,1) en n-1 associations binaires", "fr_examples": { "split arrange": "décomposer, puis réarranger" }, "aliases": [] }, "suffix": { "category": "rw", "help": "suffixe les éléments donnés avec la chaîne donnée", "fr_examples": { "suffix:boxes=1": "Ajoute un suffixe numérique au nom des boîtes en vue de mettre un MCD et sa copie sur le même diagramme." }, "aliases": [ "append" ], "op_tk": true }, "swapcase": { "category": "rw", "help": "réécrit les éléments donnés en inversant la casse de chaque lettre", "aliases": [ "swap_case" ], "op_tk": true }, "title": { "category": "rw", "help": "réécrit les éléments donnés en mettant la première lettre de chaque mot en majuscule", "aliases": [ "titlecase", "title_case" ], "op_tk": true }, "truncate": { "category": "rw", "help": "tronque les éléments donnés à la longueur donnée (par défaut : 64)", "fr_examples": { "truncate:boxes=10": "tronque les noms des boîtes à 10 caractères" }, "aliases": [ "trunc", "shorten" ], "op_tk": true }, "uml": { "category": "cv", "help": "convertit le modèle conceptuel en diagramme de classes UML", "fr_examples": { "uml": "format PlantUML", "uml:plantuml": "idem", "uml --defer": "calcule le rendu graphique via un service web", "uml:plantuml=-": "supprime les styles par défaut", "uml:plantuml='skinparam backgroundColor yellow\nskinparam classAttributeFontName Arial\n'": "ajoute des styles personnalisés" }, "aliases": [ "uml", "class_diagram" ] }, "share": { "category": "cv", "help": "encode le MCD dans une URL pour Mocodo online", "fr_examples": { "qr --defer": "génère un QR code via un service web" }, "aliases": [ "url", "link", "qr", "qr_code" ] }, "upper": { "category": "rw", "help": "réécrit les éléments donnés en majuscules", "fr_examples": { "upper:boxes": "noms des boîtes en majuscules" }, "aliases": [ "uppercase", "upper_case" ], "op_tk": true } } ================================================ FILE: mocodo/rewrite/__init__.py ================================================ ================================================ FILE: mocodo/rewrite/_arrange.py ================================================ import importlib from time import time from mocodo.mocodo_error import subarg_error def run(source, subargs, **kargs): algo = subargs.get("algo", "bb") try: module = importlib.import_module(f".rewrite.arrange_{algo}", package="mocodo") except ModuleNotFoundError: raise subarg_error("algo", algo) timeout = subargs.get("timeout") if timeout is None: has_expired = lambda: False else: try: timeout = time() + float(timeout) has_expired = lambda: time() > timeout except Exception: raise subarg_error("timeout", timeout) return module.arrange(source, subargs, has_expired) ================================================ FILE: mocodo/rewrite/_drain.py ================================================ from collections import defaultdict __import__("sys").path[0:0] = ["."] from ..parse_mcd import Token, Visitor from ..tools.parser_tools import parse_source, reconstruct_source, first_child class DrainerFirstPass(Visitor): """ In the first pass, the attributes to drain are suppressed from the tree and stored in a dictionary. """ def __init__(self): self.drained_attrs = defaultdict(list) def assoc_clause(self, tree): # Guard: ensure that exactly one cardinality is 11 cards = [node.children[0].value for node in tree.find_data("card")] if cards.count("11") != 1: return # Guard: ensure that the association has at least one attributes attrs = [*tree.find_data("typed_attr")] if len(attrs) == 0: return # Find out the name of the entity that is the target of the 11 leg entity_name_refs = [node.children[0] for node in tree.find_data("entity_name_ref")] i = cards.index("11") entity_name_ref = entity_name_refs[i].children[0].value # Store the attributes to drain self.drained_attrs[entity_name_ref].extend(map(reconstruct_source, attrs)) # Remove the attributes from the association drop = False children = [] for child in tree.children: if isinstance(child, Token): if child.type == "COLON": drop = True elif child.type == "NL": drop = False if not drop: children.append(child) tree.children = children class DrainerSecondPass(Visitor): """ In the second pass, the drained attributes are reinjected into the appropriate entities. """ def __init__(self, drained_attrs): self.drained_attrs = drained_attrs def entity_clause(self, tree): # Guard: ensure that the entity must get drained attributes entity_name_def = first_child(tree, "entity_name_def").children[0] if not entity_name_def in self.drained_attrs: return # Add the drained attributes to the entity attrs = self.drained_attrs[entity_name_def] last_token = tree.children[-1] last_token.value = f", {', '.join(attrs)}{last_token.value}" def run(source, **kargs): tree = parse_source(source) visitor = DrainerFirstPass() visitor.visit(tree) visitor = DrainerSecondPass(visitor.drained_attrs) visitor.visit(tree) return reconstruct_source(tree) ================================================ FILE: mocodo/rewrite/_drown.py ================================================ from ..parse_mcd import Visitor from ..tools.parser_tools import first_child, parse_source, reconstruct_source class Numbering(Visitor): def __init__(self, tree, params): self.size = len(str(len(set(node.children[0].value for node in tree.find_data("box_name"))))) def new_name(base_name, name, i): if name == params["df"]: return name return f"{base_name} {i:0{self.size}d}_" def update_translations(token_name, base_name, n): nodes = tree.find_data(token_name) names = [node.children[0].children[0].value for node in nodes] self.names.update({name: new_name(base_name, name, i) for (i, name) in enumerate(names, n)}) self.numbers.update({new_name(base_name, name, i): f"{i:0{self.size}d}" for (i, name) in enumerate(names, n)}) self.names = {} self.numbers = {} update_translations("entity_name_def", _("ENTITY"), 1) update_translations("assoc_name_def", _("ASSOC"), len(self.names) + 1) self.attr_base = _("attr") self.leg_note_base = _("role") def update_box_attrs(self, tree, token_name): number = self.numbers[first_child(tree, token_name).children[0].value] for (i, attr) in enumerate(tree.find_data("attr"), 1): if attr.children[0] == "": continue # do nothing for a spacer attribute attr.children[0].value = f"{self.attr_base} {number} {i}" def box_name(self, tree): token = tree.children[0] token.value = self.names[token.value] def entity_clause(self, tree): self.update_box_attrs(tree, "entity_name_def") def assoc_clause(self, tree): self.update_box_attrs(tree, "assoc_name_def") for (i, attr) in enumerate(tree.find_data("leg_note"), 1): attr.children[0].value = f"{self.leg_note_base} {i}" def run(source, params, **kargs): tree = parse_source(source) visitor = Numbering(tree, params) visitor.visit(tree) return reconstruct_source(tree) ================================================ FILE: mocodo/rewrite/_explode.py ================================================ __import__("sys").path[0:0] = ["."] import contextlib from ..parse_mcd import Token, Visitor from ..tools.parser_tools import parse_source, reconstruct_source, first_child class Exploder(Visitor): def __init__(self, subargs, params): arity = 3 with contextlib.suppress(ValueError, KeyError): arity = float(subargs["arity"]) self.empty_only = (arity == 2.5) self.threshold = int(arity) self.allow_weak = "weak" in subargs if self.allow_weak: # Don't create an identifier for the new weak entity self.explosion_template = ":" # Prefix the potential first attribute with an underscore self.explosion_prefix = " _" # Strenghten the new legs self.explosion_card = "_11" else: # Create a strong identifier for the new entity self.explosion_template = ": id. {}" # Separate the potential first attribute with a comma self.explosion_prefix = ", " # Don't strengthen the new legs self.explosion_card = "11" self.df_label = params["df"] def line(self, tree): # It is not possible to use a method `assoc_clause` since the amount of indentation needs # to be known for the added clauses. # Guard: store the indentation and abort if the clause is not an association. indent = next(tree.find_data("indent"), None) indent = indent.children[0].value if indent else "" tree = next(tree.find_data("assoc_clause"), None) if tree is None: return # Guard: ensure that there are enough legs and all their max cards are N. cards = [node.children[0].value for node in tree.find_data("card")] if len(cards) < self.threshold or any(card[1] != "N" for card in cards): return # Guard: on demand, avoid processing binary associations with no attributes. if self.empty_only and not first_child(tree, "typed_attr"): return # Guard: don't explode a clustered association if weak is not allowed card_prefixes = [node.children[0].value == "/" for node in tree.find_data("card_prefix")] if any(card_prefixes) and not self.allow_weak: return # Guard: don't explode an association of several clusters if sum(card_prefixes) > 1: return # Back the legs up as a list of strings. legs = list(map(reconstruct_source, tree.find_data("assoc_leg"))) # Convert the original association into an entity. assoc_name = first_child(tree, "assoc_name_def").children[0] state = "Waiting for first comma" children = [] for child in tree.children: if isinstance(child, Token): if child.type == "COMMA" and state == "Waiting for first comma": child.value = self.explosion_template.format(assoc_name.lower()) children.append(child) state = "Scanning legs" continue if child.type == "COLON": # There were at least one attribute state = "Scanning attributes" child.value = self.explosion_prefix if child.type == "NL": # useful where there are no attributes state = "EOL" if state != "Scanning legs": children.append(child) tree.children = children # Construct the clauses of the new associations clauses = [] prefix = first_child(tree, "box_def_prefix") for leg in legs: card = self.explosion_card # TODO: check this logic if leg.startswith("/"): # when there is a cluster leg = leg[1:] # suppress it card = "11" # and switch to a non-strenghtening leg clauses.append(f"{indent}{prefix}{self.df_label}, {card} {assoc_name}, {leg}") # Suffix the resulting string to the last node (a "NL" token") tree.children[-1].value += "\n".join(clauses) + "\n" def run(source, subargs=None, params=None, **kargs): subargs = subargs or {} params = params or {"df": "DF"} tree = parse_source(source) visitor = Exploder(subargs, params) visitor.visit(tree) return reconstruct_source(tree) ================================================ FILE: mocodo/rewrite/_grow.py ================================================ import contextlib import random import re from ..tools.parser_tools import parse_source from ..mocodo_error import MocodoError def random_booleans(n, p): p = min(n, p) result = [True] * p + [False] * (n - p) random.shuffle(result) return result def calculate_cards(scheme, refs): if len(refs) > len(set(refs)): # If the association is reflexive # Process the cases of n-ary associations with n > 2 # (the other ones have already been filtered out by the caller) scheme = scheme.replace("_", "") # silently forbid weak associations scheme = scheme.replace("/", "") # silently forbid cluster associations (head, tail) = scheme.split("-") cards = [head] + [tail] * (len(refs) - 1) cards = [card.replace("*", random.choice("01")) for card in cards] random.shuffle(cards) return cards def extract_weak_entities(cards, refs): for (card, ref) in zip(cards, refs): if "_" in card: yield ref def biased_choice(elements): # Return a random element from a list, with a bias towards the last elements. i = max(random.randrange(0, len(elements)) for _ in range(3)) return elements[i] def biased_sample(elements, k): # Return a random sample of k elements from a list, with a bias towards the last elements. max_sample = [-1] # incorrect sample, but it will be replaced on the first iteration for _ in range(3): sample = random.sample(range(len(elements)), k) if min(sample) > min(max_sample): max_sample = sample return [elements[i] for i in max_sample] def run(source, subargs=None, params=None, **kargs): match_card_scheme = re.compile(r"^[/_]?[01*][1N]-[/_]?[01*][1N]$").match subargs = subargs or {} default = { "n": 10, "arity_1": 2, "arity_3": 2, "arity_4": 0, "ent_attrs": 4, "doubles": 1, "composite_ids": 1, "assoc_attrs": 2, "*1-*N": 3, "01-11": 1, "from_scratch": False, } # Complete the given subargs with default values settings = {} for (k, v) in default.items(): if k in subargs: cast = type(v) # cast to the same type as the default value with contextlib.suppress(TypeError): settings[k] = cast(subargs[k]) continue # If the cast fails, silently fall back to the default value settings[k] = v # Complete the settings with the remaining card schemes for (k, v) in subargs.items(): if match_card_scheme(k): with contextlib.suppress(TypeError): settings[k] = int(v) # Silently correct incompatible numbers n = settings["n"] for (k, v) in list(settings.items()): if k in ("n", "assoc_attrs"): continue elif k == "ent_attrs": settings[k] = max(v, 1) elif k in ("doubles", "arity_1", "arity_3", "arity_4"): settings[k] = min(v, n - 1) association_bases = [None, _("Reflexive"), _("Binary"), _("Ternary"), _("Quaternary")] entity_base = _("Entity") weak_entity_base = _("Weak Entity") attr_base = _("attr") id_base = _("id") card_schemes = [] for (k, v) in settings.items(): if match_card_scheme(k): card_schemes.extend([k] * v) card_schemes.extend(["*N-*N"] * (n - len(card_schemes))) # Try to find a combination of card schemes and arities that is compatible arities = [1] * settings["arity_1"] + [3] * settings["arity_3"] + [4] * settings["arity_4"] + [2] * n arities = arities[:n] max_tries = 100 while max_tries > 0: random.shuffle(arities) # Slightly push the non binary arities towards the end (one iteration of bubble sort). # Reason: the non binary arities are tougher to place when there are not many entities yet for i in range(len(arities) - 1): if arities[i] != 2 and arities[i + 1] == 2: (arities[i], arities[i + 1]) = (arities[i + 1], arities[i]) random.shuffle(card_schemes) # avoid being stuck with a card scheme incompatible with a small arity max_tries -= 1 for (scheme, arity) in zip(card_schemes, arities): if "_" in scheme and arity == 1: break # A reflexive association cannot be weak if "/" in scheme and arity < 3: break # A cluster association is better with at least 3 entities if "11" in scheme and (arity > 2 or "/" in scheme): break # A DF is better with at most 2 entities or a cluster else: # All associations are compatible with their card schemes break else: raise MocodoError(28, _("Cannot find a suitable combination of card schemes and arities.")) # fmt: skip for key in ("assoc_attrs", "composite_ids"): settings[key] = random_booleans(n, settings[key]) ent_attr_names = [] for is_composite in settings["composite_ids"]: if is_composite: ent_attr_names.append([f"{id_base}", f"_{id_base}"] + [f"{attr_base}"] * random.randint(0, settings["ent_attrs"] - 2)) else: ent_attr_names.append([f"{id_base}"] + [f"{attr_base}"] * random.randint(0, settings["ent_attrs"] - 1)) if "from_scratch" in subargs: source = f"ENTITY_NAME_PLACEHOLDER 1_: {id_base} 1 1, {attr_base} 1 2, {attr_base} 1 3\n" tree = parse_source(source) entities = [node.children[0].children[0].value for node in tree.find_data("entity_name_def")] associations = [node.children[0].children[0].value for node in tree.find_data("assoc_name_def")] counter = len(entities) + len(associations) + 1 clauses = [source] ref_pool = [] weak_entities = set() for i in range(settings["n"] - settings["doubles"]): if arities[i] == 1: # to keep the MCD connected, don't create a new entity if the association is reflexive old_entity = biased_choice(entities) refs = [old_entity, old_entity] else: # Normal case: create a new entity new_entity = f"ENTITY_NAME_PLACEHOLDER {counter}_" new_ent_attrs = [f"{ent_attr_name} {counter} {j}" for (j, ent_attr_name) in enumerate(ent_attr_names[i], 1)] clauses.append(f"{new_entity}: {', '.join(new_ent_attrs)}") try: # Prefer all distinct entities old_refs = biased_sample(entities, arities[i] - 1) except ValueError: # If there are not enough entities, allow reflexive associations old_refs = [biased_choice(entities) for _ in range(arities[i] - 1)] refs = [new_entity] + old_refs entities.append(new_entity) counter += 1 new_association = f"{association_bases[arities[i]]} {counter}_" ref_pool.append(refs) cards = calculate_cards(card_schemes[i], refs) clauses.append(", ".join([new_association] + [f"{card} {ref}" for (card, ref) in zip(cards, refs)])) weak_entities.update(extract_weak_entities(cards, refs)) new_assoc_attrs = [f"{attr_base} {counter} {j}" for j in range(1, settings["assoc_attrs"][i] + 1)] if new_assoc_attrs: clauses[-1] += f": {', '.join(new_assoc_attrs)}" associations.append(new_association) counter += 1 for i in range(settings["n"] - settings["doubles"], settings["n"]): refs = biased_choice(ref_pool) arity = len(set(refs)) new_association = f"{association_bases[arity]} {counter}_" cards = calculate_cards(card_schemes[i], refs) clauses.append(", ".join([new_association] + [f"{card} {ref}" for (card, ref) in zip(cards, refs)])) weak_entities.update(extract_weak_entities(cards, refs)) new_assoc_attrs = [f"{attr_base} {counter} {j}" for j in range(1, settings["assoc_attrs"][i] + 1)] if new_assoc_attrs: clauses[-1] += f": {', '.join(new_assoc_attrs)}" associations.append(new_association) counter += 1 text = "\n".join(clauses) j = len("ENTITY_NAME_PLACEHOLDER") for e in weak_entities: text = re.sub(fr"\b{e}\b", lambda m: f"{weak_entity_base}{m.group(0)[j:]}", text) text = text.replace("ENTITY_NAME_PLACEHOLDER", entity_base) return text ================================================ FILE: mocodo/rewrite/_split.py ================================================ from collections import Counter __import__("sys").path[0:0] = ["."] from ..parse_mcd import Token, Visitor from ..tools.parser_tools import parse_source, reconstruct_source, first_child class Splitter(Visitor): def line(self, tree): # It is not possible to use a method `assoc_clause` since the amount of indentation needs # to be known for the added clauses. # Guard: store the indentation and abort if the clause is not an association. indent = next(tree.find_data("indent"), None) indent = indent.children[0].value if indent else "" tree = next(tree.find_data("assoc_clause"), None) if tree is None: return # Guard: abort if the association has less than 3 legs. legs = [node for node in tree.find_data("assoc_leg")] if len(legs) < 3: return # Reorder the legs with "11" cards first, then "01", then the other ones. d = {"11": 0, "01": 1} legs.sort(key=lambda leg: d.get(first_child(leg, "card").value, 2)) # Guard: ensure that the first max cardinality is 1. if first_child(legs[0], "card")[1] != "1": return # Guard: don't split a clustered association card_prefixes = [first_child(leg, "card_prefix") == "/" for leg in legs] if any(card_prefixes): return # Accumulate the constituting elements (cf. grammar.lark) of the original legs. new_items = [] for leg in legs: acc = [] acc.append(first_child(leg, "card_hidden")) acc.append(first_child(leg, "card_prefix")) acc.append(first_child(leg, "card")) acc.append(first_child(leg, "leg_arrow")) acc.append(first_child(leg, "leg_note")) acc[-1] = f" [{acc[-1]}] " if acc[-1] else " " acc.append(first_child(leg, "entity_name_ref").children[0]) new_items.append("".join(acc)) # Concatenate any attributes of the association. attrs = [] for child in tree.find_data("typed_attr"): acc = [] acc.append(first_child(child, "attr")) datatype = first_child(child, "datatype") if datatype: acc.append(f" [{datatype}]") attrs.append("".join(acc)) # Construct the lines of the exploded association. The *1 leg is the first one. result = [] box_def_prefix = first_child(tree, "box_def_prefix") assoc_name_def = first_child(tree, "assoc_name_def").children[0] for (i, new_item) in enumerate(new_items[1:]): result.append(f"{indent}{box_def_prefix}{assoc_name_def}{i}, {new_items[0]}, {new_item}") # Add the attributes to the first clause only. Duplicating them would be an error. result[0] += f": {', '.join(attrs)}" if attrs else "" # The first clause starts at the same position as the association name: unindent. result[0] = result[0].lstrip() # Add a blank line after the exploded association. result.append("") # Replace the original tree by a pseudo-tree containing only the desired string. token = first_child(tree, "box_name") tree.children = [Token("MOCK", "\n".join(result), line=token.line, column=token.column)] def run(source, **kargs): tree = parse_source(source) visitor = Splitter() visitor.visit(tree) return reconstruct_source(tree) ================================================ FILE: mocodo/rewrite/arrange_bb.py ================================================ import contextlib from functools import lru_cache from itertools import count, product from math import hypot from random import choice, random, shuffle from .cross import cross from ..mocodo_error import MocodoError from ..argument_parser import non_negative_integer, positive_integer from ..mcd import Mcd def arrange(source, subargs, has_expired): is_organic = False if "wide" in subargs: col_count = 8 with contextlib.suppress(Exception): col_count = int(subargs["wide"]) or 1 mcd = Mcd(source) (q, r) = divmod(mcd.get_non_phantom_count(), col_count) row_count = q + bool(r) source = mcd.get_refitted_clauses(col_count, row_count) elif "balanced" in subargs: nth_fit = 0 with contextlib.suppress(Exception): nth_fit = int(subargs["balanced"]) mcd = Mcd(source) source = mcd.get_refitted_clauses(nth_fit) elif "current" in subargs: pass else: is_organic = True mcd = Mcd(source) layout_data = mcd.get_layout_data() successors = layout_data["successors"] col_count = layout_data["col_count"] row_count = layout_data["row_count"] multiplicity = layout_data["multiplicity"] min_objective = non_negative_integer(subargs.get("min_objective", 0)) max_objective = non_negative_integer(subargs.get("max_objective", 15)) call_limit = positive_integer(subargs.get("call_limit", 10000)) verbose = bool(subargs.get("verbose") is not None) # -t arrange:verbose => {"verbose": ""} => True has_expired = has_expired or (lambda: False) @lru_cache def bounded_neighborhood(x1, y1): result = set() for x2 in range( max(0, x1 - radius), min(col_count, x1 + radius + 1), ): for y2 in range( max(0, y1 - radius + abs(x1 - x2)), min(row_count, y1 + radius - abs(x1 - x2) + 1), ): if x1 != x2 or y1 != y2: result.add((x2, y2)) return result @lru_cache def organic_neighborhood(x1, y1): result = set() for x2 in range(x1 - radius, x1 + radius + 1): for y2 in range(y1 - radius + abs(x1 - x2), y1 + radius - abs(x1 - x2) + 1): if x1 != x2 or y1 != y2: result.add((x2, y2)) return result @lru_cache def bounded_hull(coords): result = set() for (x, y) in coords: if x - 1 >= 0: result.add((x - 1, y)) if x + 1 < col_count: result.add((x + 1, y)) if y - 1 >= 0: result.add((x, y - 1)) if y + 1 < row_count: result.add((x, y + 1)) return result.difference(coords) @lru_cache def organic_hull(coords): result = set() for (x, y) in coords: result.add((x - 1, y)) result.add((x + 1, y)) result.add((x, y - 1)) result.add((x, y + 1)) return result.difference(coords) def recurs(box_coords, next_boxes, placed_segments, cumulated_distances): if cumulated_distances > objective: # print("cut") return None if len(next_boxes) == 0: return { "coords": box_coords, "crossings": 0, "distances": cumulated_distances, } outside_hull_count = len(next_boxes) - len(hull(frozenset(box_coords.values()))) if outside_hull_count * outside_hull_minimal_distance + cumulated_distances > objective: # print("Lower bound cut") return None if has_expired(): raise MocodoError(10, _('Layout calculation time exceeded.')) # fmt: skip if next(iteration) > call_limit: # print("call limit exceeded") return None box_to_place = next_boxes[0] placed_successors = { box_coords[box]: box for box in successors[box_to_place] if box in box_coords } if placed_successors: placed_successor_coords = iter(placed_successors) (x1, y1) = next(placed_successor_coords) possible_coords = neighborhood(x1, y1).copy() # print(placed_successors[0], possible_coords) for (x1, y1) in placed_successor_coords: possible_coords.intersection_update(neighborhood(x1, y1)) if not possible_coords: # print("neighborhood intersection is empty") return None else: # print("the box to place has no successors: all empty coords are possible") possible_coords = set(product(range(col_count), range(row_count))) possible_coords.difference_update(box_coords.values()) if not possible_coords: # print("neighborhood intersection is not free") return None non_crossing_possible_coords = [] for (x1, y1) in possible_coords: for ((x2, y2), (x3, y3, x4, y4)) in product(placed_successors, placed_segments): if cross(x1, y1, x2, y2, x3, y3, x4, y4): break else: non_crossing_possible_coords.append((x1, y1)) if not non_crossing_possible_coords: # print("all possible coords result in a crossing with existing segment") return None weighted_possible_coords = [] for (x1, y1) in non_crossing_possible_coords: cumulated_distance = 0 for ((x2, y2), placed_box) in placed_successors.items(): cumulated_distance += ( distances[abs(x1 - x2)][abs(y1 - y2)] * multiplicity[(box_to_place, placed_box)] ) weighted_possible_coords.append((cumulated_distance, random(), x1, y1)) weighted_possible_coords.sort() for (cumulated_distance, __, x1, y1) in weighted_possible_coords: # For the double underscore, see __main__.py box_coords[box_to_place] = (x1, y1) new_segments = [(x1, y1, x2, y2) for (x2, y2) in placed_successors] new_next_boxes = list( successors[box_to_place].difference(box_coords).difference(next_boxes) ) if len(next_boxes) == 1 and len(new_next_boxes) == 0 and len(box_coords) != box_count: # print("the placed boxes have no more non placed successors") new_next_boxes = list(set(range(box_count)).difference(box_coords)) if new_next_boxes: new_next_boxes = [choice(new_next_boxes)] shuffle(new_next_boxes) result = recurs( box_coords, next_boxes[1:] + new_next_boxes, placed_segments + new_segments, cumulated_distances + cumulated_distance, ) if result: return result del box_coords[box_to_place] box_count = col_count * row_count neighborhood = organic_neighborhood if is_organic else bounded_neighborhood hull = organic_hull if is_organic else bounded_hull radius = 3 distances = [[hypot(i, j) - 1 for j in range(radius + 1)] for i in range(radius + 1)] outside_hull_minimal_distance = distances[1][2] if all(not successor for successor in successors): # print("no link: return a random layout") layout = list(range(box_count)) shuffle(layout) result = { "layout": layout, "crossings": 0, "distances": 0, } mcd.set_layout(**result) return mcd.get_clauses() for objective in range(min_objective, max_objective + 1): if verbose: print("Objective %s." % objective) boxes = list(range(box_count)) shuffle(boxes) for first_box in boxes: iteration = count() if successors[first_box]: if verbose: print(" Starting from box %s." % first_box) result = recurs({first_box: (0, 0)}, list(successors[first_box]), [], 0) if result: coords = result["coords"] if is_organic: min_x = min(x for (x, y) in coords.values()) max_x = max(x for (x, y) in coords.values()) min_y = min(y for (x, y) in coords.values()) max_y = max(y for (x, y) in coords.values()) for (box_index, (x, y)) in coords.items(): coords[box_index] = (x - min_x, y - min_y) result["row_count"] = row_count = max_y - min_y + 1 result["col_count"] = col_count = max_x - min_x + 1 result["layout"] = [None] * row_count * col_count for (box_index, (x, y)) in coords.items(): result["layout"][x + y * col_count] = box_index mcd.set_layout(**result) if mcd.row_count > mcd.col_count: # If the layout is taller than wide, transpose it. return mcd.get_diagonally_flipped_clauses() else: return mcd.get_clauses() if is_organic: break objective += 1 if is_organic: raise MocodoError(41, _('Failed to calculate a non-constrained planar layout.')) # fmt: skip else: raise MocodoError(9, _('Failed to calculate a planar layout satisfying the given constraint.')) # fmt: skip if __name__ == "__main__": # python -m mocodo.rewrite._arrange_bb from time import time import random from ..argument_parser import parsed_arguments from ..mcd import Mcd clauses = """ SUSPENDISSE: diam SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus CONSECTETUER: elit, sed MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM DF, 11 LOREM, 1N SUSPENDISSE LOREM: ipsum, dolor, sit TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec DIGNISSIM: ligula, massa, varius DF, 11 RISUS, 0N RISUS AMET, 11> LOREM, 01 CONSECTETUER: adipiscing RISUS: ultricies, _cras, elementum SEMPER, 0N RISUS, 1N DIGNISSIM """ params = parsed_arguments() mcd = Mcd(clauses.replace(" ", ""), **params) starting_time = time() random.seed(42) rearrangement = arrange(mcd, {"is_organic": True}, lambda: False) if rearrangement: print() mcd.set_layout(**rearrangement) print(mcd.get_clauses()) print() print("Cumulated distances:", rearrangement["distances"]) print("Duration:", time() - starting_time) print() ================================================ FILE: mocodo/rewrite/arrange_ga.py ================================================ from collections import namedtuple from random import choice, random, randrange, sample from .fitness import fitness from ..argument_parser import rate, positive_integer from ..mcd import Mcd def arrange(source, subargs, has_expired): mcd = Mcd(source) layout_data = mcd.get_layout_data() links = layout_data["links"] successors = layout_data["successors"] col_count = layout_data["col_count"] row_count = layout_data["row_count"] multiplicity = layout_data["multiplicity"] verbose = subargs.get("verbose", False) population_size = positive_integer(subargs.get("population_size", 1000)) # number of individuals to evolve max_generations = positive_integer(subargs.get("max_generations", 300)) plateau = positive_integer(subargs.get("plateau", 30)) # maximal number of consecutive generations without improvement crossover_rate = rate(subargs.get("crossover_rate", 0.9)) mutation_rate = rate(subargs.get("mutation_rate", 0.06)) sample_size = positive_integer(subargs.get("sample_size", 7)) verbose = bool(subargs.get("verbose") is not None) # -r arrange:verbose => {"verbose": ""} => True def make_individual(): """ Construct a chromosome. Select a random node for the first gene. The next ones are chosen sequentially. When a gene has another gene to the west, the corresponding node is preferably selected among the successors of the latter. NB: Applying the same technic for the north and nortwest directions produces better individual, but worse final results. """ pool = list(range(box_count)) chromosome = [pool.pop(randrange(box_count))] (x, y) = (0, 0) for i in range(1, box_count): x += 1 if x == col_count: x = 0 y += 1 candidates = set() else: candidates = successors[chromosome[i-1]].intersection(pool) chromosome.append(pool.pop(pool.index(choice(tuple(candidates))) if candidates else randrange(len(pool)))) return Individual(evaluate(chromosome), chromosome) def crossover(chromosome_1, chromosome_2): """ Produce two children for a given pair of individuals. A random rectangular zone is first selected. The corresponding genes in the first individual are copied in the first child. The remaining places are filled with the genes of the second individual taken one by one. Symmetrical operations for the second child. """ (x1, y1) = (randrange(col_count), randrange(row_count)) (x2, y2) = (randrange(x1+1, col_count+1), randrange(y1+1, row_count+1)) def mate(chromosome_1, chromosome_2): used = set(chromosome_1[x+y*col_count] for x in range(x1, x2) for y in range(y1, y2)) not_used = (allele for allele in chromosome_2 if allele not in used) return [chromosome_1[x+y*col_count] if x1 <= x < x2 and y1 <= y < y2 else next(not_used) for y in range(row_count) for x in range(col_count)] return (mate(chromosome_1, chromosome_2), mate(chromosome_2, chromosome_1)) def next_population(): """ Evolve the population. The best individual is kept. The others are selected by tournament. Some selected pairs produce two children. Each selected individual may mutate at certain places. Mutation of a gene simply consists in swapping it with another one. """ result = [best] while len(result) < population_size: chromosomes = crossover(tournament(), tournament()) if random() < crossover_rate else [tournament()] for chromosome in chromosomes: for i in range(box_count): if random() < mutation_rate: j = randrange(box_count) (chromosome[i], chromosome[j]) = (chromosome[j], chromosome[i]) result.append(Individual(evaluate(chromosome), chromosome)) return result[:population_size] def tournament(): """ Return a COPY of the best chromosome selected among a random sample. """ return min(sample(population, sample_size)).chromosome[:] evaluate = fitness(links, multiplicity, col_count, row_count) Individual = namedtuple("Individual", ["score", "chromosome"]) box_count = col_count * row_count patience = plateau previous_best_score = None population = sorted([make_individual() for _ in range(population_size)]) best = population[0] for generation in range(max_generations): if best.score == previous_best_score: patience -= 1 else: if verbose: print("% 3d: %s" % (generation, best.score)) previous_best_score = best.score patience = plateau if best.score == (0, 0) or patience == 0 or has_expired(): # Even if the best individual is not perfect, we stop here # without raising an exception. break population = sorted(next_population()) best = population[0] generation += 1 result = { "distances": best.score[1], "crossings": best.score[0], "layout": best.chromosome } mcd.set_layout(**result) return mcd.get_clauses() if __name__ == "__main__": # python -m mocodo.rewrite._arrange_ga from time import time import random from ..argument_parser import parsed_arguments from ..mcd import Mcd clauses = """ SUSPENDISSE: diam SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus CONSECTETUER: elit, sed MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM DF, 11 LOREM, 1N SUSPENDISSE LOREM: ipsum, dolor, sit TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec DIGNISSIM: ligula, massa, varius DF, 11 RISUS, 0N RISUS AMET, 11> LOREM, 01 CONSECTETUER: adipiscing RISUS: ultricies, _cras, elementum SEMPER, 0N RISUS, 1N DIGNISSIM """ params = parsed_arguments() mcd = Mcd(clauses.replace(" ", ""), **params) starting_time = time() random.seed(42) rearrangement = arrange(mcd, {}, lambda: False) if rearrangement: print() mcd.set_layout(**rearrangement) print(mcd.get_clauses()) print() print("Cumulated distances:", rearrangement["distances"]) print("Duration:", time() - starting_time) print() ================================================ FILE: mocodo/rewrite/arrows.py ================================================ from ..parse_mcd import Visitor from ..tools.parser_tools import first_child, parse_source, reconstruct_source class ArrowsHere(Visitor): def assoc_leg(self, tree): card = first_child(tree, "card") if card != "11": return if first_child(tree, "leg_arrow") != "": return card.value += "<" class ArrowsAcross(Visitor): def assoc_clause(self, tree): legs = [node for node in tree.find_data("assoc_leg")] cards = [first_child(leg, "card") for leg in legs] for (i, card) in enumerate(cards): if card == "11": for (j, other_leg) in enumerate(legs): if i != j and first_child(other_leg, "leg_arrow") == "": other_card = first_child(other_leg, "card") other_card.value += ">" def create_df_arrows(source, where): visitor = ArrowsHere() if where == "here" else ArrowsAcross() tree = parse_source(source) visitor.visit(tree) return reconstruct_source(tree) ================================================ FILE: mocodo/rewrite/cards.py ================================================ from ..parse_mcd import Visitor from ..tools.parser_tools import parse_source, reconstruct_source, first_child from ..tools.various import invert_dict def fix_card( card, fixes=invert_dict( { "01": ["O1", "o1", "10", "1O", "1o", "Ol", "ol", "l0", "lO", "lo"], "0N": ["ON", "oN", "NO", "No", "N0"], "0n": ["On", "on", "no", "nO", "n0"], "1N": ["N1", "Nl"], "1n": ["n1", "nl"], } ), ): return fixes.get(card, card) class DfInference(Visitor): def __init__(self, df_label): self.df_label = df_label def assoc_clause(self, tree): assoc_name = first_child(tree, "assoc_name_def").children[0] if assoc_name == self.df_label: return cards = [] legs = list(tree.find_data("assoc_leg")) for leg in legs: card_prefix = first_child(leg, "card_prefix") if card_prefix == "/": return card = first_child(leg, "card") cards.append(card) if "11" in cards: assoc_name.value = self.df_label def infer_dfs(source, df_label): tree = parse_source(source) visitor = DfInference(df_label) visitor.visit(tree) return reconstruct_source(tree) class RoleInference(Visitor): def assoc_clause(self, tree): assoc_name = first_child(tree, "assoc_name_def").children[0] legs = list(tree.find_data("assoc_leg")) cards = [first_child(leg, "card") for leg in legs] roles = [first_child(leg, "leg_note") for leg in legs] if "01" in cards or "11" in cards: for (card, role) in zip(cards, roles): if role: continue if card[1] != "1": # *1 vs *N card.value += f" [{assoc_name}]" elif card == "01" and "11" in cards: # 01 vs 11 card.value += f" [{assoc_name}]" elif card == "11" and cards.count("11") > 1: # 11 vs 11 card.value += f" [{assoc_name}]" def infer_roles(source): tree = parse_source(source) visitor = RoleInference() visitor.visit(tree) return reconstruct_source(tree) ================================================ FILE: mocodo/rewrite/constraints.py ================================================ import re from ..parse_mcd import Visitor from ..tools.parser_tools import first_child, parse_source from ..tools.string_tools import surrounds from ..tools.various import first_missing_positive class CreateCifs(Visitor): def __init__(self): self.candidates = set() self.existing = set() self.invisible_numbers = [] def entity_clause(self, tree): ent_name = str(first_child(tree, "box_name")) m = re.match(r"(?i)invisible(\d+)$", ent_name) if m: self.invisible_numbers.append(int(m.group(1))) def assoc_clause(self, tree): legs = [node for node in tree.find_data("assoc_leg")] assoc_name = str(first_child(tree, "assoc_name_def").children[0]) target_names = [] entity_names = set() for leg in legs: entity_name = first_child(leg, "entity_name_ref").children[0].value entity_names.add(entity_name) if first_child(leg, "card_prefix") == "/": target_names.append(entity_name) for target_name in target_names: self.candidates.add((assoc_name, target_name, *sorted(entity_names - {target_name}))) def constraint_clause(self, tree): if first_child(tree, "constraint_name").upper() != "CIF": return assoc_name = None target_name = None source_names = [] for node in tree.find_data("constraint_target"): leg = first_child(node, "constraint_leg") box_name = first_child(node, "box_name_ref").children[0].value if surrounds(leg, ".."): if assoc_name is not None: return assoc_name = box_name elif surrounds(leg, "->"): if target_name is not None: return target_name = box_name elif leg == "" or surrounds(leg, "--"): source_names.append(box_name) else: return self.existing.add((assoc_name, target_name, *sorted(source_names))) def get_new_cifs(self, hidden_links_from_sources=False): self.new_cifs = self.candidates - self.existing if not self.new_cifs: return "" sep = ", " if hidden_links_from_sources else ", --" new_invisible_clauses = ["\n"] new_cif_clauses = [""] for (assoc_name, *entity_names) in sorted(self.new_cifs): n = first_missing_positive(self.invisible_numbers) self.invisible_numbers.append(n) new_invisible_clauses.append(f"-INVISIBLE_{n}, XX {entity_names[0]}, XX {entity_names[0]}") new_cif_clauses.append(f"(CIF) ..{assoc_name}, ->{sep.join(entity_names)}: INVISIBLE_{n}, INVISIBLE_{n}") if new_cif_clauses == [""]: return "" return "\n".join(new_invisible_clauses + new_cif_clauses) def create_cifs(source, subsubarg): visitor = CreateCifs() tree = parse_source(source) visitor.visit(tree) return source + visitor.get_new_cifs(subsubarg == "light") ================================================ FILE: mocodo/rewrite/cross.py ================================================ from functools import lru_cache crossed_strings = frozenset(["-++-", "-++0", "-+0-", "-0+-", "0++-", "+--+", "0--+", "+0-+", "+-0+", "+--0"]) @lru_cache def cross(x1, y1, x2, y2, x3, y3, x4, y4): """ Tests whether the segments ((x1,y1), (x2,y2)) and ((x3,y3), (x4,y4)) intersect. Two segments sharing exactly one extremity are NOT considered as intersecting. """ a = (x4-x3)*(y1-y3) - (y4-y3)*(x1-x3) b = (x4-x3)*(y2-y3) - (y4-y3)*(x2-x3) c = (x2-x1)*(y3-y1) - (y2-y1)*(x3-x1) d = (x2-x1)*(y4-y1) - (y2-y1)*(x4-x1) if a or b or c or d: return "".join("+" if x>0 else ("-" if x<0 else "0") for x in (a, b, c, d)) in crossed_strings else: # the segments are collinear if x1 == x2: # both segments are vertical if y1 < y2: if y3 < y4: return y2 > y3 and y1 < y4 else: return y2 > y4 and y1 < y3 else: if y3 < y4: return y1 > y3 and y2 < y4 else: return y1 > y4 and y2 < y3 else: if x1 < x2: if x3 < x4: return x2 > x3 and x1 < x4 else: return x2 > x4 and x1 < x3 else: if x3 < x4: return x1 > x3 and x2 < x4 else: return x1 > x4 and x2 < x3 ================================================ FILE: mocodo/rewrite/damerau_levenshtein.py ================================================ # http://mwh.geek.nz/2009/04/26/python-damerau-levenshtein-distance/ def distance(seq1, seq2): """Calculate the Damerau-Levenshtein distance between sequences. This distance is the number of additions, deletions, substitutions, and transpositions needed to transform the first sequence into the second. Although generally used with strings, any sequences of comparable objects will work. Transpositions are exchanges of *consecutive* characters; all other operations are self-explanatory. This implementation is O(N*M) time and O(M) space, for N and M the lengths of the two sequences. >>> damerau_levenshtein('ba', 'abc') 2 >>> damerau_levenshtein('fee', 'deed') 2 It works with arbitrary sequences too: >>> damerau_levenshtein('abcd', ['b', 'a', 'c', 'd', 'e']) 2 """ # codesnippet:D0DE4716-B6E6-4161-9219-2903BF8F547F # Conceptually, this is based on a len(seq1) + 1 * len(seq2) + 1 matrix. # However, only the current and two previous rows are needed at once, # so we only store those. one_ago = None this_row = list(range(1, len(seq2) + 1)) + [0] for x in range(len(seq1)): # Python lists wrap around for negative indices, so put the # leftmost column at the *end* of the list. This matches with # the zero-indexed strings and saves extra calculation. two_ago, one_ago, this_row = one_ago, this_row, [0] * len(seq2) + [x + 1] for y in range(len(seq2)): del_cost = one_ago[y] + 1 add_cost = this_row[y - 1] + 1 sub_cost = one_ago[y - 1] + (seq1[x] != seq2[y]) this_row[y] = min(del_cost, add_cost, sub_cost) # This block deals with transpositions if (x > 0 and y > 0 and seq1[x] == seq2[y - 1] and seq1[x-1] == seq2[y] and seq1[x] != seq2[y]): this_row[y] = min(this_row[y], two_ago[y - 2] + 1) return this_row[len(seq2) - 1] ================================================ FILE: mocodo/rewrite/fitness.py ================================================ from math import hypot from .cross import cross def fitness(links, multiplicity, col_count, row_count, max_distance = 4): """ Return (by closure) a function evaluating the aesthetic quality of a given layout. """ def evaluate(layout): for (position, index) in enumerate(layout): coordinates[index] = divmod(position, col_count) segments = [(coordinates[p1], coordinates[p2], multiplicity[p1, p2]) for (p1, p2) in links] total_distances = 0 short_segments = [] for ((y1, x1), (y2, x2), m) in segments: distance = distances[abs(x1-x2)][abs(y1-y2)] * m if distance <= max_distance: short_segments.append((x1, y1, x2, y2)) total_distances += distance crossing_count = (link_count - len(short_segments)) * link_count for (i, (x1, y1, x2, y2)) in enumerate(short_segments): for (x3, y3, x4, y4) in short_segments[i+1:]: crossing_count += cross(x1, y1, x2, y2, x3, y3, x4, y4) return (crossing_count, total_distances) distances = [[hypot(i, j) - 1 for j in range(row_count)] for i in range(col_count)] coordinates = [(0, 0)] * (row_count * col_count) link_count = len(links) return evaluate ================================================ FILE: mocodo/rewrite/guess_entities.py ================================================ from ..tools.string_tools import rstrip_digit_or_underline from ..tools.parser_tools import parse_source def run(source, id_prefix): if id_prefix is None: id_prefix = "id. " tree = parse_source(source) entity_name_refs =set(node.children[0].children[0].value for node in tree.find_data("entity_name_ref")) entity_name_defs = set(node.children[0].children[0].value for node in tree.find_data("entity_name_def")) new_entity_names = entity_name_refs - entity_name_defs new_entity_clauses = [source] for ent in new_entity_names: lower_ent = rstrip_digit_or_underline(ent.lower()) if lower_ent in ("date", "calendrier", "calendar"): clause = f"{ent}: date" elif lower_ent == "période": clause = f"{ent}: début, _fin" elif lower_ent == "period": clause = f"{ent}: start, _end" else: clause = f"{ent}: {id_prefix}{lower_ent}, " new_entity_clauses.append(clause) return "\n".join(new_entity_clauses) ================================================ FILE: mocodo/rewrite/obfuscate.py ================================================ import random import re from pathlib import Path from .damerau_levenshtein import distance from ..mocodo_error import MocodoError MIN_DISTANCE = 3 # Minimal Damereau-Levenshtein distance between two words. def random_words_generator( lorem_text, find_all_words=re.compile(r"(?u)[^\W\d_]{3,}").findall, ): # use dict.fromkeys instead of set for preserving order words = list(dict.fromkeys(word.lower() for word in find_all_words(lorem_text))) random.shuffle(words) previous_words = set() for word in words: for previous_word in previous_words: if distance(word, previous_word) < MIN_DISTANCE: # Keeping this word would be too confusing, let's try the next one. break else: yield word previous_words.add(word) def obfuscator_factory(pool, params): pool = pool or "" # Initialize the random word generator try: lorem_path = Path(pool) lorem_text = lorem_path.read_text(encoding="utf8") except IOError: lorem_dir = Path(params["script_directory"], "resources", "lorem") lorem_path = lorem_dir / f"{lorem_path.stem}.txt" try: lorem_text = lorem_path.read_text(encoding="utf8") except IOError: lorem_path = lorem_dir / "lorem.txt" lorem_text = lorem_path.read_text(encoding="utf8") random_word = random_words_generator(lorem_text) # Define and return the inner obfuscator function cache = {} def obfuscate(name): suffix = "" if name not in cache or name == params["df"]: try: new_name = next(random_word) except StopIteration: raise MocodoError(12, _('Obfuscation failed. Not enough substitution words in "{filename}".').format(filename=lorem_path)) from None # fmt: skip if name.isupper(): new_name = new_name.upper() elif name.islower(): new_name = new_name.lower() elif name.partition(" ")[0].istitle(): new_name = new_name.title() cache[name] = new_name return cache[name] + suffix return obfuscate ================================================ FILE: mocodo/rewrite/op_tk.py ================================================ import contextlib from pathlib import Path import random import re __import__("sys").path[0:0] = ["."] from ..parse_mcd import Transformer from ..tools.parser_tools import transform_source from ..tools.string_tools import ascii, camel, snake, pascal, TRUNCATE_DEFAULT_SIZE from ..mocodo_error import MocodoError from .cards import fix_card, infer_dfs, infer_roles from .types import read_default_datatypes, create_type_placeholders, guess_types from .obfuscate import obfuscator_factory from .arrows import create_df_arrows from .constraints import create_cifs ELEMENT_TO_TOKENS = { "arrows": ["leg_arrow"], "attrs": ["attr"], "boxes": ["box_name"], "card_prefixes": ["card_prefix"], "cards": ["card"], "roles": ["leg_note"], "leg_notes": ["leg_note"], "constraint_notes": ["constraint_note"], "labels": ["box_name", "attr"], "texts": ["box_name", "attr", "leg_note", "constraint_note"], "notes": ["leg_note", "constraint_note"], "types": ["datatype"], } GENERAL_OPERATIONS = { # operations that can be applied to any token "ascii": ascii, "camel": camel, "capitalize": str.capitalize, "casefold": str.casefold, "echo": lambda x: x, "lower": str.lower, "pascal": pascal, "snake": snake, "swapcase": str.swapcase, "title": str.title, "upper": str.upper, } class Mapper(Transformer): def __init__(self, op_name, pre_token, subsubarg, params): tokens = ELEMENT_TO_TOKENS[pre_token] op = GENERAL_OPERATIONS.get(op_name) if op is None: # op_tk operations with limited applicability and/or using a subsubarg if op_name == "randomize" and pre_token == "types": resource_dir = Path(params["script_directory"], "resources") pool = list(dict(read_default_datatypes(resource_dir)).values()) op = lambda _: random.choice(pool) elif op_name == "delete" and pre_token in ("attrs", "notes", "roles", "constraint_notes", "arrows", "types", "card_prefixes"): op = lambda _: "" elif op_name == "delete" and pre_token == "cards": op = lambda _: "XX" elif op_name == "fix" and pre_token == "cards": op = fix_card elif op_name == "randomize" and pre_token in ("labels", "texts", "boxes", "attrs", "notes", "roles", "constraint_notes"): op = obfuscator_factory(subsubarg, params) elif op_name == "truncate": truncate_size = TRUNCATE_DEFAULT_SIZE if subsubarg and subsubarg.isdigit(): truncate_size = int(subsubarg) or truncate_size op = lambda x: x[:truncate_size] elif op_name == "slice": slice_start = slice_stop = None if subsubarg: with contextlib.suppress(ValueError): slice_start = int(subsubarg.partition(":")[0]) with contextlib.suppress(ValueError): slice_stop = int(subsubarg.partition(":")[2]) op = lambda x: x[slice_start:slice_stop] elif op_name == "replace": (substring, __, repl) = subsubarg.partition("/") op = lambda x: x.replace(substring, repl) elif op_name == "suffix": op = lambda x: f"{x}{subsubarg}" elif op_name == "prefix": op = lambda x: f"{subsubarg}{x}" else: raise MocodoError(24, _('Operation "{op_name}" cannot be applied to "{pre_token}".').format(op_name=op_name, pre_token=pre_token)) update_first_child = lambda tree: tree[0].update(value=op(tree[0].value)) for token in tokens: setattr(self, token, update_first_child) def run(source, op_name, subargs, params, **kargs): if op_name == "randomize" and not subargs: subargs = {"labels": ""} # used for obfuscation elif op_name == "delete" and not subargs: subargs = dict.fromkeys("types notes attrs cards".split(), "") for (pre_token, subsubarg) in subargs.items(): # filter special non-op_tk operations if op_name == "create" and pre_token == "types": source = create_type_placeholders(source, subsubarg) if subsubarg is not None else guess_types(source, params) elif op_name == "create" and pre_token == "df_arrows": source = create_df_arrows(source, subsubarg) elif op_name == "create" and re.match("(?i)dfs?$", pre_token): source = infer_dfs(source, params["df"]) elif op_name == "create" and pre_token == "roles": source = infer_roles(source) elif op_name == "create" and re.match("(?i)cifs?$", pre_token): source = create_cifs(source, subsubarg) else: # apply a normal op_tk operation source = transform_source(source, Mapper(op_name, pre_token, subsubarg, params)) if op_name == "delete": # After a delete operation, remove any [] that may have been left behind # (it's too tedious to remove them from the AST, and it's not worth it). source = re.sub(r" *\[\]", " ", source) return source ================================================ FILE: mocodo/rewrite/types.py ================================================ from pathlib import Path import re from ..parse_mcd import Visitor from ..tools.parser_tools import first_child, parse_source, reconstruct_source from ..tools.string_tools import ascii, snake, strip_surrounds class CreateTypePlaceholder(Visitor): def __init__(self, subsubarg): self.default = strip_surrounds(subsubarg, "[]") def typed_attr(self, tree): if len(tree.children) == 1: tree.children[0].children[0].value += f" [{self.default}]" def create_type_placeholders(source, subsubarg): visitor = CreateTypePlaceholder(subsubarg) tree = parse_source(source) visitor.visit(tree) return reconstruct_source(tree) def read_default_datatypes(resource_dir, language="en"): path = Path(resource_dir, f"default_datatypes_{language}.tsv") if not path.exists(): return for line in path.read_text(encoding="utf8").splitlines(): fields = line.split("\t") if len(fields) < 2: continue yield tuple(fields[:2]) class GuessType(Visitor): def __init__(self,params): resource_dir = Path(params["script_directory"], "resources") self.field_types = dict(read_default_datatypes(resource_dir)) # English always used as fallback language = params.get("language", "en") if language != "en": self.field_types.update(read_default_datatypes(resource_dir, language)) # Convert the dict to a list of tuples, and sort them by decreasing length # so that the longest match is found first. self.field_types = sorted(self.field_types.items(), key=lambda x: -len(x[0])) # Precompile the regexes self.field_types = [(re.compile(k).search, v) for (k, v) in self.field_types] def typed_attr(self, tree): n = len(tree.children) # Possible lengths of the children list: # 0: empty attribute # 1: attribute without datatype # 3: attribute with empty datatype # 4: attribute with non-empty datatype if n not in (1, 3): return attr = first_child(tree, "attr") needle = snake(ascii(attr)).replace("_", " ").lower() for (found, datatype) in self.field_types: if found(needle): break else: datatype = "" if n == 1: # concatenate to the attribute label the datatype between brackets tree.children[0].children[0].value += f" [{datatype}]" else: # insert the datatype between brackets tree.children[1].value += datatype def guess_types(source, params): visitor = GuessType(params) tree = parse_source(source) visitor.visit(tree) return reconstruct_source(tree) ================================================ FILE: mocodo/tools/__init__.py ================================================ ================================================ FILE: mocodo/tools/graphviz_tools.py ================================================ import re def create_name_to_index(): """ Return a function that maps names to unique numbers. Used to generate short "names" for Graphviz nodes. """ cache = {} def name_to_index(name): if name not in cache: cache[name] = len(cache) + 1 return cache[name] return name_to_index def minify_graphviz(text): # Suppress comments text = re.sub(r"(?m)^ *//.*$", "", text) # Suppress empty lines text = re.sub(r"(?m)^\s*\n", "", text) # Suppress leading spaces text = re.sub(r"(?m)^\s+", "", text) # Suppress newline before delimiters text = re.sub(r"\n([]<>])", r"\1", text) # Factorize edges while True: (text, n) = re.subn(r"(?m)(\d+) (-[->]) ([^[\n]+)\n\1 -- ", r"\1 \2 \3,", text) if n == 0: break # Suppress spaces around arrows text = re.sub(r"(?m)(\d+) (-[->]) ", r"\1\2", text) # Suppress spaces before opening brackets text = re.sub(r"(?m)^(edge|node) *\[", r"\1[", text) # Suppress newlines after opening brackets text = re.sub(r"\[\n", r"[", text) # Supress spaces after \d+ -> \d+ text = re.sub(r"(?m)^([\d>-]+) +", r"\1", text) return text NODE_OPTIONS_TEMPLATE = """ shape=none fontcolor="{cell_font_color}" fontsize={cell_font_size} fontname="Helvetica" """ # This constant is public TABLE_TEMPLATE = """ TABLE_CONTENT_PLACEHOLDER
    """ HEADER_TEMPLATE = """ HEADER_TEXT_PLACEHOLDER """ SPECIAL_FONT_TEMPLATE = """TEXT_PLACEHOLDER""" ROW_TEMPLATE = """ {cells}""" def row_format_to_td_templates( row_format, # a LaTeX-like table format string (e.g. "|l|c|r|") ALIGN={ "l": ' align="left"', "c": "", "r": ' align="right"', }, ): """ Return a list of td element templates from a LaTeX-table-like format string. NB: an uppercase letter means "use the special font". """ acc = [] row_format = f" {row_format} " for (i, symbol) in enumerate(row_format[1:-1], 1): text = "TEXT_PLACEHOLDER" if symbol.isupper(): text = SPECIAL_FONT_TEMPLATE symbol = symbol.lower() if symbol in ALIGN: align = ALIGN[symbol] sides = ' sides="TB' if row_format[i - 1] == "|": sides += "L" if row_format[i + 1] == "|": sides += "R" sides += '"' if sides == ' sides="TBLR"': sides = "" acc.append(f"{text}") return acc def table_as_label(header_text, rows, row_format, style): """Return a table has an HTML-like Graphviz label.""" header = HEADER_TEMPLATE.format(**style) header = header.replace("HEADER_TEXT_PLACEHOLDER", header_text) td_templates = row_format_to_td_templates(row_format) header = header.replace("COL_COUNT_PLACEHOLDER", str(len(td_templates))) trs = [] for row in rows: cells = [] for (text, td_template) in zip(row, td_templates): cells.append(td_template.replace("TEXT_PLACEHOLDER", text)) trs.append(ROW_TEMPLATE.format(cells="".join(cells))) trs = "\n".join(trs) table = TABLE_TEMPLATE.format(**style) table = table.replace("TABLE_CONTENT_PLACEHOLDER", header + trs) return table if __name__ == "__main__": header_text = "CLIENT" rows = [ ["PK", "id. client", "varchar(8)"], ["", "nom", "varchar(30)"], ["", "prénom", "varchar(30)"], ["", "âge", "int"], ] row_format = "|cl|L|" style = { "cell_bg_color": "#c0d4ff", "stroke_color": "#578dff", "header_bg_color": "#97b8ff", "header_font_color": "#131114", "header_font_size": 14, "cell_font_color": "#3e3c42", "cell_font_size": 12, } node_options = NODE_OPTIONS_TEMPLATE.format(**style) table = table_as_label(header_text, rows, row_format, style) print("digraph {") print(f"node [{node_options}]") print(f'1 [label=<{table}>]') print("}") ================================================ FILE: mocodo/tools/load_mini_yaml.py ================================================ from ast import literal_eval import re def parse_value(v): v = v.strip() if v.startswith("'"): if v != "''": v = v.replace("''", "\\'") v = literal_eval(f"r{v}") v = v.replace("\\'", "'") v = re.sub(r"(?", "=>".').format(pin=pin)) # fmt: skip if t == "INHERITANCE_ARROW" and expected == {'MORETHAN'}: raise MocodoError(518, _('{pin}Please change the old foreign key syntax ("->") by the new one (">").').format(pin=pin)) # fmt: skip if t == "SP" and expected == {'COMMA', 'COLON', 'NL'}: raise MocodoError(519, _('{pin}The constraint targets must be comma-separated.').format(pin=pin)) # fmt: skip if expected == {'HASHTAG', 'NL', 'ID_GROUPS', 'ID_MARK', 'ATTR', 'COMMA'}: raise MocodoError(500, _('{pin}An attribute label cannot start with "{v[1]!r}".').format(pin=pin, v=v)) # fmt: skip if expected == {'ID_MARK', 'NL', 'ATTR', 'COMMA'}: raise MocodoError(500, _('{pin}An attribute label cannot start with "{v[1]!r}".').format(pin=pin, v=v)) # fmt: skip if expected == {'LBRACKET', 'NL', 'COMMA'}: raise MocodoError(521, _('{pin}An attribute label cannot contain "{v}".').format(pin=pin, v=v)) # fmt: skip if t in ("COMMA", "NL", "BREAK") and expected == {'MORETHAN'}: raise MocodoError(522, _('{pin}An attribute starting with "#" must contain two ">".').format(pin=pin)) # fmt: skip if t in ("COMMA", "NL", "BREAK") and expected == {'ATTR'}: raise MocodoError(523, _('{pin}Expected an entity name.').format(pin=pin)) # fmt: skip if expected == {'COLON', 'COMMA', 'NL'}: raise MocodoError(524, _('{pin}A box name cannot contain "{v}".').format(pin=pin, v=v)) # fmt: skip if expected == {"COMMA"}: raise MocodoError(525, _('{pin}Expected a comma.').format(pin=pin, v=v)) # fmt: skip) if t == "BOX_NAME" and expected == {'COMMA', 'NL'}: raise MocodoError(526, _('{pin}Malformed number.').format(pin=pin, v=v)) # fmt: skip if previous in ("NUMBER", "BOX_NAME") and expected == {'COMMA', 'NL'}: raise MocodoError(527, _('{pin}More than two coordinates.').format(pin=pin, v=v)) # fmt: skip if expected == {'LBRACKET', 'NL', 'COMMA', 'MORETHAN'}: raise MocodoError(528, _('{pin}An attribute label cannot have more than one optionality marker.').format(pin=pin, v=v)) raise MocodoError(504, _('{pin}Token "{t}" encountered. Expected tokens: {expected}.').format(pin=pin, expected=expected, t=t)) class Reconstructor(Visitor): def __init__(self): self.result = [] def __default__(self, tree): for child in tree.children: if isinstance(child, Token): self.result.append((child.line, child.column, child.value)) def reconstruct_source(tree): visitor = Reconstructor() try: visitor.visit(tree) except Exception as visitor_error: raise visitor_error.__context__ if visitor_error.__context__ else visitor_error return "".join(s for (_, _, s) in sorted(visitor.result)) def transform_source(source, transformer): tree = parse_source(source) try: new_tree = transformer.transform(tree) except Exception as transformer_error: raise transformer_error.__context__ if transformer_error.__context__ else transformer_error return reconstruct_source(new_tree) class ClauseExtractor(Transformer): discard = lambda self, tree: Discard COLON = discard COMMA = discard NL = discard SP = discard LBRACKET = discard RBRACKET = discard LPAREN = discard RPAREN = discard CONSTRAINT_LPAREN = discard CONSTRAINT_RPAREN = discard HASHTAG = discard MORETHAN = discard PERCENT = discard BACKSLASH = discard def _item(self, key, tree): return (key, tree[0].value) card = lambda self, tree: self._item("card", tree) entity_name_ref = lambda self, tree: self._item("entity", tree) box_name_ref = lambda self, tree: self._item("name", tree) leg_note = lambda self, tree: self._item("leg_note", tree) constraint_note = lambda self, tree: self._item("constraint_note", tree) constraint_leg = lambda self, tree: self._item("constraint_leg", tree) constraint_name = lambda self, tree: self._item("name", tree) card_prefix = lambda self, tree: self._item("card_prefix", tree) card_hidden = lambda self, tree: self._item("card_hidden", tree) leg_arrow = lambda self, tree: self._item("leg_arrow", tree) inheritance_arrow = lambda self, tree: self._item("inheritance_arrow", tree) entity_name_def = lambda self, tree: self._item("name", tree) inheritance_name = lambda self, tree: self._item("name", tree) assoc_name_def = lambda self, tree: self._item("name", tree) attr = lambda self, tree: self._item("attribute_label", tree) box_def_prefix = lambda self, tree: self._item("box_def_prefix", tree) id_mark = lambda self, tree: self._item("id_mark", tree) def start(self, tree): return tree def line(self, tree): d = {} for t in tree: d.update(t) return d def break_(self, tree): return {"type": "break"} def phantoms(self, tree): return {"type": "phantoms", "count": tree[0].value.count(":")} def comment(self, tree): return {"type": "comment", "text": f"%{tree[0].value.rstrip()}"} def entity_clause(self, tree): return tree + [("type", "entity")] def assoc_clause(self, tree): return tree + [("type", "association")] def constraint_clause(self, tree): return tree + [("type", "constraint")] def inheritance_clause(self, tree): # Tweak the tree to make it look like an association d = dict(item for item in tree if isinstance(item, tuple)) d["name"] = d.get("name", "") d["type"] = "inheritance" d["legs"] = [{"entity": d["entity"], "rank": -1}] + d["legs"] del d["entity"] return list(d.items()) def typed_attr(self, tree): return ("attr", dict(tree)) def assoc_leg(self, tree): return ("leg", dict(tree)) def inheritance_parent(self, tree): return ("entity", tree[0][1]) def inheritance_child(self, tree): return ("leg", dict(tree)) def foreign_reference(self, tree): return ("foreign_reference", dict(tree)) def box_name(self, tree): return tree[0] def seq(self, tree): items = [] for (rank, (_, d)) in enumerate(tree): d["rank"] = rank items.append(d) return (f"{tree[0][0]}s", items) def assoc_attr(self, tree): if tree[0] == "_": tree[-1][1]["id_mark"] = "_" return ("attr", tree[-1][1]) def assoc_leg(self, tree): return ("leg", dict(tree)) def constraint_target(self, tree): return ("constraint_target", dict(tree)) def foreign_reference(self, tree): return ("foreign_reference", dict(tree)) def entity_or_table_attr(self, tree): d = dict(tree) if "foreign_reference" in d: d.update(d.pop("foreign_reference")) if "attr" in d: d.update(d.pop("attr")) return ("attr", d) def this_table_attr(self, tree): return ("attribute_label", tree[0][1]) def that_table(self, tree): return ("that_table", tree[0][1]) def that_table_attr(self, tree): return ("that_table_attribute_label", tree[0][1]) def constraint_coords(self, tree): return ("constraint_coords", [self._constraint_coord(x) for x in tree]) def _constraint_coord(self, x): try: x = float(x) return int(x) if x == int(x) else x except (TypeError, ValueError): return x[1] def indent(self, tree): return {"type": "break", "indent": tree[0].value} # Add a default type for dangling indents def datatype(self, tree): return ("datatype", tree[0].value if tree else "") def id_groups(self, tree): return ("id_groups", "".join(sorted(set(tree[0].value)))) def extract_clauses(source): tree = parse_source(source) extractor = ClauseExtractor() result = extractor.transform(tree) for line in result: if "type" not in line: line["type"] = "break" # Uniformize indentations. First, find the set of all distinct indentations. # Include the empty string if absent. Then, sort them by length. Finally, # replace each indentation by a number of spaces proportional to its index # in the sorted list. indents = set(line.get("indent", "") for line in result) indents.add("") indents = sorted(indents, key=len) indents = {indent: " " * i for (i, indent) in enumerate(indents)} for (line, raw_line) in zip(result, source.splitlines()): line["indent"] = indents[line.get("indent", "")] line["source"] = line["indent"] + raw_line.lstrip() return result def first_child(tree, name, i=0): result = next(tree.find_data(name), None) if result is None: return "" return result.children[i] def is_identifier(i, id_groups, id_mark): # Entity (foo, bar) if i == 0: if "0" not in id_groups: return True # foo / 1_foo elif "0" in id_groups: return True # 0_bar / 01_bar elif id_mark == "_" and id_groups == "": return True # _bar return False # 0_foo / 01_foo / _foo / bar / 1_bar if __name__ == "__main__": # python -m mocodo.tools.parser_tools source = "EMPLOYER, 01 PARTICIPANT, 0N ENTREPRISE:\nCLIENT: num client, nom" tree = parse_source(source) print(tree.pretty()) print(reconstruct_source(tree)) print(extract_clauses(source)) ================================================ FILE: mocodo/tools/pluralize_fr.py ================================================ irregular_plurals = { 'aieul': 'aieux', 'aval': 'avals', 'bail': 'baux', 'bal': 'bals', 'betail': 'bestiaux', 'bijou': 'bijoux', 'bleu': 'bleus', 'caillou': 'cailloux', 'cal': 'cals', 'carnaval': 'carnavals', 'chacal': 'chacals', 'choral': 'chorals', 'chou': 'choux', 'ciel': 'cieux', 'corail': 'coraux', 'credit-bail': 'credits-baux', 'email': 'emaux', 'emeu': 'emeus', 'emmenthal': 'emmenthals', 'enfeu': 'enfeus', 'etal': 'etals', 'fatal': 'fatals', 'festival': 'festivals', 'gemmail': 'gemmaux', 'genou': 'genoux', 'gentilhomme': 'gentilshommes', 'glacial': 'glacials', 'hibou': 'hiboux', 'joujou': 'joujoux', 'landau': 'landaus', 'madame': 'mesdames', 'mademoiselle': 'mesdemoiselles', 'mistral': 'mistrals', 'monsieur': 'messieurs', 'natal': 'natals', 'naval': 'navals', 'oeil': 'yeux', 'oeil-de-boeuf': 'oeils-de-boeuf', 'oeil-de-chat': 'oeils-de-chat', 'pal': 'pals', 'pascal': 'pascals', 'perinatal': 'perinatals', 'pneu': 'pneus', 'postnatal': 'postnatals', 'pou': 'poux', 'prenatal': 'prenatals', 'recital': 'recitals', 'regal': 'regals', 'sarrau': 'sarraus', 'soupirail': 'soupiraux', 'travail': 'travaux', 'unau': 'unaus', 'vantail': 'vantaux', 'veto': 'veto', 'vitrail': 'vitraux' } def pluralize(word): word = word.lower() if word in irregular_plurals: return irregular_plurals[word] if word.endswith(("eau", "oeu")): return word + "x" if word.endswith(("s", "x", "z")): return word if word.endswith("al"): return word[:-2] + "aux" if word.endswith(("au", "eu")): return word + "x" return word + "s" ================================================ FILE: mocodo/tools/string_tools.py ================================================ import textwrap import re from unicodedata import combining, normalize import base64 import zlib TRUNCATE_DEFAULT_SIZE = 64 def wrap_label(s, wrapping_ratio=2): """ Break a given short string into a list of lines, trying to keep the width/height ratio just above wrapping_ratio. """ min_width = max(map(len, re.split(r"[-\s]+", s))) for width in range(min_width, len(s)): lines = textwrap.wrap(s, width=width) if max(map(len, lines)) / len(lines) > wrapping_ratio: return lines return [s] def rstrip_digit_or_underline(s): """ Get rid of single digit suffix, if any. Works on empty strings too. Used for entity and association raw names. """ return s[:-1] if s[-1:].isdigit() or s[-1:] == "_" else s def surrounds(s, ends): """ Check if s is surrounded by the first and last characters of ends. Works on empty strings too. """ return s[:1] == ends[:1] and s[-1:] == ends[-1:] def strip_surrounds(s, ends): """ Remove the first and last characters of s if they match the first and last characters of ends. Works on empty strings too. """ return s[1:-1] if surrounds(s, ends) else s def is_a_description(note): if not note: return False if note.startswith(("+", "-")): return False return " " in note # ASCII LATIN = "ä æ ǽ đ ð ƒ ħ ı ł ø ǿ ö œ ß ŧ ü Ä Æ Ǽ Đ Ð Ƒ Ħ I Ł Ø Ǿ Ö Œ ẞ Ŧ Ü " ASCII = "ae ae ae d d f h i l o o oe oe ss t ue AE AE AE D D F H I L O O OE OE SS T UE" def ascii(s, outliers=str.maketrans(dict(zip(LATIN.split(), ASCII.split())))): return "".join(c for c in normalize("NFD", s.translate(outliers)) if not combining(c)) def raw_to_bid(box_raw_name): return re.sub(r"\W", "_", ascii(box_raw_name).upper()) def aggressive_split( input_string, find_all_words = re.compile(r"(?u)[^\W_]+").findall ): # Split the string, and further split the words result = [] for word in find_all_words(input_string): previous_is_digit = word[0].isdigit() previous_is_lower = word[0].islower() i = 0 for (j, c) in enumerate(word[1:], 1): current_is_lower = c.islower() current_is_digit = c.isdigit() if ( previous_is_digit and not current_is_digit or not previous_is_digit and current_is_digit or previous_is_lower and not current_is_lower ): result.append(word[i:j]) i = j previous_is_lower = current_is_lower previous_is_digit = current_is_digit result.append(word[i:]) return result # A decorator which calls def spare_protected_suffix( function, is_protected_suffix = re.compile(r"[_0-9]").match, ): def wrapper(input_string): suffix = input_string[-1:] if is_protected_suffix(suffix): input_string = input_string[:-1] else: suffix = "" result = function(input_string) return result + suffix return wrapper @spare_protected_suffix def camel(input_string): result = "".join([word.capitalize() for word in aggressive_split(input_string)]) return result[:1].lower() + result[1:] @spare_protected_suffix def pascal(input_string): return "".join([word.capitalize() for word in aggressive_split(input_string)]) @spare_protected_suffix def snake(input_string): return "_".join([word for word in aggressive_split(input_string)]) def urlsafe_encoding(text): return base64.urlsafe_b64encode(zlib.compress(text.encode('utf-8'), 9)).decode('ascii') # Markdown def markdown_table(rows, formats=None): """ Return a Markdown table from a list of rows, each row being a tuple. The first row is the header. For maximum readability, in text mode, each column is filled with spaces so that the longest cell fits in the column. 'formats' is a sequence of formatting symbols "l", "c", "r". """ rows = [list(map(str, row)) for row in rows] widths = [max(map(len, column)) for column in zip(*rows)] result = ["| " + " | ".join(f"{c:<{w}}" for (w, c) in zip(widths, row)) + " |" for row in rows] formats = formats or "l" * len(rows[0]) formats = [("-" if x == "r" else ":") + "-" * w + ("-" if x == "l" else ":") for (w, x) in zip(widths, formats)] result[1:1] = ["|" + "|".join(formats) + "|"] return "\n".join(result) ================================================ FILE: mocodo/tools/various.py ================================================ def invert_dict(d): return {v: k for (k, vs) in d.items() for v in vs} def first_missing_positive(l): return min(set(range(1, len(l) + 2)).difference(l)) ================================================ FILE: pyproject.toml ================================================ [project] name = "mocodo" version = "4.3.3" # previously dynamically set by poetry-version-plugin description = "Modélisation Conceptuelle de Données. Nickel. Ni souris." authors = [{ name = "Aristide Grange" }] requires-python = ">=3.6.1,<4" readme = "README.md" license = "MIT" keywords = [ "education", "relational", "database", "drawing", "ERD", "SVG", "Merise", ] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Framework :: IPython", "Intended Audience :: Education", "Intended Audience :: Information Technology", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Topic :: Database", "Topic :: Education", ] dependencies = ["requests>=2.22.0,<3"] [project.urls] Homepage = "https://www.mocodo.net/" Repository = "https://github.com/laowantong/mocodo/" issues = "https://github.com/laowantong/mocodo/issues" [project.scripts] mocodo = "mocodo.__main__:main" [project.optional-dependencies] svg = [ "cairosvg>=2.7.1", ] clipboard = [ "pyperclip>=1.9.0", ] [dependency-groups] dev = [ "pytest>=7.0.1", ] [tool.poetry-version-plugin] source = "init" # read version from __init__.py [build-system] requires = ["hatchling"] build-backend = "hatchling.build" ================================================ FILE: setup.py ================================================ """A setuptools based setup module. See: https://packaging.python.org/en/latest/distributing.html https://github.com/pypa/sampleproject """ # Always prefer setuptools over distutils from setuptools import setup # To use a consistent encoding from codecs import open from . import __version__ long_description = """ Mocodo is an open-source tool for designing and teaching relational databases. It takes as an input a textual description of both entities and associations of an entity-relationship diagram (ERD). It outputs a vectorial drawing in SVG and a relational schema in various formats (SQL, LaTeX, Markdown, etc.). Installation ------------ The recommended way to install Mocodo is to use pip: :: pip install mocodo If this fails, ensure first you have a working Python 3 installation. Usage ------- Generate the conceptual diagram of a default ERD: :: mocodo Show the argument list: :: mocodo --help More ------ `Mocodo online `_ `Documentation `_ `Source code on GitHub `_""" with open("README.rst", "w", "utf8") as f: f.write(long_description) setup( name='mocodo', # Versions should comply with PEP440. For a discussion on single-sourcing # the version across setup.py and the project code, see # https://packaging.python.org/en/latest/single_source_version.html version=version, description='Modélisation Conceptuelle de Données. Nickel. Ni souris.', long_description=long_description, # The project's main homepage. url='https://github.com/laowantong/mocodo', # Author details author='Aristide Grange', author_email='mocodo@wingi.net', # Choose your license license='MIT', # See https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[ # How mature is this project? Common values are # 3 - Alpha # 4 - Beta # 5 - Production/Stable 'Development Status :: 5 - Production/Stable', # Indicate who your project is intended for 'Intended Audience :: Education', 'Intended Audience :: Information Technology', 'Topic :: Database', # Pick your license as you wish (should match "license" above) 'License :: OSI Approved :: MIT License', # Specify the Python versions you support here. In particular, ensure # that you indicate whether you support Python 2, Python 3 or both. 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', ], # What does your project relate to? keywords='relational database drawing ERD SVG', # You can just specify the packages manually here if your project is # simple. Or you can use find_packages(). packages=["mocodo"], # List run-time dependencies here. These will be installed by pip when # your project is installed. For an analysis of "install_requires" vs pip's # requirements files see: # https://packaging.python.org/en/latest/requirements.html # install_requires=['peppercorn'], # List additional groups of dependencies here (e.g. development # dependencies). You can install these using the following syntax, # for example: # $ pip install -e .[dev,test] # extras_require={ # 'dev': ['check-manifest'], # 'test': ['coverage'], # }, # If there are data files included in your packages that need to be # installed, specify them here. If using Python 2.6 or less, then these # have to be included in MANIFEST.in as well. package_data={ 'mocodo': [ 'resources/colors/*.json', 'resources/lorem/*.txt', 'resources/relation_templates/*.json', 'resources/i18n/*.mo', 'resources/shapes/*.json', 'resources/pristine_sandbox.mcd', 'resources/font_metrics.json' ], }, # Although 'package_data' is the preferred approach, in some case you may # need to place data files outside of your packages. See: # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # noqa # In this case, 'data_file' will be installed into '/my_data' # data_files=[('my_data', ['data/data_file'])], # To provide executable scripts, use entry points in preference to the # "scripts" keyword. Entry points provide cross-platform support and allow # pip to create the appropriate form of executable for the target platform. entry_points={ 'console_scripts': ['mocodo=mocodo.__main__:main'], }, # Not all packages, however, are capable of running in compressed form, # because they may expect to be able to access either source code or data files # as normal operating system files. zip_safe=False, ) ================================================ FILE: test/__init__.py ================================================ ================================================ FILE: test/launch_tests.sh ================================================ python -m unittest ================================================ FILE: test/test_api.py ================================================ import unittest import gettext import re from pathlib import Path gettext.NullTranslations().install() __import__("sys").path[0:0] = ["mocodo"] from mocodo import __version__ from mocodo.api import mocodo class TestMocodoApi(unittest.TestCase): def test_unrecognized_arguments(self): result = mocodo("foo bar") self.assertEqual(result, None) def test_no_arguments(self): paths = list(map(Path, [ "pristine_sandbox.svg", "pristine_sandbox_static.svg", "pristine_sandbox_geo.json" ])) for path in paths: path.unlink(missing_ok=True) result = mocodo() self.assertEqual(mocodo(""), result) for path in paths: self.assertTrue(path.is_file()) for path in paths: path.unlink() def test_help(self): result = mocodo("--help") self.assertEqual(result, None) def test_version(self): result = mocodo("--version") self.assertEqual(result, __version__) def test_language(self): result = mocodo("--language=en") self.assertRegex(result, re.compile(r"Output file.+generated")) result = mocodo("--language=fr") self.assertRegex(result, re.compile(r"Fichier de sortie.+succès")) def test_input_and_output_dir(self): path_json = Path("test/ccp_geo.json") path_svg = Path("test/ccp.svg") path_json.unlink(missing_ok=True) path_svg.unlink(missing_ok=True) mocodo("-i doc/mocodo_notebook/ccp.mcd --output_dir test") self.assertTrue(path_json.is_file()) self.assertTrue(path_svg.is_file()) path_json.unlink() path_svg.unlink() if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_argument_parser.py ================================================ import unittest import argparse __import__("sys").path[0:0] = ["mocodo"] from mocodo.argument_parser import extract_subargs class TestSubArguments(unittest.TestCase): def test_extract_subargs(self): parser = argparse.ArgumentParser() parser.add_argument( "--actual", metavar="STR", nargs="*", type=extract_subargs, ) args = parser.parse_args( [ # NB: names not necessarily the same as in the current version "--actual", "arrange:algo=bb,grid=organic", "randomize:cards=_/,types=mysql,labels=four_letter_words", "map:ascii=labels,n=3,x=3.14,delete='',guess=types,create=types", "drain", "flip:vertical,horizontal,diagonal", """data_dict:type,box="",label="libellé de l'attribut",tsv""", ] ) expected = [ ( "arrange", {"algo": "bb", "grid": "organic"}, ), ( "randomize", {"cards": "_/", "types": "mysql", "labels": "four_letter_words"}, ), ( "map", {"ascii": "labels", "n": "3", "x": "3.14", "delete": "", "guess": "types", "create": "types"}, ), ("drain", {}), ( "flip", {"vertical": None, "horizontal": None, "diagonal": None}, ), ( "data_dict", {"type": None, "box": "", "label": "libellé de l'attribut", "tsv": None} ), ] self.assertEqual(args.actual, expected) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_arrange_bb.py ================================================ import unittest from random import seed import gettext __import__("sys").path[0:0] = ["mocodo"] from mocodo.rewrite._arrange import run as arrange from mocodo.mocodo_error import MocodoError gettext.NullTranslations().install() class ArrangeBB(unittest.TestCase): def test_constraints(self): source = """ SUSPENDISSE: diam SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus CONSECTETUER: elit, sed MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM DF, 11 LOREM, 1N SUSPENDISSE LOREM: ipsum, dolor, sit TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec DIGNISSIM: ligula, massa, varius DF, 11 RISUS, 0N RISUS AMET, 11> LOREM, 01 CONSECTETUER: adipiscing RISUS: ultricies, _cras, elementum SEMPER, 0N RISUS, 1N DIGNISSIM """.replace(" ", "").strip() # balanced = 0 subargs = {"algo": "bb", "balanced": ""} seed(42) actual = arrange(source, subargs) expected = """ SEMPER, 0N RISUS, 1N DIGNISSIM MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM SUSPENDISSE: diam DF, 11 LOREM, 1N SUSPENDISSE RISUS: ultricies, _cras, elementum DIGNISSIM: ligula, massa, varius SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus LOREM: ipsum, dolor, sit DF, 11 RISUS, 0N RISUS TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec CONSECTETUER: elit, sed AMET, 11> LOREM, 01 CONSECTETUER: adipiscing """.replace(" ", "").strip() self.assertEqual(actual, expected) # balanced = 1 subargs = {"algo": "bb", "balanced": "1"} seed(42) actual = arrange(source, subargs) expected = """ DF, 11 RISUS, 0N RISUS : AMET, 11> LOREM, 01 CONSECTETUER: adipiscing LOREM: ipsum, dolor, sit DF, 11 LOREM, 1N SUSPENDISSE RISUS: ultricies, _cras, elementum TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec CONSECTETUER: elit, sed SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus SUSPENDISSE: diam SEMPER, 0N RISUS, 1N DIGNISSIM DIGNISSIM: ligula, massa, varius MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM : : """.replace(" ", "").strip() self.assertEqual(actual, expected) # organic subargs = {"algo": "bb"} seed(2) actual = arrange(source, subargs) expected = """ DF, 11 LOREM, 1N SUSPENDISSE SUSPENDISSE: diam MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM : LOREM: ipsum, dolor, sit SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus DIGNISSIM: ligula, massa, varius SEMPER, 0N RISUS, 1N DIGNISSIM AMET, 11> LOREM, 01 CONSECTETUER: adipiscing CONSECTETUER: elit, sed TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec RISUS: ultricies, _cras, elementum : : : DF, 11 RISUS, 0N RISUS """.replace(" ", "").strip() self.assertEqual(actual, expected) # wide = 8 (default) subargs = {"algo": "bb", "wide": None} # the mere presence of the key is enough seed(2) actual = arrange(source, subargs) expected = """ DF, 11 RISUS, 0N RISUS RISUS: ultricies, _cras, elementum TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec : CONSECTETUER: elit, sed SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus SUSPENDISSE: diam : SEMPER, 0N RISUS, 1N DIGNISSIM DIGNISSIM: ligula, massa, varius MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM AMET, 11> LOREM, 01 CONSECTETUER: adipiscing LOREM: ipsum, dolor, sit DF, 11 LOREM, 1N SUSPENDISSE """.replace(" ", "").strip() self.assertEqual(actual, expected) # wide = 3 subargs = {"algo": "bb", "wide": "3"} # the mere presence of the key is enough seed(2) actual = arrange(source, subargs) expected = """ DF, 11 LOREM, 1N SUSPENDISSE SUSPENDISSE: diam MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM SEMPER, 0N RISUS, 1N DIGNISSIM LOREM: ipsum, dolor, sit SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus DIGNISSIM: ligula, massa, varius RISUS: ultricies, _cras, elementum AMET, 11> LOREM, 01 CONSECTETUER: adipiscing CONSECTETUER: elit, sed TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec DF, 11 RISUS, 0N RISUS """.replace(" ", "").strip() self.assertEqual(actual, expected) # timeout too low subargs = {"algo": "bb", "timeout": 0.000001} seed(2) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.10", arrange, source, subargs) def test_non_connected_graph(self): source = """ SUSPENDISSE: diam SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus CONSECTETUER: elit, sed MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM DF, 11 LOREM, 1N SUSPENDISSE LOREM: ipsum, dolor, sit DIGNISSIM: ligula, massa, varius DF, 11 RISUS, 0N RISUS AMET, 11> LOREM, 01 CONSECTETUER: adipiscing RISUS: ultricies, _cras, elementum SEMPER, 0N RISUS, 1N DIGNISSIM """.replace(" ", "").strip() subargs = {"algo": "bb", "balanced": ""} seed(42) actual = arrange(source, subargs) expected = """ DF, 11 RISUS, 0N RISUS SUSPENDISSE: diam SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus CONSECTETUER: elit, sed RISUS: ultricies, _cras, elementum DF, 11 LOREM, 1N SUSPENDISSE LOREM: ipsum, dolor, sit AMET, 11> LOREM, 01 CONSECTETUER: adipiscing SEMPER, 0N RISUS, 1N DIGNISSIM DIGNISSIM: ligula, massa, varius MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM : """.replace(" ", "").strip() self.assertEqual(actual, expected) def test_no_link(self): source = """ SUSPENDISSE: diam CONSECTETUER: elit, sed LOREM: ipsum, dolor, sit DIGNISSIM: ligula, massa, varius RISUS: ultricies, _cras, elementum """.replace(" ", "").strip() subargs = {"algo": "bb", "balanced": "0"} seed(42) actual = arrange(source, subargs) expected = """ : DIGNISSIM: ligula, massa, varius : CONSECTETUER: elit, sed : LOREM: ipsum, dolor, sit : : RISUS: ultricies, _cras, elementum : SUSPENDISSE: diam : : : """.replace(" ", "").strip() self.assertEqual(actual, expected) def test_inheritance(self): source = """ SUSPENDISSE: diam /XT\\ SUSPENDISSE -> CONSECTETUER, LOREM CONSECTETUER: elit, sed MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM DF, 11 LOREM, 1N SUSPENDISSE LOREM: ipsum, dolor, sit TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec DIGNISSIM: ligula, massa, varius DF, 11 RISUS, 0N RISUS AMET, 11> LOREM, 01 CONSECTETUER: adipiscing RISUS: ultricies, _cras, elementum SEMPER, 0N RISUS, 1N DIGNISSIM """.replace(" ", "").strip() subargs = {"algo": "bb"} seed(4) actual = arrange(source, subargs) expected = """ DF, 11 LOREM, 1N SUSPENDISSE SUSPENDISSE: diam : : : LOREM: ipsum, dolor, sit /XT\\ SUSPENDISSE -> CONSECTETUER, LOREM : : : AMET, 11> LOREM, 01 CONSECTETUER: adipiscing CONSECTETUER: elit, sed TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec RISUS: ultricies, _cras, elementum DF, 11 RISUS, 0N RISUS : MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM DIGNISSIM: ligula, massa, varius SEMPER, 0N RISUS, 1N DIGNISSIM : """.replace(" ", "").strip() self.assertEqual(actual, expected) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_arrange_ga.py ================================================ import unittest from random import seed import gettext __import__("sys").path[0:0] = ["mocodo"] from mocodo.rewrite._arrange import run as arrange gettext.NullTranslations().install() class ArrangeGA(unittest.TestCase): def test_arrange(self): source = """ SUSPENDISSE: diam SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus CONSECTETUER: elit, sed MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM DF, 11 LOREM, 1N SUSPENDISSE LOREM: ipsum, dolor, sit TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec DIGNISSIM: ligula, massa, varius DF, 11 RISUS, 0N RISUS AMET, 11> LOREM, 01 CONSECTETUER: adipiscing RISUS: ultricies, _cras, elementum SEMPER, 0N RISUS, 1N DIGNISSIM """.replace(" ", "").strip() subargs = {"algo": "ga", "max_generations": 50, "population_size": 100} seed(1) actual = arrange(source, subargs) expected = """ DF, 11 RISUS, 0N RISUS SUSPENDISSE: diam DF, 11 LOREM, 1N SUSPENDISSE LOREM: ipsum, dolor, sit RISUS: ultricies, _cras, elementum SOLLICITUDIN, 0N SUSPENDISSE, 0N CONSECTETUER, 0N LOREM: lectus CONSECTETUER: elit, sed AMET, 11> LOREM, 01 CONSECTETUER: adipiscing SEMPER, 0N RISUS, 1N DIGNISSIM MAECENAS, 1N DIGNISSIM, 1N DIGNISSIM DIGNISSIM: ligula, massa, varius TORTOR, 0N RISUS, 11 DIGNISSIM, 1N CONSECTETUER: nec """.replace(" ", "").strip() self.assertEqual(actual, expected) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_association.py ================================================ import gettext import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.association import * from mocodo.tools.parser_tools import extract_clauses gettext.NullTranslations().install() def association_wrapper(s, **kargs): Association.reset_df_counter() return Association(extract_clauses(s)[0], **kargs) class ParseTest(unittest.TestCase): def test_reflexive(self): a = association_wrapper("ÊTRE AMI, 0N BANDIT, 0N BANDIT") self.assertEqual(a.bid, "ETRE_AMI") self.assertEqual(a.name_view, "ÊTRE AMI") self.assertEqual(a.attributes, []) for (i, leg) in enumerate(a.legs): self.assertEqual(leg.card_view, "0,N") self.assertEqual(leg.entity_bid, "BANDIT") self.assertEqual(leg.arrow, "") self.assertEqual(leg.note, None) def test_double(self): l = [ association_wrapper("EMPLOYER, 01 PARTICIPANT, 0N ENTREPRISE"), association_wrapper("EMPLOYER, 01 PARTICIPANT, 0N ENTREPRISE:"), ] for a in l: self.assertEqual(a.bid, "EMPLOYER") self.assertEqual(a.name_view, "EMPLOYER") self.assertEqual(a.legs[0].card_view, "0,1") self.assertEqual(a.legs[0].entity_bid, "PARTICIPANT") self.assertEqual(a.legs[0].arrow, "") self.assertEqual(a.legs[0].note, None) self.assertEqual(a.legs[1].card_view, "0,N") self.assertEqual(a.legs[1].entity_bid, "ENTREPRISE") self.assertEqual(a.legs[1].arrow, "") self.assertEqual(a.legs[1].note, None) self.assertEqual(l[0].attributes, []) self.assertEqual(l[1].attributes[0].kind, "phantom") def test_triple(self): a = association_wrapper("SUIVRE, 0N DATE, 11 ÉTUDIANT, 0N ENSEIGNANT") self.assertEqual(a.bid, "SUIVRE") self.assertEqual(a.name_view, "SUIVRE") self.assertEqual(a.attributes, []) self.assertEqual(a.legs[0].card_view, "0,N") self.assertEqual(a.legs[0].entity_bid, "DATE") self.assertEqual(a.legs[0].arrow, "") self.assertEqual(a.legs[0].note, None) self.assertEqual(a.legs[1].card_view, "1,1") self.assertEqual(a.legs[1].entity_bid, "ETUDIANT") self.assertEqual(a.legs[1].arrow, "") self.assertEqual(a.legs[1].note, None) self.assertEqual(a.legs[2].card_view, "0,N") self.assertEqual(a.legs[2].entity_bid, "ENSEIGNANT") self.assertEqual(a.legs[2].arrow, "") self.assertEqual(a.legs[2].note, None) def test_arrow(self): a = association_wrapper("EMPLOYER, 01> PARTICIPANT, 0N< ENTREPRISE") self.assertEqual(a.legs[0].arrow, ">") self.assertEqual(a.legs[1].arrow, "<") self.assertEqual(a.legs[0].card_view, "0,1") self.assertEqual(a.legs[1].card_view, "0,N") def test_note(self): a = association_wrapper("ENGENDRER, 0N [Parent] PERSONNE, 1N [Enfant] PERSONNE") self.assertEqual(a.legs[0].note, "Parent") self.assertEqual(a.legs[1].note, "Enfant") self.assertEqual(a.legs[0].card_view, "0,N") self.assertEqual(a.legs[1].card_view, "1,N") def test_note_in_agregation(self): a = association_wrapper("ENGENDRER, 0N [Père] PERSONNE, 0N [Mère] PERSONNE, /1N [Enfant] PERSONNE") self.assertEqual(a.legs[0].note, "Père") self.assertEqual(a.legs[1].note, "Mère") self.assertEqual(a.legs[2].note, "Enfant") self.assertEqual(a.legs[0].card_view, "0,N") self.assertEqual(a.legs[1].card_view, "0,N") self.assertEqual(a.legs[2].card_view, "1,N") def test_attributes(self): l = [ association_wrapper("SOUTENIR, 01 ÉTUDIANT, 0N DATE: note stage, heure soutenance"), association_wrapper("SOUTENIR, 01 ÉTUDIANT, 0N DATE: note stage , heure soutenance "), association_wrapper("SOUTENIR, 01 ÉTUDIANT, 0N DATE: note stage,heure soutenance "), ] for a in l: self.assertEqual([att.label for att in a.attributes], ["note stage", "heure soutenance"]) self.assertEqual([att.__class__ for att in a.attributes], [SimpleAssociationAttribute, SimpleAssociationAttribute]) def test_identifiers(self): a = association_wrapper("Réserver, 1N Client, 0N Chambre: _Date, Durée") self.assertEqual([att.label for att in a.attributes], ["Date", "Durée"]) self.assertEqual([att.__class__ for att in a.attributes], [StrongAttribute, SimpleAssociationAttribute]) def test_other_card(self): a = association_wrapper("SOUTENIR, XX ÉTUDIANT, XX DATE: note stage") self.assertTrue(not a.legs[0].card_view.strip()) self.assertTrue(not a.legs[1].card_view.strip()) a = association_wrapper("SOUTENIR, AB ÉTUDIANT, CD DATE: note stage") self.assertEqual(a.legs[0].card_view, "A,B") self.assertEqual(a.legs[1].card_view, "C,D") a = association_wrapper("SOUTENIR, XY ÉTUDIANT, XY DATE: note stage") self.assertEqual(a.legs[0].card_view, "Y") self.assertEqual(a.legs[1].card_view, "Y") def test_numbered_association_wrapper(self): a = association_wrapper("SOUTENIR1, 01 ÉTUDIANT, 0N DATE: note stage") self.assertEqual(a.bid, "SOUTENIR1") self.assertEqual(a.name_view, "SOUTENIR") a = association_wrapper("SOUTENIR123, 01 ÉTUDIANT, 0N DATE: note stage") self.assertEqual(a.bid, "SOUTENIR123") self.assertEqual(a.name_view, "SOUTENIR12") def test_df(self): a = association_wrapper("DF, 0N CLIENT, 11 COMMANDE") self.assertEqual(a.bid, "DF0") self.assertEqual(a.name_view, "DF") a = association_wrapper("CIF, 0N CLIENT, 11 COMMANDE", df_label="CIF") self.assertEqual(a.bid, "CIF") self.assertEqual(a.name_view, "CIF") self.assertRaisesRegex(MocodoError, r"Mocodo Err\.37", association_wrapper, "DF, 0N CLIENT, 0N COMMANDE") def test_cluster_of_one_entity(self): self.assertRaisesRegex(MocodoError, r"Mocodo Err\.51", association_wrapper, "SUIVRE, 0N DATE, /1N ÉTUDIANT") def test_cluster_of_two_entities(self): a = association_wrapper("SUIVRE, 0N DATE, /1N ÉTUDIANT, 0N ENSEIGNANT") self.assertEqual(a.legs[0].entity_bid, "DATE") self.assertEqual(a.legs[0].kind, "cluster_leg") self.assertEqual(a.legs[1].entity_bid, "ETUDIANT") self.assertEqual(a.legs[1].kind, "cluster_peg") self.assertEqual(a.legs[2].entity_bid, "ENSEIGNANT") self.assertEqual(a.legs[2].kind, "cluster_leg") if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_convert_chen.py ================================================ import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.common import Common from mocodo.convert._chen import * params = {"df": "DF"} common = Common(params) class TestChen(unittest.TestCase): def test111N(self): source = """ Employé: employé Travailler, 11 Département, 1N Employé Département: département """ actual = run(source, common=common, testing=True) expected = """ [Département] ==N== [Employé] ==1== """ self.assertEqual(actual.strip(), expected.strip()) def test011N(self): source = """ Mer: mer Recevoir, 01 Rivière, 1N Mer Rivière: rivière """ actual = run(source, common=common, testing=True) expected = """ [Mer] ==1== [Rivière] --N-- """ self.assertEqual(actual.strip(), expected.strip()) def test0N1N(self): source = """ PROJET: projet REQUÉRIR, 1N PROJET, 0N PIÈCE PIÈCE: pièce COMPOSER, 0N PIÈCE, 0N PIÈCE """ actual = run(source, common=common, testing=True) expected = """ [PIÈCE] --M-- [PIÈCE] --M-- [PIÈCE] --N-- [PROJET] ==N== """ self.assertEqual(actual.strip(), expected.strip()) def test_110N(self): source = """ Œuvre: œuvre DF, 0N Œuvre, _11 Exemplaire Exemplaire: exemplaire """ actual = run(source, common=common, testing=True) expected = """ [[Exemplaire]] ==N== <> [Œuvre] --1-- <> """ self.assertEqual(actual.strip(), expected.strip()) def test_gerund(self): source = """ Produit: produit DF, _11 Ligne de commande, 0N Produit Ligne de commande: _quantité DF, _11 Ligne de commande, 1N Commande Commande: commande """ actual = run(source, common=common, testing=True) expected = """ [] ==N== <> [] ==N== <> [Commande] ==1== <> [Produit] --1-- <> """ self.assertEqual(actual.strip(), expected.strip()) def test_NNN(self): source = """ Employé: employé Appliquer, 0N Employé, 1N Projet, 1N Compétence Projet: projet Compétence: compétence """ actual = run(source, common=common, testing=True) expected = """ [Compétence] ==N== [Employé] --N-- [Projet] ==N== """ self.assertEqual(actual.strip(), expected.strip()) def test_1NN(self): source = """ Ingénieur: ingénieur Gérer, /1N Responsable, 1N Ingénieur, 1N Projet Projet: projet Responsable: responsable """ actual = run(source, common=common, testing=True) expected = """ [Ingénieur] ==N== [Projet] ==N== [Responsable] ==1== """ self.assertEqual(actual.strip(), expected.strip()) def test_11N(self): source = """ Projet: projet Affecter, /1N Site, /1N Projet, 0N Employé Site: site Employé: employé """ actual = run(source, common=common, testing=True) expected = """ [Employé] --N-- [Projet] ==1== [Site] ==1== """ self.assertEqual(actual.strip(), expected.strip()) def test_111(self): source = """ Technicien: technicien Utiliser, /1N Technicien, /1N Carnet, /1N Projet Projet: projet Carnet: carnet """ actual = run(source, common=common, testing=True) expected = """ [Carnet] ==1== [Projet] ==1== [Technicien] ==1== """ self.assertEqual(actual.strip(), expected.strip()) def test_attrs(self): source = """ COMMANDE: Num. commande, Date, Montant INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité PRODUIT: Réf. produit, Libellé, Prix unitaire """ actual = run(source, common=common, subargs={"attrs": 1}, testing=True) expected = """ -- (Quantité) [COMMANDE] -- (Date) [COMMANDE] -- (Montant) [COMMANDE] -- (_Num. commande_) [COMMANDE] ==N== [PRODUIT] -- (Libellé) [PRODUIT] -- (Prix unitaire) [PRODUIT] -- (_Réf. produit_) [PRODUIT] --M-- """ self.assertEqual(actual.strip(), expected.strip()) def test_id_weakness(self): source = """ Œuvre: œuvre DF, 0N Œuvre, _11 Exemplaire Exemplaire: exemplaire """ actual = run(source, common=common, subargs={"attrs": 1}, testing=True) expected = """ [[Exemplaire]] -- (.exemplaire.) [[Exemplaire]] ==N== <> [Œuvre] -- (_œuvre_) [Œuvre] --1-- <> """ self.assertEqual(actual.strip(), expected.strip()) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_cross.py ================================================ import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.rewrite.cross import cross class CrossTests(unittest.TestCase): def test_non_parallel_normal_intersection(self): self.assertTrue(cross(4,4, 7,4, 4,3, 6,5)) self.assertTrue(cross(2,1, 4,3, 1,2, 5,1)) self.assertTrue(cross(1,4, 7,4, 4,1, 4,7)) self.assertTrue(cross(0,0, 0,5,-1,2, 1,2)) self.assertTrue(cross(0,0, 0,5, 1,0,-1,4)) def test_non_parallel_intersection_is_one_extremity(self): self.assertTrue(cross(2,0, 3,1, 3,0, 3,3)) self.assertTrue(cross(5,0, 0,5, 3,2, 5,4)) self.assertTrue(cross(0,0, 0,5, 7,5, -7,-5)) self.assertTrue(cross(3,0, 3,3, 2,0, 3,1)) self.assertTrue(cross(2,0, 3,1, 3,0, 3,3)) self.assertTrue(cross(3,3, 3,0, 3,1, 2,0)) self.assertTrue(cross(3,1, 2,0, 3,3, 3,0)) self.assertTrue(cross(3,0, 3,3, 3,2, 2,3)) def test_non_parallel_intersection_is_two_extremities(self): """ This is not considered as a valid intersection. """ self.assertTrue(not cross(0,0, 0,5, 0,0, 5,0)) self.assertTrue(not cross(0,0, 0,1, 0,1, 1,1)) def test_non_parallel_no_intersection(self): self.assertTrue(not cross(2,2, 8,2, 3,3, 9,6)) self.assertTrue(not cross(1,2, 5,6, 3,2, 8,5)) self.assertTrue(not cross(0,0, 0,5, 1,2, 4,2)) self.assertTrue(not cross(0,0, 0,5, 1,0, 4,6)) self.assertTrue(not cross(0,0, 1,0, 3,0, 3,3)) self.assertTrue(not cross(0,0, 1,0, 0,1, 0,2)) def test_parallel_no_intersection(self): self.assertTrue(not cross(1,2, 5,6, 3,2, 5,4)) self.assertTrue(not cross(3,3, 5,3, 4,4, 6,4)) self.assertTrue(not cross(3,3, 4,3, 5,4, 6,4)) self.assertTrue(not cross(0,0, 5,0, 1,1, 5,1)) def test_colinear_intersection_is_two_extremities(self): """ This is not considered as a valid intersection. """ self.assertTrue(not cross(0,0, 1,2, 1,2, 2,4)) def test_colinear_no_intersection(self): self.assertTrue(not cross(3,3, 4,3, 5,3, 6,3)) def test_colinear_intersection_is_partial_segment(self): self.assertTrue(cross(0,0, 4,2, 2,1, 6,3)) def test_colinear_intersection_is_partial_segment_horizontal(self): self.assertTrue(cross(3,3, 5,3, 4,3, 6,3)) def test_colinear_intersection_is_partial_segment_vertical(self): self.assertTrue(cross(3,3, 3,5, 3,4, 3,6)) def test_colinear_intersection_is_complete_segment_horizontal(self): self.assertTrue(cross(0,2, 2,2,-2,2, 4,2)) self.assertTrue(cross(0,2, 2,2, 0,2, 4,2)) self.assertTrue(cross(0,2, 2,2,-2,2, 2,2)) self.assertTrue(cross(0,2, 2,2, 1,2, 2,2)) self.assertTrue(cross(2,2, 0,2, 1,2, 2,2)) self.assertTrue(cross(0,2, 2,2, 2,2, 1,2)) self.assertTrue(cross(2,2, 0,2, 2,2, 1,2)) self.assertTrue(cross(1,2, 2,2, 0,2, 2,2)) self.assertTrue(cross(1,2, 2,2, 2,2, 0,2)) self.assertTrue(cross(2,2, 1,2, 0,2, 2,2)) self.assertTrue(cross(2,2, 1,2, 2,2, 0,2)) def test_colinear_intersection_is_complete_segment_vertical(self): self.assertTrue(cross(2,0, 2,2, 2,-2, 2,4)) self.assertTrue(cross(2,0, 2,2, 2, 0, 2,4)) self.assertTrue(cross(2,0, 2,2, 2,-2, 2,2)) def test_colinear_intersection_is_complete_segment(self): self.assertTrue(cross(0,0, 6,3, 2,1, 4,2)) self.assertTrue(cross(0,0, 6,3, 2,1, 6,3)) self.assertTrue(cross(0,0, 6,3, 0,0, 4,2)) if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_data/small_pool.txt ================================================ Foo Bar Biz Buz ================================================ FILE: test/test_data/templates/bad_array_element.yaml ================================================ foo: - order: 100 bar: null - order: 200 bizz: null - buzz ================================================ FILE: test/test_data/templates/bad_circular_1.yaml ================================================ parent: 'bad_circular_2' ================================================ FILE: test/test_data/templates/bad_circular_2.yaml ================================================ parent: 'bad_circular_1' ================================================ FILE: test/test_data/templates/bad_no_order.yaml ================================================ foo: - order: 100 bar: null - bizz: null ================================================ FILE: test/test_data/templates/bad_non_increasing_order.yaml ================================================ foo: - order: 100 bar: null - order: 50 bizz: null ================================================ FILE: test/test_data/templates/bad_non_numeric_order.yaml ================================================ foo: - order: 100 bar: null - order: 'bad' bizz: null ================================================ FILE: test/test_data/templates/bad_not_an_object.yaml ================================================ - 42 ================================================ FILE: test/test_data/templates/bad_object_value.yaml ================================================ foo: 'bar': null ================================================ FILE: test/test_data/templates/child.yaml ================================================ parent: 'root' buzz: 'updated value' baz: - order: 5 foo: 'insert_before' - order: 10 qux: 'updated value' quux: 'new_item' - order: 15 foo: 'insert_between' - order: 20 - order: 35 foo: 'insert_after' force_empty_list: ================================================ FILE: test/test_data/templates/expected_child.yaml ================================================ foo: 'bar' buzz: 'updated value' baz: - order: 5 foo: 'insert_before' - order: 10 qux: 'updated value' unchanged: 'unchanged' quux: 'new_item' - order: 15 foo: 'insert_between' - order: 30 qux: 'quux' - order: 35 foo: 'insert_after' empty: force_empty_list: unchanged: 'unchanged' ================================================ FILE: test/test_data/templates/expected_grandchild.yaml ================================================ foo: 'bar' buzz: 'reupdated_value' baz: - order: 1 name: 'foo' - order: 10 qux: 'updated value' unchanged: 'unchanged' quux: 'new_item' - order: 15 foo: 'insert_between' - order: 20 name: 'bar' - order: 30 qux: 'quux' empty: force_empty_list: unchanged: 'unchanged' biz: - order: 1 foo: 'bar' baz: 'qux' - order: 2 foo: 'bar' baz: 'qux' ================================================ FILE: test/test_data/templates/grandchild.yaml ================================================ parent: 'test/test_data/templates/child.yaml' buzz: 'reupdated_value' biz: - order: 1 foo: 'bar' baz: 'qux' - order: 2 foo: 'bar' baz: 'qux' baz: - order: 1 name: 'foo' - order: 5 - order: 20 name: 'bar' - order: 35 ================================================ FILE: test/test_data/templates/root.yaml ================================================ foo: 'bar' buzz: 42 baz: - order: 10 qux: 'quux' unchanged: 'unchanged' - order: 20 biz: 'buz' - order: 30 qux: 'quux' empty: unchanged: 'unchanged' force_empty_list: - order: 10 qux: 'quux' ================================================ FILE: test/test_data_dict.py ================================================ import re import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.common import Common from mocodo.convert import _data_dict as data_dict class TestDataDict(unittest.TestCase): def test_data_dict(self): source = """ CLIENT: Réf. client [varchar(8)], Nom, Adresse [varchar(40)] DF, 0N CLIENT, 11 COMMANDE COMMANDE: Num commande [tinyint(4)], Date [date], Montant [decimal(5,2) DEFAULT '0.00'] INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité [tinyint(4)] PRODUIT: Réf. produit [varchar(8)], Libellé [varchar(20)], Prix unitaire [decimal(5,2)] """ params = {"language": "fr"} common = Common(params) # Markdown table with two columns subargs ={ "label": "Libellé des attributs", "type": "Type de données", "md": True, } actual = data_dict.run(source, common=common, subargs=subargs)["text"] expected = """ | Libellé des attributs | Type de données | |:----------------------|:----------------------------| | Adresse | varchar(40) | | Date | date | | Libellé | varchar(20) | | Montant | decimal(5,2) DEFAULT '0.00' | | Nom | | | Num commande | tinyint(4) | | Prix unitaire | decimal(5,2) | | Quantité | tinyint(4) | | Réf. client | varchar(8) | | Réf. produit | varchar(8) | """ self.assertEqual(actual.strip(), re.sub("(?m)^ +", "", expected).strip()) # Markdown table with 3 columns in another order and a Markdown emphasis subargs ={ "**box**": "Entité ou association", "type": "Type", "label": "", # omitted translation => default to a language-dependent one "md": True, } actual = data_dict.run(source, common=common, subargs=subargs)["text"] expected = """ | **Entité ou association** | Type | Libellé de l'attribut | |:--------------------------|:----------------------------|:----------------------| | **CLIENT** | | Nom | | **"** | varchar(40) | Adresse | | **"** | varchar(8) | Réf. client | | **COMMANDE** | date | Date | | **"** | decimal(5,2) DEFAULT '0.00' | Montant | | **"** | tinyint(4) | Num commande | | **INCLURE** | tinyint(4) | Quantité | | **PRODUIT** | decimal(5,2) | Prix unitaire | | **"** | varchar(20) | Libellé | | **"** | varchar(8) | Réf. produit | """ self.assertEqual(actual.strip(), re.sub("(?m)^ +", "", expected).strip()) # With just one column, the table is converted to a list and the header is omitted subargs = {"md": True, "label": ""} actual = data_dict.run(source, common=common, subargs=subargs)["text"] expected = """ - Adresse - Date - Libellé - Montant - Nom - Num commande - Prix unitaire - Quantité - Réf. client - Réf. produit """ self.assertEqual(actual.strip(), re.sub("(?m)^ +", "", expected).strip()) # By default: md,box,label,type subargs = {"tsv": True} actual = data_dict.run(source, common=common, subargs=subargs)["text"] expected = """ Entité ou association\tLibellé de l'attribut\tType CLIENT\tAdresse\tvarchar(40) CLIENT\tNom\t CLIENT\tRéf. client\tvarchar(8) COMMANDE\tDate\tdate COMMANDE\tMontant\tdecimal(5,2) DEFAULT '0.00' COMMANDE\tNum commande\ttinyint(4) INCLURE\tQuantité\ttinyint(4) PRODUIT\tLibellé\tvarchar(20) PRODUIT\tPrix unitaire\tdecimal(5,2) PRODUIT\tRéf. produit\tvarchar(8) """ self.assertEqual(actual.strip(), re.sub("(?m)^ +", "", expected).strip()) def test_data_dict_with_invisible_boxes(self): source = """ CLIENT: Réf. client, Nom, Prénom, Adresse -Reflexive 6_, 11 COMMANDE, 01 COMMANDE -Entity 7_: id 7 1, _id 7 2, attr 7 3, attr 7 4 -Reflexive 13_, 11 PRODUIT, 1N PRODUIT DF, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant -Ternary 8_, 0N Entity 7_, 0N COMMANDE, 1N PRODUIT PRODUIT: Réf. produit, Libellé, Prix unitaire -Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 -Binary 15_, 0N Entity 14_, 01 Entity 11_ INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité -Binary 10_, 1N Entity 9_, 1N PRODUIT: attr 10 1 -Binary 16_, 0N Entity 14_, 1N Entity 11_ -Entity 11_: id 11 1, attr 11 2 -Binary 12_, 0N Entity 11_, 11 Entity 9_: attr 12 1 -Entity 9_: id 9 1, attr 9 2, attr 9 3 """ params = {"language": "fr"} common = Common(params) actual = data_dict.run(source, common=common, subargs={})["text"] expected = """ | Entité ou association | Libellé de l'attribut | Type | |:----------------------|:----------------------|:-----| | CLIENT | Adresse | | | " | Nom | | | " | Prénom | | | " | Réf. client | | | COMMANDE | Date | | | " | Montant | | | " | Num. commande | | | INCLURE | Quantité | | | PRODUIT | Libellé | | | " | Prix unitaire | | | " | Réf. produit | | """ self.assertEqual(actual.strip(), re.sub("(?m)^ +", "", expected).strip()) if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_entity.py ================================================ import unittest from collections import defaultdict __import__("sys").path[0:0] = ["mocodo"] from mocodo.entity import * from mocodo.tools.parser_tools import extract_clauses def entity_wrapper(s, legs_to_strengthen=None, is_child=False): if legs_to_strengthen is None: legs_to_strengthen = [] e = Entity(extract_clauses(s)[0]) e.add_attributes(legs_to_strengthen, is_child, fk_format=None) return e class EntityTest(unittest.TestCase): def test_default(self): entities = [ entity_wrapper("PARTICIPANT: numero, nom, adresse"), entity_wrapper("PARTICIPANT:numero,nom,adresse"), entity_wrapper(" PARTICIPANT: numero, nom, adresse "), entity_wrapper("PARTICIPANT :numero ,nom ,adresse"), ] for e in entities: self.assertEqual(e.bid, "PARTICIPANT") self.assertEqual(e.name_view, "PARTICIPANT") self.assertEqual([a.label for a in e.attributes], ["numero", "nom", "adresse"]) self.assertEqual([a.kind for a in e.attributes], ["strong", "simple", "simple"]) def test_datatypes(self): e = entity_wrapper("PARTICIPANT: numero [type1], nom [type2] , adresse[type3]") self.assertEqual([a.label for a in e.attributes], ["numero", "nom", "adresse"]) self.assertEqual([a.datatype for a in e.attributes], ["type1", "type2", "type3"]) e = entity_wrapper("PARTICIPANT: numero [type a,b,c], nom [type2], adresse [type3]") self.assertEqual([a.datatype for a in e.attributes], ["type a,b,c", "type2", "type3"]) e = entity_wrapper("PARTICIPANT: numero [], nom, adresse [type3]") self.assertEqual([a.datatype for a in e.attributes], ["", "", "type3"]) def test_numbered_entity(self): e = entity_wrapper("PARTICIPANT5: numero, nom, adresse") self.assertEqual(e.bid, "PARTICIPANT5") self.assertEqual(e.name_view, "PARTICIPANT") e = entity_wrapper("PARTICIPANT123: numero, nom, adresse") self.assertEqual(e.bid, "PARTICIPANT123") self.assertEqual(e.name_view, "PARTICIPANT12") def test_blank(self): e = entity_wrapper("MOT-CLEF: mot-clé, ,") self.assertEqual([a.label for a in e.attributes], ["mot-clé", "", ""]) self.assertEqual([a.kind for a in e.attributes], ["strong", "phantom", "phantom"]) def test_all_blank(self): e = entity_wrapper("BLANK: , ,") self.assertEqual([a.label for a in e.attributes], ["", "", ""]) self.assertEqual([a.kind for a in e.attributes], ["phantom", "phantom", "phantom"]) def test_no_identifier_at_first_position(self): e = entity_wrapper("POSITION: _abscisse, ordonnee") self.assertEqual([a.label for a in e.attributes], ["abscisse", "ordonnee"]) self.assertEqual([a.kind for a in e.attributes], ["simple", "simple"]) def test_no_identifier_for_children(self): e = entity_wrapper("POSITION: abscisse, _ordonnee", [], is_child=True) self.assertEqual([a.label for a in e.attributes], ["abscisse", "ordonnee"]) self.assertEqual([a.kind for a in e.attributes], ["simple", "simple"]) def test_multiple_strong_identifier(self): e = entity_wrapper("POSITION: abscisse, _ordonnee") self.assertEqual([a.label for a in e.attributes], ["abscisse", "ordonnee"]) self.assertEqual([a.kind for a in e.attributes], ["strong", "strong"]) def test_weak_identifier(self): e = entity_wrapper("LIVRE: Num. exemplaire, Etat du livre, Date d'achat", ["placeholder"]) self.assertEqual([a.label for a in e.attributes], ["Num. exemplaire", "Etat du livre", "Date d'achat"]) self.assertEqual([a.kind for a in e.attributes], ["weak", "simple", "simple"]) def test_weak_composite_identifier(self): e = entity_wrapper("POSITION: abscisse, _ordonnee, foobar", ["placeholder"]) self.assertEqual([a.label for a in e.attributes], ["abscisse", "ordonnee", "foobar"]) self.assertEqual([a.kind for a in e.attributes], ["weak", "weak", "simple"]) class CandidateIdentifiersTest(unittest.TestCase): def check(self, expected_candidates, expected_id_texts, legs_to_strengthen=None): for source in self.sources: entity = entity_wrapper(source, legs_to_strengthen) self.assertEqual(entity.candidates, expected_candidates) actual_id_texts = [a.id_text for a in entity.attributes] self.assertEqual(actual_id_texts, expected_id_texts) # Strong entities without alternate identifiers def test_simple_id(self): self.sources = [ "FOOBAR: foo, bar, biz, qux", ] self.check( {"0": {"foo"}}, ["ID", "", "", ""] ) def test_no_id(self): self.sources = [ "FOOBAR: _foo, bar, biz, qux", "FOOBAR: 0_foo, bar, biz, qux", ] self.check( {}, ["", "", "", ""] ) def test_composite_id(self): self.sources = [ "FOOBAR: foo, _bar, biz, qux", "FOOBAR: foo, 0_bar, biz, qux", ] self.check( {"0": {"foo", "bar"}}, ["ID", "ID", "", ""] ) def test_pushed_simple_id(self): self.sources = [ "FOOBAR: _foo, _bar, biz, qux", "FOOBAR: _foo, 0_bar, biz, qux", "FOOBAR: 0_foo, _bar, biz, qux", "FOOBAR: 0_foo, 0_bar, biz, qux", ] self.check( {"0": {"bar"}}, ["", "ID", "", ""] ) def test_pushed_composite_id(self): self.sources = [ "FOOBAR: _foo, _bar, _biz, qux", "FOOBAR: _foo, _bar, 0_biz, qux", "FOOBAR: _foo, 0_bar, _biz, qux", "FOOBAR: _foo, 0_bar, 0_biz, qux", "FOOBAR: 0_foo, _bar, _biz, qux", "FOOBAR: 0_foo, _bar, 0_biz, qux", "FOOBAR: 0_foo, 0_bar, _biz, qux", "FOOBAR: 0_foo, 0_bar, 0_biz, qux", ] self.check( {"0": {"bar", "biz"}}, ["", "ID", "ID", ""] ) # Weak entities without alternate identifiers def test_simple_weak_id(self): self.sources = [ "FOOBAR: foo, bar, biz, qux", ] self.check( {"0": {"foo"}}, ["id", "", "", ""], legs_to_strengthen = ["placeholder"] ) def test_no_weak_id(self): self.sources = [ "FOOBAR: _foo, bar, biz, qux", "FOOBAR: 0_foo, bar, biz, qux", ] self.check( {}, ["", "", "", ""], legs_to_strengthen = ["placeholder"] ) def test_composite_weak_id(self): self.sources = [ "FOOBAR: foo, _bar, biz, qux", "FOOBAR: foo, 0_bar, biz, qux", ] self.check( {"0": {"foo", "bar"}}, ["id", "id", "", ""], legs_to_strengthen = ["placeholder"] ) def test_pushed_simple_weak_id(self): self.sources = [ "FOOBAR: _foo, _bar, biz, qux", "FOOBAR: _foo, 0_bar, biz, qux", "FOOBAR: 0_foo, _bar, biz, qux", "FOOBAR: 0_foo, 0_bar, biz, qux", ] self.check( {"0": {"bar"}}, ["", "id", "", ""], legs_to_strengthen = ["placeholder"] ) def test_pushed_composite_weak_id(self): self.sources = [ "FOOBAR: _foo, _bar, _biz, qux", "FOOBAR: _foo, _bar, 0_biz, qux", "FOOBAR: _foo, 0_bar, _biz, qux", "FOOBAR: _foo, 0_bar, 0_biz, qux", "FOOBAR: 0_foo, _bar, _biz, qux", "FOOBAR: 0_foo, _bar, 0_biz, qux", "FOOBAR: 0_foo, 0_bar, _biz, qux", "FOOBAR: 0_foo, 0_bar, 0_biz, qux", ] self.check( {"0": {"bar", "biz"}}, ["", "id", "id", ""], legs_to_strengthen = ["placeholder"] ) # Strong entities with alternate identifiers def test_alt_ids_and_simple_id(self): self.sources = [ "FOOBAR: foo, 1_bar, 12_biz, 2_qux", ] self.check( {"0": {"foo"}, "1": {"bar", "biz"}, "2": {"biz", "qux"}}, ["ID", "1", "1 2", "2"] ) self.sources = [ "FOOBAR: 1_foo, bar, 12_biz, 2_qux", ] self.check( {"0": {"foo"}, "1": {"foo", "biz"}, "2": {"biz", "qux"}}, ["1 ID", "", "1 2", "2"] ) def test_alt_ids_and_no_id(self): self.sources = [ "FOOBAR: _foo, 1_bar, 12_biz, 2_qux", "FOOBAR: 0_foo, 1_bar, 12_biz, 2_qux", ] self.check( {"1": {"bar", "biz"}, "2": {"biz", "qux"}}, ["", "1", "1 2", "2"] ) def test_alt_ids_and_composite_id(self): # NB: the "0" prefix is mandatory since "bar" belongs to an alt id. # Otherwise, cf. test_alt_ids_and_simple_id(). self.sources = [ "FOOBAR: foo, 01_bar, 12_biz, 2_qux", ] self.check( {"0": {"foo", "bar"}, "1": {"bar", "biz"}, "2": {"biz", "qux"}}, ["ID", "1 ID", "1 2", "2"] ) # When "bar" is not part of an alt id, the "0" prefix is optional. self.sources = [ "FOOBAR: foo, _bar, 12_biz, 2_qux", "FOOBAR: foo, 0_bar, 12_biz, 2_qux", ] self.check( {"0": {"foo", "bar"}, "1": {"biz"}, "2": {"biz", "qux"}}, ["ID", "ID", "1 2", "2"] ) # When "bar" is not part of an alt id, the "0" prefix is optional. self.sources = [ "FOOBAR: 1_foo, 0_bar, 12_biz, 2_qux", "FOOBAR: 1_foo, _bar, 12_biz, 2_qux", ] self.check( {"0": {"foo", "bar"}, "1": {"foo", "biz"}, "2": {"biz", "qux"}}, ["1 ID", "ID", "1 2", "2"] ) def test_alt_ids_and_pushed_simple_id(self): # NB: the "0" prefix on "bar" is mandatory since "bar" belongs to an alt id. # Otherwise, cf. test_alt_ids_and_no_id(). self.sources = [ "FOOBAR: _foo, 01_bar, 12_biz, 2_qux", "FOOBAR: 0_foo, 01_bar, 12_biz, 2_qux", ] self.check( {"0": {"bar"}, "1": {"bar", "biz"}, "2": {"biz", "qux"}}, ["", "1 ID", "1 2", "2"] ) # When "bar" is not part of an alt id, the "0" prefix is optional. self.sources = [ "FOOBAR: _foo, _bar, 12_biz, 2_qux", "FOOBAR: 0_foo, _bar, 12_biz, 2_qux", "FOOBAR: _foo, 0_bar, 12_biz, 2_qux", "FOOBAR: 0_foo, 0_bar, 12_biz, 2_qux", ] self.check( {"0": {"bar"}, "1": {"biz"}, "2": {"biz", "qux"}}, ["", "ID", "1 2", "2"] ) # NB: the "0" prefix on "foo" is mandatory since "foo" belongs to an alt id. # Otherwise, cf. test_alt_ids_and_composite_id(). self.sources = [ "FOOBAR: 01_foo, 0_bar, 12_biz, 2_qux", "FOOBAR: 01_foo, _bar, 12_biz, 2_qux", ] self.check( {"0": {"bar"}, "1": {"foo", "biz"}, "2": {"biz", "qux"}}, ["1", "ID", "1 2", "2"] ) def test_alt_ids_and_pushed_composite_id(self): self.sources = [ "FOOBAR: _foo, 01_bar, 02_biz, 12_qux", "FOOBAR: 0_foo, 01_bar, 02_biz, 12_qux", ] self.check( {"0": {"bar", "biz"}, "1": {"bar", "qux"}, "2": {"biz", "qux"}}, ["", "1 ID", "2 ID", "1 2"] ) self.sources = [ "FOOBAR: _foo, _bar, 02_biz, 12_qux", "FOOBAR: 0_foo, _bar, 02_biz, 12_qux", "FOOBAR: _foo, 0_bar, 02_biz, 12_qux", "FOOBAR: 0_foo, 0_bar, 02_biz, 12_qux", ] self.check( {"0": {"bar", "biz"}, "1": {"qux"}, "2": {"biz", "qux"}}, ["", "ID", "2 ID", "1 2"] ) self.sources = [ "FOOBAR: 02_foo, _bar, _biz, 12_qux", "FOOBAR: 02_foo, _bar, 0_biz, 12_qux", "FOOBAR: 02_foo, 0_bar, _biz, 12_qux", "FOOBAR: 02_foo, 0_bar, 0_biz, 12_qux", ] self.check( {"0": {"bar", "biz"}, "1": {"qux"}, "2": {"foo", "qux"}}, ["2", "ID", "ID", "1 2"] ) # Weak entities with alternate identifiers # Just one test, since the logic is the same as for strong entities. def test_alt_ids_and_simple_id(self): self.sources = [ "FOOBAR: foo, 1_bar, 12_biz, 2_qux", ] self.check( {"0": {"foo"}, "1": {"bar", "biz"}, "2": {"biz", "qux"}}, ["id", "1", "1 2", "2"], legs_to_strengthen =["placeholder"] ) self.sources = [ "FOOBAR: 1_foo, bar, 12_biz, 2_qux", ] self.check( {"0": {"foo"}, "1": {"foo", "biz"}, "2": {"biz", "qux"}}, ["1 id", "", "1 2", "2"], legs_to_strengthen = ["placeholder"] ) if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_fitness.py ================================================ import unittest from math import hypot __import__("sys").path[0:0] = ["mocodo"] from mocodo.argument_parser import parsed_arguments from mocodo.rewrite.fitness import * from mocodo.mcd import Mcd class ArrangeBB(unittest.TestCase): def test_optimal_layout(self): source = """ SCELERISQUE LOREM: blandit, elit, ligula EROS, 11 SCELERISQUE LOREM, 1N PELLENTESQUE IPSUM: metus, congue NIBH, 1N SCELERISQUE LOREM, 11 PELLENTESQUE IPSUM PELLENTESQUE IPSUM: tincidunt, bibendum, consequat, integer """ params = parsed_arguments([]) mcd = Mcd(source, params) params.update(mcd.get_layout_data()) d = mcd.get_layout_data() evaluate = fitness(d["links"], d["multiplicity"], d["col_count"], d["row_count"]) size = d["col_count"] * d["row_count"] (crossing_count, total_distances) = evaluate(list(range(size))) self.assertEqual(crossing_count, 0) self.assertEqual(total_distances, 0.0) def test_optimal_layout_with_reflexive_association(self): source = """ Assistas, 01 Hci poilu, 0N Hci poilu Hci poilu: graffiti, champignon, troussa, graffiti Rayonnait, 0N Hci poilu, 0N Lappa: monobloc Brisa: souffrait Pillards, 0N Brisa, 0N Lappa, 0N Hci poilu: disions, lascar Lappa: graffiti, champignon Puni, 11 Lappa, 0N Lappa """ params = parsed_arguments([]) mcd = Mcd(source, params) params.update(mcd.get_layout_data()) d = mcd.get_layout_data() evaluate = fitness(d["links"], d["multiplicity"], d["col_count"], d["row_count"]) size = d["col_count"] * d["row_count"] (crossing_count, total_distances) = evaluate(list(range(size))) self.assertEqual(crossing_count, 0) self.assertEqual(total_distances, 0.0) def test_diagonal_reflexive_association(self): source = """ Norm : Draw, Unit, Folk, Peer, Tour, Hall : : Baby, 1N Norm, 0N> Norm """ params = parsed_arguments([]) mcd = Mcd(source, params) params.update(mcd.get_layout_data()) d = mcd.get_layout_data() evaluate = fitness(d["links"], d["multiplicity"], d["col_count"], d["row_count"]) size = d["col_count"] * d["row_count"] (crossing_count, total_distances) = evaluate(list(range(size))) self.assertEqual(crossing_count, 0) self.assertEqual(round(total_distances, 4), 0.8284) def test_2_0_link(self): source = """ CLIENT: Réf. client, Nom, Prénom, Adresse PASSER, 0N CLIENT, 11 COMMANDE : COMMANDE: Num commande, Date, Montant """ params = parsed_arguments([]) mcd = Mcd(source, params) params.update(mcd.get_layout_data()) d = mcd.get_layout_data() evaluate = fitness(d["links"], d["multiplicity"], d["col_count"], d["row_count"]) size = d["col_count"] * d["row_count"] (crossing_count, total_distances) = evaluate(list(range(size))) self.assertEqual(crossing_count, 0) self.assertEqual(total_distances, 1.0) def test_1_1_link(self): source = """ CLIENT: Réf. client, Nom, Prénom, Adresse PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num commande, Date, Montant : """ params = parsed_arguments([]) mcd = Mcd(source, params) params.update(mcd.get_layout_data()) d = mcd.get_layout_data() evaluate = fitness(d["links"], d["multiplicity"], d["col_count"], d["row_count"]) size = d["col_count"] * d["row_count"] (crossing_count, total_distances) = evaluate(list(range(size))) self.assertEqual(crossing_count, 0) self.assertEqual(total_distances, hypot(1, 1) - 1) def test_2_1_link(self): source = """ : CLIENT: Réf. client, Nom, Prénom, Adresse PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num commande, Date, Montant : : """ params = parsed_arguments([]) mcd = Mcd(source, params) params.update(mcd.get_layout_data()) d = mcd.get_layout_data() evaluate = fitness(d["links"], d["multiplicity"], d["col_count"], d["row_count"]) size = d["col_count"] * d["row_count"] (crossing_count, total_distances) = evaluate(list(range(size))) self.assertEqual(crossing_count, 0) self.assertEqual(total_distances, hypot(2, 1) - 1) def test_k33(self): source = """ DIGNISSIM: nec sem, nunc, vulputate IMPERDIET: a praesent, nibh, semper TINCIDUNT: faucibus, orci, cursus RHONCUS, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT SODALES, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT QUIS ENIM, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT """ params = parsed_arguments([]) mcd = Mcd(source, params) params.update(mcd.get_layout_data()) d = mcd.get_layout_data() evaluate = fitness(d["links"], d["multiplicity"], d["col_count"], d["row_count"]) size = d["col_count"] * d["row_count"] (crossing_count, total_distances) = evaluate(list(range(size))) self.assertEqual(crossing_count, 9) def test_k33_better(self): source = """ DIGNISSIM: nec sem, nunc, vulputate RHONCUS, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT IMPERDIET: a praesent, nibh, semper SODALES, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT TINCIDUNT: faucibus, orci, cursus QUIS ENIM, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT """ params = parsed_arguments([]) mcd = Mcd(source, params) params.update(mcd.get_layout_data()) d = mcd.get_layout_data() evaluate = fitness(d["links"], d["multiplicity"], d["col_count"], d["row_count"]) size = d["col_count"] * d["row_count"] (crossing_count, total_distances) = evaluate(list(range(size))) self.assertEqual(crossing_count, 3) if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_grid.py ================================================ import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.grid import Grid class GridTests(unittest.TestCase): def test_constructor(self): grid = Grid(16) assert grid == [None, (1, 1), (2, 1), (3, 1), (2, 2), (3, 2), (3, 2), (3, 3), (3, 3), (3, 3), (4, 3), (4, 3), (4, 3), (5, 3), (5, 3), (5, 3), (4, 4)] def test_nth_next(self): grid = Grid(16) assert grid[3] == (3, 1) assert grid.get_nth_next(3, 0) == (3, 1) assert grid.get_nth_next(3, 1) == (2, 2) assert grid.get_nth_next(3, 2) == (3, 2) assert grid.get_nth_next(3, 3) == (3, 3) if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_guess_title.py ================================================ import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.guess_title import guess_title class TestDumps(unittest.TestCase): def test_guess_title(self): # The central entity is the most referenced one source = """ CLIENT: Réf. client, Nom, Prénom, Adresse PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num commande, Date, Montant INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité PRODUIT: Réf. produit, Libellé, Prix unitaire """ actual = guess_title(source, "fr") self.assertEqual(actual, "Commandes") # A more intricate case source = """ AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise PIÈCE: réf. pièce, libellé pièce COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, _11 AYANT-DROIT, 0N EMPLOYÉ EMPLOYÉ: matricule, nom employé PROJET: num. projet, nom projet FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT TRAVAILLER, 0N EMPLOYÉ, 1N PROJET SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N< [filiale] SOCIÉTÉ, 01 [mère] SOCIÉTÉ """ actual = guess_title(source, "fr") self.assertEqual(actual, "Employés") # A case showing that the numeric suffix is ignored source = """ Egestas: vivamus, semper, aliquam Lorem1: ipsum Pharetra, 0N Curabitur, 0N Lorem1, 0N Vitae justo: massa Vitae justo: lobortis, purus Ultricies, 11 Rhoncus, 0N Egestas Imperdiet, 0N Egestas, 0N Curabitur, 0N Lorem1 Curabitur: blandit, suscipit adipiscing, 0N Curabitur, 0N Vitae justo, 0N Lorem2 Rhoncus: dolor a, bibendum, euismod, consectetuer, leo Porttitor, 1N Rhoncus, 0N Lorem2 Lorem2: dolor """ actual = guess_title(source, "fr") self.assertEqual(actual, "Lorems") # When the most referenced entity is a date, # the second most referenced entity is chosen. source = source.replace("Lorem", "Date") actual = guess_title(source, "fr") self.assertEqual(actual, "Curabiturs") if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_inheritance.py ================================================ import gettext import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.inheritance import * from mocodo.tools.parser_tools import extract_clauses gettext.NullTranslations().install() def inheritance_wrapper(s, **kargs): return Inheritance(extract_clauses(s)[0], **kargs) class parse_test(unittest.TestCase): def test_backslash_conservation(self): a = inheritance_wrapper(r"/XT\ FOO => BAR") self.assertTrue(a.bid.startswith("FOO_PARENT")) self.assertEqual(a.name_view, "XT") self.assertEqual(a.legs[0].entity_bid, "FOO") self.assertEqual(a.legs[1].entity_bid, "BAR") self.assertEqual(a.kind, "=>") def test_numbered_inheritance(self): a = inheritance_wrapper(r"/XT1\ FOO => BAR") self.assertTrue(a.bid.startswith("FOO_PARENT")) self.assertEqual(a.name_view, "XT") self.assertEqual(a.legs[0].entity_bid, "FOO") self.assertEqual(a.legs[1].entity_bid, "BAR") self.assertEqual(a.kind, "=>") a = inheritance_wrapper(r"/1\ FOO => BAR") self.assertTrue(a.bid.startswith("FOO_PARENT")) self.assertEqual(a.name_view, "") self.assertEqual(a.legs[0].entity_bid, "FOO") self.assertEqual(a.legs[1].entity_bid, "BAR") self.assertEqual(a.kind, "=>") if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_mcd.py ================================================ import gettext import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.argument_parser import parsed_arguments from mocodo.mcd import * gettext.NullTranslations().install() params = parsed_arguments([]) class McdTest(unittest.TestCase): def test_entity_recognition(self): clauses = [ "PROJET: num. projet, nom projet, budget projet", "PROJET ABC: num. projet, nom projet, budget projet", "PROJET CDE:", ] mcd = Mcd("\n".join(clauses), **params) self.assertEqual(mcd.box_count, len(clauses)) for box in mcd.boxes: self.assertEqual(box.kind, "entity") def test_association_recognition(self): entities = ["FONCTION:", "DEPARTEMENT:", "EMPLOYE:", "PERSONNE:", "ETUDIANT:", "DATE:", "CLIENT:", "COMMANDE:", "BANDIT:", "EMPLOYE_ABC:"] associations = [ "ASSUMER, 1N EMPLOYÉ, 1N FONCTION: date début, date fin", "DIRIGER, 11 DÉPARTEMENT, 01 EMPLOYÉ", "ENGENDRER, 0N [Parent] PERSONNE, 1N [Enfant] PERSONNE", "SOUTENIR, XX ÉTUDIANT, XX DATE: note stage", "DF, 0N CLIENT, 11 COMMANDE", "ÊTRE AMI, 0N BANDIT, 0N BANDIT", "ASSURER2, 1N EMPLOYÉ ABC, 1N FONCTION: date début, date fin", ] clauses = entities + associations mcd = Mcd("\n".join(clauses), **params) self.assertEqual(mcd.box_count, len(clauses)) for box in mcd.boxes: if box.bid + ":" in entities: self.assertEqual(box.kind, "entity") else: self.assertIn(box.kind, ["association", "df"]) def test_constraint_recognition(self): clauses = [ "Lorem: lorem, ipsum", "Ipsum, XX Lorem, XX Dolor", "Dolor: dolor, sit", "Sit, XX Dolor, XX Amet", "Amet: consectetur, adipiscing", "(A) --Lorem, ..Ipsum, Dolor: 30, 90", "(B) ->Dolor, <-->Sit, -->Amet: 69, 90", ] mcd = Mcd("\n".join(clauses), **params) self.assertEqual(mcd.box_count, len(clauses) - 2) self.assertEqual(len(mcd.constraints), 2) for constraint in mcd.constraints: self.assertEqual(constraint.kind, "constraint") def test_rows(self): source = """ BARATTE: piston, racloir, fusil MARTEAU, 0N BARATTE, 11 TINET: ciseaux TINET: fendoir, grattoir CROCHET: égrenoir, _gorgeoir, bouillie DF, 11 BARATTE, 1N ROULEAU BALANCE, 0N ROULEAU, 0N TINET: charrue BANNETON, 01 CROCHET, 11 FLÉAU, 1N TINET: pulvérisateur PORTE, 11 CROCHET, 0N CROCHET ROULEAU: tribulum HERSE, 1N FLÉAU, 1N FLÉAU FLÉAU: battadère, van, mesure """ mcd = Mcd(source, **params) self.assertEqual([element.bid for element in mcd.rows[0]], ["BARATTE", "MARTEAU", "TINET", "CROCHET"]) self.assertEqual([element.bid for element in mcd.rows[1]], ["DF0", "BALANCE", "BANNETON", "PORTE"]) self.assertEqual([element.bid for element in mcd.rows[2]], ['PHANTOM_#1', 'ROULEAU', 'HERSE', 'PHANTOM_#2']) self.assertEqual([element.bid for element in mcd.rows[3]], ['PHANTOM_#3', 'FLEAU', 'PHANTOM_#4', 'PHANTOM_#5']) def test_layout(self): clauses = [ "BARATTE: piston, racloir, fusil", "MARTEAU, 0N BARATTE, 11 TINET: ciseaux", "TINET: fendoir, grattoir", "CROCHET: égrenoir, _gorgeoir, bouillie", "", "DF, 11 BARATTE, 1N ROULEAU", "BALANCE, 0N ROULEAU, 0N TINET: charrue", "BANNETON, 01 CROCHET, 11 FLÉAU, 1N TINET: pulvérisateur", "PORTE, 11 CROCHET, 0N CROCHET", "", "ROULEAU: tribulum", "HERSE, 1N FLÉAU, 1N FLÉAU", "", "FLÉAU: battadère, van, mesure", ] mcd = Mcd("\n".join(clauses), **params) self.assertEqual(mcd.get_layout(), list(range(16))) self.assertEqual(mcd.get_layout_data(), { 'col_count': 4, 'row_count': 4, 'links': ( (0, 1), # from BARATTE to MARTEAU (0, 4), # from BARATTE to DF (1, 2), (2, 5), (2, 6), (3, 6), (3, 7), (4, 9), (5, 9), (6, 13), (10, 13) ), 'multiplicity': { (0, 1): 1, (0, 4): 1, (1, 0): 1, (1, 2): 1, (2, 1): 1, (2, 5): 1, (2, 6): 1, (3, 6): 1, (3, 7): 2, # 2 links between CROCHET and PORTE (4, 0): 1, (4, 9): 1, (5, 2): 1, (5, 9): 1, (6, 2): 1, (6, 3): 1, (6, 13): 1, (7, 3): 2, # 2 links between PORTE and CROCHET (9, 4): 1, (9, 5): 1, (10, 13): 2, (13, 6): 1, (13, 10): 2 }, 'successors': [ {1, 4}, # BARATTE has MARTEAU and DF as successors {0, 2}, {1, 5, 6}, {6, 7}, {0, 9}, {2, 9}, {2, 3, 13}, {3}, # reflexive association PORTE: no multiple edges set(), # phantom {4, 5}, {13}, set(), set(), {6, 10}, set(), set()] } ) expected = """ BARATTE: piston, racloir, fusil MARTEAU, 0N BARATTE, 11 TINET: ciseaux TINET: fendoir, grattoir CROCHET: égrenoir, _gorgeoir, bouillie DF, 11 BARATTE, 1N ROULEAU BALANCE, 0N ROULEAU, 0N TINET: charrue BANNETON, 01 CROCHET, 11 FLÉAU, 1N TINET: pulvérisateur PORTE, 11 CROCHET, 0N CROCHET : ROULEAU: tribulum HERSE, 1N FLÉAU, 1N FLÉAU : : FLÉAU: battadère, van, mesure : : """.strip().replace(" ", "") mcd.set_layout(list(range(16))) self.assertEqual(mcd.get_clauses(), expected) def test_input_errors(self): clauses = [ "PROJET: num. projet, nom projet, budget projet", "ASSUMER, 1N PROJET, 1N INDIVIDU", ] self.assertRaisesRegex(MocodoError, r"Mocodo Err\.1", Mcd, "\n".join(clauses), params) def test_duplicate_errors(self): clauses = [ "DF, 11 BARATTE, 1N ROULEAU", "BARATTE: piston, racloir, fusil", "TINET: fendoir, grattoir", "BALANCE, 0N ROULEAU, 0N TINET: charrue", "BARATTE: tribulum", ] self.assertRaisesRegex(MocodoError, r"Mocodo Err\.6", Mcd, "\n".join(clauses), params) clauses = [ "BARATTE, 11 BARATTE, 1N ROULEAU", "BARATTE: piston, racloir, fusil", "TINET: fendoir, grattoir", "BALANCE, 0N ROULEAU, 0N TINET: charrue", "ROULEAU: tribulum", ] self.assertRaisesRegex(MocodoError, r"Mocodo Err\.8", Mcd, "\n".join(clauses), params) clauses = [ "BARATTE: piston, racloir, fusil", "BARATTE, 11 BARATTE, 1N ROULEAU", "TINET: fendoir, grattoir", "BALANCE, 0N ROULEAU, 0N TINET: charrue", "ROULEAU: tribulum", ] self.assertRaisesRegex(MocodoError, r"Mocodo Err\.8", Mcd, "\n".join(clauses), params) def test_constraint_errors(self): clauses = [ "Lorem: lorem, ipsum", "Ipsum, XX Lorem, XX Lorem", "(A) --Lorem, ..Ipsum, Dolor: 30, 90", ] self.assertRaisesRegex(MocodoError, r"Mocodo Err\.40", Mcd, "\n".join(clauses), params) def test_flip(self): source = """ % The comments are placed before BARATTE: piston, racloir, fusil MARTEAU, 0N BARATTE, 11 TINET: ciseaux TINET: fendoir, grattoir CROCHET: égrenoir, _gorgeoir, bouillie % ... the first line of the file DF, 11 BARATTE, 1N ROULEAU BALANCE, 0N ROULEAU, 0N TINET: charrue BANNETON, 01 CROCHET, 11 FLÉAU, 1N TINET: pulvérisateur PORTE, 11 CROCHET, 0N CROCHET ROULEAU: tribulum HERSE, 1N FLÉAU, 1N FLÉAU FLÉAU: battadère, van, mesure """ mcd = Mcd(source, **params) expected = """ % The comments are placed before % ... the first line of the file : FLÉAU: battadère, van, mesure : : : ROULEAU: tribulum HERSE, 1N FLÉAU, 1N FLÉAU : DF, 11 BARATTE, 1N ROULEAU BALANCE, 0N ROULEAU, 0N TINET: charrue BANNETON, 01 CROCHET, 11 FLÉAU, 1N TINET: pulvérisateur PORTE, 11 CROCHET, 0N CROCHET BARATTE: piston, racloir, fusil MARTEAU, 0N BARATTE, 11 TINET: ciseaux TINET: fendoir, grattoir CROCHET: égrenoir, _gorgeoir, bouillie """.replace(" ", "").strip() actual = mcd.get_vertically_flipped_clauses().replace(" ", "").strip() self.assertEqual(actual, expected) expected = """ % The comments are placed before % ... the first line of the file CROCHET: égrenoir, _gorgeoir, bouillie TINET: fendoir, grattoir MARTEAU, 0N BARATTE, 11 TINET: ciseaux BARATTE: piston, racloir, fusil PORTE, 11 CROCHET, 0N CROCHET BANNETON, 01 CROCHET, 11 FLÉAU, 1N TINET: pulvérisateur BALANCE, 0N ROULEAU, 0N TINET: charrue DF, 11 BARATTE, 1N ROULEAU : HERSE, 1N FLÉAU, 1N FLÉAU ROULEAU: tribulum : : : FLÉAU: battadère, van, mesure : """.strip().replace(" ", "") actual = mcd.get_horizontally_flipped_clauses().replace(" ", "").strip() self.assertEqual(actual, expected) expected = """ % The comments are placed before % ... the first line of the file BARATTE: piston, racloir, fusil DF, 11 BARATTE, 1N ROULEAU : : MARTEAU, 0N BARATTE, 11 TINET: ciseaux BALANCE, 0N ROULEAU, 0N TINET: charrue ROULEAU: tribulum FLÉAU: battadère, van, mesure TINET: fendoir, grattoir BANNETON, 01 CROCHET, 11 FLÉAU, 1N TINET: pulvérisateur HERSE, 1N FLÉAU, 1N FLÉAU : CROCHET: égrenoir, _gorgeoir, bouillie PORTE, 11 CROCHET, 0N CROCHET : : """.strip().replace(" ", "") actual = mcd.get_diagonally_flipped_clauses().replace(" ", "").strip() self.assertEqual(actual, expected) def test_explicit_fit(self): # initially: (5, 4) for 11 nodes source = """ Item: Norm, Wash, Haul Milk, 0N Item, 0N Draw Draw: Lady, Face, Soon, Dish, Ever Unit, 1N Draw, 11 Folk: Peer, Tour, Folk: Hall, Fold, Baby, Bind, Gene, Aids, Free Pack, 1N Folk, 1N Seem Seem: Teen, Amid Disk, 0N Flip, 1N Seem Flip : Gold, Ride Call: Ride, Soon Gear , 1N Call, 1N Folk """ mcd = Mcd(source, **params) # minimal fit: (4, 3) expected = """ Item: Norm, Wash, Haul Milk, 0N Item, 0N Draw Draw: Lady, Face, Soon, Dish, Ever Unit, 1N Draw, 11 Folk: Peer, Tour, Folk: Hall, Fold, Baby, Bind, Gene, Aids, Free Pack, 1N Folk, 1N Seem Seem: Teen, Amid Disk, 0N Flip, 1N Seem Flip : Gold, Ride Call: Ride, Soon Gear , 1N Call, 1N Folk : """.strip().replace(" ", "") actual = mcd.get_refitted_clauses(0).strip().replace(" ", "") self.assertEqual(actual, expected) # 1st next fit: (5, 3) expected = """ Item: Norm, Wash, Haul Milk, 0N Item, 0N Draw Draw: Lady, Face, Soon, Dish, Ever Unit, 1N Draw, 11 Folk: Peer, Tour, Folk: Hall, Fold, Baby, Bind, Gene, Aids, Free Pack, 1N Folk, 1N Seem Seem: Teen, Amid Disk, 0N Flip, 1N Seem Flip : Gold, Ride Call: Ride, Soon Gear , 1N Call, 1N Folk : : : : """.strip().replace(" ", "") actual = mcd.get_refitted_clauses(1).strip().replace(" ", "") self.assertEqual(actual, expected) # 2nd next fit: (4, 4) expected = """ Item: Norm, Wash, Haul Milk, 0N Item, 0N Draw Draw: Lady, Face, Soon, Dish, Ever Unit, 1N Draw, 11 Folk: Peer, Tour, Folk: Hall, Fold, Baby, Bind, Gene, Aids, Free Pack, 1N Folk, 1N Seem Seem: Teen, Amid Disk, 0N Flip, 1N Seem Flip : Gold, Ride Call: Ride, Soon Gear , 1N Call, 1N Folk : : : : : """.strip().replace(" ", "") actual = mcd.get_refitted_clauses(2).strip().replace(" ", "") self.assertEqual(actual, expected) def test_implicit_fit_produces_min_grid_next(self): # initially: (4, 5) for 11 nodes source = """ Item: Norm, Wash, Haul Milk, 0N Item, 0N Draw Draw: Lady, Face, Soon, Dish, Ever Unit, 1N Draw, 11 Folk: Peer, Tour, Folk: Hall, Fold, Baby, Bind, Gene, Aids, Free Pack, 1N Folk, 1N Seem Seem: Teen, Amid Disk, 0N Flip, 1N Seem Flip : Gold, Ride Call: Ride, Soon Gear , 1N Call, 1N Folk """ mcd = Mcd(source, **params) # (4, 5) not being a preferred grid, it is equivalent to nth_fit == 1 expected = """ Item: Norm, Wash, Haul Milk, 0N Item, 0N Draw Draw: Lady, Face, Soon, Dish, Ever Unit, 1N Draw, 11 Folk: Peer, Tour, Folk: Hall, Fold, Baby, Bind, Gene, Aids, Free Pack, 1N Folk, 1N Seem Seem: Teen, Amid Disk, 0N Flip, 1N Seem Flip : Gold, Ride Call: Ride, Soon Gear , 1N Call, 1N Folk : : : : """.strip().replace(" ", "") actual = mcd.get_refitted_clauses(1).strip().replace(" ", "") self.assertEqual(actual, expected) def test_no_overlapping(self): source = """ CLIENT: Réf. client, Nom, Prénom, Adresse PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num commande, Date, Montant INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité PRODUIT: Réf. produit, Libellé, Prix unitaire """.replace(" ", "") mcd = Mcd(source, **params) self.assertEqual(mcd.get_overlaps(), []) def test_no_overlapping_with_reflexive_associations(self): source = """ : : A MÈRE, 01 ANIMAL, 0N> [mère] ANIMAL : : DF, 0N ESPÈCE, _11 ANIMAL ANIMAL: nom, sexe, date naissance, date décès A PÈRE, 0N ANIMAL, 0N> [père présumé] ANIMAL PEUT COHABITER AVEC, 0N ESPÈCE, 0N [commensale] ESPÈCE: nb. max. commensaux ESPÈCE: code espèce, libellé OCCUPE, 1N ANIMAL, /1N PÉRIODE, 1N ENCLOS PÉRIODE: date début, _date fin : PEUT VIVRE DANS, 1N ESPÈCE, 1N ENCLOS: nb. max. congénères ENCLOS: num. enclos : """.replace(" ", "") mcd = Mcd(source, **params) self.assertEqual(mcd.get_overlaps(), []) def test_horizontal_legs_overlap(self): source = """ CLIENT: Réf. client, Nom, Prénom, Adresse COMMANDE: Num commande, Date, Montant PRODUIT: Réf. produit, Libellé, Prix unitaire PASSER, 0N CLIENT, 11 COMMANDE INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité """.replace( " ", "" ) mcd = Mcd(source, **params) self.assertEqual( mcd.get_overlaps(), [ ("PASSER", "CLIENT", "COMMANDE", "COMMANDE"), ("PASSER", "COMMANDE", "INCLURE", "COMMANDE"), ("INCLURE", "COMMANDE", "PRODUIT", "PRODUIT"), ("INCLURE", "PRODUIT", "PASSER", "PASSER"), ], ) def test_vertical_legs_overlap(self): source = """ CLIENT: Réf. client, Nom, Prénom, Adresse\n COMMANDE: Num commande, Date, Montant\n PRODUIT: Réf. produit, Libellé, Prix unitaire\n PASSER, 0N CLIENT, 11 COMMANDE\n INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité """ mcd = Mcd(source, **params) self.assertEqual( mcd.get_overlaps(), [ ("PASSER", "CLIENT", "COMMANDE", "COMMANDE"), ("PASSER", "COMMANDE", "INCLURE", "COMMANDE"), ("INCLURE", "COMMANDE", "PRODUIT", "PRODUIT"), ("INCLURE", "PRODUIT", "PASSER", "PASSER"), ], ) def test_leg_overlaps_entity(self): source = """ COMMANDE: Num commande, Date, Montant PRODUIT: Réf. produit, Libellé, Prix unitaire PASSER, 0N CLIENT, 11 COMMANDE CLIENT: Réf. client, Nom, Prénom, Adresse : INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité ::: """ mcd = Mcd(source, **params) self.assertEqual( mcd.get_overlaps(), [('PASSER', 'COMMANDE', 'PRODUIT', 'PRODUIT')], ) def test_leg_overlaps_association(self): source = """ PRODUIT: Réf. produit, Libellé, Prix unitaire COMMANDE: Num commande, Date, Montant PASSER, 0N CLIENT, 11 COMMANDE CLIENT: Réf. client, Nom, Prénom, Adresse INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité """ mcd = Mcd(source, **params) self.assertEqual( mcd.get_overlaps(), [('INCLURE', 'PRODUIT', 'PASSER', 'PASSER')], ) if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_mcd_dimensions.py ================================================ import gettext import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.argument_parser import parsed_arguments from mocodo.mcd import * gettext.NullTranslations().install() params = parsed_arguments([]) style = { "cartouche_text_height_ratio" : 0.85, "card_text_height_ratio" : 0.85, "df_text_height_ratio" : 1.1, "attribute_text_height_ratio" : 0.65, "line_skip_height" : 0, "card_baseline" : 3, "card_underline_skip_height" : -2, "underline_skip_height" : -3, "underline_depth" : 1, "card_underline_depth" : 1, "dash_width" : 4, "margin" : 9, "box_stroke_depth" : 1.5, "inner_stroke_depth" : 1.5, "rect_margin_width" : 8, "rect_margin_height" : 6, "round_rect_margin_width" : 7, "round_rect_margin_height" : 5, "round_corner_radius" : 14, "leg_stroke_depth" : 1, "card_margin" : 5, "arrow_width" : 12, "arrow_half_height" : 6, "arrow_axis" : 8, "note_overlay_height" : 40, "note_baseline" : 24, "entity_cartouche_font" : None, "association_cartouche_font" : None, "entity_attribute_font" : None, "association_attribute_font" : None, "card_font" : None, "label_font" : None, "note_font" : None } def stub_for_get_font_metrics(s): stub_for_get_font_metrics.get_pixel_width = lambda s: 5 * len(s) stub_for_get_font_metrics.get_pixel_height = lambda: 10 return stub_for_get_font_metrics def get_dimensions(mcd, verbose=False): mcd.calculate_size(style) result = [] log = ["["] for box in mcd.boxes: log.append("{'bid': %s, " % repr(box.bid)) d = {"bid": box.bid} for key in "xywh": d[key] = getattr(box, key) log.append("'%s': %s, " % (key, d[key])) log[-1] = log[-1][:-2] log.append("},\n") result.append(d) log[-1] = log[-1][:-2] log.append("]") if verbose: print("".join(log)) return result class McdGeometryTest(unittest.TestCase): def test_simplest_mcd(self): clauses = [ "My entity: first, second", ] mcd = Mcd("\n".join(clauses), stub_for_get_font_metrics, **params) self.assertEqual(get_dimensions(mcd), [{'bid': 'MY_ENTITY', 'x': 9, 'y': 9, 'w': 62, 'h': 54}]) def test_read_me_mcd(self): clauses = [ "DF, 11 Élève, 1N Classe", "Classe: Num. classe, Num. salle", "Faire Cours, 1N Classe, 1N Prof: Vol. horaire", "Catégorie: Code catégorie, Nom catégorie", "", "Élève: Num. élève, Nom élève", "Noter, 1N Élève, 0N Prof, 0N Matière, 1N Date: Note", "Prof: Num. prof, Nom prof", "Relever, 0N Catégorie, 11 Prof", "", "Date: Date", "Matière: Libellé matière", "Enseigner, 11 Prof, 1N Matière", ] mcd = Mcd("\n".join(clauses), stub_for_get_font_metrics, **params) self.assertEqual(get_dimensions(mcd), [ {'bid': 'DF0', 'x': 30, 'y': 24, 'w': 24, 'h': 24}, {'bid': 'CLASSE', 'x': 95, 'y': 9, 'w': 72, 'h': 54}, {'bid': 'FAIRE_COURS', 'x': 195, 'y': 15, 'w': 74, 'h': 42}, {'bid': 'CATEGORIE', 'x': 294, 'y': 9, 'w': 86, 'h': 54}, {'bid': 'ELEVE', 'x': 9, 'y': 83, 'w': 66, 'h': 54}, {'bid': 'NOTER', 'x': 111, 'y': 89, 'w': 40, 'h': 42}, {'bid': 'PROF', 'x': 201, 'y': 83, 'w': 62, 'h': 54}, {'bid': 'RELEVER', 'x': 312, 'y': 89, 'w': 50, 'h': 42}, {'bid': 'DATE', 'x': 24, 'y': 157, 'w': 36, 'h': 44}, {'bid': 'MATIERE', 'x': 85, 'y': 157, 'w': 92, 'h': 44}, {'bid': 'ENSEIGNER', 'x': 202, 'y': 158, 'w': 60, 'h': 42}, {'bid': 'PHANTOM_#1', 'x': 337, 'y': 179, 'w': 0, 'h': 0} ]) def test_mocodo_online_mcd(self): clauses = [ "PEUT VIVRE DANS, 1N ESPÈCE, 1N ENCLOS: nb. max. congénères", "ENCLOS: num. enclos", "OCCUPE, 1N ANIMAL, 1N PÉRIODE, 1N ENCLOS", "PÉRIODE: date début, _date fin", "", "ESPÈCE: code espèce, libellé", "DF, 0N ESPÈCE, _11 ANIMAL", "ANIMAL: nom, sexe, date naissance, date décès", "A MÈRE, 01 ANIMAL, 0N> [mère] ANIMAL", "", "PEUT COHABITER AVEC, 0N ESPÈCE, 0N [commensale] ESPÈCE: nb. max. commensaux", ":", "A PÈRE, 0N ANIMAL, 0N> [père présumé] ANIMAL", "-INVISIBLE:" ] mcd = Mcd("\n".join(clauses), stub_for_get_font_metrics, **params) self.assertEqual(get_dimensions(mcd), [ {'bid': 'PEUT_VIVRE_DANS', 'x': 9, 'y': 15, 'w': 110, 'h': 42}, {'bid': 'ENCLOS', 'x': 144, 'y': 14, 'w': 72, 'h': 44}, {'bid': 'OCCUPE', 'x': 241, 'y': 15, 'w': 44, 'h': 42}, {'bid': 'PERIODE', 'x': 320, 'y': 9, 'w': 66, 'h': 54}, {'bid': 'ESPECE', 'x': 28, 'y': 93, 'w': 72, 'h': 54}, {'bid': 'DF0', 'x': 168, 'y': 108, 'w': 24, 'h': 24}, {'bid': 'ANIMAL', 'x': 220, 'y': 83, 'w': 86, 'h': 74}, {'bid': 'A_MERE', 'x': 331, 'y': 99, 'w': 44, 'h': 42}, {'bid': 'PEUT_COHABITER_AVEC', 'x': 9, 'y': 177, 'w': 110, 'h': 42}, {'bid': 'PHANTOM_#1', 'x': 180, 'y': 198, 'w': 0, 'h': 0}, {'bid': 'A_PERE', 'x': 241, 'y': 177, 'w': 44, 'h': 42}, {'bid': 'INVISIBLE', 'x': 353, 'y': 198, 'w': 0, 'h': 0} ]) if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_parser_tools.py ================================================ from pathlib import Path import re import gettext import unittest import pprint __import__("sys").path[0:0] = ["mocodo"] from mocodo.parse_mcd import Lark_StandAlone, UnexpectedToken from mocodo.tools.parser_tools import reconstruct_source, parse_source, extract_clauses from mocodo.mocodo_error import MocodoError gettext.NullTranslations().install() parser = Lark_StandAlone() valid_lines = r""" : ::: : : : : : : % commented % commented % % foo\\ , % foo /TX\\ AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, _11 AYANT-DROIT, 0N EMPLOYÉ FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie CONTRÔLER, 0N< [filiale] SOCIÉTÉ, 01 [mère] SOCIÉTÉ DF, 0N> CLIENT, 11 COMMANDE GRATTE-CIEL: latitude, _longitude, nom, hauteur, année de construction Peut recevoir, 1N> Groupe sanguin, 1N< Groupe sanguin Engendre, 0N< Personne, 22> Personne Agent 0070: bar DF4, 11 Agent 0070, 1N Agent1 Agent1: bar DF, _11 ŒUVRE, _11 EXEMPLAIRE Réserver, /1N Client, 1N Chambre, 0N Date: Durée LIGULA, 0N LACUS, /1N EROS, 0N TELLUS, 0N CONSEQUAT: metus Réserver: _Durée +LIGULA, 01 LACUS, 1N EROS: metus -LIGULA, 01 LACUS, 1N EROS: metus PASSER, 0N [Un client peut passer un nombre quelconque de commandes.] CLIENT, 11 [Toute commande est passée par un en un seul client.] COMMANDE INCLURE, 1N [Une commande peut inclure plusieurs produits distincts, et en inclut au moins un.] COMMANDE, 0N [Certains produits ne sont jamais commandés, d'autres le sont plusieurs fois.] PRODUIT: Quantité CLIENT: CLIENT: , ,, COMMANDE: , , PRODUIT: , , MEAN: wash, rest, king, HERE, 0N NICE, 0N MEAN: wood, much, , stop NICE: _poke, news, , lawn Unit, 1N Draw, 11 Folk: Peer, Tour, PASSER, XX CLIENT, XX COMMANDE INCLURE, XX COMMANDE, XX PRODUIT: ŒUVRE: 612.NAT.34, J'apprends à lire à mes souris blanches, mai 1975 EXEMPLAIRE2: 1, bon état, 12/6/1975 EXEMPLAIRE3: 2, bon état, 1/8/1977 EXEMPLAIRE4: 3, reliure rongée, 3/4/2005 DF, -1N ŒUVRE, -_11 EXEMPLAIRE1 +Prof: Num. prof, Nom prof -Prof: Num. prof, Nom prof Enseignant: num. ens. [numéro identifiant un enseignant], nom ens. [nom enseignant], tél. ens. [téléphone enseignant] COMMANDE: Num commande, Date, Montant, #Réf. client>CLIENT>Réf. client COMMANDE: Num commande, Date, Montant, #Réf. client > CLIENT > Réf. client INCLURE: #Num commande>COMMANDE>Num commande, _#Réf. produit>PRODUIT>Réf. produit, Quantité INCLURE: #Num commande > COMMANDE > Num commande, _#Réf. produit > PRODUIT > Réf. produit, Quantité CLIENT: Réf. client [varchar(8)], Nom [varchar(20)], Adresse [varchar(40)] INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité [tinyint(4)] PARTICIPANT: numero [], nom, adresse [type3] COMMANDE: Num commande, Date, Montant, #Réf. client!>CLIENT>Réf. client COMMANDE: Num commande, Date, Montant, Réf. client! L33T, 0N> H4X0R, 0N< H4X0R L33T123, 0N> H4X0R12, 0N< H4X0R0 AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise DIRIGER, EMPLOYÉ, PROJET A, B, C Foo, Bar DIRIGER, EMPLOYÉ, PROJET: biz, buz A, B, C: biz, buz Foo, Bar: biz, buz AYANT-DROIT : nom ayant-droit , lien DIRIGER , 0N EMPLOYÉ , 01 PROJET : fizz, buzz AYANT-DROIT  :  nom ayant-droit  ,  lien  DIRIGER  ,  0N  EMPLOYÉ  ,  01  PROJET  : fizz, buzz  AYANT-DROIT : nom ayant-droit , lien DIRIGER , 0N EMPLOYÉ , 01 PROJET : fizz, buzz () [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET (II) ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET (III) [bla bla.] (IV) (]) (+) (/) (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, 30 (II) ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, 30 (III) [bla bla.]: 12.5, 30 (IV) : 12.5, 30 (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: FOO, 30 (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, BAR (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: FOO, BAR (IV) : 12.5, 30 (A) --Lorem, .....Ipsum, -Dolor: 30, 10 (B) ->Dolor, <-->Sit, -->Amet: 69, 10 (XX) ->foo, -->foo, -> foo, --> foo (I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer /XT\ Personne ==> Homme, Femme: sexe /XT1\\ Personne <= Homme, Femme: sexe /XT\\ Personne => Homme, Femme: sexe /T\\ Personne <= Homme, Femme: sexe /T\\ Personne => Homme, Femme: sexe /X\\ Personne <= Homme, Femme: sexe /X\\ Personne => Homme, Femme: sexe /\\ Personne <= Homme, Femme: sexe /\\ Personne => Homme, Femme: sexe /1\\ FOO => BAR /T\\foo==>foo11 Étudiant: num, 1_nom, 1_prénom, adresse, 2_mail Étudiant: 0_num, 1_nom, 1_prénom, adresse, 2_mail Position: 0_latitude, 0_longitude, altitude Foo: bar, 1_baz, 21_qux, 123_quux Réserver, 1N Client, 0N Chambre: _date, durée """.splitlines() line = "-" * 80 path = Path("test/test_parser_tools_snapshot.txt") with path.open(mode="w") as file: for source in valid_lines: file.write(f"{line}\n{source}\n") tree = parse_source(source).pretty() file.write(f"{line}{tree}".strip()) file.write(f"\n{line}\n") clauses = extract_clauses(source) output = pprint.pformat(clauses, sort_dicts=False) file.write(f"{output}\n\n") def fuzzer(seed=0): import random random.seed(seed) for _ in range(50): (left, right) = random.sample(valid_lines, 2) source = f"{left[:random.randint(0, len(left))]}{right[random.randint(0, len(right)):]}" try: tree = parse_source(source) print(source) except Exception as error: print(source) print(error) input() # fuzzer(4) mocodo_errors = [ *[(501, c) for c in "0123456789!#$&')*,.;<=>?@[\\]^_`{|}~"], *[(501, f" {c}") for c in "0123456789!#$&')*,.;<=>?@[\\]^_`{|}~"], (501, ": foobar"), (501, " : foobar"), (502, "AYANT-: nom ay, lien"), (502, "FOO, 0N Bar, 1N Biz [bla]"), (502, "FOO, 0N Bar, 1N Biz [bla], 0N Buz"), (502, " foo /TX\\# ] #/ "), (502, "BACK\\SLASH:"), (502, " foo_11<11_ ]<-->]foo"), (503, "FOOBAR"), (503, " FOOBAR>"), (503, " FOOBAR >"), (503, " FOOBAR+"), (505, "/T\\, foobar"), (506, "DIRIGER, 0 EMPLOYÉ, 01 PROJET"), (506, "DIRIGER, 0 EMPLOYÉ, 01 PROJET: biz, buz"), (506, "DIRIGER, 0NV"), (507, "/ANYTHING\\ Personne => Homme, Femme: sexe"), (507, "/X12\\ Personne => Homme, Femme: sexe"), (507, "/1N\\"), (508, "(I) : 1, 2, 3"), (508, "(I) : 1, 2,"), (509, "DIRIGER,"), (509, "DIRIGER, "), (510, "(I) : "), (510, "(I) ->Foo ..Bar : "), (510, "(I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, "), (510, "(I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: FOOBAR, "), (510, "(A) : Ipsum, --Lorem: 30, 90"), (511, " +"), (511, " +# "), (511, "/T\\ \\.. ,\\foo"), (511, "+..==>"), (511, " /TX\\ foo -> .bar"), (512, "PARTICIPANT: numero [, nom, adresse"), (514, "(IIII)"), (514, "( )"), (514, "(    )"), (515, "(A) --Lorem, >Ipsum: 30, 90"), (515, "(A) --Lorem, : Ipsum: 30, 90"), (515, "(A) --Lorem 30, 90"), (516, "(A) >Ipsum, --Lorem: 30, 90"), (516, "())"), (516, "((I))"), (517, "/TX\\ foo bar "), (518, "COMMANDE: Num commande, Date, Montant, #Réf. client->CLIENT->Réf. client"), (518, "INCLURE: #Num commande->COMMANDE->Num commande, _#Réf. produit->PRODUIT->Réf. produit, Quantité"), (519, "(A) ->Ipsum ->Lorem: 30, 90"), (500, "FOOBAR: foo, /bar"), (500, "FOO, 1N Bar: -->Amet"), (521, "FOOBAR: foo>bar, biz"), (522, "FOO: #bar, biz"), (522, "FOO: #bar!, biz"), (522, "FOO: #bar"), (522, "FOO: #bar!"), (522, "FOO: biz, #bar"), (522, "FOO: #bar>buzz, biz"), (522, "FOO: #bar>buzz!, biz"), (522, "FOO: #bar!>buzz, biz"), (522, "FOO: #bar>buzz"), (522, "FOO: biz, #bar>buzz"), (523, "FOO: #bar>buzz>, biz"), (523, "FOO: #bar>buzz>"), (523, "FOO: biz, #bar>buzz> "), (524, "DIRIGER, 0N EMPLOYÉ > PRODUIT, Quantité"), (525, "(I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5"), (526, "(A) ->Ipsum, ->Lorem: 30, 9N"), (527, "(I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 3,14 3,14"), (528, "FOO: bar!?, biz"), ] class MocodoErrorTest(unittest.TestCase): def test_mocodo_errors(self): for n, source in mocodo_errors: try: parse_source(source) except MocodoError as e: actual_error_number = re.search(r"\d+", str(e)).group() assert actual_error_number == str(n), f"Expected error {n} for:\n{source}" except Exception as e: print(source) pin = e.get_context(source) print(f"{line}\n{source}\n{line}\n{pin}") if isinstance(e, UnexpectedToken): print(f'Unexpected token "{repr(e.token)}" at line {e.line}, column {e.column}.') print(f"Expected: {set(e.expected)}.\n") assert False else: assert False, f"\n\nExpected Mocodo Error {n} for:\n{source}" class TestReconstructSource(unittest.TestCase): def test_reconstruct_source(self): for source in valid_lines: if not source: continue tree = parse_source(source) new_source = reconstruct_source(tree) self.assertEqual(source.strip(), new_source.strip()) def test_alignment(self): source = """ AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise :: % comment PIÈCE: réf. pièce, libellé pièce (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET """ clauses = extract_clauses(source) for raw_line, clause in zip(source.splitlines(), clauses): self.assertEqual(clause["source"].lstrip(), raw_line.lstrip()) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_parser_tools_snapshot.txt ================================================ -------------------------------------------------------------------------------- --------------------------------------------------------------------------------start break_ -------------------------------------------------------------------------------- [{'type': 'break'}] -------------------------------------------------------------------------------- : --------------------------------------------------------------------------------start line phantoms : -------------------------------------------------------------------------------- [{'type': 'phantoms', 'count': 1, 'indent': '', 'source': ':'}] -------------------------------------------------------------------------------- ::: --------------------------------------------------------------------------------start line phantoms ::: -------------------------------------------------------------------------------- [{'type': 'phantoms', 'count': 3, 'indent': '', 'source': ':::'}] -------------------------------------------------------------------------------- : : : --------------------------------------------------------------------------------start line phantoms : : : -------------------------------------------------------------------------------- [{'type': 'phantoms', 'count': 3, 'indent': '', 'source': ': : :'}] -------------------------------------------------------------------------------- : : : --------------------------------------------------------------------------------start line indent phantoms : : : -------------------------------------------------------------------------------- [{'type': 'phantoms', 'indent': ' ', 'count': 3, 'source': ' : : :'}] -------------------------------------------------------------------------------- % commented --------------------------------------------------------------------------------start line indent comment % commented -------------------------------------------------------------------------------- [{'type': 'comment', 'indent': ' ', 'text': '% commented', 'source': ' % commented'}] -------------------------------------------------------------------------------- % commented % --------------------------------------------------------------------------------start line comment % commented % -------------------------------------------------------------------------------- [{'type': 'comment', 'text': '% commented %', 'indent': '', 'source': '% commented %'}] -------------------------------------------------------------------------------- % foo\\ , % foo /TX\\ --------------------------------------------------------------------------------start line indent comment % foo\\ , % foo /TX\\ -------------------------------------------------------------------------------- [{'type': 'comment', 'indent': ' ', 'text': '% foo\\\\ , % foo /TX\\\\', 'source': ' % foo\\\\ , % foo /TX\\\\'}] -------------------------------------------------------------------------------- AYANT-DROIT: nom ayant-droit, lien --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name AYANT-DROIT : seq entity_or_table_attr typed_attr attr nom ayant-droit , entity_or_table_attr typed_attr attr lien -------------------------------------------------------------------------------- [{'name': 'AYANT-DROIT', 'attrs': [{'attribute_label': 'nom ayant-droit', 'rank': 0}, {'attribute_label': 'lien', 'rank': 1}], 'type': 'entity', 'indent': '', 'source': 'AYANT-DROIT: nom ayant-droit, lien'}] -------------------------------------------------------------------------------- DIRIGER, 0N EMPLOYÉ, 01 PROJET --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DIRIGER , seq assoc_leg card 0N entity_name_ref box_name EMPLOYÉ , assoc_leg card 01 entity_name_ref box_name PROJET -------------------------------------------------------------------------------- [{'name': 'DIRIGER', 'legs': [{'card': '0N', 'entity': 'EMPLOYÉ', 'rank': 0}, {'card': '01', 'entity': 'PROJET', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DIRIGER, 0N EMPLOYÉ, 01 PROJET'}] -------------------------------------------------------------------------------- REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name REQUÉRIR , seq assoc_leg card 1N entity_name_ref box_name PROJET , assoc_leg card 0N entity_name_ref box_name PIÈCE : seq assoc_attr typed_attr attr qté requise -------------------------------------------------------------------------------- [{'name': 'REQUÉRIR', 'legs': [{'card': '1N', 'entity': 'PROJET', 'rank': 0}, {'card': '0N', 'entity': 'PIÈCE', 'rank': 1}], 'attrs': [{'attribute_label': 'qté requise', 'rank': 0}], 'type': 'association', 'indent': '', 'source': 'REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise'}] -------------------------------------------------------------------------------- COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name COMPOSER , seq assoc_leg card 0N [ leg_note composée ] entity_name_ref box_name PIÈCE , assoc_leg card 0N [ leg_note composante ] entity_name_ref box_name PIÈCE : seq assoc_attr typed_attr attr quantité -------------------------------------------------------------------------------- [{'name': 'COMPOSER', 'legs': [{'card': '0N', 'leg_note': 'composée', 'entity': 'PIÈCE', 'rank': 0}, {'card': '0N', 'leg_note': 'composante', 'entity': 'PIÈCE', 'rank': 1}], 'attrs': [{'attribute_label': 'quantité', 'rank': 0}], 'type': 'association', 'indent': '', 'source': 'COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité'}] -------------------------------------------------------------------------------- DF, _11 AYANT-DROIT, 0N EMPLOYÉ --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DF , seq assoc_leg card_prefix _ card 11 entity_name_ref box_name AYANT-DROIT , assoc_leg card 0N entity_name_ref box_name EMPLOYÉ -------------------------------------------------------------------------------- [{'name': 'DF', 'legs': [{'card_prefix': '_', 'card': '11', 'entity': 'AYANT-DROIT', 'rank': 0}, {'card': '0N', 'entity': 'EMPLOYÉ', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DF, _11 AYANT-DROIT, 0N EMPLOYÉ'}] -------------------------------------------------------------------------------- FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name FOURNIR , seq assoc_leg card 1N entity_name_ref box_name PROJET , assoc_leg card 1N entity_name_ref box_name PIÈCE , assoc_leg card 1N entity_name_ref box_name SOCIÉTÉ : seq assoc_attr typed_attr attr qté fournie -------------------------------------------------------------------------------- [{'name': 'FOURNIR', 'legs': [{'card': '1N', 'entity': 'PROJET', 'rank': 0}, {'card': '1N', 'entity': 'PIÈCE', 'rank': 1}, {'card': '1N', 'entity': 'SOCIÉTÉ', 'rank': 2}], 'attrs': [{'attribute_label': 'qté fournie', 'rank': 0}], 'type': 'association', 'indent': '', 'source': 'FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie'}] -------------------------------------------------------------------------------- CONTRÔLER, 0N< [filiale] SOCIÉTÉ, 01 [mère] SOCIÉTÉ --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name CONTRÔLER , seq assoc_leg card 0N leg_arrow < [ leg_note filiale ] entity_name_ref box_name SOCIÉTÉ , assoc_leg card 01 [ leg_note mère ] entity_name_ref box_name SOCIÉTÉ -------------------------------------------------------------------------------- [{'name': 'CONTRÔLER', 'legs': [{'card': '0N', 'leg_arrow': '<', 'leg_note': 'filiale', 'entity': 'SOCIÉTÉ', 'rank': 0}, {'card': '01', 'leg_note': 'mère', 'entity': 'SOCIÉTÉ', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'CONTRÔLER, 0N< [filiale] SOCIÉTÉ, 01 [mère] SOCIÉTÉ'}] -------------------------------------------------------------------------------- DF, 0N> CLIENT, 11 COMMANDE --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DF , seq assoc_leg card 0N leg_arrow > entity_name_ref box_name CLIENT , assoc_leg card 11 entity_name_ref box_name COMMANDE -------------------------------------------------------------------------------- [{'name': 'DF', 'legs': [{'card': '0N', 'leg_arrow': '>', 'entity': 'CLIENT', 'rank': 0}, {'card': '11', 'entity': 'COMMANDE', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DF, 0N> CLIENT, 11 COMMANDE'}] -------------------------------------------------------------------------------- GRATTE-CIEL: latitude, _longitude, nom, hauteur, année de construction --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name GRATTE-CIEL : seq entity_or_table_attr typed_attr attr latitude , entity_or_table_attr id_mark _ typed_attr attr longitude , entity_or_table_attr typed_attr attr nom , entity_or_table_attr typed_attr attr hauteur , entity_or_table_attr typed_attr attr année de construction -------------------------------------------------------------------------------- [{'name': 'GRATTE-CIEL', 'attrs': [{'attribute_label': 'latitude', 'rank': 0}, {'id_mark': '_', 'attribute_label': 'longitude', 'rank': 1}, {'attribute_label': 'nom', 'rank': 2}, {'attribute_label': 'hauteur', 'rank': 3}, {'attribute_label': 'année de construction', 'rank': 4}], 'type': 'entity', 'indent': '', 'source': 'GRATTE-CIEL: latitude, _longitude, nom, hauteur, année de ' 'construction'}] -------------------------------------------------------------------------------- Peut recevoir, 1N> Groupe sanguin, 1N< Groupe sanguin --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name Peut recevoir , seq assoc_leg card 1N leg_arrow > entity_name_ref box_name Groupe sanguin , assoc_leg card 1N leg_arrow < entity_name_ref box_name Groupe sanguin -------------------------------------------------------------------------------- [{'name': 'Peut recevoir', 'legs': [{'card': '1N', 'leg_arrow': '>', 'entity': 'Groupe sanguin', 'rank': 0}, {'card': '1N', 'leg_arrow': '<', 'entity': 'Groupe sanguin', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'Peut recevoir, 1N> Groupe sanguin, 1N< Groupe sanguin'}] -------------------------------------------------------------------------------- Engendre, 0N< Personne, 22> Personne --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name Engendre , seq assoc_leg card 0N leg_arrow < entity_name_ref box_name Personne , assoc_leg card 22 leg_arrow > entity_name_ref box_name Personne -------------------------------------------------------------------------------- [{'name': 'Engendre', 'legs': [{'card': '0N', 'leg_arrow': '<', 'entity': 'Personne', 'rank': 0}, {'card': '22', 'leg_arrow': '>', 'entity': 'Personne', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'Engendre, 0N< Personne, 22> Personne'}] -------------------------------------------------------------------------------- Agent 0070: bar --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name Agent 0070 : seq entity_or_table_attr typed_attr attr bar -------------------------------------------------------------------------------- [{'name': 'Agent 0070', 'attrs': [{'attribute_label': 'bar', 'rank': 0}], 'type': 'entity', 'indent': '', 'source': 'Agent 0070: bar'}] -------------------------------------------------------------------------------- DF4, 11 Agent 0070, 1N Agent1 --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DF4 , seq assoc_leg card 11 entity_name_ref box_name Agent 0070 , assoc_leg card 1N entity_name_ref box_name Agent1 -------------------------------------------------------------------------------- [{'name': 'DF4', 'legs': [{'card': '11', 'entity': 'Agent 0070', 'rank': 0}, {'card': '1N', 'entity': 'Agent1', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DF4, 11 Agent 0070, 1N Agent1'}] -------------------------------------------------------------------------------- Agent1: bar --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name Agent1 : seq entity_or_table_attr typed_attr attr bar -------------------------------------------------------------------------------- [{'name': 'Agent1', 'attrs': [{'attribute_label': 'bar', 'rank': 0}], 'type': 'entity', 'indent': '', 'source': 'Agent1: bar'}] -------------------------------------------------------------------------------- DF, _11 ŒUVRE, _11 EXEMPLAIRE --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DF , seq assoc_leg card_prefix _ card 11 entity_name_ref box_name ŒUVRE , assoc_leg card_prefix _ card 11 entity_name_ref box_name EXEMPLAIRE -------------------------------------------------------------------------------- [{'name': 'DF', 'legs': [{'card_prefix': '_', 'card': '11', 'entity': 'ŒUVRE', 'rank': 0}, {'card_prefix': '_', 'card': '11', 'entity': 'EXEMPLAIRE', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DF, _11 ŒUVRE, _11 EXEMPLAIRE'}] -------------------------------------------------------------------------------- Réserver, /1N Client, 1N Chambre, 0N Date: Durée --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name Réserver , seq assoc_leg card_prefix / card 1N entity_name_ref box_name Client , assoc_leg card 1N entity_name_ref box_name Chambre , assoc_leg card 0N entity_name_ref box_name Date : seq assoc_attr typed_attr attr Durée -------------------------------------------------------------------------------- [{'name': 'Réserver', 'legs': [{'card_prefix': '/', 'card': '1N', 'entity': 'Client', 'rank': 0}, {'card': '1N', 'entity': 'Chambre', 'rank': 1}, {'card': '0N', 'entity': 'Date', 'rank': 2}], 'attrs': [{'attribute_label': 'Durée', 'rank': 0}], 'type': 'association', 'indent': '', 'source': 'Réserver, /1N Client, 1N Chambre, 0N Date: Durée'}] -------------------------------------------------------------------------------- LIGULA, 0N LACUS, /1N EROS, 0N TELLUS, 0N CONSEQUAT: metus --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name LIGULA , seq assoc_leg card 0N entity_name_ref box_name LACUS , assoc_leg card_prefix / card 1N entity_name_ref box_name EROS , assoc_leg card 0N entity_name_ref box_name TELLUS , assoc_leg card 0N entity_name_ref box_name CONSEQUAT : seq assoc_attr typed_attr attr metus -------------------------------------------------------------------------------- [{'name': 'LIGULA', 'legs': [{'card': '0N', 'entity': 'LACUS', 'rank': 0}, {'card_prefix': '/', 'card': '1N', 'entity': 'EROS', 'rank': 1}, {'card': '0N', 'entity': 'TELLUS', 'rank': 2}, {'card': '0N', 'entity': 'CONSEQUAT', 'rank': 3}], 'attrs': [{'attribute_label': 'metus', 'rank': 0}], 'type': 'association', 'indent': '', 'source': 'LIGULA, 0N LACUS, /1N EROS, 0N TELLUS, 0N CONSEQUAT: metus'}] -------------------------------------------------------------------------------- Réserver: _Durée --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name Réserver : seq entity_or_table_attr id_mark _ typed_attr attr Durée -------------------------------------------------------------------------------- [{'name': 'Réserver', 'attrs': [{'id_mark': '_', 'attribute_label': 'Durée', 'rank': 0}], 'type': 'entity', 'indent': '', 'source': 'Réserver: _Durée'}] -------------------------------------------------------------------------------- +LIGULA, 01 LACUS, 1N EROS: metus --------------------------------------------------------------------------------start line assoc_clause box_def_prefix + assoc_name_def box_name LIGULA , seq assoc_leg card 01 entity_name_ref box_name LACUS , assoc_leg card 1N entity_name_ref box_name EROS : seq assoc_attr typed_attr attr metus -------------------------------------------------------------------------------- [{'box_def_prefix': '+', 'name': 'LIGULA', 'legs': [{'card': '01', 'entity': 'LACUS', 'rank': 0}, {'card': '1N', 'entity': 'EROS', 'rank': 1}], 'attrs': [{'attribute_label': 'metus', 'rank': 0}], 'type': 'association', 'indent': '', 'source': '+LIGULA, 01 LACUS, 1N EROS: metus'}] -------------------------------------------------------------------------------- -LIGULA, 01 LACUS, 1N EROS: metus --------------------------------------------------------------------------------start line assoc_clause box_def_prefix - assoc_name_def box_name LIGULA , seq assoc_leg card 01 entity_name_ref box_name LACUS , assoc_leg card 1N entity_name_ref box_name EROS : seq assoc_attr typed_attr attr metus -------------------------------------------------------------------------------- [{'box_def_prefix': '-', 'name': 'LIGULA', 'legs': [{'card': '01', 'entity': 'LACUS', 'rank': 0}, {'card': '1N', 'entity': 'EROS', 'rank': 1}], 'attrs': [{'attribute_label': 'metus', 'rank': 0}], 'type': 'association', 'indent': '', 'source': '-LIGULA, 01 LACUS, 1N EROS: metus'}] -------------------------------------------------------------------------------- PASSER, 0N [Un client peut passer un nombre quelconque de commandes.] CLIENT, 11 [Toute commande est passée par un en un seul client.] COMMANDE --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name PASSER , seq assoc_leg card 0N [ leg_note Un client peut passer un nombre quelconque de commandes. ] entity_name_ref box_name CLIENT , assoc_leg card 11 [ leg_note Toute commande est passée par un en un seul client. ] entity_name_ref box_name COMMANDE -------------------------------------------------------------------------------- [{'name': 'PASSER', 'legs': [{'card': '0N', 'leg_note': 'Un client peut passer un nombre quelconque de ' 'commandes.', 'entity': 'CLIENT', 'rank': 0}, {'card': '11', 'leg_note': 'Toute commande est passée par un en un seul client.', 'entity': 'COMMANDE', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'PASSER, 0N [Un client peut passer un nombre quelconque de ' 'commandes.] CLIENT, 11 [Toute commande est passée par un en un ' 'seul client.] COMMANDE'}] -------------------------------------------------------------------------------- INCLURE, 1N [Une commande peut inclure plusieurs produits distincts, et en inclut au moins un.] COMMANDE, 0N [Certains produits ne sont jamais commandés, d'autres le sont plusieurs fois.] PRODUIT: Quantité --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name INCLURE , seq assoc_leg card 1N [ leg_note Une commande peut inclure plusieurs produits distincts, et en inclut au moins un. ] entity_name_ref box_name COMMANDE , assoc_leg card 0N [ leg_note Certains produits ne sont jamais commandés, d'autres le sont plusieurs fois. ] entity_name_ref box_name PRODUIT : seq assoc_attr typed_attr attr Quantité -------------------------------------------------------------------------------- [{'name': 'INCLURE', 'legs': [{'card': '1N', 'leg_note': 'Une commande peut inclure plusieurs produits ' 'distincts, et en inclut au moins un.', 'entity': 'COMMANDE', 'rank': 0}, {'card': '0N', 'leg_note': "Certains produits ne sont jamais commandés, d'autres " 'le sont plusieurs fois.', 'entity': 'PRODUIT', 'rank': 1}], 'attrs': [{'attribute_label': 'Quantité', 'rank': 0}], 'type': 'association', 'indent': '', 'source': 'INCLURE, 1N [Une commande peut inclure plusieurs produits ' 'distincts, et en inclut au moins un.] COMMANDE, 0N [Certains ' "produits ne sont jamais commandés, d'autres le sont plusieurs " 'fois.] PRODUIT: Quantité'}] -------------------------------------------------------------------------------- CLIENT: --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name CLIENT : -------------------------------------------------------------------------------- [{'name': 'CLIENT', 'type': 'entity', 'indent': '', 'source': 'CLIENT: '}] -------------------------------------------------------------------------------- CLIENT: , ,, --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name CLIENT : seq entity_or_table_attr typed_attr , entity_or_table_attr typed_attr , entity_or_table_attr typed_attr , entity_or_table_attr typed_attr -------------------------------------------------------------------------------- [{'name': 'CLIENT', 'attrs': [{'rank': 0}, {'rank': 1}, {'rank': 2}, {'rank': 3}], 'type': 'entity', 'indent': '', 'source': 'CLIENT: , ,,'}] -------------------------------------------------------------------------------- COMMANDE: , , --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name COMMANDE : seq entity_or_table_attr typed_attr , entity_or_table_attr typed_attr , entity_or_table_attr typed_attr -------------------------------------------------------------------------------- [{'name': 'COMMANDE', 'attrs': [{'rank': 0}, {'rank': 1}, {'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'COMMANDE: , ,'}] -------------------------------------------------------------------------------- PRODUIT: , , --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name PRODUIT : seq entity_or_table_attr typed_attr , entity_or_table_attr typed_attr , entity_or_table_attr typed_attr -------------------------------------------------------------------------------- [{'name': 'PRODUIT', 'attrs': [{'rank': 0}, {'rank': 1}, {'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'PRODUIT: , , '}] -------------------------------------------------------------------------------- MEAN: wash, rest, king, --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name MEAN : seq entity_or_table_attr typed_attr attr wash , entity_or_table_attr typed_attr attr rest , entity_or_table_attr typed_attr attr king , entity_or_table_attr typed_attr -------------------------------------------------------------------------------- [{'name': 'MEAN', 'attrs': [{'attribute_label': 'wash', 'rank': 0}, {'attribute_label': 'rest', 'rank': 1}, {'attribute_label': 'king', 'rank': 2}, {'rank': 3}], 'type': 'entity', 'indent': '', 'source': 'MEAN: wash, rest, king,'}] -------------------------------------------------------------------------------- HERE, 0N NICE, 0N MEAN: wood, much, , stop --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name HERE , seq assoc_leg card 0N entity_name_ref box_name NICE , assoc_leg card 0N entity_name_ref box_name MEAN : seq assoc_attr typed_attr attr wood , assoc_attr typed_attr attr much , assoc_attr typed_attr , assoc_attr typed_attr attr stop -------------------------------------------------------------------------------- [{'name': 'HERE', 'legs': [{'card': '0N', 'entity': 'NICE', 'rank': 0}, {'card': '0N', 'entity': 'MEAN', 'rank': 1}], 'attrs': [{'attribute_label': 'wood', 'rank': 0}, {'attribute_label': 'much', 'rank': 1}, {'rank': 2}, {'attribute_label': 'stop', 'rank': 3}], 'type': 'association', 'indent': '', 'source': 'HERE, 0N NICE, 0N MEAN: wood, much, , stop'}] -------------------------------------------------------------------------------- NICE: _poke, news, , lawn --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name NICE : seq entity_or_table_attr id_mark _ typed_attr attr poke , entity_or_table_attr typed_attr attr news , entity_or_table_attr typed_attr , entity_or_table_attr typed_attr attr lawn -------------------------------------------------------------------------------- [{'name': 'NICE', 'attrs': [{'id_mark': '_', 'attribute_label': 'poke', 'rank': 0}, {'attribute_label': 'news', 'rank': 1}, {'rank': 2}, {'attribute_label': 'lawn', 'rank': 3}], 'type': 'entity', 'indent': '', 'source': 'NICE: _poke, news, , lawn'}] -------------------------------------------------------------------------------- Unit, 1N Draw, 11 Folk: Peer, Tour, --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name Unit , seq assoc_leg card 1N entity_name_ref box_name Draw , assoc_leg card 11 entity_name_ref box_name Folk : seq assoc_attr typed_attr attr Peer , assoc_attr typed_attr attr Tour , assoc_attr typed_attr -------------------------------------------------------------------------------- [{'name': 'Unit', 'legs': [{'card': '1N', 'entity': 'Draw', 'rank': 0}, {'card': '11', 'entity': 'Folk', 'rank': 1}], 'attrs': [{'attribute_label': 'Peer', 'rank': 0}, {'attribute_label': 'Tour', 'rank': 1}, {'rank': 2}], 'type': 'association', 'indent': '', 'source': 'Unit, 1N Draw, 11 Folk: Peer, Tour, '}] -------------------------------------------------------------------------------- PASSER, XX CLIENT, XX COMMANDE --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name PASSER , seq assoc_leg card XX entity_name_ref box_name CLIENT , assoc_leg card XX entity_name_ref box_name COMMANDE -------------------------------------------------------------------------------- [{'name': 'PASSER', 'legs': [{'card': 'XX', 'entity': 'CLIENT', 'rank': 0}, {'card': 'XX', 'entity': 'COMMANDE', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'PASSER, XX CLIENT, XX COMMANDE'}] -------------------------------------------------------------------------------- INCLURE, XX COMMANDE, XX PRODUIT: --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name INCLURE , seq assoc_leg card XX entity_name_ref box_name COMMANDE , assoc_leg card XX entity_name_ref box_name PRODUIT : seq assoc_attr typed_attr -------------------------------------------------------------------------------- [{'name': 'INCLURE', 'legs': [{'card': 'XX', 'entity': 'COMMANDE', 'rank': 0}, {'card': 'XX', 'entity': 'PRODUIT', 'rank': 1}], 'attrs': [{'rank': 0}], 'type': 'association', 'indent': '', 'source': 'INCLURE, XX COMMANDE, XX PRODUIT: '}] -------------------------------------------------------------------------------- ŒUVRE: 612.NAT.34, J'apprends à lire à mes souris blanches, mai 1975 --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name ŒUVRE : seq entity_or_table_attr typed_attr attr 612.NAT.34 , entity_or_table_attr typed_attr attr J'apprends à lire à mes souris blanches , entity_or_table_attr typed_attr attr mai 1975 -------------------------------------------------------------------------------- [{'name': 'ŒUVRE', 'attrs': [{'attribute_label': '612.NAT.34', 'rank': 0}, {'attribute_label': "J'apprends à lire à mes souris blanches", 'rank': 1}, {'attribute_label': 'mai 1975', 'rank': 2}], 'type': 'entity', 'indent': '', 'source': "ŒUVRE: 612.NAT.34, J'apprends à lire à mes souris blanches, mai " '1975'}] -------------------------------------------------------------------------------- EXEMPLAIRE2: 1, bon état, 12/6/1975 --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name EXEMPLAIRE2 : seq entity_or_table_attr typed_attr attr 1 , entity_or_table_attr typed_attr attr bon état , entity_or_table_attr typed_attr attr 12/6/1975 -------------------------------------------------------------------------------- [{'name': 'EXEMPLAIRE2', 'attrs': [{'attribute_label': '1', 'rank': 0}, {'attribute_label': 'bon état', 'rank': 1}, {'attribute_label': '12/6/1975', 'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'EXEMPLAIRE2: 1, bon état, 12/6/1975'}] -------------------------------------------------------------------------------- EXEMPLAIRE3: 2, bon état, 1/8/1977 --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name EXEMPLAIRE3 : seq entity_or_table_attr typed_attr attr 2 , entity_or_table_attr typed_attr attr bon état , entity_or_table_attr typed_attr attr 1/8/1977 -------------------------------------------------------------------------------- [{'name': 'EXEMPLAIRE3', 'attrs': [{'attribute_label': '2', 'rank': 0}, {'attribute_label': 'bon état', 'rank': 1}, {'attribute_label': '1/8/1977', 'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'EXEMPLAIRE3: 2, bon état, 1/8/1977'}] -------------------------------------------------------------------------------- EXEMPLAIRE4: 3, reliure rongée, 3/4/2005 --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name EXEMPLAIRE4 : seq entity_or_table_attr typed_attr attr 3 , entity_or_table_attr typed_attr attr reliure rongée , entity_or_table_attr typed_attr attr 3/4/2005 -------------------------------------------------------------------------------- [{'name': 'EXEMPLAIRE4', 'attrs': [{'attribute_label': '3', 'rank': 0}, {'attribute_label': 'reliure rongée', 'rank': 1}, {'attribute_label': '3/4/2005', 'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'EXEMPLAIRE4: 3, reliure rongée, 3/4/2005'}] -------------------------------------------------------------------------------- DF, -1N ŒUVRE, -_11 EXEMPLAIRE1 --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DF , seq assoc_leg card_hidden - card 1N entity_name_ref box_name ŒUVRE , assoc_leg card_hidden - card_prefix _ card 11 entity_name_ref box_name EXEMPLAIRE1 -------------------------------------------------------------------------------- [{'name': 'DF', 'legs': [{'card_hidden': '-', 'card': '1N', 'entity': 'ŒUVRE', 'rank': 0}, {'card_hidden': '-', 'card_prefix': '_', 'card': '11', 'entity': 'EXEMPLAIRE1', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DF, -1N ŒUVRE, -_11 EXEMPLAIRE1'}] -------------------------------------------------------------------------------- +Prof: Num. prof, Nom prof --------------------------------------------------------------------------------start line entity_clause box_def_prefix + entity_name_def box_name Prof : seq entity_or_table_attr typed_attr attr Num. prof , entity_or_table_attr typed_attr attr Nom prof -------------------------------------------------------------------------------- [{'box_def_prefix': '+', 'name': 'Prof', 'attrs': [{'attribute_label': 'Num. prof', 'rank': 0}, {'attribute_label': 'Nom prof', 'rank': 1}], 'type': 'entity', 'indent': '', 'source': '+Prof: Num. prof, Nom prof'}] -------------------------------------------------------------------------------- -Prof: Num. prof, Nom prof --------------------------------------------------------------------------------start line entity_clause box_def_prefix - entity_name_def box_name Prof : seq entity_or_table_attr typed_attr attr Num. prof , entity_or_table_attr typed_attr attr Nom prof -------------------------------------------------------------------------------- [{'box_def_prefix': '-', 'name': 'Prof', 'attrs': [{'attribute_label': 'Num. prof', 'rank': 0}, {'attribute_label': 'Nom prof', 'rank': 1}], 'type': 'entity', 'indent': '', 'source': '-Prof: Num. prof, Nom prof'}] -------------------------------------------------------------------------------- Enseignant: num. ens. [numéro identifiant un enseignant], nom ens. [nom enseignant], tél. ens. [téléphone enseignant] --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name Enseignant : seq entity_or_table_attr typed_attr attr num. ens. [ datatype numéro identifiant un enseignant ] , entity_or_table_attr typed_attr attr nom ens. [ datatype nom enseignant ] , entity_or_table_attr typed_attr attr tél. ens. [ datatype téléphone enseignant ] -------------------------------------------------------------------------------- [{'name': 'Enseignant', 'attrs': [{'attribute_label': 'num. ens.', 'datatype': 'numéro identifiant un enseignant', 'rank': 0}, {'attribute_label': 'nom ens.', 'datatype': 'nom enseignant', 'rank': 1}, {'attribute_label': 'tél. ens.', 'datatype': 'téléphone enseignant', 'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'Enseignant: num. ens. [numéro identifiant un enseignant], nom ' 'ens. [nom enseignant], tél. ens. [téléphone enseignant]'}] -------------------------------------------------------------------------------- COMMANDE: Num commande, Date, Montant, #Réf. client>CLIENT>Réf. client --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name COMMANDE : seq entity_or_table_attr typed_attr attr Num commande , entity_or_table_attr typed_attr attr Date , entity_or_table_attr typed_attr attr Montant , entity_or_table_attr # foreign_reference this_table_attr attr Réf. client > that_table entity_name_ref box_name CLIENT > that_table_attr attr Réf. client -------------------------------------------------------------------------------- [{'name': 'COMMANDE', 'attrs': [{'attribute_label': 'Num commande', 'rank': 0}, {'attribute_label': 'Date', 'rank': 1}, {'attribute_label': 'Montant', 'rank': 2}, {'attribute_label': 'Réf. client', 'that_table': 'CLIENT', 'that_table_attribute_label': 'Réf. client', 'rank': 3}], 'type': 'entity', 'indent': '', 'source': 'COMMANDE: Num commande, Date, Montant, #Réf. client>CLIENT>Réf. ' 'client'}] -------------------------------------------------------------------------------- COMMANDE: Num commande, Date, Montant, #Réf. client > CLIENT > Réf. client --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name COMMANDE : seq entity_or_table_attr typed_attr attr Num commande , entity_or_table_attr typed_attr attr Date , entity_or_table_attr typed_attr attr Montant , entity_or_table_attr # foreign_reference this_table_attr attr Réf. client > that_table entity_name_ref box_name CLIENT > that_table_attr attr Réf. client -------------------------------------------------------------------------------- [{'name': 'COMMANDE', 'attrs': [{'attribute_label': 'Num commande', 'rank': 0}, {'attribute_label': 'Date', 'rank': 1}, {'attribute_label': 'Montant', 'rank': 2}, {'attribute_label': 'Réf. client', 'that_table': 'CLIENT', 'that_table_attribute_label': 'Réf. client', 'rank': 3}], 'type': 'entity', 'indent': '', 'source': 'COMMANDE: Num commande, Date, Montant, #Réf. client > CLIENT > ' 'Réf. client'}] -------------------------------------------------------------------------------- INCLURE: #Num commande>COMMANDE>Num commande, _#Réf. produit>PRODUIT>Réf. produit, Quantité --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name INCLURE : seq entity_or_table_attr # foreign_reference this_table_attr attr Num commande > that_table entity_name_ref box_name COMMANDE > that_table_attr attr Num commande , entity_or_table_attr id_mark _ # foreign_reference this_table_attr attr Réf. produit > that_table entity_name_ref box_name PRODUIT > that_table_attr attr Réf. produit , entity_or_table_attr typed_attr attr Quantité -------------------------------------------------------------------------------- [{'name': 'INCLURE', 'attrs': [{'attribute_label': 'Num commande', 'that_table': 'COMMANDE', 'that_table_attribute_label': 'Num commande', 'rank': 0}, {'id_mark': '_', 'attribute_label': 'Réf. produit', 'that_table': 'PRODUIT', 'that_table_attribute_label': 'Réf. produit', 'rank': 1}, {'attribute_label': 'Quantité', 'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'INCLURE: #Num commande>COMMANDE>Num commande, _#Réf. ' 'produit>PRODUIT>Réf. produit, Quantité'}] -------------------------------------------------------------------------------- INCLURE: #Num commande > COMMANDE > Num commande, _#Réf. produit > PRODUIT > Réf. produit, Quantité --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name INCLURE : seq entity_or_table_attr # foreign_reference this_table_attr attr Num commande > that_table entity_name_ref box_name COMMANDE > that_table_attr attr Num commande , entity_or_table_attr id_mark _ # foreign_reference this_table_attr attr Réf. produit > that_table entity_name_ref box_name PRODUIT > that_table_attr attr Réf. produit , entity_or_table_attr typed_attr attr Quantité -------------------------------------------------------------------------------- [{'name': 'INCLURE', 'attrs': [{'attribute_label': 'Num commande', 'that_table': 'COMMANDE', 'that_table_attribute_label': 'Num commande', 'rank': 0}, {'id_mark': '_', 'attribute_label': 'Réf. produit', 'that_table': 'PRODUIT', 'that_table_attribute_label': 'Réf. produit', 'rank': 1}, {'attribute_label': 'Quantité', 'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'INCLURE: #Num commande > COMMANDE > Num commande, _#Réf. produit ' '> PRODUIT > Réf. produit, Quantité'}] -------------------------------------------------------------------------------- CLIENT: Réf. client [varchar(8)], Nom [varchar(20)], Adresse [varchar(40)] --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name CLIENT : seq entity_or_table_attr typed_attr attr Réf. client [ datatype varchar(8) ] , entity_or_table_attr typed_attr attr Nom [ datatype varchar(20) ] , entity_or_table_attr typed_attr attr Adresse [ datatype varchar(40) ] -------------------------------------------------------------------------------- [{'name': 'CLIENT', 'attrs': [{'attribute_label': 'Réf. client', 'datatype': 'varchar(8)', 'rank': 0}, {'attribute_label': 'Nom', 'datatype': 'varchar(20)', 'rank': 1}, {'attribute_label': 'Adresse', 'datatype': 'varchar(40)', 'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'CLIENT: Réf. client [varchar(8)], Nom [varchar(20)], Adresse ' '[varchar(40)]'}] -------------------------------------------------------------------------------- INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité [tinyint(4)] --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name INCLURE , seq assoc_leg card 1N entity_name_ref box_name COMMANDE , assoc_leg card 0N entity_name_ref box_name PRODUIT : seq assoc_attr typed_attr attr Quantité [ datatype tinyint(4) ] -------------------------------------------------------------------------------- [{'name': 'INCLURE', 'legs': [{'card': '1N', 'entity': 'COMMANDE', 'rank': 0}, {'card': '0N', 'entity': 'PRODUIT', 'rank': 1}], 'attrs': [{'attribute_label': 'Quantité', 'datatype': 'tinyint(4)', 'rank': 0}], 'type': 'association', 'indent': '', 'source': 'INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité [tinyint(4)]'}] -------------------------------------------------------------------------------- PARTICIPANT: numero [], nom, adresse [type3] --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name PARTICIPANT : seq entity_or_table_attr typed_attr attr numero [ ] , entity_or_table_attr typed_attr attr nom , entity_or_table_attr typed_attr attr adresse [ datatype type3 ] -------------------------------------------------------------------------------- [{'name': 'PARTICIPANT', 'attrs': [{'attribute_label': 'numero', 'rank': 0}, {'attribute_label': 'nom', 'rank': 1}, {'attribute_label': 'adresse', 'datatype': 'type3', 'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'PARTICIPANT: numero [], nom, adresse [type3]'}] -------------------------------------------------------------------------------- COMMANDE: Num commande, Date, Montant, #Réf. client!>CLIENT>Réf. client --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name COMMANDE : seq entity_or_table_attr typed_attr attr Num commande , entity_or_table_attr typed_attr attr Date , entity_or_table_attr typed_attr attr Montant , entity_or_table_attr # foreign_reference this_table_attr attr Réf. client! > that_table entity_name_ref box_name CLIENT > that_table_attr attr Réf. client -------------------------------------------------------------------------------- [{'name': 'COMMANDE', 'attrs': [{'attribute_label': 'Num commande', 'rank': 0}, {'attribute_label': 'Date', 'rank': 1}, {'attribute_label': 'Montant', 'rank': 2}, {'attribute_label': 'Réf. client!', 'that_table': 'CLIENT', 'that_table_attribute_label': 'Réf. client', 'rank': 3}], 'type': 'entity', 'indent': '', 'source': 'COMMANDE: Num commande, Date, Montant, #Réf. client!>CLIENT>Réf. ' 'client'}] -------------------------------------------------------------------------------- COMMANDE: Num commande, Date, Montant, Réf. client! --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name COMMANDE : seq entity_or_table_attr typed_attr attr Num commande , entity_or_table_attr typed_attr attr Date , entity_or_table_attr typed_attr attr Montant , entity_or_table_attr typed_attr attr Réf. client! -------------------------------------------------------------------------------- [{'name': 'COMMANDE', 'attrs': [{'attribute_label': 'Num commande', 'rank': 0}, {'attribute_label': 'Date', 'rank': 1}, {'attribute_label': 'Montant', 'rank': 2}, {'attribute_label': 'Réf. client!', 'rank': 3}], 'type': 'entity', 'indent': '', 'source': 'COMMANDE: Num commande, Date, Montant, Réf. client!'}] -------------------------------------------------------------------------------- L33T, 0N> H4X0R, 0N< H4X0R --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name L33T , seq assoc_leg card 0N leg_arrow > entity_name_ref box_name H4X0R , assoc_leg card 0N leg_arrow < entity_name_ref box_name H4X0R -------------------------------------------------------------------------------- [{'name': 'L33T', 'legs': [{'card': '0N', 'leg_arrow': '>', 'entity': 'H4X0R', 'rank': 0}, {'card': '0N', 'leg_arrow': '<', 'entity': 'H4X0R', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'L33T, 0N> H4X0R, 0N< H4X0R'}] -------------------------------------------------------------------------------- L33T123, 0N> H4X0R12, 0N< H4X0R0 --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name L33T123 , seq assoc_leg card 0N leg_arrow > entity_name_ref box_name H4X0R12 , assoc_leg card 0N leg_arrow < entity_name_ref box_name H4X0R0 -------------------------------------------------------------------------------- [{'name': 'L33T123', 'legs': [{'card': '0N', 'leg_arrow': '>', 'entity': 'H4X0R12', 'rank': 0}, {'card': '0N', 'leg_arrow': '<', 'entity': 'H4X0R0', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'L33T123, 0N> H4X0R12, 0N< H4X0R0'}] -------------------------------------------------------------------------------- AYANT-DROIT: nom ayant-droit, lien --------------------------------------------------------------------------------start line indent entity_clause entity_name_def box_name AYANT-DROIT : seq entity_or_table_attr typed_attr attr nom ayant-droit , entity_or_table_attr typed_attr attr lien -------------------------------------------------------------------------------- [{'type': 'entity', 'indent': ' ', 'name': 'AYANT-DROIT', 'attrs': [{'attribute_label': 'nom ayant-droit', 'rank': 0}, {'attribute_label': 'lien', 'rank': 1}], 'source': ' AYANT-DROIT: nom ayant-droit, lien'}] -------------------------------------------------------------------------------- DIRIGER, 0N EMPLOYÉ, 01 PROJET --------------------------------------------------------------------------------start line indent assoc_clause assoc_name_def box_name DIRIGER , seq assoc_leg card 0N entity_name_ref box_name EMPLOYÉ , assoc_leg card 01 entity_name_ref box_name PROJET -------------------------------------------------------------------------------- [{'type': 'association', 'indent': ' ', 'name': 'DIRIGER', 'legs': [{'card': '0N', 'entity': 'EMPLOYÉ', 'rank': 0}, {'card': '01', 'entity': 'PROJET', 'rank': 1}], 'source': ' DIRIGER, 0N EMPLOYÉ, 01 PROJET'}] -------------------------------------------------------------------------------- REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise --------------------------------------------------------------------------------start line indent assoc_clause assoc_name_def box_name REQUÉRIR , seq assoc_leg card 1N entity_name_ref box_name PROJET , assoc_leg card 0N entity_name_ref box_name PIÈCE : seq assoc_attr typed_attr attr qté requise -------------------------------------------------------------------------------- [{'type': 'association', 'indent': ' ', 'name': 'REQUÉRIR', 'legs': [{'card': '1N', 'entity': 'PROJET', 'rank': 0}, {'card': '0N', 'entity': 'PIÈCE', 'rank': 1}], 'attrs': [{'attribute_label': 'qté requise', 'rank': 0}], 'source': ' REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise'}] -------------------------------------------------------------------------------- DIRIGER, EMPLOYÉ, PROJET --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DIRIGER , seq assoc_leg entity_name_ref box_name EMPLOYÉ , assoc_leg entity_name_ref box_name PROJET -------------------------------------------------------------------------------- [{'name': 'DIRIGER', 'legs': [{'entity': 'EMPLOYÉ', 'rank': 0}, {'entity': 'PROJET', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DIRIGER, EMPLOYÉ, PROJET'}] -------------------------------------------------------------------------------- A, B, C --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name A , seq assoc_leg entity_name_ref box_name B , assoc_leg entity_name_ref box_name C -------------------------------------------------------------------------------- [{'name': 'A', 'legs': [{'entity': 'B', 'rank': 0}, {'entity': 'C', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'A, B, C'}] -------------------------------------------------------------------------------- Foo, Bar --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name Foo , seq assoc_leg entity_name_ref box_name Bar -------------------------------------------------------------------------------- [{'name': 'Foo', 'legs': [{'entity': 'Bar', 'rank': 0}], 'type': 'association', 'indent': '', 'source': 'Foo, Bar'}] -------------------------------------------------------------------------------- DIRIGER, EMPLOYÉ, PROJET: biz, buz --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DIRIGER , seq assoc_leg entity_name_ref box_name EMPLOYÉ , assoc_leg entity_name_ref box_name PROJET : seq assoc_attr typed_attr attr biz , assoc_attr typed_attr attr buz -------------------------------------------------------------------------------- [{'name': 'DIRIGER', 'legs': [{'entity': 'EMPLOYÉ', 'rank': 0}, {'entity': 'PROJET', 'rank': 1}], 'attrs': [{'attribute_label': 'biz', 'rank': 0}, {'attribute_label': 'buz', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DIRIGER, EMPLOYÉ, PROJET: biz, buz'}] -------------------------------------------------------------------------------- A, B, C: biz, buz --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name A , seq assoc_leg entity_name_ref box_name B , assoc_leg entity_name_ref box_name C : seq assoc_attr typed_attr attr biz , assoc_attr typed_attr attr buz -------------------------------------------------------------------------------- [{'name': 'A', 'legs': [{'entity': 'B', 'rank': 0}, {'entity': 'C', 'rank': 1}], 'attrs': [{'attribute_label': 'biz', 'rank': 0}, {'attribute_label': 'buz', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'A, B, C: biz, buz'}] -------------------------------------------------------------------------------- Foo, Bar: biz, buz --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name Foo , seq assoc_leg entity_name_ref box_name Bar : seq assoc_attr typed_attr attr biz , assoc_attr typed_attr attr buz -------------------------------------------------------------------------------- [{'name': 'Foo', 'legs': [{'entity': 'Bar', 'rank': 0}], 'attrs': [{'attribute_label': 'biz', 'rank': 0}, {'attribute_label': 'buz', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'Foo, Bar: biz, buz'}] -------------------------------------------------------------------------------- AYANT-DROIT : nom ayant-droit , lien --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name AYANT-DROIT : seq entity_or_table_attr typed_attr attr nom ayant-droit , entity_or_table_attr typed_attr attr lien -------------------------------------------------------------------------------- [{'name': 'AYANT-DROIT', 'attrs': [{'attribute_label': 'nom ayant-droit', 'rank': 0}, {'attribute_label': 'lien', 'rank': 1}], 'type': 'entity', 'indent': '', 'source': 'AYANT-DROIT : nom ayant-droit , lien '}] -------------------------------------------------------------------------------- DIRIGER , 0N EMPLOYÉ , 01 PROJET : fizz, buzz --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DIRIGER , seq assoc_leg card 0N entity_name_ref box_name EMPLOYÉ , assoc_leg card 01 entity_name_ref box_name PROJET : seq assoc_attr typed_attr attr fizz , assoc_attr typed_attr attr buzz -------------------------------------------------------------------------------- [{'name': 'DIRIGER', 'legs': [{'card': '0N', 'entity': 'EMPLOYÉ', 'rank': 0}, {'card': '01', 'entity': 'PROJET', 'rank': 1}], 'attrs': [{'attribute_label': 'fizz', 'rank': 0}, {'attribute_label': 'buzz', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DIRIGER , 0N EMPLOYÉ , 01 PROJET : fizz, ' 'buzz '}] -------------------------------------------------------------------------------- AYANT-DROIT  :  nom ayant-droit  ,  lien  --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name AYANT-DROIT   :  seq entity_or_table_attr typed_attr attr nom ayant-droit   ,  entity_or_table_attr typed_attr attr lien -------------------------------------------------------------------------------- [{'name': 'AYANT-DROIT', 'attrs': [{'attribute_label': 'nom ayant-droit', 'rank': 0}, {'attribute_label': 'lien', 'rank': 1}], 'type': 'entity', 'indent': '', 'source': 'AYANT-DROIT\xa0 :\xa0 nom ayant-droit\xa0 ,\xa0 lien\xa0 '}] -------------------------------------------------------------------------------- DIRIGER  ,  0N  EMPLOYÉ  ,  01  PROJET  : fizz, buzz  --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DIRIGER   ,   seq assoc_leg card 0N    entity_name_ref box_name EMPLOYÉ   ,   assoc_leg card 01    entity_name_ref box_name PROJET   : seq assoc_attr typed_attr attr fizz ,  assoc_attr typed_attr attr buzz -------------------------------------------------------------------------------- [{'name': 'DIRIGER', 'legs': [{'card': '0N', 'entity': 'EMPLOYÉ', 'rank': 0}, {'card': '01', 'entity': 'PROJET', 'rank': 1}], 'attrs': [{'attribute_label': 'fizz', 'rank': 0}, {'attribute_label': 'buzz', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DIRIGER\xa0 ,\xa0\xa00N\xa0\xa0EMPLOYÉ\xa0 ,\xa0\xa001\xa0\xa0' 'PROJET\xa0 : fizz,\xa0buzz\xa0 '}] -------------------------------------------------------------------------------- AYANT-DROIT : nom ayant-droit , lien --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name AYANT-DROIT : seq entity_or_table_attr typed_attr attr nom ayant-droit , entity_or_table_attr typed_attr attr lien -------------------------------------------------------------------------------- [{'name': 'AYANT-DROIT', 'attrs': [{'attribute_label': 'nom ayant-droit', 'rank': 0}, {'attribute_label': 'lien', 'rank': 1}], 'type': 'entity', 'indent': '', 'source': 'AYANT-DROIT\t :\t nom ayant-droit\t ,\t lien\t '}] -------------------------------------------------------------------------------- DIRIGER , 0N EMPLOYÉ , 01 PROJET : fizz, buzz --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name DIRIGER , seq assoc_leg card 0N entity_name_ref box_name EMPLOYÉ , assoc_leg card 01 entity_name_ref box_name PROJET : seq assoc_attr typed_attr attr fizz , assoc_attr typed_attr attr buzz -------------------------------------------------------------------------------- [{'name': 'DIRIGER', 'legs': [{'card': '0N', 'entity': 'EMPLOYÉ', 'rank': 0}, {'card': '01', 'entity': 'PROJET', 'rank': 1}], 'attrs': [{'attribute_label': 'fizz', 'rank': 0}, {'attribute_label': 'buzz', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'DIRIGER\t ,\t\t0N\t\tEMPLOYÉ\t ,\t\t01\t\tPROJET\t : fizz,\t' 'buzz\t '}] -------------------------------------------------------------------------------- () [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET --------------------------------------------------------------------------------start line constraint_clause ( ) [ constraint_note bla bla. ] seq constraint_target constraint_leg .. box_name_ref box_name PIÈCE , constraint_target constraint_leg -> box_name_ref box_name REQUÉRIR , constraint_target constraint_leg -- box_name_ref box_name FOURNIR , constraint_target box_name_ref box_name PROJET -------------------------------------------------------------------------------- [{'constraint_note': 'bla bla.', 'constraint_targets': [{'constraint_leg': '..', 'name': 'PIÈCE', 'rank': 0}, {'constraint_leg': '->', 'name': 'REQUÉRIR', 'rank': 1}, {'constraint_leg': '--', 'name': 'FOURNIR', 'rank': 2}, {'name': 'PROJET', 'rank': 3}], 'type': 'constraint', 'indent': '', 'source': '() [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET'}] -------------------------------------------------------------------------------- (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET --------------------------------------------------------------------------------start line constraint_clause ( constraint_name I ) [ constraint_note bla bla. ] seq constraint_target constraint_leg .. box_name_ref box_name PIÈCE , constraint_target constraint_leg -> box_name_ref box_name REQUÉRIR , constraint_target constraint_leg -- box_name_ref box_name FOURNIR , constraint_target box_name_ref box_name PROJET -------------------------------------------------------------------------------- [{'name': 'I', 'constraint_note': 'bla bla.', 'constraint_targets': [{'constraint_leg': '..', 'name': 'PIÈCE', 'rank': 0}, {'constraint_leg': '->', 'name': 'REQUÉRIR', 'rank': 1}, {'constraint_leg': '--', 'name': 'FOURNIR', 'rank': 2}, {'name': 'PROJET', 'rank': 3}], 'type': 'constraint', 'indent': '', 'source': '(I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET'}] -------------------------------------------------------------------------------- (II) ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET --------------------------------------------------------------------------------start line constraint_clause ( constraint_name II ) seq constraint_target constraint_leg .. box_name_ref box_name PIÈCE , constraint_target constraint_leg -> box_name_ref box_name REQUÉRIR , constraint_target constraint_leg -- box_name_ref box_name FOURNIR , constraint_target box_name_ref box_name PROJET -------------------------------------------------------------------------------- [{'name': 'II', 'constraint_targets': [{'constraint_leg': '..', 'name': 'PIÈCE', 'rank': 0}, {'constraint_leg': '->', 'name': 'REQUÉRIR', 'rank': 1}, {'constraint_leg': '--', 'name': 'FOURNIR', 'rank': 2}, {'name': 'PROJET', 'rank': 3}], 'type': 'constraint', 'indent': '', 'source': '(II) ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET'}] -------------------------------------------------------------------------------- (III) [bla bla.] --------------------------------------------------------------------------------start line constraint_clause ( constraint_name III ) [ constraint_note bla bla. ] -------------------------------------------------------------------------------- [{'name': 'III', 'constraint_note': 'bla bla.', 'type': 'constraint', 'indent': '', 'source': '(III) [bla bla.]'}] -------------------------------------------------------------------------------- (IV) --------------------------------------------------------------------------------start line constraint_clause ( constraint_name IV ) -------------------------------------------------------------------------------- [{'name': 'IV', 'type': 'constraint', 'indent': '', 'source': '(IV) '}] -------------------------------------------------------------------------------- (]) --------------------------------------------------------------------------------start line constraint_clause ( constraint_name ] ) -------------------------------------------------------------------------------- [{'name': ']', 'type': 'constraint', 'indent': '', 'source': '(])'}] -------------------------------------------------------------------------------- (+) --------------------------------------------------------------------------------start line constraint_clause ( constraint_name + ) -------------------------------------------------------------------------------- [{'name': '+', 'type': 'constraint', 'indent': '', 'source': '(+)'}] -------------------------------------------------------------------------------- (/) --------------------------------------------------------------------------------start line constraint_clause ( constraint_name / ) -------------------------------------------------------------------------------- [{'name': '/', 'type': 'constraint', 'indent': '', 'source': '(/)'}] -------------------------------------------------------------------------------- (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, 30 --------------------------------------------------------------------------------start line constraint_clause ( constraint_name I ) [ constraint_note bla bla. ] seq constraint_target constraint_leg .. box_name_ref box_name PIÈCE , constraint_target constraint_leg -> box_name_ref box_name REQUÉRIR , constraint_target constraint_leg -- box_name_ref box_name FOURNIR , constraint_target box_name_ref box_name PROJET : constraint_coords 12.5 , 30 -------------------------------------------------------------------------------- [{'name': 'I', 'constraint_note': 'bla bla.', 'constraint_targets': [{'constraint_leg': '..', 'name': 'PIÈCE', 'rank': 0}, {'constraint_leg': '->', 'name': 'REQUÉRIR', 'rank': 1}, {'constraint_leg': '--', 'name': 'FOURNIR', 'rank': 2}, {'name': 'PROJET', 'rank': 3}], 'constraint_coords': [12.5, 30], 'type': 'constraint', 'indent': '', 'source': '(I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, 30'}] -------------------------------------------------------------------------------- (II) ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, 30 --------------------------------------------------------------------------------start line constraint_clause ( constraint_name II ) seq constraint_target constraint_leg .. box_name_ref box_name PIÈCE , constraint_target constraint_leg -> box_name_ref box_name REQUÉRIR , constraint_target constraint_leg -- box_name_ref box_name FOURNIR , constraint_target box_name_ref box_name PROJET : constraint_coords 12.5 , 30 -------------------------------------------------------------------------------- [{'name': 'II', 'constraint_targets': [{'constraint_leg': '..', 'name': 'PIÈCE', 'rank': 0}, {'constraint_leg': '->', 'name': 'REQUÉRIR', 'rank': 1}, {'constraint_leg': '--', 'name': 'FOURNIR', 'rank': 2}, {'name': 'PROJET', 'rank': 3}], 'constraint_coords': [12.5, 30], 'type': 'constraint', 'indent': '', 'source': '(II) ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, 30'}] -------------------------------------------------------------------------------- (III) [bla bla.]: 12.5, 30 --------------------------------------------------------------------------------start line constraint_clause ( constraint_name III ) [ constraint_note bla bla. ] : constraint_coords 12.5 , 30 -------------------------------------------------------------------------------- [{'name': 'III', 'constraint_note': 'bla bla.', 'constraint_coords': [12.5, 30], 'type': 'constraint', 'indent': '', 'source': '(III) [bla bla.]: 12.5, 30'}] -------------------------------------------------------------------------------- (IV) : 12.5, 30 --------------------------------------------------------------------------------start line constraint_clause ( constraint_name IV ) : constraint_coords 12.5 , 30 -------------------------------------------------------------------------------- [{'name': 'IV', 'constraint_coords': [12.5, 30], 'type': 'constraint', 'indent': '', 'source': '(IV) : 12.5, 30'}] -------------------------------------------------------------------------------- (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: FOO, 30 --------------------------------------------------------------------------------start line constraint_clause ( constraint_name I ) [ constraint_note bla bla. ] seq constraint_target constraint_leg .. box_name_ref box_name PIÈCE , constraint_target constraint_leg -> box_name_ref box_name REQUÉRIR , constraint_target constraint_leg -- box_name_ref box_name FOURNIR , constraint_target box_name_ref box_name PROJET : constraint_coords box_name_ref box_name FOO , 30 -------------------------------------------------------------------------------- [{'name': 'I', 'constraint_note': 'bla bla.', 'constraint_targets': [{'constraint_leg': '..', 'name': 'PIÈCE', 'rank': 0}, {'constraint_leg': '->', 'name': 'REQUÉRIR', 'rank': 1}, {'constraint_leg': '--', 'name': 'FOURNIR', 'rank': 2}, {'name': 'PROJET', 'rank': 3}], 'constraint_coords': ['FOO', 30], 'type': 'constraint', 'indent': '', 'source': '(I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: FOO, 30'}] -------------------------------------------------------------------------------- (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, BAR --------------------------------------------------------------------------------start line constraint_clause ( constraint_name I ) [ constraint_note bla bla. ] seq constraint_target constraint_leg .. box_name_ref box_name PIÈCE , constraint_target constraint_leg -> box_name_ref box_name REQUÉRIR , constraint_target constraint_leg -- box_name_ref box_name FOURNIR , constraint_target box_name_ref box_name PROJET : constraint_coords 12.5 , box_name_ref box_name BAR -------------------------------------------------------------------------------- [{'name': 'I', 'constraint_note': 'bla bla.', 'constraint_targets': [{'constraint_leg': '..', 'name': 'PIÈCE', 'rank': 0}, {'constraint_leg': '->', 'name': 'REQUÉRIR', 'rank': 1}, {'constraint_leg': '--', 'name': 'FOURNIR', 'rank': 2}, {'name': 'PROJET', 'rank': 3}], 'constraint_coords': [12.5, 'BAR'], 'type': 'constraint', 'indent': '', 'source': '(I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: 12.5, BAR'}] -------------------------------------------------------------------------------- (I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: FOO, BAR --------------------------------------------------------------------------------start line constraint_clause ( constraint_name I ) [ constraint_note bla bla. ] seq constraint_target constraint_leg .. box_name_ref box_name PIÈCE , constraint_target constraint_leg -> box_name_ref box_name REQUÉRIR , constraint_target constraint_leg -- box_name_ref box_name FOURNIR , constraint_target box_name_ref box_name PROJET : constraint_coords box_name_ref box_name FOO , box_name_ref box_name BAR -------------------------------------------------------------------------------- [{'name': 'I', 'constraint_note': 'bla bla.', 'constraint_targets': [{'constraint_leg': '..', 'name': 'PIÈCE', 'rank': 0}, {'constraint_leg': '->', 'name': 'REQUÉRIR', 'rank': 1}, {'constraint_leg': '--', 'name': 'FOURNIR', 'rank': 2}, {'name': 'PROJET', 'rank': 3}], 'constraint_coords': ['FOO', 'BAR'], 'type': 'constraint', 'indent': '', 'source': '(I) [bla bla.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET: FOO, BAR'}] -------------------------------------------------------------------------------- (IV) : 12.5, 30 --------------------------------------------------------------------------------start line constraint_clause ( constraint_name IV ) : constraint_coords 12.5 , 30 -------------------------------------------------------------------------------- [{'name': 'IV', 'constraint_coords': [12.5, 30], 'type': 'constraint', 'indent': '', 'source': '(IV) : 12.5, 30'}] -------------------------------------------------------------------------------- (A) --Lorem, .....Ipsum, -Dolor: 30, 10 --------------------------------------------------------------------------------start line constraint_clause ( constraint_name A ) seq constraint_target constraint_leg -- box_name_ref box_name Lorem , constraint_target constraint_leg ..... box_name_ref box_name Ipsum , constraint_target constraint_leg - box_name_ref box_name Dolor : constraint_coords 30 , 10 -------------------------------------------------------------------------------- [{'name': 'A', 'constraint_targets': [{'constraint_leg': '--', 'name': 'Lorem', 'rank': 0}, {'constraint_leg': '.....', 'name': 'Ipsum', 'rank': 1}, {'constraint_leg': '-', 'name': 'Dolor', 'rank': 2}], 'constraint_coords': [30, 10], 'type': 'constraint', 'indent': '', 'source': '(A) --Lorem, .....Ipsum, -Dolor: 30, 10'}] -------------------------------------------------------------------------------- (B) ->Dolor, <-->Sit, -->Amet: 69, 10 --------------------------------------------------------------------------------start line constraint_clause ( constraint_name B ) seq constraint_target constraint_leg -> box_name_ref box_name Dolor , constraint_target constraint_leg <--> box_name_ref box_name Sit , constraint_target constraint_leg --> box_name_ref box_name Amet : constraint_coords 69 , 10 -------------------------------------------------------------------------------- [{'name': 'B', 'constraint_targets': [{'constraint_leg': '->', 'name': 'Dolor', 'rank': 0}, {'constraint_leg': '<-->', 'name': 'Sit', 'rank': 1}, {'constraint_leg': '-->', 'name': 'Amet', 'rank': 2}], 'constraint_coords': [69, 10], 'type': 'constraint', 'indent': '', 'source': '(B) ->Dolor, <-->Sit, -->Amet: 69, 10'}] -------------------------------------------------------------------------------- (XX) ->foo, -->foo, -> foo, --> foo --------------------------------------------------------------------------------start line constraint_clause ( constraint_name XX ) seq constraint_target constraint_leg -> box_name_ref box_name foo , constraint_target constraint_leg --> box_name_ref box_name foo , constraint_target constraint_leg -> box_name_ref box_name foo , constraint_target constraint_leg --> box_name_ref box_name foo -------------------------------------------------------------------------------- [{'name': 'XX', 'constraint_targets': [{'constraint_leg': '->', 'name': 'foo', 'rank': 0}, {'constraint_leg': '-->', 'name': 'foo', 'rank': 1}, {'constraint_leg': '->', 'name': 'foo', 'rank': 2}, {'constraint_leg': '-->', 'name': 'foo', 'rank': 3}], 'type': 'constraint', 'indent': '', 'source': '(XX) ->foo, -->foo, -> foo, --> foo'}] -------------------------------------------------------------------------------- (I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer --------------------------------------------------------------------------------start line constraint_clause ( constraint_name I ) seq constraint_target constraint_leg -> box_name_ref box_name Stocker , constraint_target constraint_leg .. box_name_ref box_name Dépôt , constraint_target constraint_leg .. box_name_ref box_name Article , constraint_target constraint_leg -- box_name_ref box_name Composer , constraint_target constraint_leg -- box_name_ref box_name Louer -------------------------------------------------------------------------------- [{'name': 'I', 'constraint_targets': [{'constraint_leg': '->', 'name': 'Stocker', 'rank': 0}, {'constraint_leg': '..', 'name': 'Dépôt', 'rank': 1}, {'constraint_leg': '..', 'name': 'Article', 'rank': 2}, {'constraint_leg': '--', 'name': 'Composer', 'rank': 3}, {'constraint_leg': '--', 'name': 'Louer', 'rank': 4}], 'type': 'constraint', 'indent': '', 'source': '(I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer'}] -------------------------------------------------------------------------------- /XT\ Personne ==> Homme, Femme: sexe --------------------------------------------------------------------------------start line inheritance_clause / inheritance_name XT \ inheritance_parent entity_name_ref box_name Personne inheritance_arrow ==> seq inheritance_child entity_name_ref box_name Homme , inheritance_child entity_name_ref box_name Femme : seq typed_attr attr sexe -------------------------------------------------------------------------------- [{'name': 'XT', 'inheritance_arrow': '==>', 'legs': [{'entity': 'Personne', 'rank': -1}, {'entity': 'Homme', 'rank': 0}, {'entity': 'Femme', 'rank': 1}], 'attrs': [{'attribute_label': 'sexe', 'rank': 0}], 'type': 'inheritance', 'indent': '', 'source': '/XT\\ Personne ==> Homme, Femme: sexe'}] -------------------------------------------------------------------------------- /XT1\\ Personne <= Homme, Femme: sexe --------------------------------------------------------------------------------start line inheritance_clause / inheritance_name XT1 \\ inheritance_parent entity_name_ref box_name Personne inheritance_arrow <= seq inheritance_child entity_name_ref box_name Homme , inheritance_child entity_name_ref box_name Femme : seq typed_attr attr sexe -------------------------------------------------------------------------------- [{'name': 'XT1', 'inheritance_arrow': '<=', 'legs': [{'entity': 'Personne', 'rank': -1}, {'entity': 'Homme', 'rank': 0}, {'entity': 'Femme', 'rank': 1}], 'attrs': [{'attribute_label': 'sexe', 'rank': 0}], 'type': 'inheritance', 'indent': '', 'source': '/XT1\\\\ Personne <= Homme, Femme: sexe'}] -------------------------------------------------------------------------------- /XT\\ Personne => Homme, Femme: sexe --------------------------------------------------------------------------------start line inheritance_clause / inheritance_name XT \\ inheritance_parent entity_name_ref box_name Personne inheritance_arrow => seq inheritance_child entity_name_ref box_name Homme , inheritance_child entity_name_ref box_name Femme : seq typed_attr attr sexe -------------------------------------------------------------------------------- [{'name': 'XT', 'inheritance_arrow': '=>', 'legs': [{'entity': 'Personne', 'rank': -1}, {'entity': 'Homme', 'rank': 0}, {'entity': 'Femme', 'rank': 1}], 'attrs': [{'attribute_label': 'sexe', 'rank': 0}], 'type': 'inheritance', 'indent': '', 'source': '/XT\\\\ Personne => Homme, Femme: sexe'}] -------------------------------------------------------------------------------- /T\\ Personne <= Homme, Femme: sexe --------------------------------------------------------------------------------start line inheritance_clause / inheritance_name T \\ inheritance_parent entity_name_ref box_name Personne inheritance_arrow <= seq inheritance_child entity_name_ref box_name Homme , inheritance_child entity_name_ref box_name Femme : seq typed_attr attr sexe -------------------------------------------------------------------------------- [{'name': 'T', 'inheritance_arrow': '<=', 'legs': [{'entity': 'Personne', 'rank': -1}, {'entity': 'Homme', 'rank': 0}, {'entity': 'Femme', 'rank': 1}], 'attrs': [{'attribute_label': 'sexe', 'rank': 0}], 'type': 'inheritance', 'indent': '', 'source': '/T\\\\ Personne <= Homme, Femme: sexe'}] -------------------------------------------------------------------------------- /T\\ Personne => Homme, Femme: sexe --------------------------------------------------------------------------------start line inheritance_clause / inheritance_name T \\ inheritance_parent entity_name_ref box_name Personne inheritance_arrow => seq inheritance_child entity_name_ref box_name Homme , inheritance_child entity_name_ref box_name Femme : seq typed_attr attr sexe -------------------------------------------------------------------------------- [{'name': 'T', 'inheritance_arrow': '=>', 'legs': [{'entity': 'Personne', 'rank': -1}, {'entity': 'Homme', 'rank': 0}, {'entity': 'Femme', 'rank': 1}], 'attrs': [{'attribute_label': 'sexe', 'rank': 0}], 'type': 'inheritance', 'indent': '', 'source': '/T\\\\ Personne => Homme, Femme: sexe'}] -------------------------------------------------------------------------------- /X\\ Personne <= Homme, Femme: sexe --------------------------------------------------------------------------------start line inheritance_clause / inheritance_name X \\ inheritance_parent entity_name_ref box_name Personne inheritance_arrow <= seq inheritance_child entity_name_ref box_name Homme , inheritance_child entity_name_ref box_name Femme : seq typed_attr attr sexe -------------------------------------------------------------------------------- [{'name': 'X', 'inheritance_arrow': '<=', 'legs': [{'entity': 'Personne', 'rank': -1}, {'entity': 'Homme', 'rank': 0}, {'entity': 'Femme', 'rank': 1}], 'attrs': [{'attribute_label': 'sexe', 'rank': 0}], 'type': 'inheritance', 'indent': '', 'source': '/X\\\\ Personne <= Homme, Femme: sexe'}] -------------------------------------------------------------------------------- /X\\ Personne => Homme, Femme: sexe --------------------------------------------------------------------------------start line inheritance_clause / inheritance_name X \\ inheritance_parent entity_name_ref box_name Personne inheritance_arrow => seq inheritance_child entity_name_ref box_name Homme , inheritance_child entity_name_ref box_name Femme : seq typed_attr attr sexe -------------------------------------------------------------------------------- [{'name': 'X', 'inheritance_arrow': '=>', 'legs': [{'entity': 'Personne', 'rank': -1}, {'entity': 'Homme', 'rank': 0}, {'entity': 'Femme', 'rank': 1}], 'attrs': [{'attribute_label': 'sexe', 'rank': 0}], 'type': 'inheritance', 'indent': '', 'source': '/X\\\\ Personne => Homme, Femme: sexe'}] -------------------------------------------------------------------------------- /\\ Personne <= Homme, Femme: sexe --------------------------------------------------------------------------------start line inheritance_clause / \\ inheritance_parent entity_name_ref box_name Personne inheritance_arrow <= seq inheritance_child entity_name_ref box_name Homme , inheritance_child entity_name_ref box_name Femme : seq typed_attr attr sexe -------------------------------------------------------------------------------- [{'inheritance_arrow': '<=', 'legs': [{'entity': 'Personne', 'rank': -1}, {'entity': 'Homme', 'rank': 0}, {'entity': 'Femme', 'rank': 1}], 'attrs': [{'attribute_label': 'sexe', 'rank': 0}], 'name': '', 'type': 'inheritance', 'indent': '', 'source': '/\\\\ Personne <= Homme, Femme: sexe'}] -------------------------------------------------------------------------------- /\\ Personne => Homme, Femme: sexe --------------------------------------------------------------------------------start line inheritance_clause / \\ inheritance_parent entity_name_ref box_name Personne inheritance_arrow => seq inheritance_child entity_name_ref box_name Homme , inheritance_child entity_name_ref box_name Femme : seq typed_attr attr sexe -------------------------------------------------------------------------------- [{'inheritance_arrow': '=>', 'legs': [{'entity': 'Personne', 'rank': -1}, {'entity': 'Homme', 'rank': 0}, {'entity': 'Femme', 'rank': 1}], 'attrs': [{'attribute_label': 'sexe', 'rank': 0}], 'name': '', 'type': 'inheritance', 'indent': '', 'source': '/\\\\ Personne => Homme, Femme: sexe'}] -------------------------------------------------------------------------------- /1\\ FOO => BAR --------------------------------------------------------------------------------start line inheritance_clause / inheritance_name 1 \\ inheritance_parent entity_name_ref box_name FOO inheritance_arrow => seq inheritance_child entity_name_ref box_name BAR -------------------------------------------------------------------------------- [{'name': '1', 'inheritance_arrow': '=>', 'legs': [{'entity': 'FOO', 'rank': -1}, {'entity': 'BAR', 'rank': 0}], 'type': 'inheritance', 'indent': '', 'source': '/1\\\\ FOO => BAR'}] -------------------------------------------------------------------------------- /T\\foo==>foo11 --------------------------------------------------------------------------------start line inheritance_clause / inheritance_name T \\ inheritance_parent entity_name_ref box_name foo inheritance_arrow ==> seq inheritance_child entity_name_ref box_name foo11 -------------------------------------------------------------------------------- [{'name': 'T', 'inheritance_arrow': '==>', 'legs': [{'entity': 'foo', 'rank': -1}, {'entity': 'foo11', 'rank': 0}], 'type': 'inheritance', 'indent': '', 'source': '/T\\\\foo==>foo11'}] -------------------------------------------------------------------------------- Étudiant: num, 1_nom, 1_prénom, adresse, 2_mail --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name Étudiant : seq entity_or_table_attr typed_attr attr num , entity_or_table_attr id_groups 1 id_mark _ typed_attr attr nom , entity_or_table_attr id_groups 1 id_mark _ typed_attr attr prénom , entity_or_table_attr typed_attr attr adresse , entity_or_table_attr id_groups 2 id_mark _ typed_attr attr mail -------------------------------------------------------------------------------- [{'name': 'Étudiant', 'attrs': [{'attribute_label': 'num', 'rank': 0}, {'id_groups': '1', 'id_mark': '_', 'attribute_label': 'nom', 'rank': 1}, {'id_groups': '1', 'id_mark': '_', 'attribute_label': 'prénom', 'rank': 2}, {'attribute_label': 'adresse', 'rank': 3}, {'id_groups': '2', 'id_mark': '_', 'attribute_label': 'mail', 'rank': 4}], 'type': 'entity', 'indent': '', 'source': 'Étudiant: num, 1_nom, 1_prénom, adresse, 2_mail'}] -------------------------------------------------------------------------------- Étudiant: 0_num, 1_nom, 1_prénom, adresse, 2_mail --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name Étudiant : seq entity_or_table_attr id_groups 0 id_mark _ typed_attr attr num , entity_or_table_attr id_groups 1 id_mark _ typed_attr attr nom , entity_or_table_attr id_groups 1 id_mark _ typed_attr attr prénom , entity_or_table_attr typed_attr attr adresse , entity_or_table_attr id_groups 2 id_mark _ typed_attr attr mail -------------------------------------------------------------------------------- [{'name': 'Étudiant', 'attrs': [{'id_groups': '0', 'id_mark': '_', 'attribute_label': 'num', 'rank': 0}, {'id_groups': '1', 'id_mark': '_', 'attribute_label': 'nom', 'rank': 1}, {'id_groups': '1', 'id_mark': '_', 'attribute_label': 'prénom', 'rank': 2}, {'attribute_label': 'adresse', 'rank': 3}, {'id_groups': '2', 'id_mark': '_', 'attribute_label': 'mail', 'rank': 4}], 'type': 'entity', 'indent': '', 'source': 'Étudiant: 0_num, 1_nom, 1_prénom, adresse, 2_mail'}] -------------------------------------------------------------------------------- Position: 0_latitude, 0_longitude, altitude --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name Position : seq entity_or_table_attr id_groups 0 id_mark _ typed_attr attr latitude , entity_or_table_attr id_groups 0 id_mark _ typed_attr attr longitude , entity_or_table_attr typed_attr attr altitude -------------------------------------------------------------------------------- [{'name': 'Position', 'attrs': [{'id_groups': '0', 'id_mark': '_', 'attribute_label': 'latitude', 'rank': 0}, {'id_groups': '0', 'id_mark': '_', 'attribute_label': 'longitude', 'rank': 1}, {'attribute_label': 'altitude', 'rank': 2}], 'type': 'entity', 'indent': '', 'source': 'Position: 0_latitude, 0_longitude, altitude'}] -------------------------------------------------------------------------------- Foo: bar, 1_baz, 21_qux, 123_quux --------------------------------------------------------------------------------start line entity_clause entity_name_def box_name Foo : seq entity_or_table_attr typed_attr attr bar , entity_or_table_attr id_groups 1 id_mark _ typed_attr attr baz , entity_or_table_attr id_groups 21 id_mark _ typed_attr attr qux , entity_or_table_attr id_groups 123 id_mark _ typed_attr attr quux -------------------------------------------------------------------------------- [{'name': 'Foo', 'attrs': [{'attribute_label': 'bar', 'rank': 0}, {'id_groups': '1', 'id_mark': '_', 'attribute_label': 'baz', 'rank': 1}, {'id_groups': '12', 'id_mark': '_', 'attribute_label': 'qux', 'rank': 2}, {'id_groups': '123', 'id_mark': '_', 'attribute_label': 'quux', 'rank': 3}], 'type': 'entity', 'indent': '', 'source': 'Foo: bar, 1_baz, 21_qux, 123_quux'}] -------------------------------------------------------------------------------- Réserver, 1N Client, 0N Chambre: _date, durée --------------------------------------------------------------------------------start line assoc_clause assoc_name_def box_name Réserver , seq assoc_leg card 1N entity_name_ref box_name Client , assoc_leg card 0N entity_name_ref box_name Chambre : seq assoc_attr _ typed_attr attr date , assoc_attr typed_attr attr durée -------------------------------------------------------------------------------- [{'name': 'Réserver', 'legs': [{'card': '1N', 'entity': 'Client', 'rank': 0}, {'card': '0N', 'entity': 'Chambre', 'rank': 1}], 'attrs': [{'attribute_label': 'date', 'id_mark': '_', 'rank': 0}, {'attribute_label': 'durée', 'rank': 1}], 'type': 'association', 'indent': '', 'source': 'Réserver, 1N Client, 0N Chambre: _date, durée'}] ================================================ FILE: test/test_read_template.py ================================================ import gettext import unittest from pathlib import Path __import__("sys").path[0:0] = ["mocodo"] from mocodo.convert.read_template import read_template from mocodo.tools import load_mini_yaml from mocodo.mocodo_error import MocodoError from mocodo.dev import update_transfo_metadata # just update the graph and index gettext.NullTranslations().install() TEMPLATE_FOLDER = Path("test", "test_data", "templates") class TestReadTemplate(unittest.TestCase): def test_root_template(self): template = read_template("root", TEMPLATE_FOLDER) expected = load_mini_yaml.run(Path(TEMPLATE_FOLDER, "root.yaml")) self.assertEqual(template, expected) def test_child_template(self): template = read_template("child", TEMPLATE_FOLDER) expected = load_mini_yaml.run(Path(TEMPLATE_FOLDER, "expected_child.yaml")) self.assertEqual(template, expected) def test_grandchild_template(self): template = read_template("grandchild", TEMPLATE_FOLDER) expected = load_mini_yaml.run(Path(TEMPLATE_FOLDER, "expected_grandchild.yaml")) self.assertEqual(template, expected) def test_errors(self): self.assertRaisesRegex(MocodoError, r"Mocodo Err\.30", read_template, "bad_circular_1", TEMPLATE_FOLDER) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.31", read_template, "not_a_file", TEMPLATE_FOLDER) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.32", read_template, "bad_array_element", TEMPLATE_FOLDER) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.32", read_template, "bad_not_an_object", TEMPLATE_FOLDER) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.32", read_template, "bad_object_value", TEMPLATE_FOLDER) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.36", read_template, "bad_no_order", TEMPLATE_FOLDER) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.38", read_template, "bad_non_numeric_order", TEMPLATE_FOLDER) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.39", read_template, "bad_non_increasing_order", TEMPLATE_FOLDER) if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_relations.py ================================================ from pathlib import Path import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.argument_parser import parsed_arguments from mocodo.mcd import Mcd from mocodo.convert.relations import * from mocodo.tools.string_tools import markdown_table from mocodo.tools.load_mini_yaml import run as load_mini_yaml minimal_template = load_mini_yaml(Path("mocodo/resources/relation_templates/text.yaml")) debug_template = load_mini_yaml(Path("mocodo/resources/relation_templates/debug.yaml")) params = parsed_arguments([]) params["title"] = "Untitled" params["guess_title"] = False def debug_table(t): tsv = t.get_text(debug_template).rstrip("\n") tsv = tsv.replace("this relation name", "relation") rows = [line.split("\t") for line in tsv.split("\n")] return re.sub("(?m)^", " ", markdown_table(rows)) class relationsTest(unittest.TestCase): def test_arrows_are_ignored(self): source = """ Personne: Num. SS, Nom, Prénom, Sexe Engendrer, 0N< Personne, 22> Personne """ t = Relations(Mcd(source, params), params) d1 = t.get_text(debug_template) source = """ Personne: Num. SS, Nom, Prénom, Sexe Engendrer, 0N Personne, 22 Personne """ t = Relations(Mcd(source, params), params) d2 = t.get_text(debug_template) self.assertEqual(d1, d2) def test_reciprocical_relative_entities(self): source = """ Aids: Norm, Free, Soon, Pack, Face, Seem, Teen Yard, 0N Unit, ON Aids Ever, 1N Unit, 1N Item Item: Norm, Wash, Haul, Milk, Draw, Lady, Face, Soon, Dish : Amid, 1n Aids, 1n Disk, _11 Flip: Gold Same, _11 Unit, 0N Flip Unit: Folk, Peer, Tour, Hall Fold, _11 Unit, 1N Baby, _11 Item Baby: Soon Disk: Soon, Ride, Folk, Call, Gear, Tent, Lean Flip: Lend Pump, _11 Flip, 1N Unit: Both, Raid Gene: Soon Bind, _11 Baby, 1n Gene """ mcd = Mcd(source, params) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.11", Relations, mcd, params) source = """ Disk: Soon, Ride, Folk, Call, Gear, Tent, Lean Flip: Lend Pump, _11 Flip, 1N Unit: Both, Raid Gene: Soon Bind, _11 Baby, 1n Gene Amid, 1n Aids, 1n Disk, _11 Flip: Gold Same, _11 Unit, 0N Flip Unit: Folk, Peer, Tour, Hall Fold, _11 Unit, 1N Baby, _11 Item Baby: Soon Aids: Norm, Free, Soon, Pack, Face, Seem, Teen Yard, 0N Unit, ON Aids Ever, 1N Unit, 1N Item Item: Norm, Wash, Haul, Milk, Draw, Lady, Face, Soon, Dish : """ mcd = Mcd(source, params) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.11", Relations, mcd, params) mcd = Mcd(source, params) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.11", Relations, mcd, params) source = """ ITEM, 1N NORM, 1N WASH NORM: haul WASH: soon BABY, 1N WASH, 1N FACE FACE: gene AAA, _11 FLIP, 1N WASH FLIP: soona GEAR, _11 FLIP, _11 FACE """ mcd = Mcd(source, params) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.11", Relations, mcd, params) source = """ ITEM, 1N NORM, 1N WASH NORM: haul WASH: soon BABY, 1N WASH, 1N FACE FACE: gene CCC, _11 FLIP, 1N WASH FLIP: soona GEAR, _11 FLIP, _11 FACE """ mcd = Mcd(source, params) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.11", Relations, mcd, params) def test_association_attributes(self): source = """ Client: Id. client Réserver, 1N Client, 0N Chambre: _Date, Durée Chambre: Num. chambre, Prix """ expected = """ Chambre (_Num. chambre_, Prix) Réserver (_Id. client_, _#Num. chambre_, _Date_, Durée) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), expected) def test_weak_entities_strengthened_by_itself(self): source = """ SCELERISQUE: blandit, elit DF, _11 SCELERISQUE, 1N SCELERISQUE """ mcd = Mcd(source, params) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.16", Relations, mcd, params) def test_weak_entities_strengthened_by_several_entities(self): source = """ Baby: Soon, protect_baby Yard, _11 Unit, ON Baby: Hall : Unit: Folk, Peer Item: Norm, Wash Ever, _11 Unit, 1N Item: Tour """ expected = """ Baby (_Soon_, protect_baby) Item (_Norm_, Wash) Unit (_#Norm_, _#Soon_, _Folk_, Peer, Hall, Tour) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), expected) def test_weak_entities_with_cycle(self): source = """ ITEM: norm, wash, haul MILK, _11 ITEM, 1N DRAW: lady, face SOON, 1N ITEM, _11 DRAW DRAW: ever, unit, tour, fold """ mcd = Mcd(source, params) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.17", Relations, mcd, params) def test_disambiguation_by_role(self): source = """ Entité A: id. entité A DF, 11 Entité centrale, 1N Entité A DF, 11 Entité centrale, 1N [-nouveau nom B] Entité B Entité B: id. entité B Entité E: id. entité E DF, 11 Entité centrale, 1N [suffixe] Entité E Entité centrale: id. entité centrale DF, 11 Entité centrale, 1N [+hérissons] Entité C : Entité D: id. entité D DF, 11 Entité centrale, 1N [Description affichée au survol.] Entité D Entité C: id. entité C """ expected = """ Entité centrale (_id. entité centrale_, id. entité A, nouveau nom B, id. entité E suffixe, id. entité Chérissons, id. entité D) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), expected) def test_disambiguation_by_number(self): source = """ Entité A: id DF, 11 Entité centrale, 1N Entité A DF, 11 Entité centrale, 1N Entité B Entité B: id Entité E: id DF, 11 Entité centrale, 1N [Description affichée au survol.] Entité E Entité centrale: id DF, 11 Entité centrale, 1N [suffixe] Entité C : Entité D: id DF, 11 Entité centrale, 1N [suffixe] Entité D Entité C: id """ expected = """ Entité centrale (_id_, id 2, id 3, id 4, id suffixe 1, id suffixe 2) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), expected) def test_disambiguation_by_role_then_number(self): source = """ FOO: id BAR: id DF, 11 FOO, 1N BAR DF, 11 FOO, 1N BAR DF, 11 FOO, 1N [role] BAR DF, 11 FOO, 1N [+role] BAR DF, 11 FOO, 1N [+_role] BAR DF, 11 FOO, 1N [-role] BAR DF, 11 FOO, 1N [string containing spaces] BAR """ expected = """ FOO (_id_, id 2, id 3, id role, idrole, id_role, role, id 4) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), expected) def test_inheritance_leftwards_double_arrow(self): source = """ /\\ ANIMAL <= CARNIVORE, HERBIVORE ANIMAL: animal, poids CARNIVORE: quantité viande HERBIVORE: plante préférée """ text = """ ANIMAL (_animal_, poids, est carnivore, quantité viande, est herbivore, plante préférée) """.strip().replace(" ", "") mcd = Mcd(source, params) t = Relations(mcd, params) self.assertEqual(t.get_text(minimal_template), text) expected = """ | relation | attribute | optionality | unicities | nature | is primary | adjacent source | outer source | association name | datatype | leg note | |:---------|:----------------|:------------|:----------|:--------------------------|:-----------|:----------------|:-------------|:-----------------|:--------------------|:---------| | ANIMAL | animal | ! | | primary_key | True | | | | | | | ANIMAL | poids | | | normal_attribute | False | | | | | | | ANIMAL | est carnivore | ! | | deleted_child_entity_name | False | CARNIVORE | CARNIVORE | | BOOLEAN_PLACEHOLDER | | | ANIMAL | quantité viande | ? | | deleted_child_attribute | False | CARNIVORE | | | | | | ANIMAL | est herbivore | ! | | deleted_child_entity_name | False | HERBIVORE | HERBIVORE | | BOOLEAN_PLACEHOLDER | | | ANIMAL | plante préférée | ? | | deleted_child_attribute | False | HERBIVORE | | | | | """ actual = debug_table(t) self.assertEqual(actual.strip(), expected.strip()) def test_inheritance_leftwards_simple_arrow(self): source = """ /\\ ANIMAL <- CARNIVORE, HERBIVORE: type ANIMAL: animal, poids CARNIVORE: quantité viande HERBIVORE: plante préférée """ text = """ ANIMAL (_animal_, poids, type, quantité viande, plante préférée) """.strip().replace(" ", "") mcd = Mcd(source, params) t = Relations(mcd, params) self.assertEqual(t.get_text(minimal_template), text) expected = """ | relation | attribute | optionality | unicities | nature | is primary | adjacent source | outer source | association name | datatype | leg note | |:---------|:----------------|:------------|:----------|:-----------------------------|:-----------|:----------------|:-------------|:-----------------|:-------------------------|:---------| | ANIMAL | animal | ! | | primary_key | True | | | | | | | ANIMAL | poids | | | normal_attribute | False | | | | | | | ANIMAL | type | ? | | deleted_child_discriminator_ | False | | | | UNSIGNED_INT_PLACEHOLDER | | | ANIMAL | quantité viande | ? | | deleted_child_attribute | False | CARNIVORE | | | | | | ANIMAL | plante préférée | ? | | deleted_child_attribute | False | HERBIVORE | | | | | """ actual = debug_table(t) self.assertEqual(actual.strip(), expected.strip()) def test_inheritance_rightwards_simple_arrow(self): source = """ /\\ ANIMAL -> CARNIVORE, HERBIVORE: type ANIMAL: animal, poids CARNIVORE: quantité viande HERBIVORE: plante préférée """ text = """ ANIMAL (_animal_, poids, type) CARNIVORE (_#animal_, quantité viande) HERBIVORE (_#animal_, plante préférée) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), text) expected = """ | relation | attribute | optionality | unicities | nature | is primary | adjacent source | outer source | association name | datatype | leg note | |:----------|:----------------|:------------|:----------|:-----------------------------|:-----------|:----------------|:-------------|:-----------------|:-------------------------|:---------| | ANIMAL | animal | ! | | primary_key | True | | | | | | | ANIMAL | poids | | | normal_attribute | False | | | | | | | ANIMAL | type | ? | | deleted_child_discriminator_ | False | | | | UNSIGNED_INT_PLACEHOLDER | | | CARNIVORE | animal | ! | | parent_primary_key | True | ANIMAL | ANIMAL | | | | | CARNIVORE | quantité viande | | | normal_attribute | False | | | | | | | HERBIVORE | animal | ! | | parent_primary_key | True | ANIMAL | ANIMAL | | | | | HERBIVORE | plante préférée | | | normal_attribute | False | | | | | | """ actual = debug_table(t) self.assertEqual(actual.strip(), expected.strip()) def test_inheritance_rightwards_double_arrow_with_totality(self): source = """ /T\\ ANIMAL => CARNIVORE, HERBIVORE: type ANIMAL: animal, poids CARNIVORE: quantité viande HERBIVORE: plante préférée """ text = """ CARNIVORE (_animal_, poids, quantité viande) HERBIVORE (_animal_, poids, plante préférée) """.strip().replace(" ", "") mcd = Mcd(source, params) t = Relations(mcd, params) self.assertEqual(t.get_text(minimal_template), text) expected = """ | relation | attribute | optionality | unicities | nature | is primary | adjacent source | outer source | association name | datatype | leg note | |:----------|:----------------|:------------|:----------|:---------------------------|:-----------|:----------------|:-------------|:-----------------|:---------|:---------| | CARNIVORE | animal | ! | | deleted_parent_primary_key | True | ANIMAL | ANIMAL | T | | | | CARNIVORE | poids | | | deleted_parent_attribute | False | ANIMAL | | T | | | | CARNIVORE | quantité viande | | | normal_attribute | False | | | | | | | HERBIVORE | animal | ! | | deleted_parent_primary_key | True | ANIMAL | ANIMAL | T | | | | HERBIVORE | poids | | | deleted_parent_attribute | False | ANIMAL | | T | | | | HERBIVORE | plante préférée | | | normal_attribute | False | | | | | | """ actual = debug_table(t) self.assertEqual(actual.strip(), expected.strip()) def test_inheritance_rightwards_double_arrow_without_totality(self): source = """ /X\\ ANIMAL => CARNIVORE, HERBIVORE: type ANIMAL: animal, poids CARNIVORE: quantité viande HERBIVORE: plante préférée """ mcd = Mcd(source, params) self.assertRaisesRegex(MocodoError, r"Mocodo Err\.25", Relations, mcd, params) def test_inheritance_leftwards_simple_arrow_unpretty(self): source = """ /\\ ANIMAL <-- CARNIVORE, HERBIVORE: type ANIMAL: animal, poids CARNIVORE: quantité viande HERBIVORE: plante préférée """ text = """ ANIMAL (_animal_, poids, type, quantité viande, plante préférée) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), text) expected = """ | relation | attribute | optionality | unicities | nature | is primary | adjacent source | outer source | association name | datatype | leg note | |:---------|:----------------|:------------|:----------|:-----------------------------|:-----------|:----------------|:-------------|:-----------------|:-------------------------|:---------| | ANIMAL | animal | ! | | primary_key | True | | | | | | | ANIMAL | poids | | | normal_attribute | False | | | | | | | ANIMAL | type | ? | | deleted_child_discriminator_ | False | | | | UNSIGNED_INT_PLACEHOLDER | | | ANIMAL | quantité viande | ? | | deleted_child_attribute | False | CARNIVORE | | | | | | ANIMAL | plante préférée | ? | | deleted_child_attribute | False | HERBIVORE | | | | | """ actual = debug_table(t) self.assertEqual(actual.strip(), expected.strip()) def test_inheritance_with_unique_child(self): source = """ CARNIVORE: quantité viande /\\ ANIMAL -> CARNIVORE: type ANIMAL: animal, poids """ text = """ ANIMAL (_animal_, poids, type) CARNIVORE (_#animal_, quantité viande) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), text) expected = """ | relation | attribute | optionality | unicities | nature | is primary | adjacent source | outer source | association name | datatype | leg note | |:----------|:----------------|:------------|:----------|:-----------------------------|:-----------|:----------------|:-------------|:-----------------|:-------------------------|:---------| | ANIMAL | animal | ! | | primary_key | True | | | | | | | ANIMAL | poids | | | normal_attribute | False | | | | | | | ANIMAL | type | ? | | deleted_child_discriminator_ | False | | | | UNSIGNED_INT_PLACEHOLDER | | | CARNIVORE | animal | ! | | parent_primary_key | True | ANIMAL | ANIMAL | | | | | CARNIVORE | quantité viande | | | normal_attribute | False | | | | | | """ actual = debug_table(t) self.assertEqual(actual.strip(), expected.strip()) def test_inheritance_with_distant_leg_note(self): source = """ HERBIVORE: plante préférée : : /XT\\ ANIMAL => CARNIVORE, HERBIVORE: type alimentation ANIMAL: nom, sexe, date naissance, date décès A MÈRE, 01 ANIMAL, 0N> [mère] ANIMAL CARNIVORE: quantité viande : : """ text = """ CARNIVORE (_nom_, sexe, date naissance, date décès, nom mère, quantité viande) HERBIVORE (_nom_, sexe, date naissance, date décès, nom mère, plante préférée) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), text) def test_two_same_type_inheritances(self): # example from user fduchatea on https://github.com/laowantong/mocodo/issues/64 source = """ : : Inscrites : : : UtilisatricesPlus : nom, prénom /XT\\ UtilisatricesPlus --> Inscrites, Abonnées : catégorie Abonnées : Utilisatrices : idU, email /XT\\ Utilisatrices --> Invitées, UtilisatricesPlus : catégorie Invitées : adresseIP : """ text = """ Invitées (_#idU_, adresseIP) Utilisatrices (_idU_, email, catégorie) UtilisatricesPlus (_#idU_, nom, prénom, catégorie) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), text) def test_roles_with_inheritance(self): # https://github.com/laowantong/mocodo/issues/110 source = """ Équipe: id équipe,nom équipe Accueille, 11 Match, 0N [-hôte] Équipe Reçoit, 11 Match, 0N [-visiteur] Équipe Match: id match Rencontre: id rencontre /XT\\ Rencontre <- Match: type rencontre """ text = """ Équipe (_id équipe_, nom équipe) Rencontre (_id rencontre_, type rencontre, id match, #hôte, #visiteur) """.strip().replace(" ", "") t = Relations(Mcd(source, params), params) self.assertEqual(t.get_text(minimal_template), text) if __name__ == '__main__': unittest.main() ================================================ FILE: test/test_rewrite_cards.py ================================================ import unittest import random __import__("sys").path[0:0] = ["mocodo"] from mocodo.rewrite import op_tk class TestUpdateCards(unittest.TestCase): def test_infer_df(self): # Actually not a card update, but I don't want to create a new test file just for this. source = """ BAKE, 0N TEND, 01 TALL FISH, -1N TALL, -11 TOUR: slot DOWN, 0N [cold] TOUR, 0N [echo] TOUR: hang WRAP, _11 PORK, 0N TEND FINE, 11 DRAW, 0N BULK HERE, 1N TALL, 1N TOUR, /11 HOST: mask GOAL, 11 TEND, 1N AIDS ZONE, 0N TEND, /1N TALL LUCK, 0N< [find] HOST, /11 [hill] HOST AIDS, XX VARY, ?? WRAP """ actual = op_tk.run(source, "create", {"df": 1}, {"df": "DF"}) expected = """ BAKE, 0N TEND, 01 TALL DF, -1N TALL, -11 TOUR: slot DOWN, 0N [cold] TOUR, 0N [echo] TOUR: hang DF, _11 PORK, 0N TEND DF, 11 DRAW, 0N BULK HERE, 1N TALL, 1N TOUR, /11 HOST: mask DF, 11 TEND, 1N AIDS ZONE, 0N TEND, /1N TALL LUCK, 0N< [find] HOST, /11 [hill] HOST AIDS, XX VARY, ?? WRAP """ self.assertEqual(actual.strip(), expected.strip()) def test_fix_cards(self): source = "A, ON B, No No" actual = op_tk.run(source, "fix", {"cards": 1}, {}).strip() expected = "A, 0N B, 0N No" self.assertEqual(actual, expected) def test_delete_cards(self): source = "A, 01 B, 0N C" actual = op_tk.run(source, "delete", {"cards": 1}, {}).strip() expected = "A, XX B, XX C" self.assertEqual(actual, expected) def test_change_capitalization_cards(self): source = "A, 0N B, _1N No" actual = op_tk.run(source, "lower", {"cards": 1}, {}).strip() expected = "A, 0n B, _1n No" self.assertEqual(actual, expected) source = expected actual = op_tk.run(source, "upper", {"cards": 1}, {}).strip() expected = "A, 0N B, _1N No" self.assertEqual(actual, expected) def test_infer_roles(self): # Cases *N vs *1 source = "Rule, 0N Else, 11 Peel" actual = op_tk.run(source, "create", {"roles": 1}, {}).strip() expected = "Rule, 0N [Rule] Else, 11 Peel" self.assertEqual(actual, expected) source = "Rule, 1N Else, 11 Peel" actual = op_tk.run(source, "create", {"roles": 1}, {}).strip() expected = "Rule, 1N [Rule] Else, 11 Peel" self.assertEqual(actual, expected) source = "Rule, 0N Else, 01 Peel" actual = op_tk.run(source, "create", {"roles": 1}, {}).strip() expected = "Rule, 0N [Rule] Else, 01 Peel" self.assertEqual(actual, expected) source = "Rule, 1N Else, 01 Peel" actual = op_tk.run(source, "create", {"roles": 1}, {}).strip() expected = "Rule, 1N [Rule] Else, 01 Peel" self.assertEqual(actual, expected) source = "Rule, XX Else, 11 Peel" actual = op_tk.run(source, "create", {"roles": 1}, {}).strip() expected = "Rule, XX [Rule] Else, 11 Peel" self.assertEqual(actual, expected) source = "Rule, XX Else, 01 Peel" actual = op_tk.run(source, "create", {"roles": 1}, {}).strip() expected = "Rule, XX [Rule] Else, 01 Peel" self.assertEqual(actual, expected) # Cases *1 vs *1 source = "Rule, 01 Else, 11 Peel" actual = op_tk.run(source, "create", {"roles": 1}, {}).strip() expected = "Rule, 01 [Rule] Else, 11 Peel" self.assertEqual(actual, expected) source = "Rule, 11 Else, 11 Peel" actual = op_tk.run(source, "create", {"roles": 1}, {}).strip() expected = "Rule, 11 [Rule] Else, 11 [Rule] Peel" # slightly overkill self.assertEqual(actual, expected) # Other cases source = "Rule, 0N Else, 1N Peel" actual = op_tk.run(source, "create", {"roles": 1}, {}).strip() expected = "Rule, 0N Else, 1N Peel" self.assertEqual(actual, expected) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_rewrite_decompose.py ================================================ import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.rewrite import ( _explode as explode, _drain as drain, _split as split, ) class TestUpdater(unittest.TestCase): def test_split(self): source = """ Easy, 0N Team, 0N Tire, 11 Bath: foo, bar Plea, 0N Toll, 01 Calm, 0N Path Busy, 0N Vast, 0N Goal, 0N Chop Pole, 0N Snap, 11 Vary, 0N Tide, 1N Peak Loop, 0N Soil, 11 Cute +Aunt, 01 Dirt, 1N Path, 0N Meat Come, -01 Suit, 1N Plea, 0N Each Body, 11> File, 1N< Dust, 0N Peak Odds, 1N Tone, 01 Peak, 0N Bold, 0N Slip: item, snap Chef, 1N Skip, 01 City, 0N Cell: bold [VARCHAR(42)], dead [DATE] Victima, 11 Tradidit, 01 Periculosum, 0N Superbiam Clamorem, 01 Priorum, 0N Iret, 11 Loquatur Tuam, 11 Initiis, 01 Genua, 01 Haesit Commemorat, 01 Furius, 01 Sermonis, 01 Panem Anus, 11 Dianam, 11 Emplastrum, 1N Piscium """ actual = split.run(source) expected = """ Easy0, 11 Bath, 0N Team: foo, bar Easy1, 11 Bath, 0N Tire Plea0, 01 Calm, 0N Toll Plea1, 01 Calm, 0N Path Busy, 0N Vast, 0N Goal, 0N Chop Pole0, 11 Vary, 0N Snap Pole1, 11 Vary, 0N Tide Pole2, 11 Vary, 1N Peak Loop, 0N Soil, 11 Cute +Aunt0, 01 Dirt, 1N Path +Aunt1, 01 Dirt, 0N Meat Come0, -01 Suit, 1N Plea Come1, -01 Suit, 0N Each Body0, 11> File, 1N< Dust Body1, 11> File, 0N Peak Odds0, 01 Peak, 1N Tone: item, snap Odds1, 01 Peak, 0N Bold Odds2, 01 Peak, 0N Slip Chef0, 01 City, 1N Skip: bold [VARCHAR(42)], dead [DATE] Chef1, 01 City, 0N Cell Victima0, 11 Tradidit, 01 Periculosum Victima1, 11 Tradidit, 0N Superbiam Clamorem0, 11 Loquatur, 01 Priorum Clamorem1, 11 Loquatur, 0N Iret Tuam0, 11 Initiis, 01 Genua Tuam1, 11 Initiis, 01 Haesit Commemorat0, 01 Furius, 01 Sermonis Commemorat1, 01 Furius, 01 Panem Anus0, 11 Dianam, 11 Emplastrum Anus1, 11 Dianam, 1N Piscium """ self.assertEqual(actual.strip(), expected.strip()) def test_explode_weak(self): source = """ Wake: tend, bath Bowl, 0N Wake, 1N Move, 0N Poet: turn, from Poet: edge, skip Move: aids """ actual = explode.run(source, {"weak": True}) expected = """ Wake: tend, bath Bowl: _turn, from DF, _11 Bowl, 0N Wake DF, _11 Bowl, 1N Move DF, _11 Bowl, 0N Poet Poet: edge, skip Move: aids """ self.assertEqual(actual.strip(), expected.strip()) actual = explode.run(source) expected = """ Wake: tend, bath Bowl: id. bowl, turn, from DF, 11 Bowl, 0N Wake DF, 11 Bowl, 1N Move DF, 11 Bowl, 0N Poet Poet: edge, skip Move: aids """ self.assertEqual(actual.strip(), expected.strip()) def test_explode_arity(self): # TODO: more cases source = """ Edge: what, call Love, 0N Edge, 1N Ruin: toss, noon Ruin: area, slip Gene: five, away Hate, 0N Gene, 1N Rain Rain: iron, pose """ actual = explode.run(source, {"arity": "3"}) expected = source # no change self.assertEqual(actual.strip(), expected.strip()) actual = explode.run(source, {"arity": "2"}) expected = """ Edge: what, call Love: id. love, toss, noon DF, 11 Love, 0N Edge DF, 11 Love, 1N Ruin Ruin: area, slip Gene: five, away Hate: id. hate DF, 11 Hate, 0N Gene DF, 11 Hate, 1N Rain Rain: iron, pose """ self.assertEqual(actual.strip(), expected.strip()) def test_explode_cluster(self): source = """ Date: Date Réserver, /1N Client, 1N Chambre, 0N Date: Durée Chambre: Numéro, Prix Client: Id. client, Nom client """ actual = explode.run(source, {"weak": True}) expected = """ Date: Date Réserver: _Durée DF, 11 Réserver, 1N Client DF, _11 Réserver, 1N Chambre DF, _11 Réserver, 0N Date Chambre: Numéro, Prix Client: Id. client, Nom client """ self.assertEqual(actual.strip(), expected.strip()) def test_drain(self): source = """ Wake: tend, bath Bowl, 0N Wake, 11 Move: turn [type 1], from [type 2] Move: aids Hour, 01 Poet, 11 Move: chew Poet: edge, skip Draw, 01 Poet, 0N Rice: road Rice: easy, link """ actual = drain.run(source) expected = """ Wake: tend, bath Bowl, 0N Wake, 11 Move Move: aids, turn [type 1], from [type 2], chew Hour, 01 Poet, 11 Move Poet: edge, skip Draw, 01 Poet, 0N Rice: road Rice: easy, link """ self.assertEqual(actual.strip(), expected.strip()) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_rewrite_grow.py ================================================ import unittest import random import gettext __import__("sys").path[0:0] = ["mocodo"] from mocodo.rewrite._grow import run as grow class TestGrow(unittest.TestCase): def test_default(self): gettext.NullTranslations().install() random.seed(1) actual = grow("FOO: bar, biz", {}) expected = """ FOO: bar, biz Reflexive 2_, 1N FOO, 0N FOO Entity 3_: id 3 1, attr 3 2 Binary 4_, 1N Entity 3_, 11 FOO: attr 4 1 Reflexive 5_, 0N Entity 3_, 11 Entity 3_ Entity 6_: id 6 1, _id 6 2, attr 6 3 Ternary 7_, 1N Entity 6_, 1N FOO, 1N Entity 3_ Entity 8_: id 8 1, attr 8 2, attr 8 3, attr 8 4 Binary 9_, 0N Entity 8_, 0N Entity 6_ Entity 10_: id 10 1, attr 10 2, attr 10 3, attr 10 4 Binary 11_, 0N Entity 10_, 1N Entity 8_ Entity 12_: id 12 1 Binary 13_, 0N Entity 12_, 1N Entity 8_: attr 13 1 Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 Binary 15_, 11 Entity 14_, 01 Entity 10_ Entity 16_: id 16 1, attr 16 2 Binary 17_, 0N Entity 16_, 1N Entity 14_ Binary 18_, 1N Entity 14_, 01 Entity 10_ """.replace(" ", "") self.assertEqual(actual.strip(), expected.strip()) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_rewrite_labels.py ================================================ import unittest import random import gettext __import__("sys").path[0:0] = ["mocodo"] from mocodo.rewrite import op_tk gettext.NullTranslations().install() class TestUpdateLabels(unittest.TestCase): def test_upper(self): source = "composer, 0n [composée] pièce, 0n [composante] pièce: quantité" actual = op_tk.run(source, "upper", {"labels": ""}, {}) expected = "COMPOSER, 0n [composée] PIÈCE, 0n [composante] PIÈCE: QUANTITÉ" self.assertEqual(actual.strip(), expected.strip()) def test_obfuscate(self): source = """ Vitae justo: lobortis, purus adipiscing, 0N Curabitur, 0N Vitae justo, 0N DATE2 DATE2: date Pharetra, 0N Curabitur, 0N DATE, 0N Vitae justo: massa Curabitur: blandit, suscipit Porttitor, 1N Rhoncus, 0N DATE2 DATE: date Imperdiet, 0N Egestas, 0N Curabitur, 0N DATE Rhoncus: dolor a, bibendum, euismod, consectetuer, leo Egestas: vivamus, semper, aliquam Ultricies, 11 Rhoncus, 0N Egestas """ subargs = {"labels": "en4.txt"} params = {"script_directory": "mocodo", "df": "DF"} random.seed(42) actual = op_tk.run(source, "randomize", subargs, params) expected = """ Feel: turn, grin land, 0N Near, 0N Feel, 0N SILK SILK: debt Shoe, 0N Near, 0N LOSS, 0N Feel: poet Near: stir, auto Slew, 1N Tape, 0N SILK LOSS: debt Knee, 0N Code, 0N Near, 0N LOSS Tape: they, bath, unit, haul, draw Code: four, duck, icon Golf, 11 Tape, 0N Code """ self.assertEqual(actual.strip(), expected.strip()) def test_obfuscate_df(self): source = """ DF, 11 Curabitur, 0N Vitae justo, 0N DATE2 DF, 1N Rhoncus, 11 DATE2 DF, 11 Rhoncus, 0N Egestas """ subargs = {"labels": "en4.txt"} params = {"script_directory": "mocodo", "df": "DF"} random.seed(42) actual = op_tk.run(source, "randomize", subargs, params) expected = """ FEEL, 11 Turn, 0N Grin, 0N LAND NEAR, 1N Silk, 11 LAND DEBT, 11 Silk, 0N Shoe """ self.assertEqual(actual.strip(), expected.strip()) def test_obfuscate_pool_too_small(self): source = """ DF, 11 Curabitur, 0N Vitae justo, 0N DATE2 DF, 1N Rhoncus, 11 DATE2 DF, 11 Rhoncus, 0N Egestas """ subargs = {"labels": "test/test_data/small_pool.txt"} params = {"script_directory": "mocodo", "df": "DF"} random.seed(42) self.assertRaises(op_tk.MocodoError, op_tk.run, source, "randomize", subargs, params) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_rewrite_types.py ================================================ import unittest __import__("sys").path[0:0] = ["mocodo"] from mocodo.rewrite.types import guess_types, create_type_placeholders class TestUpdateTypes(unittest.TestCase): def test_create(self): source = """ MEAN: wash, rest [], king [int], HERE, 0N NICE, 0N MEAN: wood, much [], stop [int] NICE: _poke, news [], , lawn [int] """ actual = create_type_placeholders(source, "TODO") expected = """ MEAN: wash [TODO], rest [], king [int], HERE, 0N NICE, 0N MEAN: wood [TODO], much [], stop [int] NICE: _poke [TODO], news [], , lawn [int] """ self.assertEqual(actual.strip(), expected.strip()) def test_guess_types(self): source = """ ALREADY_TYPED: foo [int], bar [float], baz [date] EMPTY_BRACKETS: foo [], bar [], baz [] NON_TYPABLE: foo, bar, baz TYPABLE: person id, name, birth date """ actual = guess_types(source, {"script_directory": "mocodo"}) expected = """ ALREADY_TYPED: foo [int], bar [float], baz [date] EMPTY_BRACKETS: foo [], bar [], baz [] NON_TYPABLE: foo [], bar [], baz [] TYPABLE: person id [VARCHAR(8)], name [VARCHAR(255)], birth date [DATE] """ self.assertEqual(actual.strip(), expected.strip()) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_string_tools.py ================================================ import unittest from pathlib import Path __import__("sys").path[0:0] = ["mocodo"] from mocodo.tools.string_tools import * class StringToolsTest(unittest.TestCase): def test_wrap_label(self): sample = [ "Adresse 1", "Adresse 1 du détenteur", "Apporteur", "Ayant-droit", "Code pays", "Code pays de résidence du détenteur", "Code pays détenteur", "Code postal", "Commune du détenteur", "Date de création", "Date de création en BDNI", "Date de réception", "Date de réception en BDNI", "Nom", "Nom du détenteur", "Numéro de SIREN du détenteur", "Numéro détenteur", "Raison sociale", "Raison sociale ou situation civile", ] wrapped = [ ["Adresse", "1"], ["Adresse 1", "du", "détenteur"], ["Apporteur"], ["Ayant-", "droit"], ["Code pays"], ["Code pays de", "résidence du", "détenteur"], ["Code pays", "détenteur"], ["Code", "postal"], ["Commune", "du", "détenteur"], ["Date de", "création"], ["Date de", "création", "en BDNI"], ["Date de", "réception"], ["Date de", "réception", "en BDNI"], ["Nom"], ["Nom du", "détenteur"], ["Numéro de", "SIREN du", "détenteur"], ["Numéro", "détenteur"], ["Raison", "sociale"], ["Raison", "sociale ou", "situation", "civile"], ] for (label, expected) in zip(sample, wrapped): self.assertEqual(wrap_label(label), expected) def test_rstrip_digit_or_underline(self): self.assertEqual(rstrip_digit_or_underline("foo"), "foo") self.assertEqual(rstrip_digit_or_underline("foo1"), "foo") self.assertEqual(rstrip_digit_or_underline("foo12"), "foo1") self.assertEqual(rstrip_digit_or_underline(""), "") self.assertEqual(rstrip_digit_or_underline("1"), "") self.assertEqual(rstrip_digit_or_underline("foo_"), "foo") self.assertEqual(rstrip_digit_or_underline("foo1_"), "foo1") self.assertEqual(rstrip_digit_or_underline("_"), "") def test_surrounds(self): self.assertTrue(surrounds("(foobar)", "()")) self.assertTrue(surrounds("_foobar_", "_")) self.assertTrue(surrounds("_foobar_", "__")) self.assertFalse(surrounds("(foobar)", ")(")) self.assertTrue(surrounds("()", "()")) self.assertFalse(surrounds("", "()")) self.assertFalse(surrounds("foobar", "fo")) def test_snake(self): self.assertEqual(snake("foo"), "foo") self.assertEqual(snake("Foo"), "Foo") self.assertEqual(snake("FOO"), "FOO") self.assertEqual(snake("fooBar"), "foo_Bar") self.assertEqual(snake("fooBar"), "foo_Bar") self.assertEqual(snake("FOOBar"), "FOOBar") self.assertEqual(snake("fooBAR"), "foo_BAR") self.assertEqual(snake("foo bar"), "foo_bar") self.assertEqual(snake("foo bar"), "foo_bar") self.assertEqual(snake("foo-bar"), "foo_bar") self.assertEqual(snake("foo--bar"), "foo_bar") self.assertEqual(snake("foo_bar"), "foo_bar") self.assertEqual(snake("foo__bar"), "foo_bar") self.assertEqual(snake("foo1bar"), "foo_1_bar") self.assertEqual(snake("foo1Bar"), "foo_1_Bar") self.assertEqual(snake("foO1Bar"), "fo_O_1_Bar") self.assertEqual(snake("FOO BAR"), "FOO_BAR") self.assertEqual(snake("-FOO-BAR-"), "FOO_BAR") self.assertEqual(snake("_FOO_BAR_"), "FOO_BAR_") self.assertEqual(snake("_FOO_BAR1"), "FOO_BAR1") def test_camel(self): self.assertEqual(camel("foo"), "foo") self.assertEqual(camel("Foo"), "foo") self.assertEqual(camel("FOO"), "foo") self.assertEqual(camel("fooBar"), "fooBar") self.assertEqual(camel("FooBar"), "fooBar") self.assertEqual(camel("FOOBar"), "foobar") self.assertEqual(camel("fooBAR"), "fooBar") self.assertEqual(camel("foo bar"), "fooBar") self.assertEqual(camel("foo bar"), "fooBar") self.assertEqual(camel("foo-bar"), "fooBar") self.assertEqual(camel("foo--bar"), "fooBar") self.assertEqual(camel("foo_bar"), "fooBar") self.assertEqual(camel("foo__bar"), "fooBar") self.assertEqual(camel("foo1bar"), "foo1Bar") self.assertEqual(camel("foo1Bar"), "foo1Bar") self.assertEqual(camel("foO1Bar"), "foO1Bar") self.assertEqual(camel("FOO BAR"), "fooBar") self.assertEqual(camel("-FOO-BAR-"), "fooBar") self.assertEqual(camel("-FOO-BAR_"), "fooBar_") self.assertEqual(camel("-FOO-BAR1"), "fooBar1") def test_pascal(self): self.assertEqual(pascal("foo"), "Foo") self.assertEqual(pascal("Foo"), "Foo") self.assertEqual(pascal("FOO"), "Foo") self.assertEqual(pascal("fooBar"), "FooBar") self.assertEqual(pascal("FooBar"), "FooBar") self.assertEqual(pascal("FOOBar"), "Foobar") self.assertEqual(pascal("fooBAR"), "FooBar") self.assertEqual(pascal("foo bar"), "FooBar") self.assertEqual(pascal("foo bar"), "FooBar") self.assertEqual(pascal("foo-bar"), "FooBar") self.assertEqual(pascal("foo--bar"), "FooBar") self.assertEqual(pascal("foo_bar"), "FooBar") self.assertEqual(pascal("foo__bar"), "FooBar") self.assertEqual(pascal("foo1bar"), "Foo1Bar") self.assertEqual(pascal("foo1Bar"), "Foo1Bar") self.assertEqual(pascal("foO1Bar"), "FoO1Bar") self.assertEqual(pascal("FOO BAR"), "FooBar") self.assertEqual(pascal("-FOO-BAR-"), "FooBar") self.assertEqual(pascal("-FOO-BAR_"), "FooBar_") self.assertEqual(pascal("-FOO-BAR1"), "FooBar1") def test_aggressive_split(self): f = aggressive_split self.assertEqual(f(''), []) self.assertEqual(f('p'), ['p']) self.assertEqual(f('3-'), ['3']) self.assertEqual(f('NOM'), ['NOM']) self.assertEqual(f('CONTRÔLER'), ['CONTRÔLER']) self.assertEqual(f('Nom'), ['Nom']) self.assertEqual(f('nom'), ['nom']) self.assertEqual(f('Nom lieu'), ['Nom', 'lieu']) self.assertEqual(f('nom de compte'), ['nom', 'de', 'compte']) self.assertEqual(f('Nom_lieu'), ['Nom', 'lieu']) self.assertEqual(f('Nom_Lieu'), ['Nom', 'Lieu']) self.assertEqual(f('NomLieu'), ['Nom', 'Lieu']) self.assertEqual(f('nomlieu'), ['nomlieu']) self.assertEqual(f('Gratte-ciel'), ['Gratte', 'ciel']) self.assertEqual(f('Identité_pays'), ['Identité', 'pays']) self.assertEqual(f('ID_Produit'), ['ID', 'Produit']) self.assertEqual(f('FolderID'), ['Folder', 'ID']) self.assertEqual(f('IdVoyage-'), ['Id', 'Voyage']) self.assertEqual(f('N°Lieu'), ['N', 'Lieu']) self.assertEqual(f('N°lieu'), ['N', 'lieu']) self.assertEqual(f('NumLieuTournage-'), ['Num', 'Lieu', 'Tournage']) self.assertEqual(f('nomPays_EN'), ['nom', 'Pays', 'EN']) self.assertEqual(f('Conditions(Affichage)'), ['Conditions', 'Affichage']) self.assertEqual(f('Conditions (Affichage)'), ['Conditions', 'Affichage']) self.assertEqual(f('COMPOSE3'), ['COMPOSE', '3']) self.assertEqual(f('téléphone2'), ['téléphone', '2']) self.assertEqual(f('téléphone23'), ['téléphone', '23']) self.assertEqual(f('téléphone 2'), ['téléphone', '2']) self.assertEqual(f('beginning-date'), ['beginning', 'date']) self.assertEqual(f('latitude_L'), ['latitude', 'L']) self.assertEqual(f('id_insert → equipement_id'), ['id', 'insert', 'equipement', 'id']) self.assertEqual(f('CNIPays'), ['CNIPays']) self.assertEqual(f('réf. client'), ['réf', 'client']) self.assertEqual(f('kWday_summer'), ['k', 'Wday', 'summer']) self.assertEqual(f('DescriptionŒuvre'), ['Description', 'Œuvre']) if __name__ == "__main__": unittest.main() ================================================ FILE: test/test_zoo.py ================================================ import json import time from pathlib import Path import shutil from mocodo.argument_parser import parsed_arguments, init_localization from mocodo.common import Common from mocodo.convert.read_template import read_template from mocodo.font_metrics import font_metrics_factory from mocodo.mcd import Mcd from mocodo.convert.relations import Relations from mocodo.mocodo_error import MocodoError import random import re from mocodo.convert import ( _chen as chen, _crow as crow, _uml as uml, ) from mocodo.rewrite import ( op_tk, _explode as explode, _drain as drain, _split as split, _drown as drown, ) from mocodo.rewrite.arrange_bb import arrange from mocodo.mcd_to_svg import main as dump_mcd_to_svg import os TEMPLATE_DIR = Path("mocodo/resources/relation_templates/") ZOO_DIR = Path("test/zoo") def save(path, text): text = re.sub(r"(?m)^.+Generated by Mocodo.*\n+", "", text) Path(path).write_text(text, encoding="utf8") def main(): def dump_static_svg(mcd, mcd_path): output_name = params["output_name"] = str(mcd_path)[:-4] Path(f"{output_name}_geo.json").unlink(missing_ok=True) dump_mcd_to_svg(mcd, common, suppress_mocodo_header=True) if Path(f"{output_name}_static.svg").is_file(): Path(f"{output_name}.svg").unlink() Path(f"{output_name}_static.svg").rename(f"{output_name}.svg") Path(f"{output_name}_geo.json").unlink(missing_ok=True) def convert_dot_to_svg(dot_path): svg_path = dot_path.with_suffix(".svg") os.system(f"dot -Tsvg {dot_path} -o {svg_path}") return svg_path def may_dump_rewritten_version(subfolder, source, stem, operation_name, subargs=None): try: module = globals()[operation_name] # e.g., explode, drain, ... except KeyError: module = op_tk result = module.run(source, op_name=operation_name, subargs=subargs, params=params).rstrip() suffix = ",".join(f"{k}={v}" if v else k for (k, v) in subargs.items()) if suffix: suffix = f"_{suffix}" new_mcd_path = subfolder / f"{stem}_rw_{operation_name}{suffix}.mcd" if result == source: result = "% same as the source file" save(new_mcd_path, result + "\n") return for path in subfolder.glob(f"{stem}_rw_{operation_name}*.mcd"): if result == path.read_text(encoding="utf8"): result = f"% same as {path.name}" save(new_mcd_path, result + "\n") return if operation_name in ("explode", "split"): random.seed(42) result = arrange(result, {}, has_expired=lambda: False) mcd = Mcd(result, get_font_metrics, **params) save(new_mcd_path, result + "\n") dump_static_svg(mcd, new_mcd_path) templates = [] template_index = json.loads(Path(TEMPLATE_DIR / "_index.json").read_text(encoding="utf8")) for name in template_index: if re.match(r"html|markdown|latex|text", name): name += "-bce" elif re.match(r"mysql|oracle|postgresql|sqlite|mssql", name): continue templates.append(read_template(name, TEMPLATE_DIR)) params = parsed_arguments([]) params["guess_title"] = False params["id_gutter_visibility"] = "auto" params["id_gutter_strong_string"] = "ID" params["id_gutter_weak_string"] = "id" params["id_gutter_alts"] = dict(zip("123456789", "123456789")) params["guess_title"] = False params["language"] = "fr" params["df"] = "DF" params["seed"] = 42 # only to be dumped in graphviz files common = Common(params) init_localization("fr") get_font_metrics = font_metrics_factory(params) for path in ZOO_DIR.glob("*/*"): if path.is_dir(): shutil.rmtree(path, ignore_errors=True) # suppress all subfolders elif not path.name.startswith("_"): path.unlink() # suppress all files except those starting with "_" for source_path in sorted(ZOO_DIR.glob("*/*.mcd")): print(f"Processing {source_path.name}") params["title"] = source_path.parent.name source = source_path.read_text(encoding="utf8").rstrip() # Create some rewritten versions of the source file. subfolder = Path(source_path.parent / "rewritten") subfolder.mkdir(exist_ok=True) may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "explode", {"arity": 2, "weak": ""}) may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "explode", {"arity": 2.5, "weak": ""}) may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "explode", {"arity": 3, "weak": ""}) may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "explode", {"arity": 2}) may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "explode", {"arity": 2.5}) may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "explode", {"arity": 3}) may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "drain", {}) may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "split", {}) may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "drown", {}) may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "create", {"df_arrows": "across"}) if source_path.name.startswith("_triple_N11"): may_dump_rewritten_version(subfolder, source, source_path.stem[1:], "create", {"cifs": ""}) mcd = Mcd(source, get_font_metrics, **params) # Create some converted files. subfolder = Path(source_path.parent / "exported") subfolder.mkdir(exist_ok=True) text = chen.run(source, {}, common, testing=True).replace(" ", "") + "\n" save(subfolder / f"{source_path.stem[1:]}_erd_chen.txt", text) text = chen.run(source, {"attrs": 1}, common)["text"] save(subfolder / f"{source_path.stem[1:]}_erd_chen.gv", text) convert_dot_to_svg(subfolder / f"{source_path.stem[1:]}_erd_chen.gv") text = uml.run(source, {"plantuml": "-"}, common)["text"] # delete the default preamble save(subfolder / f"{source_path.stem[1:]}_uml.puml", text) try: text = crow.run(source, {"mmd": True}, common)["text"] save(subfolder / f"{source_path.stem[1:]}_erd_crow.mmd", text) text = crow.run(source, {"gv": True}, common)["text"] save(subfolder / f"{source_path.stem[1:]}_erd_crow.gv", text) convert_dot_to_svg(subfolder / f"{source_path.stem[1:]}_erd_crow.gv") except AssertionError: save(subfolder / f"{source_path.stem[1:]}_erd_crow.mmd", "Not implemented for n-ary associations.") # Create the non-sql output files. subfolder = Path(source_path.parent / "mld") subfolder.mkdir(exist_ok=True) for template in templates: if template["extension"] in ("sql", "dbml", "d2"): continue relations = Relations(mcd, params) output = relations.get_text(template).strip() + "\n" output_path = subfolder / f"{source_path.stem[1:]}{template['stem_suffix']}.{template['extension']}" save(output_path, output) if template["extension"] == "gv": convert_dot_to_svg(output_path) elif template["extension"] == "mcd": try: relational_diagram_mcd = Mcd(output, get_font_metrics, **params) except MocodoError as e: assert e.args[0].startswith("Mocodo Err.4") # Empty MCD else: dump_static_svg(relational_diagram_mcd, output_path) # Create the svg output file. dump_static_svg(mcd, source_path.parent / f"{source_path.stem[1:]}.mcd") # Create the dbml and d2 output files. subfolder = Path(source_path.parent / "ddl") subfolder.mkdir(exist_ok=True) for template in templates: if template["extension"] in ("dbml", "d2"): output = relations.get_text(template).strip() + "\n" save(subfolder / f"{source_path.stem[1:]}{template['stem_suffix']}.{template['extension']}", output) # Create the sql output files. Warning: modifies source. Must be final. subfolder = Path(source_path.parent / "ddl") subfolder.mkdir(exist_ok=True) source = op_tk.run(source, "create", {"types": None}, params) # guess types source = op_tk.run(source, "ascii", {"labels": 1, "roles": 1}, params) source = op_tk.run(source, "snake", {"labels": 1}, params) source = op_tk.run(source, "lower", {"attrs": 1, "roles": 1}, params) source = op_tk.run(source, "upper", {"boxes": 1}, params) mcd = Mcd(source, get_font_metrics, **params) for template in templates: if template["extension"] != "sql": continue relations = Relations(mcd, params) output = relations.get_text(template).strip() + "\n" save(subfolder / f"{source_path.stem[1:]}{template['stem_suffix']}.sql", output) def test_launch_snapshot(capsys): with capsys.disabled(): print("\nCancel the long snapshot calculation by pressing Ctrl+C within 5 seconds", end="") for _ in range(5): time.sleep(1) print(".", end="", flush=True) print("\nLet's be crazy!") main() if __name__ == "__main__": main() ================================================ FILE: test/zoo/alt/_alt_0.mcd ================================================ CLIENT: Réf. client, 1_Nom, 1_Prénom, Adresse, 2_Mail FOO: foo, 1_bar, 12_biz, 2_buz, 3_qux, 123_quux UTILISER: 1_carnet, 02_projet, 12_technicien ================================================ FILE: test/zoo/alt/ddl/alt_0_ddl.d2 ================================================ "CLIENT": { shape: sql_table "Réf. client": VARCHAR(42) {constraint: PK} "Nom": VARCHAR(42) {constraint: UNQ1} "Prénom": VARCHAR(42) {constraint: UNQ1} "Adresse": VARCHAR(42) "Mail": VARCHAR(42) {constraint: UNQ2} } "FOO": { shape: sql_table "foo": VARCHAR(42) {constraint: PK} "bar": VARCHAR(42) {constraint: UNQ1} "biz": VARCHAR(42) {constraint: [UNQ1; UNQ2]} "buz": VARCHAR(42) {constraint: UNQ2} "qux": VARCHAR(42) {constraint: UNQ3} "quux": VARCHAR(42) {constraint: [UNQ1; UNQ2; UNQ3]} } "UTILISER": { shape: sql_table "carnet": VARCHAR(42) {constraint: [PK; UNQ1]} "projet": VARCHAR(42) {constraint: [PK; UNQ2]} "technicien": VARCHAR(42) {constraint: [UNQ1; UNQ2]} } ================================================ FILE: test/zoo/alt/ddl/alt_0_ddl.dbml ================================================ Table "CLIENT" { "Réf. client" VARCHAR(42) [pk, NOT NULL] "Nom" VARCHAR(42) "Prénom" VARCHAR(42) "Adresse" VARCHAR(42) "Mail" VARCHAR(42) Indexes { ("Nom", "Prénom") [unique] "Mail" [unique] } } Table "FOO" { "foo" VARCHAR(42) [pk, NOT NULL] "bar" VARCHAR(42) "biz" VARCHAR(42) "buz" VARCHAR(42) "qux" VARCHAR(42) "quux" VARCHAR(42) Indexes { ("biz", "quux", "buz") [unique] ("quux", "qux") [unique] ("bar", "biz", "quux") [unique] } } Table "UTILISER" { "carnet" VARCHAR(42) [NOT NULL] "projet" VARCHAR(42) [NOT NULL] "technicien" VARCHAR(42) Indexes { ("carnet", "projet") [pk] ("technicien", "projet") [unique] ("carnet", "technicien") [unique] } } ================================================ FILE: test/zoo/alt/ddl/alt_0_ddl.sql ================================================ CREATE TABLE CLIENT ( PRIMARY KEY (ref_client), ref_client VARCHAR(8) NOT NULL, nom VARCHAR(255), prenom VARCHAR(255), adresse VARCHAR(30), mail VARCHAR(255), UNIQUE (nom, prenom), UNIQUE (mail) ); CREATE TABLE FOO ( PRIMARY KEY (foo), foo VARCHAR(42) NOT NULL, bar VARCHAR(42), biz VARCHAR(42), buz VARCHAR(42), qux VARCHAR(42), quux VARCHAR(42), UNIQUE (bar, biz, quux), UNIQUE (biz, buz, quux), UNIQUE (qux, quux) ); CREATE TABLE UTILISER ( PRIMARY KEY (carnet, projet), carnet VARCHAR(42) NOT NULL, projet VARCHAR(42) NOT NULL, technicien VARCHAR(42), UNIQUE (carnet, technicien), UNIQUE (projet, technicien) ); ================================================ FILE: test/zoo/alt/exported/alt_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] // Associative entities 1 [label="CLIENT",shape=Mdiamond] 7 [label="FOO",shape=Mdiamond] 14 [label="UTILISER",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="Nom"] 4 [label="Prénom"] 5 [label="Adresse"] 6 [label="Mail"] 9 [label="bar"] 10 [label="biz"] 11 [label="buz"] 12 [label="qux"] 13 [label="quux"] 17 [label="technicien"] // Weak and strong entity attributes 2 [label=<Réf.
    client
    >] 8 [label=<foo>] 15 [label=<carnet>] 16 [label=<projet>] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 1 -- 4 1 -- 5 1 -- 6 7 -- 8 7 -- 9 7 -- 10 7 -- 11 7 -- 12 7 -- 13 14 -- 15 14 -- 16 14 -- 17 } ================================================ FILE: test/zoo/alt/exported/alt_0_erd_chen.txt ================================================ ================================================ FILE: test/zoo/alt/exported/alt_0_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    CLIENT
    PKRéf. client
    Nom
    Prénom
    Adresse
    Mail
    >] 2 [label=<
    FOO
    PKfoo
    bar
    biz
    buz
    qux
    quux
    >] 3 [label=<
    UTILISER
    PKcarnet
    PKprojet
    technicien
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] } ================================================ FILE: test/zoo/alt/exported/alt_0_erd_crow.mmd ================================================ erDiagram CLIENT { TYPE Ref_client PK TYPE Nom TYPE Prenom TYPE Adresse TYPE Mail } FOO { TYPE foo PK TYPE bar TYPE biz TYPE buz TYPE qux TYPE quux } UTILISER { TYPE carnet PK TYPE projet PK TYPE technicien } ================================================ FILE: test/zoo/alt/exported/alt_0_uml.puml ================================================ @startuml "alt" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("CLIENT") { {field} + pk(Réf. client) {field} + Nom {field} + Prénom {field} + Adresse {field} + Mail } Table("FOO") { {field} + pk(foo) {field} + bar {field} + biz {field} + buz {field} + qux {field} + quux } Table("UTILISER") { {field} + pk(carnet) {field} + pk(projet) {field} + technicien } @enduml ================================================ FILE: test/zoo/alt/mld/alt_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note CLIENT Réf. client ! primary_key True CLIENT Nom 1 normal_attribute False CLIENT Prénom 1 normal_attribute False CLIENT Adresse normal_attribute False CLIENT Mail 2 normal_attribute False FOO foo ! primary_key True FOO bar 1 normal_attribute False FOO biz 12 normal_attribute False FOO buz 2 normal_attribute False FOO qux 3 normal_attribute False FOO quux 123 normal_attribute False UTILISER carnet ! 1 primary_key True UTILISER projet ! 2 primary_key True UTILISER technicien 12 normal_attribute False ================================================ FILE: test/zoo/alt/mld/alt_0_dependencies.gv ================================================ digraph { node [shape=box] } ================================================ FILE: test/zoo/alt/mld/alt_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD alt
    CLIENT ( Réf. client, Nom u1, Prénom u1, Adresse, Mail u2 )
    • Le champ Réf. client constitue la clé primaire de la table. C'était déjà un identifiant de l'entité CLIENT.
    • Les champs Nom et Prénom étaient déjà de simples attributs de l'entité CLIENT. Il obéit à la contrainte d'unicité 1.
    • Le champ Adresse était déjà un simple attribut de l'entité CLIENT.
    • Le champ Mail était déjà un simple attribut de l'entité CLIENT. Il obéit à la contrainte d'unicité 2.
    FOO ( foo, bar u1, biz u1 u2, buz u2, qux u3, quux u1 u2 u3 )
    • Le champ foo constitue la clé primaire de la table. C'était déjà un identifiant de l'entité FOO.
    • Le champ bar était déjà un simple attribut de l'entité FOO. Il obéit à la contrainte d'unicité 1.
    • Le champ biz était déjà un simple attribut de l'entité FOO. Il obéit aux contraintes d'unicité 1 et 2.
    • Le champ buz était déjà un simple attribut de l'entité FOO. Il obéit à la contrainte d'unicité 2.
    • Le champ qux était déjà un simple attribut de l'entité FOO. Il obéit à la contrainte d'unicité 3.
    • Le champ quux était déjà un simple attribut de l'entité FOO. Il obéit aux contraintes d'unicité 1, 2 et 3.
    UTILISER ( carnet u1, projet u2, technicien u1 u2 )
    • Le champ carnet fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité UTILISER. Il obéit en outre à la contrainte d'unicité 1.
    • Le champ projet fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité UTILISER. Il obéit en outre à la contrainte d'unicité 2.
    • Le champ technicien était déjà un simple attribut de l'entité UTILISER. Il obéit aux contraintes d'unicité 1 et 2.
    ================================================ FILE: test/zoo/alt/mld/alt_0_mld.mcd ================================================ %%mocodo : CLIENT: Réf. client, Nom, Prénom, Adresse, Mail : FOO: foo, bar, biz, buz, qux, quux : UTILISER: carnet, _projet, technicien : ================================================ FILE: test/zoo/alt/mld/alt_0_mld.md ================================================ - **CLIENT** (Réf. client, Nom u1, Prénom u1, Adresse, Mail u2) - Le champ _Réf. client_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _CLIENT_. - Les champs _Nom_ et _Prénom_ étaient déjà de simples attributs de l'entité _CLIENT_. Il obéit à la contrainte d'unicité 1. - Le champ _Adresse_ était déjà un simple attribut de l'entité _CLIENT_. - Le champ _Mail_ était déjà un simple attribut de l'entité _CLIENT_. Il obéit à la contrainte d'unicité 2. - **FOO** (foo, bar u1, biz u1 u2, buz u2, qux u3, quux u1 u2 u3) - Le champ _foo_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _FOO_. - Le champ _bar_ était déjà un simple attribut de l'entité _FOO_. Il obéit à la contrainte d'unicité 1. - Le champ _biz_ était déjà un simple attribut de l'entité _FOO_. Il obéit aux contraintes d'unicité 1 et 2. - Le champ _buz_ était déjà un simple attribut de l'entité _FOO_. Il obéit à la contrainte d'unicité 2. - Le champ _qux_ était déjà un simple attribut de l'entité _FOO_. Il obéit à la contrainte d'unicité 3. - Le champ _quux_ était déjà un simple attribut de l'entité _FOO_. Il obéit aux contraintes d'unicité 1, 2 et 3. - **UTILISER** (carnet u1, projet u2, technicien u1 u2) - Le champ _carnet_ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _UTILISER_. Il obéit en outre à la contrainte d'unicité 1. - Le champ _projet_ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _UTILISER_. Il obéit en outre à la contrainte d'unicité 2. - Le champ _technicien_ était déjà un simple attribut de l'entité _UTILISER_. Il obéit aux contraintes d'unicité 1 et 2. ================================================ FILE: test/zoo/alt/mld/alt_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{alt}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{CLIENT} (\prim{Réf. client}, \attr{Nom}$^{u\_1}$, \attr{Prénom}$^{u\_1}$, \attr{Adresse}, \attr{Mail}$^{u\_2}$) \begin{itemize} \item Le champ \emph{Réf. client} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CLIENT}. \item Les champs \emph{Nom} et \emph{Prénom} étaient déjà de simples attributs de l'entité \emph{CLIENT}. Il obéit à la contrainte d'unicité 1. \item Le champ \emph{Adresse} était déjà un simple attribut de l'entité \emph{CLIENT}. \item Le champ \emph{Mail} était déjà un simple attribut de l'entité \emph{CLIENT}. Il obéit à la contrainte d'unicité 2. \end{itemize} \item \relat{FOO} (\prim{foo}, \attr{bar}$^{u\_1}$, \attr{biz}$^{u\_1 u\_2}$, \attr{buz}$^{u\_2}$, \attr{qux}$^{u\_3}$, \attr{quux}$^{u\_1 u\_2 u\_3}$) \begin{itemize} \item Le champ \emph{foo} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{FOO}. \item Le champ \emph{bar} était déjà un simple attribut de l'entité \emph{FOO}. Il obéit à la contrainte d'unicité 1. \item Le champ \emph{biz} était déjà un simple attribut de l'entité \emph{FOO}. Il obéit aux contraintes d'unicité 1 et 2. \item Le champ \emph{buz} était déjà un simple attribut de l'entité \emph{FOO}. Il obéit à la contrainte d'unicité 2. \item Le champ \emph{qux} était déjà un simple attribut de l'entité \emph{FOO}. Il obéit à la contrainte d'unicité 3. \item Le champ \emph{quux} était déjà un simple attribut de l'entité \emph{FOO}. Il obéit aux contraintes d'unicité 1, 2 et 3. \end{itemize} \item \relat{UTILISER} (\prim{carnet}$^{u\_1}$, \prim{projet}$^{u\_2}$, \attr{technicien}$^{u\_1 u\_2}$) \begin{itemize} \item Le champ \emph{carnet} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{UTILISER}. Il obéit en outre à la contrainte d'unicité 1. \item Le champ \emph{projet} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{UTILISER}. Il obéit en outre à la contrainte d'unicité 2. \item Le champ \emph{technicien} était déjà un simple attribut de l'entité \emph{UTILISER}. Il obéit aux contraintes d'unicité 1 et 2. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/alt/mld/alt_0_mld.txt ================================================ - CLIENT (_Réf. client_, Nom¹, Prénom¹, Adresse, Mail²) - Le champ « Réf. client » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « CLIENT ». - Les champs « Nom » et « Prénom » étaient déjà de simples attributs de l'entité « CLIENT ». Il obéit à la contrainte d'unicité 1. - Le champ « Adresse » était déjà un simple attribut de l'entité « CLIENT ». - Le champ « Mail » était déjà un simple attribut de l'entité « CLIENT ». Il obéit à la contrainte d'unicité 2. - FOO (_foo_, bar¹, biz¹², buz², qux³, quux¹²³) - Le champ « foo » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « FOO ». - Le champ « bar » était déjà un simple attribut de l'entité « FOO ». Il obéit à la contrainte d'unicité 1. - Le champ « biz » était déjà un simple attribut de l'entité « FOO ». Il obéit aux contraintes d'unicité 1 et 2. - Le champ « buz » était déjà un simple attribut de l'entité « FOO ». Il obéit à la contrainte d'unicité 2. - Le champ « qux » était déjà un simple attribut de l'entité « FOO ». Il obéit à la contrainte d'unicité 3. - Le champ « quux » était déjà un simple attribut de l'entité « FOO ». Il obéit aux contraintes d'unicité 1, 2 et 3. - UTILISER (_carnet_¹, _projet_², technicien¹²) - Le champ « carnet » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « UTILISER ». Il obéit en outre à la contrainte d'unicité 1. - Le champ « projet » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « UTILISER ». Il obéit en outre à la contrainte d'unicité 2. - Le champ « technicien » était déjà un simple attribut de l'entité « UTILISER ». Il obéit aux contraintes d'unicité 1 et 2. ================================================ FILE: test/zoo/alt/rewritten/alt_0_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/alt/rewritten/alt_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/alt/rewritten/alt_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, 1_at 1 2, 1_at 1 3, at 1 4, 2_at 1 5 ENTITÉ 2_: at 2 1, 1_at 2 2, 12_at 2 3, 2_at 2 4, 3_at 2 5, 123_at 2 6 ENTITÉ 3_: 1_at 3 1, 02_at 3 2, 12_at 3 3 ================================================ FILE: test/zoo/alt/rewritten/alt_0_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/alt/rewritten/alt_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/alt/rewritten/alt_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/alt/rewritten/alt_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/alt/rewritten/alt_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/alt/rewritten/alt_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/alt/rewritten/alt_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/basic/_basic_0.mcd ================================================ CLIENT: Réf. client, Nom, Prénom, Adresse DF, 0N> CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité PRODUIT: Réf. produit, Libellé, Prix unitaire ================================================ FILE: test/zoo/basic/_basic_1.mcd ================================================ CLIENT: Réf. client, Nom, Prénom, Adresse -Reflexive 6_, 11 COMMANDE, 01 COMMANDE -Entity 7_: id 7 1, _id 7 2, attr 7 3, attr 7 4 -Reflexive 13_, 11 PRODUIT, 1N PRODUIT DF, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant -Ternary 8_, 0N Entity 7_, 0N COMMANDE, 1N PRODUIT PRODUIT: Réf. produit, Libellé, Prix unitaire -Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 -Binary 15_, 0N Entity 14_, 01 Entity 11_ INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité -Binary 10_, 1N Entity 9_, 1N PRODUIT: attr 10 1 -Binary 16_, 0N Entity 14_, 1N Entity 11_ -Entity 11_: id 11 1, attr 11 2 -Binary 12_, 0N Entity 11_, 11 Entity 9_: attr 12 1 -Entity 9_: id 9 1, attr 9 2, attr 9 3 ================================================ FILE: test/zoo/basic/_basic_2.mcd ================================================ CLIENT: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)] PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)] INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité [INTEGER] PRODUIT: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)] ================================================ FILE: test/zoo/basic/ddl/basic_0_ddl.d2 ================================================ "CLIENT": { shape: sql_table "Réf. client": VARCHAR(42) {constraint: PK} "Nom": VARCHAR(42) "Prénom": VARCHAR(42) "Adresse": VARCHAR(42) } "COMMANDE": { shape: sql_table "Num. commande": VARCHAR(42) {constraint: PK} "Date": VARCHAR(42) "Montant": VARCHAR(42) "Réf. client": VARCHAR(42) {constraint: [FK; NOT NULL]} } "INCLURE": { shape: sql_table "Num. commande": VARCHAR(42) {constraint: [PK; FK]} "Réf. produit": VARCHAR(42) {constraint: [PK; FK]} "Quantité": VARCHAR(42) } "PRODUIT": { shape: sql_table "Réf. produit": VARCHAR(42) {constraint: PK} "Libellé": VARCHAR(42) "Prix unitaire": VARCHAR(42) } "COMMANDE"."Réf. client" -> "CLIENT"."Réf. client" "INCLURE"."Num. commande" -> "COMMANDE"."Num. commande" "INCLURE"."Réf. produit" -> "PRODUIT"."Réf. produit" ================================================ FILE: test/zoo/basic/ddl/basic_0_ddl.dbml ================================================ Table "CLIENT" { "Réf. client" VARCHAR(42) [pk, NOT NULL] "Nom" VARCHAR(42) "Prénom" VARCHAR(42) "Adresse" VARCHAR(42) } Table "COMMANDE" { "Num. commande" VARCHAR(42) [pk, NOT NULL] "Date" VARCHAR(42) "Montant" VARCHAR(42) "Réf. client" VARCHAR(42) [NOT NULL] } Table "INCLURE" { "Num. commande" VARCHAR(42) [NOT NULL] "Réf. produit" VARCHAR(42) [NOT NULL] "Quantité" VARCHAR(42) Indexes { ("Num. commande", "Réf. produit") [pk] } } Table "PRODUIT" { "Réf. produit" VARCHAR(42) [pk, NOT NULL] "Libellé" VARCHAR(42) "Prix unitaire" VARCHAR(42) } Ref:"COMMANDE"."Réf. client" > "CLIENT"."Réf. client" Ref:"INCLURE"."Num. commande" > "COMMANDE"."Num. commande" Ref:"INCLURE"."Réf. produit" > "PRODUIT"."Réf. produit" ================================================ FILE: test/zoo/basic/ddl/basic_0_ddl.sql ================================================ CREATE TABLE CLIENT ( PRIMARY KEY (ref_client), ref_client VARCHAR(8) NOT NULL, nom VARCHAR(255), prenom VARCHAR(255), adresse VARCHAR(30) ); CREATE TABLE COMMANDE ( PRIMARY KEY (num_commande), num_commande VARCHAR(8) NOT NULL, date DATE, montant DECIMAL(10,2), ref_client VARCHAR(8) NOT NULL ); CREATE TABLE INCLURE ( PRIMARY KEY (num_commande, ref_produit), num_commande VARCHAR(8) NOT NULL, ref_produit VARCHAR(8) NOT NULL, quantite INTEGER ); CREATE TABLE PRODUIT ( PRIMARY KEY (ref_produit), ref_produit VARCHAR(8) NOT NULL, libelle VARCHAR(50), prix_unitaire DECIMAL(10,2) ); ALTER TABLE COMMANDE ADD FOREIGN KEY (ref_client) REFERENCES CLIENT (ref_client); ALTER TABLE INCLURE ADD FOREIGN KEY (ref_produit) REFERENCES PRODUIT (ref_produit); ALTER TABLE INCLURE ADD FOREIGN KEY (num_commande) REFERENCES COMMANDE (num_commande); ================================================ FILE: test/zoo/basic/ddl/basic_1_ddl.d2 ================================================ "CLIENT": { shape: sql_table "Réf. client": VARCHAR(42) {constraint: PK} "Nom": VARCHAR(42) "Prénom": VARCHAR(42) "Adresse": VARCHAR(42) } "COMMANDE": { shape: sql_table "Num. commande": VARCHAR(42) {constraint: PK} "Date": VARCHAR(42) "Montant": VARCHAR(42) "Réf. client": VARCHAR(42) {constraint: [FK; NOT NULL]} } "INCLURE": { shape: sql_table "Num. commande": VARCHAR(42) {constraint: [PK; FK]} "Réf. produit": VARCHAR(42) {constraint: [PK; FK]} "Quantité": VARCHAR(42) } "PRODUIT": { shape: sql_table "Réf. produit": VARCHAR(42) {constraint: PK} "Libellé": VARCHAR(42) "Prix unitaire": VARCHAR(42) } "COMMANDE"."Réf. client" -> "CLIENT"."Réf. client" "INCLURE"."Num. commande" -> "COMMANDE"."Num. commande" "INCLURE"."Réf. produit" -> "PRODUIT"."Réf. produit" ================================================ FILE: test/zoo/basic/ddl/basic_1_ddl.dbml ================================================ Table "CLIENT" { "Réf. client" VARCHAR(42) [pk, NOT NULL] "Nom" VARCHAR(42) "Prénom" VARCHAR(42) "Adresse" VARCHAR(42) } Table "COMMANDE" { "Num. commande" VARCHAR(42) [pk, NOT NULL] "Date" VARCHAR(42) "Montant" VARCHAR(42) "Réf. client" VARCHAR(42) [NOT NULL] } Table "INCLURE" { "Num. commande" VARCHAR(42) [NOT NULL] "Réf. produit" VARCHAR(42) [NOT NULL] "Quantité" VARCHAR(42) Indexes { ("Num. commande", "Réf. produit") [pk] } } Table "PRODUIT" { "Réf. produit" VARCHAR(42) [pk, NOT NULL] "Libellé" VARCHAR(42) "Prix unitaire" VARCHAR(42) } Ref:"COMMANDE"."Réf. client" > "CLIENT"."Réf. client" Ref:"INCLURE"."Num. commande" > "COMMANDE"."Num. commande" Ref:"INCLURE"."Réf. produit" > "PRODUIT"."Réf. produit" ================================================ FILE: test/zoo/basic/ddl/basic_1_ddl.sql ================================================ CREATE TABLE CLIENT ( PRIMARY KEY (ref_client), ref_client VARCHAR(8) NOT NULL, nom VARCHAR(255), prenom VARCHAR(255), adresse VARCHAR(30) ); CREATE TABLE COMMANDE ( PRIMARY KEY (num_commande), num_commande VARCHAR(8) NOT NULL, date DATE, montant DECIMAL(10,2), ref_client VARCHAR(8) NOT NULL ); CREATE TABLE INCLURE ( PRIMARY KEY (num_commande, ref_produit), num_commande VARCHAR(8) NOT NULL, ref_produit VARCHAR(8) NOT NULL, quantite INTEGER ); CREATE TABLE PRODUIT ( PRIMARY KEY (ref_produit), ref_produit VARCHAR(8) NOT NULL, libelle VARCHAR(50), prix_unitaire DECIMAL(10,2) ); ALTER TABLE COMMANDE ADD FOREIGN KEY (ref_client) REFERENCES CLIENT (ref_client); ALTER TABLE INCLURE ADD FOREIGN KEY (ref_produit) REFERENCES PRODUIT (ref_produit); ALTER TABLE INCLURE ADD FOREIGN KEY (num_commande) REFERENCES COMMANDE (num_commande); ================================================ FILE: test/zoo/basic/ddl/basic_2_ddl.d2 ================================================ "CLIENT": { shape: sql_table "Réf. client": VARCHAR(8) {constraint: PK} "Nom": VARCHAR(255) "Prénom": VARCHAR(255) "Adresse": VARCHAR(255) } "COMMANDE": { shape: sql_table "Num. commande": VARCHAR(8) {constraint: PK} "Date": DATE "Montant": DECIMAL(10,2) "Réf. client": VARCHAR(8) {constraint: [FK; NOT NULL]} } "INCLURE": { shape: sql_table "Num. commande": VARCHAR(8) {constraint: [PK; FK]} "Réf. produit": VARCHAR(8) {constraint: [PK; FK]} "Quantité": INTEGER } "PRODUIT": { shape: sql_table "Réf. produit": VARCHAR(8) {constraint: PK} "Libellé": VARCHAR(50) "Prix unitaire": DECIMAL(10,2) } "COMMANDE"."Réf. client" -> "CLIENT"."Réf. client" "INCLURE"."Num. commande" -> "COMMANDE"."Num. commande" "INCLURE"."Réf. produit" -> "PRODUIT"."Réf. produit" ================================================ FILE: test/zoo/basic/ddl/basic_2_ddl.dbml ================================================ Table "CLIENT" { "Réf. client" VARCHAR(8) [pk, NOT NULL] "Nom" VARCHAR(255) "Prénom" VARCHAR(255) "Adresse" VARCHAR(255) } Table "COMMANDE" { "Num. commande" VARCHAR(8) [pk, NOT NULL] "Date" DATE "Montant" DECIMAL(10,2) "Réf. client" VARCHAR(8) [NOT NULL] } Table "INCLURE" { "Num. commande" VARCHAR(8) [NOT NULL] "Réf. produit" VARCHAR(8) [NOT NULL] "Quantité" INTEGER Indexes { ("Num. commande", "Réf. produit") [pk] } } Table "PRODUIT" { "Réf. produit" VARCHAR(8) [pk, NOT NULL] "Libellé" VARCHAR(50) "Prix unitaire" DECIMAL(10,2) } Ref:"COMMANDE"."Réf. client" > "CLIENT"."Réf. client" Ref:"INCLURE"."Num. commande" > "COMMANDE"."Num. commande" Ref:"INCLURE"."Réf. produit" > "PRODUIT"."Réf. produit" ================================================ FILE: test/zoo/basic/ddl/basic_2_ddl.sql ================================================ CREATE TABLE CLIENT ( PRIMARY KEY (ref_client), ref_client VARCHAR(8) NOT NULL, nom VARCHAR(255), prenom VARCHAR(255), adresse VARCHAR(255) ); CREATE TABLE COMMANDE ( PRIMARY KEY (num_commande), num_commande VARCHAR(8) NOT NULL, date DATE, montant DECIMAL(10,2), ref_client VARCHAR(8) NOT NULL ); CREATE TABLE INCLURE ( PRIMARY KEY (num_commande, ref_produit), num_commande VARCHAR(8) NOT NULL, ref_produit VARCHAR(8) NOT NULL, quantite INTEGER ); CREATE TABLE PRODUIT ( PRIMARY KEY (ref_produit), ref_produit VARCHAR(8) NOT NULL, libelle VARCHAR(50), prix_unitaire DECIMAL(10,2) ); ALTER TABLE COMMANDE ADD FOREIGN KEY (ref_client) REFERENCES CLIENT (ref_client); ALTER TABLE INCLURE ADD FOREIGN KEY (ref_produit) REFERENCES PRODUIT (ref_produit); ALTER TABLE INCLURE ADD FOREIGN KEY (num_commande) REFERENCES COMMANDE (num_commande); ================================================ FILE: test/zoo/basic/exported/basic_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="CLIENT"] 7 [label="COMMANDE"] 12 [label="PRODUIT"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="Nom"] 4 [label="Prénom"] 5 [label="Adresse"] 9 [label="Date"] 10 [label="Montant"] 15 [label="Libellé"] 16 [label="Prix\nunitaire"] // Weak and strong entity attributes 2 [label=<Réf.
    client
    >] 8 [label=<Num.
    commande
    >] 14 [label=<Réf.
    produit
    >] // Relationship attributes node [ fillcolor="#FFFFFF" ] 13 [label="Quantité"] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 6 [label="DF"] 11 [label="INCLURE"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 1 -- 4 1 -- 5 7 -- 8 7 -- 9 7 -- 10 12 -- 14 12 -- 15 12 -- 16 // Edges between relationships and attributes edge [color="#000000"] 11 -- 13 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 6 [color="#000000"] edge [headlabel=M] 12 -- 11 [color="#000000"] edge [headlabel=N] 7 -- 6 7 -- 11 } ================================================ FILE: test/zoo/basic/exported/basic_0_erd_chen.txt ================================================ [CLIENT] --1-- [COMMANDE] ==N== [COMMANDE] ==N== [PRODUIT] --M-- ================================================ FILE: test/zoo/basic/exported/basic_0_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    CLIENT
    PKRéf. client
    Nom
    Prénom
    Adresse
    >] 2 [label=<
    COMMANDE
    PKNum. commande
    Date
    Montant
    >] 3 [label=<
    INCLURE
    PKQuantité
    >] 4 [label=<
    PRODUIT
    PKRéf. produit
    Libellé
    Prix unitaire
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 1 -> 2 [arrowhead="crowodot" arrowtail="teetee" label="DF"] 3 -> 2 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] 3 -> 4 [arrowhead="teetee" arrowtail="crowodot" label="DF" style=dotted] } ================================================ FILE: test/zoo/basic/exported/basic_0_erd_crow.mmd ================================================ erDiagram CLIENT { TYPE Ref_client PK TYPE Nom TYPE Prenom TYPE Adresse } COMMANDE { TYPE Num_commande PK TYPE Date TYPE Montant } INCLURE { TYPE Quantite PK } PRODUIT { TYPE Ref_produit PK TYPE Libelle TYPE Prix_unitaire } CLIENT ||--o{ COMMANDE: DF INCLURE }|..|| COMMANDE: DF INCLURE }o..|| PRODUIT: DF ================================================ FILE: test/zoo/basic/exported/basic_0_uml.puml ================================================ @startuml "basic" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("CLIENT") { {field} + pk(Réf. client) {field} + Nom {field} + Prénom {field} + Adresse } "CLIENT" "1" --- "*" "COMMANDE" Table("COMMANDE") { {field} + pk(Num. commande) {field} + Date {field} + Montant } "COMMANDE" "*" --- "1..*" "PRODUIT": "INCLURE" ("COMMANDE", "PRODUIT") .. "INCLURE" Table("INCLURE") { {field} + Quantité } Table("PRODUIT") { {field} + pk(Réf. produit) {field} + Libellé {field} + Prix unitaire } @enduml ================================================ FILE: test/zoo/basic/exported/basic_1_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="CLIENT"] 10 [label="COMMANDE"] 15 [label="PRODUIT"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="Nom"] 4 [label="Prénom"] 5 [label="Adresse"] 12 [label="Date"] 13 [label="Montant"] 17 [label="Libellé"] 18 [label="Prix\nunitaire"] // Weak and strong entity attributes 2 [label=<Réf.
    client
    >] 11 [label=<Num.
    commande
    >] 16 [label=<Réf.
    produit
    >] // Relationship attributes node [ fillcolor="#FFFFFF" ] 22 [label="Quantité"] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 9 [label="DF"] 21 [label="INCLURE"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 1 -- 4 1 -- 5 10 -- 11 10 -- 12 10 -- 13 15 -- 16 15 -- 17 15 -- 18 // Edges between relationships and attributes edge [color="#000000"] 21 -- 22 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 9 [color="#000000"] edge [headlabel=M] 15 -- 21 [color="#000000"] edge [headlabel=N] 10 -- 9 10 -- 21 } ================================================ FILE: test/zoo/basic/exported/basic_1_erd_chen.txt ================================================ [CLIENT] --1-- [COMMANDE] ==N== [COMMANDE] ==N== [PRODUIT] --M-- ================================================ FILE: test/zoo/basic/exported/basic_1_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    CLIENT
    PKRéf. client
    Nom
    Prénom
    Adresse
    >] 2 [label=<
    COMMANDE
    PKNum. commande
    Date
    Montant
    >] 3 [label=<
    PRODUIT
    PKRéf. produit
    Libellé
    Prix unitaire
    >] 4 [label=<
    INCLURE
    PKQuantité
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 1 -> 2 [arrowhead="crowodot" arrowtail="teetee" label="DF"] 4 -> 2 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] 4 -> 3 [arrowhead="teetee" arrowtail="crowodot" label="DF" style=dotted] } ================================================ FILE: test/zoo/basic/exported/basic_1_erd_crow.mmd ================================================ erDiagram CLIENT { TYPE Ref_client PK TYPE Nom TYPE Prenom TYPE Adresse } COMMANDE { TYPE Num_commande PK TYPE Date TYPE Montant } PRODUIT { TYPE Ref_produit PK TYPE Libelle TYPE Prix_unitaire } INCLURE { TYPE Quantite PK } CLIENT ||--o{ COMMANDE: DF INCLURE }|..|| COMMANDE: DF INCLURE }o..|| PRODUIT: DF ================================================ FILE: test/zoo/basic/exported/basic_1_uml.puml ================================================ @startuml "basic" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("CLIENT") { {field} + pk(Réf. client) {field} + Nom {field} + Prénom {field} + Adresse } "CLIENT" "1" --- "*" "COMMANDE" Table("COMMANDE") { {field} + pk(Num. commande) {field} + Date {field} + Montant } Table("PRODUIT") { {field} + pk(Réf. produit) {field} + Libellé {field} + Prix unitaire } "COMMANDE" "*" --- "1..*" "PRODUIT": "INCLURE" ("COMMANDE", "PRODUIT") .. "INCLURE" Table("INCLURE") { {field} + Quantité } @enduml ================================================ FILE: test/zoo/basic/exported/basic_2_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="CLIENT"] 7 [label="COMMANDE"] 12 [label="PRODUIT"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="Nom"] 4 [label="Prénom"] 5 [label="Adresse"] 9 [label="Date"] 10 [label="Montant"] 15 [label="Libellé"] 16 [label="Prix\nunitaire"] // Weak and strong entity attributes 2 [label=<Réf.
    client
    >] 8 [label=<Num.
    commande
    >] 14 [label=<Réf.
    produit
    >] // Relationship attributes node [ fillcolor="#FFFFFF" ] 13 [label="Quantité"] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 6 [label="PASSER"] 11 [label="INCLURE"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 1 -- 4 1 -- 5 7 -- 8 7 -- 9 7 -- 10 12 -- 14 12 -- 15 12 -- 16 // Edges between relationships and attributes edge [color="#000000"] 11 -- 13 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 6 [color="#000000"] edge [headlabel=M] 12 -- 11 [color="#000000"] edge [headlabel=N] 7 -- 6 7 -- 11 } ================================================ FILE: test/zoo/basic/exported/basic_2_erd_chen.txt ================================================ [CLIENT] --1-- [COMMANDE] ==N== [COMMANDE] ==N== [PRODUIT] --M-- ================================================ FILE: test/zoo/basic/exported/basic_2_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    CLIENT
    PKRéf. clientVARCHAR(8)
    NomVARCHAR(255)
    PrénomVARCHAR(255)
    AdresseVARCHAR(255)
    >] 2 [label=<
    COMMANDE
    PKNum. commandeVARCHAR(8)
    DateDATE
    MontantDECIMAL(10,2)
    >] 3 [label=<
    INCLURE
    PKQuantitéINTEGER
    >] 4 [label=<
    PRODUIT
    PKRéf. produitVARCHAR(8)
    LibelléVARCHAR(50)
    Prix unitaireDECIMAL(10,2)
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 1 -> 2 [arrowhead="crowodot" arrowtail="teetee" label="PASSER"] 3 -> 2 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] 3 -> 4 [arrowhead="teetee" arrowtail="crowodot" label="DF" style=dotted] } ================================================ FILE: test/zoo/basic/exported/basic_2_erd_crow.mmd ================================================ erDiagram CLIENT { VARCHAR(8) Ref_client PK VARCHAR(255) Nom VARCHAR(255) Prenom VARCHAR(255) Adresse } COMMANDE { VARCHAR(8) Num_commande PK DATE Date DECIMAL(10-2) Montant } INCLURE { INTEGER Quantite PK } PRODUIT { VARCHAR(8) Ref_produit PK VARCHAR(50) Libelle DECIMAL(10-2) Prix_unitaire } CLIENT ||--o{ COMMANDE: PASSER INCLURE }|..|| COMMANDE: DF INCLURE }o..|| PRODUIT: DF ================================================ FILE: test/zoo/basic/exported/basic_2_uml.puml ================================================ @startuml "basic" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("CLIENT") { {field} + pk(Réf. client) VARCHAR(8) {field} + Nom VARCHAR(255) {field} + Prénom VARCHAR(255) {field} + Adresse VARCHAR(255) } "CLIENT" "1" --- "*" "COMMANDE": "PASSER" Table("COMMANDE") { {field} + pk(Num. commande) VARCHAR(8) {field} + Date DATE {field} + Montant DECIMAL(10,2) } "COMMANDE" "*" --- "1..*" "PRODUIT": "INCLURE" ("COMMANDE", "PRODUIT") .. "INCLURE" Table("INCLURE") { {field} + Quantité INTEGER } Table("PRODUIT") { {field} + pk(Réf. produit) VARCHAR(8) {field} + Libellé VARCHAR(50) {field} + Prix unitaire DECIMAL(10,2) } @enduml ================================================ FILE: test/zoo/basic/mld/basic_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note CLIENT Réf. client ! primary_key True CLIENT Nom normal_attribute False CLIENT Prénom normal_attribute False CLIENT Adresse normal_attribute False COMMANDE Num. commande ! primary_key True COMMANDE Date normal_attribute False COMMANDE Montant normal_attribute False COMMANDE Réf. client ! foreign_key False CLIENT CLIENT DF INCLURE Num. commande ! primary_foreign_key True COMMANDE COMMANDE INCLURE INCLURE Réf. produit ! primary_foreign_key True PRODUIT PRODUIT INCLURE INCLURE Quantité association_attribute False INCLURE PRODUIT Réf. produit ! primary_key True PRODUIT Libellé normal_attribute False PRODUIT Prix unitaire normal_attribute False ================================================ FILE: test/zoo/basic/mld/basic_0_dependencies.gv ================================================ digraph { node [shape=box] "CLIENT" -> "COMMANDE" "PRODUIT" -> "INCLURE" "COMMANDE" -> "INCLURE" } ================================================ FILE: test/zoo/basic/mld/basic_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD basic
    CLIENT ( Réf. client, Nom, Prénom, Adresse )
    • Le champ Réf. client constitue la clé primaire de la table. C'était déjà un identifiant de l'entité CLIENT.
    • Les champs Nom, Prénom et Adresse étaient déjà de simples attributs de l'entité CLIENT.
    COMMANDE ( Num. commande, Date, Montant, #Réf. client! )
    • Le champ Num. commande constitue la clé primaire de la table. C'était déjà un identifiant de l'entité COMMANDE.
    • Les champs Date et Montant étaient déjà de simples attributs de l'entité COMMANDE.
    • Le champ à saisie obligatoire Réf. client est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle DF à partir de l'entité CLIENT en perdant son caractère identifiant.
    INCLURE ( #Num. commande, #Réf. produit, Quantité )
    • Le champ Num. commande fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité COMMANDE.
    • Le champ Réf. produit fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité PRODUIT.
    • Le champ Quantité était déjà un simple attribut de l'association INCLURE.
    PRODUIT ( Réf. produit, Libellé, Prix unitaire )
    • Le champ Réf. produit constitue la clé primaire de la table. C'était déjà un identifiant de l'entité PRODUIT.
    • Les champs Libellé et Prix unitaire étaient déjà de simples attributs de l'entité PRODUIT.
    ================================================ FILE: test/zoo/basic/mld/basic_0_mld.mcd ================================================ %%mocodo : CLIENT: Réf. client, Nom, Prénom, Adresse : COMMANDE: Num. commande, Date, Montant, #Réf. client > CLIENT > Réf. client : INCLURE: #Num. commande > COMMANDE > Num. commande, _#Réf. produit > PRODUIT > Réf. produit, Quantité : PRODUIT: Réf. produit, Libellé, Prix unitaire : ================================================ FILE: test/zoo/basic/mld/basic_0_mld.md ================================================ - **CLIENT** (Réf. client, Nom, Prénom, Adresse) - Le champ _Réf. client_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _CLIENT_. - Les champs _Nom_, _Prénom_ et _Adresse_ étaient déjà de simples attributs de l'entité _CLIENT_. - **COMMANDE** (Num. commande, Date, Montant, _#Réf. client!_) - Le champ _Num. commande_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _COMMANDE_. - Les champs _Date_ et _Montant_ étaient déjà de simples attributs de l'entité _COMMANDE_. - Le champ à saisie obligatoire _Réf. client_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _DF_ à partir de l'entité _CLIENT_ en perdant son caractère identifiant. - **INCLURE** (_#Num. commande_, _#Réf. produit_, Quantité) - Le champ _Num. commande_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _COMMANDE_. - Le champ _Réf. produit_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _PRODUIT_. - Le champ _Quantité_ était déjà un simple attribut de l'association _INCLURE_. - **PRODUIT** (Réf. produit, Libellé, Prix unitaire) - Le champ _Réf. produit_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _PRODUIT_. - Les champs _Libellé_ et _Prix unitaire_ étaient déjà de simples attributs de l'entité _PRODUIT_. ================================================ FILE: test/zoo/basic/mld/basic_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{basic}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{CLIENT} (\prim{Réf. client}, \attr{Nom}, \attr{Prénom}, \attr{Adresse}) \begin{itemize} \item Le champ \emph{Réf. client} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CLIENT}. \item Les champs \emph{Nom}, \emph{Prénom} et \emph{Adresse} étaient déjà de simples attributs de l'entité \emph{CLIENT}. \end{itemize} \item \relat{COMMANDE} (\prim{Num. commande}, \attr{Date}, \attr{Montant}, \foreign{Réf. client!}) \begin{itemize} \item Le champ \emph{Num. commande} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{COMMANDE}. \item Les champs \emph{Date} et \emph{Montant} étaient déjà de simples attributs de l'entité \emph{COMMANDE}. \item Le champ à saisie obligatoire \emph{Réf. client} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{DF} à partir de l'entité \emph{CLIENT} en perdant son caractère identifiant. \end{itemize} \item \relat{INCLURE} (\foreign{\prim{Num. commande}}, \foreign{\prim{Réf. produit}}, \attr{Quantité}) \begin{itemize} \item Le champ \emph{Num. commande} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{COMMANDE}. \item Le champ \emph{Réf. produit} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{PRODUIT}. \item Le champ \emph{Quantité} était déjà un simple attribut de l'association \emph{INCLURE}. \end{itemize} \item \relat{PRODUIT} (\prim{Réf. produit}, \attr{Libellé}, \attr{Prix unitaire}) \begin{itemize} \item Le champ \emph{Réf. produit} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{PRODUIT}. \item Les champs \emph{Libellé} et \emph{Prix unitaire} étaient déjà de simples attributs de l'entité \emph{PRODUIT}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/basic/mld/basic_0_mld.txt ================================================ - CLIENT (_Réf. client_, Nom, Prénom, Adresse) - Le champ « Réf. client » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « CLIENT ». - Les champs « Nom », « Prénom » et « Adresse » étaient déjà de simples attributs de l'entité « CLIENT ». - COMMANDE (_Num. commande_, Date, Montant, #Réf. client!) - Le champ « Num. commande » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « COMMANDE ». - Les champs « Date » et « Montant » étaient déjà de simples attributs de l'entité « COMMANDE ». - Le champ à saisie obligatoire « Réf. client » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « DF » à partir de l'entité « CLIENT » en perdant son caractère identifiant. - INCLURE (_#Num. commande_, _#Réf. produit_, Quantité) - Le champ « Num. commande » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « COMMANDE ». - Le champ « Réf. produit » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « PRODUIT ». - Le champ « Quantité » était déjà un simple attribut de l'association « INCLURE ». - PRODUIT (_Réf. produit_, Libellé, Prix unitaire) - Le champ « Réf. produit » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « PRODUIT ». - Les champs « Libellé » et « Prix unitaire » étaient déjà de simples attributs de l'entité « PRODUIT ». ================================================ FILE: test/zoo/basic/mld/basic_1_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note CLIENT Réf. client ! primary_key True CLIENT Nom normal_attribute False CLIENT Prénom normal_attribute False CLIENT Adresse normal_attribute False COMMANDE Num. commande ! primary_key True COMMANDE Date normal_attribute False COMMANDE Montant normal_attribute False COMMANDE Réf. client ! foreign_key False CLIENT CLIENT DF INCLURE Num. commande ! primary_foreign_key True COMMANDE COMMANDE INCLURE INCLURE Réf. produit ! primary_foreign_key True PRODUIT PRODUIT INCLURE INCLURE Quantité association_attribute False INCLURE PRODUIT Réf. produit ! primary_key True PRODUIT Libellé normal_attribute False PRODUIT Prix unitaire normal_attribute False ================================================ FILE: test/zoo/basic/mld/basic_1_dependencies.gv ================================================ digraph { node [shape=box] "CLIENT" -> "COMMANDE" "PRODUIT" -> "INCLURE" "COMMANDE" -> "INCLURE" } ================================================ FILE: test/zoo/basic/mld/basic_1_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD basic
    CLIENT ( Réf. client, Nom, Prénom, Adresse )
    • Le champ Réf. client constitue la clé primaire de la table. C'était déjà un identifiant de l'entité CLIENT.
    • Les champs Nom, Prénom et Adresse étaient déjà de simples attributs de l'entité CLIENT.
    COMMANDE ( Num. commande, Date, Montant, #Réf. client! )
    • Le champ Num. commande constitue la clé primaire de la table. C'était déjà un identifiant de l'entité COMMANDE.
    • Les champs Date et Montant étaient déjà de simples attributs de l'entité COMMANDE.
    • Le champ à saisie obligatoire Réf. client est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle DF à partir de l'entité CLIENT en perdant son caractère identifiant.
    INCLURE ( #Num. commande, #Réf. produit, Quantité )
    • Le champ Num. commande fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité COMMANDE.
    • Le champ Réf. produit fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité PRODUIT.
    • Le champ Quantité était déjà un simple attribut de l'association INCLURE.
    PRODUIT ( Réf. produit, Libellé, Prix unitaire )
    • Le champ Réf. produit constitue la clé primaire de la table. C'était déjà un identifiant de l'entité PRODUIT.
    • Les champs Libellé et Prix unitaire étaient déjà de simples attributs de l'entité PRODUIT.
    ================================================ FILE: test/zoo/basic/mld/basic_1_mld.mcd ================================================ %%mocodo : CLIENT: Réf. client, Nom, Prénom, Adresse ::::::: ::: COMMANDE: Num. commande, Date, Montant, #Réf. client > CLIENT > Réf. client ::: PRODUIT: Réf. produit, Libellé, Prix unitaire : ::::: INCLURE: #Num. commande > COMMANDE > Num. commande, _#Réf. produit > PRODUIT > Réf. produit, Quantité ::: ================================================ FILE: test/zoo/basic/mld/basic_1_mld.md ================================================ - **CLIENT** (Réf. client, Nom, Prénom, Adresse) - Le champ _Réf. client_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _CLIENT_. - Les champs _Nom_, _Prénom_ et _Adresse_ étaient déjà de simples attributs de l'entité _CLIENT_. - **COMMANDE** (Num. commande, Date, Montant, _#Réf. client!_) - Le champ _Num. commande_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _COMMANDE_. - Les champs _Date_ et _Montant_ étaient déjà de simples attributs de l'entité _COMMANDE_. - Le champ à saisie obligatoire _Réf. client_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _DF_ à partir de l'entité _CLIENT_ en perdant son caractère identifiant. - **INCLURE** (_#Num. commande_, _#Réf. produit_, Quantité) - Le champ _Num. commande_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _COMMANDE_. - Le champ _Réf. produit_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _PRODUIT_. - Le champ _Quantité_ était déjà un simple attribut de l'association _INCLURE_. - **PRODUIT** (Réf. produit, Libellé, Prix unitaire) - Le champ _Réf. produit_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _PRODUIT_. - Les champs _Libellé_ et _Prix unitaire_ étaient déjà de simples attributs de l'entité _PRODUIT_. ================================================ FILE: test/zoo/basic/mld/basic_1_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{basic}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{CLIENT} (\prim{Réf. client}, \attr{Nom}, \attr{Prénom}, \attr{Adresse}) \begin{itemize} \item Le champ \emph{Réf. client} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CLIENT}. \item Les champs \emph{Nom}, \emph{Prénom} et \emph{Adresse} étaient déjà de simples attributs de l'entité \emph{CLIENT}. \end{itemize} \item \relat{COMMANDE} (\prim{Num. commande}, \attr{Date}, \attr{Montant}, \foreign{Réf. client!}) \begin{itemize} \item Le champ \emph{Num. commande} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{COMMANDE}. \item Les champs \emph{Date} et \emph{Montant} étaient déjà de simples attributs de l'entité \emph{COMMANDE}. \item Le champ à saisie obligatoire \emph{Réf. client} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{DF} à partir de l'entité \emph{CLIENT} en perdant son caractère identifiant. \end{itemize} \item \relat{INCLURE} (\foreign{\prim{Num. commande}}, \foreign{\prim{Réf. produit}}, \attr{Quantité}) \begin{itemize} \item Le champ \emph{Num. commande} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{COMMANDE}. \item Le champ \emph{Réf. produit} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{PRODUIT}. \item Le champ \emph{Quantité} était déjà un simple attribut de l'association \emph{INCLURE}. \end{itemize} \item \relat{PRODUIT} (\prim{Réf. produit}, \attr{Libellé}, \attr{Prix unitaire}) \begin{itemize} \item Le champ \emph{Réf. produit} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{PRODUIT}. \item Les champs \emph{Libellé} et \emph{Prix unitaire} étaient déjà de simples attributs de l'entité \emph{PRODUIT}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/basic/mld/basic_1_mld.txt ================================================ - CLIENT (_Réf. client_, Nom, Prénom, Adresse) - Le champ « Réf. client » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « CLIENT ». - Les champs « Nom », « Prénom » et « Adresse » étaient déjà de simples attributs de l'entité « CLIENT ». - COMMANDE (_Num. commande_, Date, Montant, #Réf. client!) - Le champ « Num. commande » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « COMMANDE ». - Les champs « Date » et « Montant » étaient déjà de simples attributs de l'entité « COMMANDE ». - Le champ à saisie obligatoire « Réf. client » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « DF » à partir de l'entité « CLIENT » en perdant son caractère identifiant. - INCLURE (_#Num. commande_, _#Réf. produit_, Quantité) - Le champ « Num. commande » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « COMMANDE ». - Le champ « Réf. produit » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « PRODUIT ». - Le champ « Quantité » était déjà un simple attribut de l'association « INCLURE ». - PRODUIT (_Réf. produit_, Libellé, Prix unitaire) - Le champ « Réf. produit » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « PRODUIT ». - Les champs « Libellé » et « Prix unitaire » étaient déjà de simples attributs de l'entité « PRODUIT ». ================================================ FILE: test/zoo/basic/mld/basic_2_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note CLIENT Réf. client ! primary_key True VARCHAR(8) CLIENT Nom normal_attribute False VARCHAR(255) CLIENT Prénom normal_attribute False VARCHAR(255) CLIENT Adresse normal_attribute False VARCHAR(255) COMMANDE Num. commande ! primary_key True VARCHAR(8) COMMANDE Date normal_attribute False DATE COMMANDE Montant normal_attribute False DECIMAL(10,2) COMMANDE Réf. client ! foreign_key False CLIENT CLIENT PASSER VARCHAR(8) INCLURE Num. commande ! primary_foreign_key True COMMANDE COMMANDE INCLURE VARCHAR(8) INCLURE Réf. produit ! primary_foreign_key True PRODUIT PRODUIT INCLURE VARCHAR(8) INCLURE Quantité association_attribute False INCLURE INTEGER PRODUIT Réf. produit ! primary_key True VARCHAR(8) PRODUIT Libellé normal_attribute False VARCHAR(50) PRODUIT Prix unitaire normal_attribute False DECIMAL(10,2) ================================================ FILE: test/zoo/basic/mld/basic_2_dependencies.gv ================================================ digraph { node [shape=box] "CLIENT" -> "COMMANDE" "PRODUIT" -> "INCLURE" "COMMANDE" -> "INCLURE" } ================================================ FILE: test/zoo/basic/mld/basic_2_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD basic
    CLIENT ( Réf. client, Nom, Prénom, Adresse )
    • Le champ Réf. client constitue la clé primaire de la table. C'était déjà un identifiant de l'entité CLIENT.
    • Les champs Nom, Prénom et Adresse étaient déjà de simples attributs de l'entité CLIENT.
    COMMANDE ( Num. commande, Date, Montant, #Réf. client! )
    • Le champ Num. commande constitue la clé primaire de la table. C'était déjà un identifiant de l'entité COMMANDE.
    • Les champs Date et Montant étaient déjà de simples attributs de l'entité COMMANDE.
    • Le champ à saisie obligatoire Réf. client est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle PASSER à partir de l'entité CLIENT en perdant son caractère identifiant.
    INCLURE ( #Num. commande, #Réf. produit, Quantité )
    • Le champ Num. commande fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité COMMANDE.
    • Le champ Réf. produit fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité PRODUIT.
    • Le champ Quantité était déjà un simple attribut de l'association INCLURE.
    PRODUIT ( Réf. produit, Libellé, Prix unitaire )
    • Le champ Réf. produit constitue la clé primaire de la table. C'était déjà un identifiant de l'entité PRODUIT.
    • Les champs Libellé et Prix unitaire étaient déjà de simples attributs de l'entité PRODUIT.
    ================================================ FILE: test/zoo/basic/mld/basic_2_mld.mcd ================================================ %%mocodo : CLIENT: Réf. client, Nom, Prénom, Adresse : COMMANDE: Num. commande, Date, Montant, #Réf. client > CLIENT > Réf. client : INCLURE: #Num. commande > COMMANDE > Num. commande, _#Réf. produit > PRODUIT > Réf. produit, Quantité : PRODUIT: Réf. produit, Libellé, Prix unitaire : ================================================ FILE: test/zoo/basic/mld/basic_2_mld.md ================================================ - **CLIENT** (Réf. client, Nom, Prénom, Adresse) - Le champ _Réf. client_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _CLIENT_. - Les champs _Nom_, _Prénom_ et _Adresse_ étaient déjà de simples attributs de l'entité _CLIENT_. - **COMMANDE** (Num. commande, Date, Montant, _#Réf. client!_) - Le champ _Num. commande_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _COMMANDE_. - Les champs _Date_ et _Montant_ étaient déjà de simples attributs de l'entité _COMMANDE_. - Le champ à saisie obligatoire _Réf. client_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _PASSER_ à partir de l'entité _CLIENT_ en perdant son caractère identifiant. - **INCLURE** (_#Num. commande_, _#Réf. produit_, Quantité) - Le champ _Num. commande_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _COMMANDE_. - Le champ _Réf. produit_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _PRODUIT_. - Le champ _Quantité_ était déjà un simple attribut de l'association _INCLURE_. - **PRODUIT** (Réf. produit, Libellé, Prix unitaire) - Le champ _Réf. produit_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _PRODUIT_. - Les champs _Libellé_ et _Prix unitaire_ étaient déjà de simples attributs de l'entité _PRODUIT_. ================================================ FILE: test/zoo/basic/mld/basic_2_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{basic}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{CLIENT} (\prim{Réf. client}, \attr{Nom}, \attr{Prénom}, \attr{Adresse}) \begin{itemize} \item Le champ \emph{Réf. client} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CLIENT}. \item Les champs \emph{Nom}, \emph{Prénom} et \emph{Adresse} étaient déjà de simples attributs de l'entité \emph{CLIENT}. \end{itemize} \item \relat{COMMANDE} (\prim{Num. commande}, \attr{Date}, \attr{Montant}, \foreign{Réf. client!}) \begin{itemize} \item Le champ \emph{Num. commande} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{COMMANDE}. \item Les champs \emph{Date} et \emph{Montant} étaient déjà de simples attributs de l'entité \emph{COMMANDE}. \item Le champ à saisie obligatoire \emph{Réf. client} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{PASSER} à partir de l'entité \emph{CLIENT} en perdant son caractère identifiant. \end{itemize} \item \relat{INCLURE} (\foreign{\prim{Num. commande}}, \foreign{\prim{Réf. produit}}, \attr{Quantité}) \begin{itemize} \item Le champ \emph{Num. commande} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{COMMANDE}. \item Le champ \emph{Réf. produit} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{PRODUIT}. \item Le champ \emph{Quantité} était déjà un simple attribut de l'association \emph{INCLURE}. \end{itemize} \item \relat{PRODUIT} (\prim{Réf. produit}, \attr{Libellé}, \attr{Prix unitaire}) \begin{itemize} \item Le champ \emph{Réf. produit} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{PRODUIT}. \item Les champs \emph{Libellé} et \emph{Prix unitaire} étaient déjà de simples attributs de l'entité \emph{PRODUIT}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/basic/mld/basic_2_mld.txt ================================================ - CLIENT (_Réf. client_, Nom, Prénom, Adresse) - Le champ « Réf. client » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « CLIENT ». - Les champs « Nom », « Prénom » et « Adresse » étaient déjà de simples attributs de l'entité « CLIENT ». - COMMANDE (_Num. commande_, Date, Montant, #Réf. client!) - Le champ « Num. commande » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « COMMANDE ». - Les champs « Date » et « Montant » étaient déjà de simples attributs de l'entité « COMMANDE ». - Le champ à saisie obligatoire « Réf. client » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « PASSER » à partir de l'entité « CLIENT » en perdant son caractère identifiant. - INCLURE (_#Num. commande_, _#Réf. produit_, Quantité) - Le champ « Num. commande » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « COMMANDE ». - Le champ « Réf. produit » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « PRODUIT ». - Le champ « Quantité » était déjà un simple attribut de l'association « INCLURE ». - PRODUIT (_Réf. produit_, Libellé, Prix unitaire) - Le champ « Réf. produit » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « PRODUIT ». - Les champs « Libellé » et « Prix unitaire » étaient déjà de simples attributs de l'entité « PRODUIT ». ================================================ FILE: test/zoo/basic/rewritten/basic_0_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/basic/rewritten/basic_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/basic/rewritten/basic_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2, at 1 3, at 1 4 DF, 0N> ENTITÉ 1_, 11 ENTITÉ 2_ ENTITÉ 2_: at 2 1, at 2 2, at 2 3 ASSOC 5_, 1N ENTITÉ 2_, 0N ENTITÉ 3_: at 5 1 ENTITÉ 3_: at 3 1, at 3 2, at 3 3 ================================================ FILE: test/zoo/basic/rewritten/basic_0_rw_explode_arity=2,weak.mcd ================================================ DF, 0N> CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant DF, _11 INCLURE, 1N COMMANDE : CLIENT: Réf. client, Nom, Prénom, Adresse : INCLURE: _Quantité DF, _11 INCLURE, 0N PRODUIT : : : PRODUIT: Réf. produit, Libellé, Prix unitaire ================================================ FILE: test/zoo/basic/rewritten/basic_0_rw_explode_arity=2.5,weak.mcd ================================================ DF, 0N> CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant DF, _11 INCLURE, 1N COMMANDE : CLIENT: Réf. client, Nom, Prénom, Adresse : INCLURE: _Quantité DF, _11 INCLURE, 0N PRODUIT : : : PRODUIT: Réf. produit, Libellé, Prix unitaire ================================================ FILE: test/zoo/basic/rewritten/basic_0_rw_explode_arity=2.5.mcd ================================================ DF, 0N> CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant DF, 11 INCLURE, 1N COMMANDE : CLIENT: Réf. client, Nom, Prénom, Adresse : INCLURE: id. inclure, Quantité DF, 11 INCLURE, 0N PRODUIT : : : PRODUIT: Réf. produit, Libellé, Prix unitaire ================================================ FILE: test/zoo/basic/rewritten/basic_0_rw_explode_arity=2.mcd ================================================ DF, 0N> CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant DF, 11 INCLURE, 1N COMMANDE : CLIENT: Réf. client, Nom, Prénom, Adresse : INCLURE: id. inclure, Quantité DF, 11 INCLURE, 0N PRODUIT : : : PRODUIT: Réf. produit, Libellé, Prix unitaire ================================================ FILE: test/zoo/basic/rewritten/basic_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/basic/rewritten/basic_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/basic/rewritten/basic_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/basic/rewritten/basic_1_rw_create_df_arrows=across.mcd ================================================ CLIENT: Réf. client, Nom, Prénom, Adresse -Reflexive 6_, 11 COMMANDE, 01> COMMANDE -Entity 7_: id 7 1, _id 7 2, attr 7 3, attr 7 4 -Reflexive 13_, 11 PRODUIT, 1N> PRODUIT DF, 0N> CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant -Ternary 8_, 0N Entity 7_, 0N COMMANDE, 1N PRODUIT PRODUIT: Réf. produit, Libellé, Prix unitaire -Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 -Binary 15_, 0N Entity 14_, 01 Entity 11_ INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité -Binary 10_, 1N Entity 9_, 1N PRODUIT: attr 10 1 -Binary 16_, 0N Entity 14_, 1N Entity 11_ -Entity 11_: id 11 1, attr 11 2 -Binary 12_, 0N> Entity 11_, 11 Entity 9_: attr 12 1 -Entity 9_: id 9 1, attr 9 2, attr 9 3 ================================================ FILE: test/zoo/basic/rewritten/basic_1_rw_drain.mcd ================================================ CLIENT: Réf. client, Nom, Prénom, Adresse -Reflexive 6_, 11 COMMANDE, 01 COMMANDE -Entity 7_: id 7 1, _id 7 2, attr 7 3, attr 7 4 -Reflexive 13_, 11 PRODUIT, 1N PRODUIT DF, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant -Ternary 8_, 0N Entity 7_, 0N COMMANDE, 1N PRODUIT PRODUIT: Réf. produit, Libellé, Prix unitaire -Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 -Binary 15_, 0N Entity 14_, 01 Entity 11_ INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité -Binary 10_, 1N Entity 9_, 1N PRODUIT: attr 10 1 -Binary 16_, 0N Entity 14_, 1N Entity 11_ -Entity 11_: id 11 1, attr 11 2 -Binary 12_, 0N Entity 11_, 11 Entity 9_ -Entity 9_: id 9 1, attr 9 2, attr 9 3, attr 12 1 ================================================ FILE: test/zoo/basic/rewritten/basic_1_rw_drown.mcd ================================================ ENTITÉ 01_: at 01 1, at 01 2, at 01 3, at 01 4 -ASSOC 08_, 11 ENTITÉ 03_, 01 ENTITÉ 03_ -ENTITÉ 02_: at 02 1, _at 02 2, at 02 3, at 02 4 -ASSOC 09_, 11 ENTITÉ 04_, 1N ENTITÉ 04_ DF, 0N ENTITÉ 01_, 11 ENTITÉ 03_ ENTITÉ 03_: at 03 1, at 03 2, at 03 3 -ASSOC 11_, 0N ENTITÉ 02_, 0N ENTITÉ 03_, 1N ENTITÉ 04_ ENTITÉ 04_: at 04 1, at 04 2, at 04 3 -ENTITÉ 05_: at 05 1, at 05 2, at 05 3, at 05 4 -ASSOC 12_, 0N ENTITÉ 05_, 01 ENTITÉ 06_ ASSOC 13_, 1N ENTITÉ 03_, 0N ENTITÉ 04_: at 13 1 -ASSOC 14_, 1N ENTITÉ 07_, 1N ENTITÉ 04_: at 14 1 -ASSOC 15_, 0N ENTITÉ 05_, 1N ENTITÉ 06_ -ENTITÉ 06_: at 06 1, at 06 2 -ASSOC 16_, 0N ENTITÉ 06_, 11 ENTITÉ 07_: at 16 1 -ENTITÉ 07_: at 07 1, at 07 2, at 07 3 ================================================ FILE: test/zoo/basic/rewritten/basic_1_rw_explode_arity=2,weak.mcd ================================================ CLIENT: Réf. client, Nom, Prénom, Adresse -Reflexive 6_, 11 COMMANDE, 01 COMMANDE : : : -Binary 12_, 0N Entity 11_, 11 Entity 9_: attr 12 1 -Entity 11_: id 11 1, attr 11 2 -DF, _11 Binary 16_, 1N Entity 11_ DF, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant DF, _11 INCLURE, 1N COMMANDE INCLURE: _Quantité : -Entity 9_: id 9 1, attr 9 2, attr 9 3 -Binary 15_, 0N Entity 14_, 01 Entity 11_ -Binary 16_: -Entity 7_: id 7 1, _id 7 2, attr 7 3, attr 7 4 -DF, _11 Ternary 8_, 0N COMMANDE : DF, _11 INCLURE, 0N PRODUIT : -DF, _11 Binary 10_, 1N Entity 9_ -Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 -DF, _11 Binary 16_, 0N Entity 14_ -DF, _11 Ternary 8_, 0N Entity 7_ -Ternary 8_: -DF, _11 Ternary 8_, 1N PRODUIT PRODUIT: Réf. produit, Libellé, Prix unitaire -DF, _11 Binary 10_, 1N PRODUIT -Binary 10_: _attr 10 1 : : : : : -Reflexive 13_, 11 PRODUIT, 1N PRODUIT : : : : ================================================ FILE: test/zoo/basic/rewritten/basic_1_rw_explode_arity=2.5,weak.mcd ================================================ : DF, 0N CLIENT, 11 COMMANDE CLIENT: Réf. client, Nom, Prénom, Adresse : : : : DF, _11 INCLURE, 1N COMMANDE COMMANDE: Num. commande, Date, Montant -Reflexive 6_, 11 COMMANDE, 01 COMMANDE : : : : INCLURE: _Quantité -Ternary 8_, 0N Entity 7_, 0N COMMANDE, 1N PRODUIT -Entity 7_: id 7 1, _id 7 2, attr 7 3, attr 7 4 : : -Binary 15_, 0N Entity 14_, 01 Entity 11_ -Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 DF, _11 INCLURE, 0N PRODUIT PRODUIT: Réf. produit, Libellé, Prix unitaire -Reflexive 13_, 11 PRODUIT, 1N PRODUIT -Entity 9_: id 9 1, attr 9 2, attr 9 3 -Binary 12_, 0N Entity 11_, 11 Entity 9_: attr 12 1 -Entity 11_: id 11 1, attr 11 2 -Binary 16_, 0N Entity 14_, 1N Entity 11_ : -DF, _11 Binary 10_, 1N PRODUIT -Binary 10_: _attr 10 1 -DF, _11 Binary 10_, 1N Entity 9_ : : : ================================================ FILE: test/zoo/basic/rewritten/basic_1_rw_explode_arity=2.5.mcd ================================================ : DF, 0N CLIENT, 11 COMMANDE CLIENT: Réf. client, Nom, Prénom, Adresse : : : : DF, 11 INCLURE, 1N COMMANDE COMMANDE: Num. commande, Date, Montant -Reflexive 6_, 11 COMMANDE, 01 COMMANDE : : : : INCLURE: id. inclure, Quantité -Ternary 8_, 0N Entity 7_, 0N COMMANDE, 1N PRODUIT -Entity 7_: id 7 1, _id 7 2, attr 7 3, attr 7 4 : : -Binary 15_, 0N Entity 14_, 01 Entity 11_ -Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 DF, 11 INCLURE, 0N PRODUIT PRODUIT: Réf. produit, Libellé, Prix unitaire -Reflexive 13_, 11 PRODUIT, 1N PRODUIT -Entity 9_: id 9 1, attr 9 2, attr 9 3 -Binary 12_, 0N Entity 11_, 11 Entity 9_: attr 12 1 -Entity 11_: id 11 1, attr 11 2 -Binary 16_, 0N Entity 14_, 1N Entity 11_ : -DF, 11 Binary 10_, 1N PRODUIT -Binary 10_: id. binary 10_, attr 10 1 -DF, 11 Binary 10_, 1N Entity 9_ : : : ================================================ FILE: test/zoo/basic/rewritten/basic_1_rw_explode_arity=2.mcd ================================================ CLIENT: Réf. client, Nom, Prénom, Adresse -Reflexive 6_, 11 COMMANDE, 01 COMMANDE : : : -Binary 12_, 0N Entity 11_, 11 Entity 9_: attr 12 1 -Entity 11_: id 11 1, attr 11 2 -DF, 11 Binary 16_, 1N Entity 11_ DF, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande, Date, Montant DF, 11 INCLURE, 1N COMMANDE INCLURE: id. inclure, Quantité : -Entity 9_: id 9 1, attr 9 2, attr 9 3 -Binary 15_, 0N Entity 14_, 01 Entity 11_ -Binary 16_: id. binary 16_ -Entity 7_: id 7 1, _id 7 2, attr 7 3, attr 7 4 -DF, 11 Ternary 8_, 0N COMMANDE : DF, 11 INCLURE, 0N PRODUIT : -DF, 11 Binary 10_, 1N Entity 9_ -Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 -DF, 11 Binary 16_, 0N Entity 14_ -DF, 11 Ternary 8_, 0N Entity 7_ -Ternary 8_: id. ternary 8_ -DF, 11 Ternary 8_, 1N PRODUIT PRODUIT: Réf. produit, Libellé, Prix unitaire -DF, 11 Binary 10_, 1N PRODUIT -Binary 10_: id. binary 10_, attr 10 1 : : : : : -Reflexive 13_, 11 PRODUIT, 1N PRODUIT : : : : ================================================ FILE: test/zoo/basic/rewritten/basic_1_rw_explode_arity=3,weak.mcd ================================================ : : : : : -DF, _11 Ternary 8_, 0N Entity 7_ -Entity 7_: id 7 1, _id 7 2, attr 7 3, attr 7 4 : : : : : -DF, _11 Ternary 8_, 1N PRODUIT -Ternary 8_: -DF, _11 Ternary 8_, 0N COMMANDE : -Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 -Binary 15_, 0N Entity 14_, 01 Entity 11_ : -Binary 10_, 1N Entity 9_, 1N PRODUIT: attr 10 1 PRODUIT: Réf. produit, Libellé, Prix unitaire INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité COMMANDE: Num. commande, Date, Montant -Reflexive 6_, 11 COMMANDE, 01 COMMANDE -Binary 16_, 0N Entity 14_, 1N Entity 11_ -Entity 11_: id 11 1, attr 11 2 -Binary 12_, 0N Entity 11_, 11 Entity 9_: attr 12 1 -Entity 9_: id 9 1, attr 9 2, attr 9 3 -Reflexive 13_, 11 PRODUIT, 1N PRODUIT : DF, 0N CLIENT, 11 COMMANDE CLIENT: Réf. client, Nom, Prénom, Adresse ================================================ FILE: test/zoo/basic/rewritten/basic_1_rw_explode_arity=3.mcd ================================================ : : : : : -DF, 11 Ternary 8_, 0N Entity 7_ -Entity 7_: id 7 1, _id 7 2, attr 7 3, attr 7 4 : : : : : -DF, 11 Ternary 8_, 1N PRODUIT -Ternary 8_: id. ternary 8_ -DF, 11 Ternary 8_, 0N COMMANDE : -Entity 14_: id 14 1, attr 14 2, attr 14 3, attr 14 4 -Binary 15_, 0N Entity 14_, 01 Entity 11_ : -Binary 10_, 1N Entity 9_, 1N PRODUIT: attr 10 1 PRODUIT: Réf. produit, Libellé, Prix unitaire INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité COMMANDE: Num. commande, Date, Montant -Reflexive 6_, 11 COMMANDE, 01 COMMANDE -Binary 16_, 0N Entity 14_, 1N Entity 11_ -Entity 11_: id 11 1, attr 11 2 -Binary 12_, 0N Entity 11_, 11 Entity 9_: attr 12 1 -Entity 9_: id 9 1, attr 9 2, attr 9 3 -Reflexive 13_, 11 PRODUIT, 1N PRODUIT : DF, 0N CLIENT, 11 COMMANDE CLIENT: Réf. client, Nom, Prénom, Adresse ================================================ FILE: test/zoo/basic/rewritten/basic_1_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/basic/rewritten/basic_2_rw_create_df_arrows=across.mcd ================================================ CLIENT: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)] PASSER, 0N> CLIENT, 11 COMMANDE COMMANDE: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)] INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité [INTEGER] PRODUIT: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)] ================================================ FILE: test/zoo/basic/rewritten/basic_2_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/basic/rewritten/basic_2_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1 [VARCHAR(8)], at 1 2 [VARCHAR(255)], at 1 3 [VARCHAR(255)], at 1 4 [VARCHAR(255)] ASSOC 4_, 0N ENTITÉ 1_, 11 ENTITÉ 2_ ENTITÉ 2_: at 2 1 [VARCHAR(8)], at 2 2 [DATE], at 2 3 [DECIMAL(10,2)] ASSOC 5_, 1N ENTITÉ 2_, 0N ENTITÉ 3_: at 5 1 [INTEGER] ENTITÉ 3_: at 3 1 [VARCHAR(8)], at 3 2 [VARCHAR(50)], at 3 3 [DECIMAL(10,2)] ================================================ FILE: test/zoo/basic/rewritten/basic_2_rw_explode_arity=2,weak.mcd ================================================ PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)] DF, _11 INCLURE, 1N COMMANDE : CLIENT: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)] : INCLURE: _Quantité [INTEGER] DF, _11 INCLURE, 0N PRODUIT : : : PRODUIT: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)] ================================================ FILE: test/zoo/basic/rewritten/basic_2_rw_explode_arity=2.5,weak.mcd ================================================ PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)] DF, _11 INCLURE, 1N COMMANDE : CLIENT: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)] : INCLURE: _Quantité [INTEGER] DF, _11 INCLURE, 0N PRODUIT : : : PRODUIT: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)] ================================================ FILE: test/zoo/basic/rewritten/basic_2_rw_explode_arity=2.5.mcd ================================================ PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)] DF, 11 INCLURE, 1N COMMANDE : CLIENT: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)] : INCLURE: id. inclure, Quantité [INTEGER] DF, 11 INCLURE, 0N PRODUIT : : : PRODUIT: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)] ================================================ FILE: test/zoo/basic/rewritten/basic_2_rw_explode_arity=2.mcd ================================================ PASSER, 0N CLIENT, 11 COMMANDE COMMANDE: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)] DF, 11 INCLURE, 1N COMMANDE : CLIENT: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)] : INCLURE: id. inclure, Quantité [INTEGER] DF, 11 INCLURE, 0N PRODUIT : : : PRODUIT: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)] ================================================ FILE: test/zoo/basic/rewritten/basic_2_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/basic/rewritten/basic_2_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/basic/rewritten/basic_2_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/cluster_NN1/_cluster_NN1_0.mcd ================================================ Voilier: num voilier, longueur Offrir, 0N Voilier, 0N Semaine, /11 Réservation: tarif Semaine: num semaine, 1_date début Réservation: num résa, arrhes, date résa ================================================ FILE: test/zoo/cluster_NN1/ddl/cluster_NN1_0_ddl.d2 ================================================ "Réservation": { shape: sql_table "num résa": VARCHAR(42) {constraint: PK} "arrhes": VARCHAR(42) "date résa": VARCHAR(42) "num voilier": VARCHAR(42) {constraint: [FK; NOT NULL]} "num semaine": VARCHAR(42) {constraint: [FK; NOT NULL]} "tarif": VARCHAR(42) } "Semaine": { shape: sql_table "num semaine": VARCHAR(42) {constraint: PK} "date début": VARCHAR(42) {constraint: UNQ1} } "Voilier": { shape: sql_table "num voilier": VARCHAR(42) {constraint: PK} "longueur": VARCHAR(42) } "Réservation"."num voilier" -> "Voilier"."num voilier" "Réservation"."num semaine" -> "Semaine"."num semaine" ================================================ FILE: test/zoo/cluster_NN1/ddl/cluster_NN1_0_ddl.dbml ================================================ Table "Réservation" { "num résa" VARCHAR(42) [pk, NOT NULL] "arrhes" VARCHAR(42) "date résa" VARCHAR(42) "num voilier" VARCHAR(42) [NOT NULL] "num semaine" VARCHAR(42) [NOT NULL] "tarif" VARCHAR(42) Indexes { ("num voilier", "num semaine") [unique] } } Table "Semaine" { "num semaine" VARCHAR(42) [pk, NOT NULL] "date début" VARCHAR(42) Indexes { "date début" [unique] } } Table "Voilier" { "num voilier" VARCHAR(42) [pk, NOT NULL] "longueur" VARCHAR(42) } Ref:"Réservation"."num voilier" > "Voilier"."num voilier" Ref:"Réservation"."num semaine" > "Semaine"."num semaine" ================================================ FILE: test/zoo/cluster_NN1/ddl/cluster_NN1_0_ddl.sql ================================================ CREATE TABLE RESERVATION ( PRIMARY KEY (num_resa), num_resa VARCHAR(8) NOT NULL, arrhes VARCHAR(42), date_resa DATE, num_voilier VARCHAR(8) NOT NULL, num_semaine VARCHAR(8) NOT NULL, tarif VARCHAR(42), UNIQUE (num_voilier, num_semaine) ); CREATE TABLE SEMAINE ( PRIMARY KEY (num_semaine), num_semaine VARCHAR(8) NOT NULL, date_debut DATE, UNIQUE (date_debut) ); CREATE TABLE VOILIER ( PRIMARY KEY (num_voilier), num_voilier VARCHAR(8) NOT NULL, longueur DECIMAL(10,2) ); ALTER TABLE RESERVATION ADD FOREIGN KEY (num_semaine) REFERENCES SEMAINE (num_semaine); ALTER TABLE RESERVATION ADD FOREIGN KEY (num_voilier) REFERENCES VOILIER (num_voilier); ================================================ FILE: test/zoo/cluster_NN1/exported/cluster_NN1_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Voilier"] 5 [label="Semaine"] 6 [label="Réservation"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="longueur"] 9 [label="date\ndébut"] 11 [label="arrhes"] 12 [label="date résa"] // Weak and strong entity attributes 2 [label=<num
    voilier
    >] 8 [label=<num
    semaine
    >] 10 [label=<num résa>] // Relationship attributes node [ fillcolor="#FFFFFF" ] 7 [label="tarif"] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="Offrir"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 8 5 -- 9 6 -- 10 6 -- 11 6 -- 12 // Edges between relationships and attributes edge [color="#000000"] 4 -- 7 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 6 -- 4 edge [headlabel=N] 1 -- 4 [color="#000000"] 5 -- 4 [color="#000000"] } ================================================ FILE: test/zoo/cluster_NN1/exported/cluster_NN1_0_erd_chen.txt ================================================ [Réservation] ==1== [Semaine] --N-- [Voilier] --N-- ================================================ FILE: test/zoo/cluster_NN1/exported/cluster_NN1_0_erd_crow.mmd ================================================ Not implemented for n-ary associations. ================================================ FILE: test/zoo/cluster_NN1/exported/cluster_NN1_0_uml.puml ================================================ @startuml "cluster_NN1" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Voilier") { {field} + pk(num voilier) {field} + longueur } diamond N_ARY_0 N_ARY_0 -- "*" "Voilier" N_ARY_0 -- "*" "Semaine" N_ARY_0 -- "1" "Réservation" Table("Semaine") { {field} + pk(num semaine) {field} + date début } Table("Réservation") { {field} + pk(num résa) {field} + arrhes {field} + date résa {field} + tarif } @enduml ================================================ FILE: test/zoo/cluster_NN1/mld/cluster_NN1_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Réservation num résa ! primary_key True Réservation arrhes normal_attribute False Réservation date résa normal_attribute False Réservation num voilier ! 1 foreign_key False Voilier Voilier Offrir Réservation num semaine ! 1 foreign_key False Semaine Semaine Offrir Réservation tarif outer_attribute False Offrir Semaine num semaine ! primary_key True Semaine date début 1 normal_attribute False Voilier num voilier ! primary_key True Voilier longueur normal_attribute False ================================================ FILE: test/zoo/cluster_NN1/mld/cluster_NN1_0_dependencies.gv ================================================ digraph { node [shape=box] "Semaine" -> "Réservation" "Voilier" -> "Réservation" } ================================================ FILE: test/zoo/cluster_NN1/mld/cluster_NN1_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD cluster_NN1
    Réservation ( num résa, arrhes, date résa, #num voilier u1, #num semaine u1, tarif )
    • Le champ num résa constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Réservation.
    • Les champs arrhes et date résa étaient déjà de simples attributs de l'entité Réservation.
    • Le champ num voilier est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle Offrir à partir de l'entité Voilier en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1.
    • Le champ num semaine est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle Offrir à partir de l'entité Semaine en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1.
    • Le champ tarif a migré à partir de l'association de dépendance fonctionnelle Offrir.
    Semaine ( num semaine, date début u1 )
    • Le champ num semaine constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Semaine.
    • Le champ date début était déjà un simple attribut de l'entité Semaine. Il obéit à la contrainte d'unicité 1.
    Voilier ( num voilier, longueur )
    • Le champ num voilier constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Voilier.
    • Le champ longueur était déjà un simple attribut de l'entité Voilier.
    ================================================ FILE: test/zoo/cluster_NN1/mld/cluster_NN1_0_mld.mcd ================================================ %%mocodo : Voilier: num voilier, longueur ::: Semaine: num semaine, date début : ::: Réservation: num résa, arrhes, date résa, #num voilier > Voilier > num voilier, #num semaine > Semaine > num semaine, tarif ::: ================================================ FILE: test/zoo/cluster_NN1/mld/cluster_NN1_0_mld.md ================================================ - **Réservation** (num résa, arrhes, date résa, _#num voilier_ u1, _#num semaine_ u1, tarif) - Le champ _num résa_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Réservation_. - Les champs _arrhes_ et _date résa_ étaient déjà de simples attributs de l'entité _Réservation_. - Le champ _num voilier_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _Offrir_ à partir de l'entité _Voilier_ en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. - Le champ _num semaine_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _Offrir_ à partir de l'entité _Semaine_ en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. - Le champ _tarif_ a migré à partir de l'association de dépendance fonctionnelle _Offrir_. - **Semaine** (num semaine, date début u1) - Le champ _num semaine_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Semaine_. - Le champ _date début_ était déjà un simple attribut de l'entité _Semaine_. Il obéit à la contrainte d'unicité 1. - **Voilier** (num voilier, longueur) - Le champ _num voilier_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Voilier_. - Le champ _longueur_ était déjà un simple attribut de l'entité _Voilier_. ================================================ FILE: test/zoo/cluster_NN1/mld/cluster_NN1_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{cluster\_NN1}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Réservation} (\prim{num résa}, \attr{arrhes}, \attr{date résa}, \foreign{num voilier}$^{u\_1}$, \foreign{num semaine}$^{u\_1}$, \attr{tarif}) \begin{itemize} \item Le champ \emph{num résa} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Réservation}. \item Les champs \emph{arrhes} et \emph{date résa} étaient déjà de simples attributs de l'entité \emph{Réservation}. \item Le champ \emph{num voilier} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{Offrir} à partir de l'entité \emph{Voilier} en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. \item Le champ \emph{num semaine} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{Offrir} à partir de l'entité \emph{Semaine} en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. \item Le champ \emph{tarif} a migré à partir de l'association de dépendance fonctionnelle \emph{Offrir}. \end{itemize} \item \relat{Semaine} (\prim{num semaine}, \attr{date début}$^{u\_1}$) \begin{itemize} \item Le champ \emph{num semaine} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Semaine}. \item Le champ \emph{date début} était déjà un simple attribut de l'entité \emph{Semaine}. Il obéit à la contrainte d'unicité 1. \end{itemize} \item \relat{Voilier} (\prim{num voilier}, \attr{longueur}) \begin{itemize} \item Le champ \emph{num voilier} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Voilier}. \item Le champ \emph{longueur} était déjà un simple attribut de l'entité \emph{Voilier}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/cluster_NN1/mld/cluster_NN1_0_mld.txt ================================================ - Réservation (_num résa_, arrhes, date résa, #num voilier¹, #num semaine¹, tarif) - Le champ « num résa » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Réservation ». - Les champs « arrhes » et « date résa » étaient déjà de simples attributs de l'entité « Réservation ». - Le champ « num voilier » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « Offrir » à partir de l'entité « Voilier » en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. - Le champ « num semaine » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « Offrir » à partir de l'entité « Semaine » en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. - Le champ « tarif » a migré à partir de l'association de dépendance fonctionnelle « Offrir ». - Semaine (_num semaine_, date début¹) - Le champ « num semaine » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Semaine ». - Le champ « date début » était déjà un simple attribut de l'entité « Semaine ». Il obéit à la contrainte d'unicité 1. - Voilier (_num voilier_, longueur) - Le champ « num voilier » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Voilier ». - Le champ « longueur » était déjà un simple attribut de l'entité « Voilier ». ================================================ FILE: test/zoo/cluster_NN1/rewritten/cluster_NN1_0_rw_create_df_arrows=across.mcd ================================================ Voilier: num voilier, longueur Offrir, 0N> Voilier, 0N> Semaine, /11 Réservation: tarif Semaine: num semaine, 1_date début Réservation: num résa, arrhes, date résa ================================================ FILE: test/zoo/cluster_NN1/rewritten/cluster_NN1_0_rw_drain.mcd ================================================ Voilier: num voilier, longueur Offrir, 0N Voilier, 0N Semaine, /11 Réservation Semaine: num semaine, 1_date début Réservation: num résa, arrhes, date résa, tarif ================================================ FILE: test/zoo/cluster_NN1/rewritten/cluster_NN1_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2 ASSOC 4_, 0N ENTITÉ 1_, 0N ENTITÉ 2_, /11 ENTITÉ 3_: at 4 1 ENTITÉ 2_: at 2 1, 1_at 2 2 ENTITÉ 3_: at 3 1, at 3 2, at 3 3 ================================================ FILE: test/zoo/cluster_NN1/rewritten/cluster_NN1_0_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/cluster_NN1/rewritten/cluster_NN1_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/cluster_NN1/rewritten/cluster_NN1_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/cluster_NN1/rewritten/cluster_NN1_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/cluster_NN1/rewritten/cluster_NN1_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/cluster_NN1/rewritten/cluster_NN1_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/cluster_NN1/rewritten/cluster_NN1_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/complex/_complex.mcd ================================================ : PÉRIODE: date début, _date fin A MÈRE, 01 ANIMAL, 0N> [mère] ANIMAL : : ENCLOS: num. enclos OCCUPE, 1N ANIMAL, /1N PÉRIODE, 1N ENCLOS ANIMAL: nom, sexe, _date naissance, date décès /\\ ANIMAL <= CARNIVORE, HERBIVORE: type alimentation CARNIVORE: quantité viande PEUT VIVRE DANS, 1N ESPÈCE, 1N ENCLOS: nb. max. congénères ESPÈCE: code espèce, 1_nom latin, nom vernaculaire DF, 0N ESPÈCE, _11 ANIMAL HERBIVORE: plante préférée : : PEUT COHABITER AVEC, 0N ESPÈCE, 0N [commensale] ESPÈCE: nb. max. commensaux : : : ================================================ FILE: test/zoo/complex/ddl/complex_ddl.d2 ================================================ "ANIMAL": { shape: sql_table "code espèce": VARCHAR(42) {constraint: [PK; FK]} "nom": VARCHAR(42) {constraint: PK} "date naissance": VARCHAR(42) {constraint: PK} "sexe": VARCHAR(42) "date décès": VARCHAR(42) "code espèce mère": VARCHAR(42) {constraint: [FK; "NULL"]} "nom mère": VARCHAR(42) {constraint: [FK; "NULL"]} "date naissance mère": VARCHAR(42) {constraint: [FK; "NULL"]} "type alimentation": UNSIGNED INT {constraint: "NULL"} "est carnivore": BOOLEAN {constraint: NOT NULL} "quantité viande": VARCHAR(42) {constraint: "NULL"} "est herbivore": BOOLEAN {constraint: NOT NULL} "plante préférée": VARCHAR(42) {constraint: "NULL"} } "ESPÈCE": { shape: sql_table "code espèce": VARCHAR(42) {constraint: PK} "nom latin": VARCHAR(42) {constraint: UNQ1} "nom vernaculaire": VARCHAR(42) } "OCCUPE": { shape: sql_table "code espèce": VARCHAR(42) {constraint: [PK; FK]} "nom": VARCHAR(42) {constraint: [PK; FK]} "date naissance": VARCHAR(42) {constraint: [PK; FK]} "num. enclos": VARCHAR(42) {constraint: PK} "date début": VARCHAR(42) {constraint: NOT NULL} "date fin": VARCHAR(42) {constraint: NOT NULL} } "PEUT COHABITER AVEC": { shape: sql_table "code espèce": VARCHAR(42) {constraint: [PK; FK]} "code espèce commensale": VARCHAR(42) {constraint: [PK; FK]} "nb. max. commensaux": VARCHAR(42) } "PEUT VIVRE DANS": { shape: sql_table "code espèce": VARCHAR(42) {constraint: [PK; FK]} "num. enclos": VARCHAR(42) {constraint: PK} "nb. max. congénères": VARCHAR(42) } "ANIMAL"."code espèce" -> "ESPÈCE"."code espèce" "ANIMAL"."code espèce mère" -> "ANIMAL"."code espèce" "ANIMAL"."nom mère" -> "ANIMAL"."nom" "ANIMAL"."date naissance mère" -> "ANIMAL"."date naissance" "OCCUPE"."code espèce" -> "ANIMAL"."code espèce" "OCCUPE"."nom" -> "ANIMAL"."nom" "OCCUPE"."date naissance" -> "ANIMAL"."date naissance" "PEUT COHABITER AVEC"."code espèce" -> "ESPÈCE"."code espèce" "PEUT COHABITER AVEC"."code espèce commensale" -> "ESPÈCE"."code espèce" "PEUT VIVRE DANS"."code espèce" -> "ESPÈCE"."code espèce" ================================================ FILE: test/zoo/complex/ddl/complex_ddl.dbml ================================================ Table "ANIMAL" { "code espèce" VARCHAR(42) [NOT NULL] "nom" VARCHAR(42) [NOT NULL] "date naissance" VARCHAR(42) [NOT NULL] "sexe" VARCHAR(42) "date décès" VARCHAR(42) "code espèce mère" VARCHAR(42) ["NULL"] "nom mère" VARCHAR(42) ["NULL"] "date naissance mère" VARCHAR(42) ["NULL"] "type alimentation" UNSIGNED_INT ["NULL"] "est carnivore" BOOLEAN [NOT NULL] "quantité viande" VARCHAR(42) ["NULL"] "est herbivore" BOOLEAN [NOT NULL] "plante préférée" VARCHAR(42) ["NULL"] Indexes { ("code espèce", "nom", "date naissance") [pk] } } Table "ESPÈCE" { "code espèce" VARCHAR(42) [pk, NOT NULL] "nom latin" VARCHAR(42) "nom vernaculaire" VARCHAR(42) Indexes { "nom latin" [unique] } } Table "OCCUPE" { "code espèce" VARCHAR(42) [NOT NULL] "nom" VARCHAR(42) [NOT NULL] "date naissance" VARCHAR(42) [NOT NULL] "num. enclos" VARCHAR(42) [NOT NULL] "date début" VARCHAR(42) [NOT NULL] "date fin" VARCHAR(42) [NOT NULL] Indexes { ("code espèce", "nom", "date naissance", "num. enclos") [pk] } } Table "PEUT COHABITER AVEC" { "code espèce" VARCHAR(42) [NOT NULL] "code espèce commensale" VARCHAR(42) [NOT NULL] "nb. max. commensaux" VARCHAR(42) Indexes { ("code espèce", "code espèce commensale") [pk] } } Table "PEUT VIVRE DANS" { "code espèce" VARCHAR(42) [NOT NULL] "num. enclos" VARCHAR(42) [NOT NULL] "nb. max. congénères" VARCHAR(42) Indexes { ("code espèce", "num. enclos") [pk] } } Ref:"ANIMAL"."code espèce" > "ESPÈCE"."code espèce" Ref:"ANIMAL".("code espèce mère", "date naissance mère", "nom mère") > "ANIMAL".("code espèce", "date naissance", "nom") Ref:"OCCUPE".("code espèce", "date naissance", "nom") > "ANIMAL".("code espèce", "date naissance", "nom") Ref:"PEUT COHABITER AVEC".("code espèce", "code espèce commensale") > "ESPÈCE".("code espèce", "code espèce") Ref:"PEUT VIVRE DANS"."code espèce" > "ESPÈCE"."code espèce" ================================================ FILE: test/zoo/complex/ddl/complex_ddl.sql ================================================ CREATE TABLE ANIMAL ( PRIMARY KEY (code_espece, nom, date_naissance), code_espece VARCHAR(8) NOT NULL, nom VARCHAR(255) NOT NULL, date_naissance DATE NOT NULL, sexe CHAR(1), date_deces DATE, code_espece_mere VARCHAR(8) NULL, nom_mere VARCHAR(255) NULL, date_naissance_mere DATE NULL, type_alimentation UNSIGNED INT NULL, est_carnivore BOOLEAN NOT NULL, quantite_viande INTEGER NULL, est_herbivore BOOLEAN NOT NULL, plante_preferee VARCHAR(42) NULL ); CREATE TABLE ESPECE ( PRIMARY KEY (code_espece), code_espece VARCHAR(8) NOT NULL, nom_latin VARCHAR(255), nom_vernaculaire VARCHAR(255), UNIQUE (nom_latin) ); CREATE TABLE OCCUPE ( PRIMARY KEY (code_espece, nom, date_naissance, num_enclos), code_espece VARCHAR(8) NOT NULL, nom VARCHAR(255) NOT NULL, date_naissance DATE NOT NULL, num_enclos VARCHAR(8) NOT NULL, date_debut DATE NOT NULL, date_fin DATE NOT NULL ); CREATE TABLE PEUT_COHABITER_AVEC ( PRIMARY KEY (code_espece, code_espece_commensale), code_espece VARCHAR(8) NOT NULL, code_espece_commensale VARCHAR(8) NOT NULL, nb_max_commensaux INTEGER ); CREATE TABLE PEUT_VIVRE_DANS ( PRIMARY KEY (code_espece, num_enclos), code_espece VARCHAR(8) NOT NULL, num_enclos VARCHAR(8) NOT NULL, nb_max_congeneres INTEGER ); ALTER TABLE ANIMAL ADD FOREIGN KEY (code_espece_mere, nom_mere, date_naissance_mere) REFERENCES ANIMAL (code_espece, nom, date_naissance); ALTER TABLE ANIMAL ADD FOREIGN KEY (code_espece) REFERENCES ESPECE (code_espece); ALTER TABLE OCCUPE ADD FOREIGN KEY (code_espece, nom, date_naissance) REFERENCES ANIMAL (code_espece, nom, date_naissance); ALTER TABLE PEUT_COHABITER_AVEC ADD FOREIGN KEY (code_espece_commensale) REFERENCES ESPECE (code_espece); ALTER TABLE PEUT_COHABITER_AVEC ADD FOREIGN KEY (code_espece) REFERENCES ESPECE (code_espece); ALTER TABLE PEUT_VIVRE_DANS ADD FOREIGN KEY (code_espece) REFERENCES ESPECE (code_espece); ================================================ FILE: test/zoo/complex/exported/complex_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="PÉRIODE"] 6 [label="ENCLOS"] 5 [label="ANIMAL",peripheries=2] 16 [label="ESPÈCE"] // Associative entities 13 [label="CARNIVORE",shape=Mdiamond] 22 [label="HERBIVORE",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 10 [label="sexe"] 12 [label="date\ndécès"] 19 [label="nom\nlatin"] 20 [label="nom\nvernaculaire"] // Weak and strong entity attributes 2 [label=<date
    début
    >] 3 [label=<date fin>] 7 [label=<num.
    enclos
    >] 9 [label=<nom> style="dashed,filled"] 11 [label=<date
    naissance
    > style="dashed,filled"] 14 [label=<quantité
    viande
    >] 18 [label=<code
    espèce
    >] 23 [label=<plante
    préférée
    >] // Relationship attributes node [ fillcolor="#FFFFFF" ] 17 [label="nb. max.\ncongénères"] 25 [label="nb. max.\ncommensaux"] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="A MÈRE"] 8 [label="OCCUPE"] 15 [label="PEUT VIVRE\nDANS"] 21 [label="DF",peripheries=2] 24 [label="PEUT\nCOHABITER\nAVEC"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 9 5 -- 10 5 -- 11 5 -- 12 6 -- 7 13 -- 14 16 -- 18 16 -- 19 16 -- 20 22 -- 23 // Edges between relationships and attributes edge [color="#000000"] 15 -- 17 24 -- 25 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 8 5 -- 4 [color="#000000"] 16 -- 21 [color="#000000"] edge [headlabel=M] 6 -- 15 16 -- 24 [color="#000000"] edge [headlabel=N] 5 -- 8 5 -- 21 6 -- 8 16 -- 15 4 -- 5 [color="#000000"] 24 -- 16 [color="#000000"] } ================================================ FILE: test/zoo/complex/exported/complex_erd_chen.txt ================================================ [ENCLOS] ==M== [ENCLOS] ==N== [ESPÈCE] --1-- <> [ESPÈCE] --M-- [ESPÈCE] --N-- [ESPÈCE] ==N== [PÉRIODE] ==1== [[ANIMAL]] --1-- [[ANIMAL]] --N-- [[ANIMAL]] ==N== <> [[ANIMAL]] ==N== ================================================ FILE: test/zoo/complex/exported/complex_erd_crow.mmd ================================================ Not implemented for n-ary associations. ================================================ FILE: test/zoo/complex/exported/complex_uml.puml ================================================ @startuml "complex" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("PÉRIODE") { {field} + pk(date début) {field} + pk(date fin) } "ANIMAL" "*" --- "1..*" "ANIMAL": "A MÈRE" Table("ENCLOS") { {field} + pk(num. enclos) } diamond N_ARY_0 N_ARY_0 -- "1..*" "ANIMAL" N_ARY_0 -- "1" "PÉRIODE" N_ARY_0 -- "1..*" "ENCLOS" Table("ANIMAL") { {field} + pk(nom) {field} + sexe {field} + pk(date naissance) {field} + date décès } note "{incomplete, overlapping}" as GENERALIZATION_1 GENERALIZATION_1 -[dotted]- HERBIVORE GENERALIZATION_1 -[dotted]- CARNIVORE ANIMAL <|-- HERBIVORE ANIMAL <|-- CARNIVORE Table("CARNIVORE") { {field} + pk(quantité viande) } "ESPÈCE" "1..*" --- "1..*" "ENCLOS": "PEUT VIVRE DANS" ("ESPÈCE", "ENCLOS") .. "PEUT VIVRE DANS" Table("PEUT VIVRE DANS") { {field} + nb. max. congénères } Table("ESPÈCE") { {field} + pk(code espèce) {field} + nom latin {field} + nom vernaculaire } "ESPÈCE" "1" *-- "*" "ANIMAL" Table("HERBIVORE") { {field} + pk(plante préférée) } "ESPÈCE" "*" --- "*" "ESPÈCE": "PEUT COHABITER AVEC" ("ESPÈCE", "ESPÈCE") .. "PEUT COHABITER AVEC" Table("PEUT COHABITER AVEC") { {field} + nb. max. commensaux } @enduml ================================================ FILE: test/zoo/complex/mld/complex_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note ANIMAL code espèce ! strengthening_primary_foreign_key True ESPÈCE ESPÈCE DF ANIMAL nom ! primary_key True ANIMAL date naissance ! primary_key True ANIMAL sexe normal_attribute False ANIMAL date décès normal_attribute False ANIMAL code espèce ? foreign_key False ANIMAL ANIMAL A MÈRE mère ANIMAL nom ? foreign_key False ANIMAL ANIMAL A MÈRE mère ANIMAL date naissance ? foreign_key False ANIMAL ANIMAL A MÈRE mère ANIMAL type alimentation ? deleted_child_discriminator_ False UNSIGNED_INT_PLACEHOLDER ANIMAL est carnivore ! deleted_child_entity_name False CARNIVORE CARNIVORE BOOLEAN_PLACEHOLDER ANIMAL quantité viande ? deleted_child_attribute False CARNIVORE ANIMAL est herbivore ! deleted_child_entity_name False HERBIVORE HERBIVORE BOOLEAN_PLACEHOLDER ANIMAL plante préférée ? deleted_child_attribute False HERBIVORE ESPÈCE code espèce ! primary_key True ESPÈCE nom latin 1 normal_attribute False ESPÈCE nom vernaculaire normal_attribute False OCCUPE code espèce ! primary_foreign_key True ANIMAL ANIMAL OCCUPE OCCUPE nom ! primary_foreign_key True ANIMAL ANIMAL OCCUPE OCCUPE date naissance ! primary_foreign_key True ANIMAL ANIMAL OCCUPE OCCUPE num. enclos ! primary_ex_foreign_key True ENCLOS ENCLOS OCCUPE OCCUPE date début ! stopped_ex_foreign_key False PÉRIODE PÉRIODE OCCUPE OCCUPE date fin ! stopped_ex_foreign_key False PÉRIODE PÉRIODE OCCUPE PEUT COHABITER AVEC code espèce ! primary_foreign_key True ESPÈCE ESPÈCE PEUT COHABITER AVEC PEUT COHABITER AVEC code espèce ! primary_foreign_key True ESPÈCE ESPÈCE PEUT COHABITER AVEC commensale PEUT COHABITER AVEC nb. max. commensaux association_attribute False PEUT COHABITER AVEC PEUT VIVRE DANS code espèce ! primary_foreign_key True ESPÈCE ESPÈCE PEUT VIVRE DANS PEUT VIVRE DANS num. enclos ! primary_ex_foreign_key True ENCLOS ENCLOS PEUT VIVRE DANS PEUT VIVRE DANS nb. max. congénères association_attribute False PEUT VIVRE DANS ================================================ FILE: test/zoo/complex/mld/complex_dependencies.gv ================================================ digraph { node [shape=box] "ANIMAL" -> "ANIMAL" "ESPÈCE" -> "ANIMAL" "ANIMAL" -> "OCCUPE" "ESPÈCE" -> "PEUT COHABITER AVEC" "ESPÈCE" -> "PEUT VIVRE DANS" } ================================================ FILE: test/zoo/complex/mld/complex_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD complex
    ANIMAL ( #code espèce, nom, date naissance, sexe, date décès, #code espèce mère?, #nom mère?, #date naissance mère?, type alimentation?, est carnivore!, quantité viande?, est herbivore!, plante préférée? )
    • Le champ code espèce fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité ESPÈCE pour renforcer l'identifiant.
    • Les champs nom et date naissance font partie de la clé primaire de la table. C'étaient déjà des identifiants de l'entité ANIMAL.
    • Les champs sexe et date décès étaient déjà de simples attributs de l'entité ANIMAL.
    • Les champs à saisie facultative code espèce mère, nom mère et date naissance mère sont des clés étrangères. Ils ont migré par l'association de dépendance fonctionnelle A MÈRE à partir de l'entité ANIMAL en perdant leur caractère identifiant.
    • Un discriminateur à saisie facultative type alimentation est ajouté pour indiquer la nature de la spécialisation. Peut être vide, du fait de l'absence de contrainte de totalité.
    • Un champ booléen à saisie obligatoire est carnivore est ajouté pour indiquer si on a affaire ou pas à la spécialisation de même nom.
    • Le champ à saisie facultative quantité viande a migré à partir de l'entité-fille CARNIVORE (supprimée).
    • Un champ booléen à saisie obligatoire est herbivore est ajouté pour indiquer si on a affaire ou pas à la spécialisation de même nom.
    • Le champ à saisie facultative plante préférée a migré à partir de l'entité-fille HERBIVORE (supprimée).
    ESPÈCE ( code espèce, nom latin u1, nom vernaculaire )
    • Le champ code espèce constitue la clé primaire de la table. C'était déjà un identifiant de l'entité ESPÈCE.
    • Le champ nom latin était déjà un simple attribut de l'entité ESPÈCE. Il obéit à la contrainte d'unicité 1.
    • Le champ nom vernaculaire était déjà un simple attribut de l'entité ESPÈCE.
    OCCUPE ( #code espèce, #nom, #date naissance, num. enclos, date début!, date fin! )
    • Les champs code espèce, nom et date naissance font partie de la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité ANIMAL.
    • Le champ num. enclos fait partie de la clé primaire de la table. Sa table d'origine (ENCLOS) ayant été supprimée, il n'est pas considéré comme clé étrangère.
    • Les champs à saisie obligatoire date début et date fin sont de simples attributs. Ils ont migré directement à partir de l'entité PÉRIODE en perdant leur caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, ils ne sont pas considérés comme clés étrangères.
    PEUT COHABITER AVEC ( #code espèce, #code espèce commensale, nb. max. commensaux )
    • Les champs code espèce et code espèce commensale constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité ESPÈCE.
    • Le champ nb. max. commensaux était déjà un simple attribut de l'association PEUT COHABITER AVEC.
    PEUT VIVRE DANS ( #code espèce, num. enclos, nb. max. congénères )
    • Le champ code espèce fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité ESPÈCE.
    • Le champ num. enclos fait partie de la clé primaire de la table. Sa table d'origine (ENCLOS) ayant été supprimée, il n'est pas considéré comme clé étrangère.
    • Le champ nb. max. congénères était déjà un simple attribut de l'association PEUT VIVRE DANS.


    NB. Les tables ENCLOS et PÉRIODE ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/complex/mld/complex_mld.mcd ================================================ ::: OCCUPE: #code espèce > ANIMAL > code espèce, _#nom > ANIMAL > nom, _#date naissance > ANIMAL > date naissance, _num. enclos, date début, date fin : ANIMAL: #code espèce > ESPÈCE > code espèce, _nom, _date naissance, sexe, date décès, #code espèce mère > ANIMAL > code espèce, #nom mère > ANIMAL > nom, #date naissance mère > ANIMAL > date naissance, type alimentation, est carnivore, quantité viande, est herbivore, plante préférée : : PEUT VIVRE DANS: #code espèce > ESPÈCE > code espèce, _num. enclos, nb. max. congénères : ESPÈCE: code espèce, nom latin, nom vernaculaire ::: ::: PEUT COHABITER AVEC: #code espèce > ESPÈCE > code espèce, _#code espèce commensale > ESPÈCE > code espèce, nb. max. commensaux ::: ================================================ FILE: test/zoo/complex/mld/complex_mld.md ================================================ - **ANIMAL** (_#code espèce_, nom, date naissance, sexe, date décès, _#code espèce mère?_, _#nom mère?_, _#date naissance mère?_, type alimentation?, est carnivore!, quantité viande?, est herbivore!, plante préférée?) - Le champ _code espèce_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _ESPÈCE_ pour renforcer l'identifiant. - Les champs _nom_ et _date naissance_ font partie de la clé primaire de la table. C'étaient déjà des identifiants de l'entité _ANIMAL_. - Les champs _sexe_ et _date décès_ étaient déjà de simples attributs de l'entité _ANIMAL_. - Les champs à saisie facultative _code espèce mère_, _nom mère_ et _date naissance mère_ sont des clés étrangères. Ils ont migré par l'association de dépendance fonctionnelle _A MÈRE_ à partir de l'entité _ANIMAL_ en perdant leur caractère identifiant. - Un discriminateur à saisie facultative _type alimentation_ est ajouté pour indiquer la nature de la spécialisation. Peut être vide, du fait de l'absence de contrainte de totalité. - Un champ booléen à saisie obligatoire _est carnivore_ est ajouté pour indiquer si on a affaire ou pas à la spécialisation de même nom. - Le champ à saisie facultative _quantité viande_ a migré à partir de l'entité-fille _CARNIVORE_ (supprimée). - Un champ booléen à saisie obligatoire _est herbivore_ est ajouté pour indiquer si on a affaire ou pas à la spécialisation de même nom. - Le champ à saisie facultative _plante préférée_ a migré à partir de l'entité-fille _HERBIVORE_ (supprimée). - **ESPÈCE** (code espèce, nom latin u1, nom vernaculaire) - Le champ _code espèce_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _ESPÈCE_. - Le champ _nom latin_ était déjà un simple attribut de l'entité _ESPÈCE_. Il obéit à la contrainte d'unicité 1. - Le champ _nom vernaculaire_ était déjà un simple attribut de l'entité _ESPÈCE_. - **OCCUPE** (_#code espèce_, _#nom_, _#date naissance_, num. enclos, date début!, date fin!) - Les champs _code espèce_, _nom_ et _date naissance_ font partie de la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité _ANIMAL_. - Le champ _num. enclos_ fait partie de la clé primaire de la table. Sa table d'origine (_ENCLOS_) ayant été supprimée, il n'est pas considéré comme clé étrangère. - Les champs à saisie obligatoire _date début_ et _date fin_ sont de simples attributs. Ils ont migré directement à partir de l'entité _PÉRIODE_ en perdant leur caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, ils ne sont pas considérés comme clés étrangères. - **PEUT COHABITER AVEC** (_#code espèce_, _#code espèce commensale_, nb. max. commensaux) - Les champs _code espèce_ et _code espèce commensale_ constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité _ESPÈCE_. - Le champ _nb. max. commensaux_ était déjà un simple attribut de l'association _PEUT COHABITER AVEC_. - **PEUT VIVRE DANS** (_#code espèce_, num. enclos, nb. max. congénères) - Le champ _code espèce_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _ESPÈCE_. - Le champ _num. enclos_ fait partie de la clé primaire de la table. Sa table d'origine (_ENCLOS_) ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ _nb. max. congénères_ était déjà un simple attribut de l'association _PEUT VIVRE DANS_.
    ---- **NB.** Les tables _ENCLOS_ et _PÉRIODE_ ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/complex/mld/complex_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{complex}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{ANIMAL} (\foreign{\prim{code espèce}}, \prim{nom}, \prim{date naissance}, \attr{sexe}, \attr{date décès}, \foreign{code espèce mère?}, \foreign{nom mère?}, \foreign{date naissance mère?}, \attr{type alimentation?}, \attr{est carnivore!}, \attr{quantité viande?}, \attr{est herbivore!}, \attr{plante préférée?}) \begin{itemize} \item Le champ \emph{code espèce} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{ESPÈCE} pour renforcer l'identifiant. \item Les champs \emph{nom} et \emph{date naissance} font partie de la clé primaire de la table. C'étaient déjà des identifiants de l'entité \emph{ANIMAL}. \item Les champs \emph{sexe} et \emph{date décès} étaient déjà de simples attributs de l'entité \emph{ANIMAL}. \item Les champs à saisie facultative \emph{code espèce mère}, \emph{nom mère} et \emph{date naissance mère} sont des clés étrangères. Ils ont migré par l'association de dépendance fonctionnelle \emph{A MÈRE} à partir de l'entité \emph{ANIMAL} en perdant leur caractère identifiant. \item Un discriminateur à saisie facultative \emph{type alimentation} est ajouté pour indiquer la nature de la spécialisation. Peut être vide, du fait de l'absence de contrainte de totalité. \item Un champ booléen à saisie obligatoire \emph{est carnivore} est ajouté pour indiquer si on a affaire ou pas à la spécialisation de même nom. \item Le champ à saisie facultative \emph{quantité viande} a migré à partir de l'entité-fille \emph{CARNIVORE} (supprimée). \item Un champ booléen à saisie obligatoire \emph{est herbivore} est ajouté pour indiquer si on a affaire ou pas à la spécialisation de même nom. \item Le champ à saisie facultative \emph{plante préférée} a migré à partir de l'entité-fille \emph{HERBIVORE} (supprimée). \end{itemize} \item \relat{ESPÈCE} (\prim{code espèce}, \attr{nom latin}$^{u\_1}$, \attr{nom vernaculaire}) \begin{itemize} \item Le champ \emph{code espèce} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{ESPÈCE}. \item Le champ \emph{nom latin} était déjà un simple attribut de l'entité \emph{ESPÈCE}. Il obéit à la contrainte d'unicité 1. \item Le champ \emph{nom vernaculaire} était déjà un simple attribut de l'entité \emph{ESPÈCE}. \end{itemize} \item \relat{OCCUPE} (\foreign{\prim{code espèce}}, \foreign{\prim{nom}}, \foreign{\prim{date naissance}}, \prim{num. enclos}, \attr{date début!}, \attr{date fin!}) \begin{itemize} \item Les champs \emph{code espèce}, \emph{nom} et \emph{date naissance} font partie de la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité \emph{ANIMAL}. \item Le champ \emph{num. enclos} fait partie de la clé primaire de la table. Sa table d'origine (\emph{ENCLOS}) ayant été supprimée, il n'est pas considéré comme clé étrangère. \item Les champs à saisie obligatoire \emph{date début} et \emph{date fin} sont de simples attributs. Ils ont migré directement à partir de l'entité \emph{PÉRIODE} en perdant leur caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, ils ne sont pas considérés comme clés étrangères. \end{itemize} \item \relat{PEUT COHABITER AVEC} (\foreign{\prim{code espèce}}, \foreign{\prim{code espèce commensale}}, \attr{nb. max. commensaux}) \begin{itemize} \item Les champs \emph{code espèce} et \emph{code espèce commensale} constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité \emph{ESPÈCE}. \item Le champ \emph{nb. max. commensaux} était déjà un simple attribut de l'association \emph{PEUT COHABITER AVEC}. \end{itemize} \item \relat{PEUT VIVRE DANS} (\foreign{\prim{code espèce}}, \prim{num. enclos}, \attr{nb. max. congénères}) \begin{itemize} \item Le champ \emph{code espèce} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{ESPÈCE}. \item Le champ \emph{num. enclos} fait partie de la clé primaire de la table. Sa table d'origine (\emph{ENCLOS}) ayant été supprimée, il n'est pas considéré comme clé étrangère. \item Le champ \emph{nb. max. congénères} était déjà un simple attribut de l'association \emph{PEUT VIVRE DANS}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/complex/mld/complex_mld.txt ================================================ - ANIMAL (_#code espèce_, _nom_, _date naissance_, sexe, date décès, #code espèce mère?, #nom mère?, #date naissance mère?, type alimentation?, est carnivore!, quantité viande?, est herbivore!, plante préférée?) - Le champ « code espèce » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « ESPÈCE » pour renforcer l'identifiant. - Les champs « nom » et « date naissance » font partie de la clé primaire de la table. C'étaient déjà des identifiants de l'entité « ANIMAL ». - Les champs « sexe » et « date décès » étaient déjà de simples attributs de l'entité « ANIMAL ». - Les champs à saisie facultative « code espèce mère », « nom mère » et « date naissance mère » sont des clés étrangères. Ils ont migré par l'association de dépendance fonctionnelle « A MÈRE » à partir de l'entité « ANIMAL » en perdant leur caractère identifiant. - Un discriminateur à saisie facultative « type alimentation » est ajouté pour indiquer la nature de la spécialisation. Peut être vide, du fait de l'absence de contrainte de totalité. - Un champ booléen à saisie obligatoire « est carnivore » est ajouté pour indiquer si on a affaire ou pas à la spécialisation de même nom. - Le champ à saisie facultative « quantité viande » a migré à partir de l'entité-fille « CARNIVORE » (supprimée). - Un champ booléen à saisie obligatoire « est herbivore » est ajouté pour indiquer si on a affaire ou pas à la spécialisation de même nom. - Le champ à saisie facultative « plante préférée » a migré à partir de l'entité-fille « HERBIVORE » (supprimée). - ESPÈCE (_code espèce_, nom latin¹, nom vernaculaire) - Le champ « code espèce » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « ESPÈCE ». - Le champ « nom latin » était déjà un simple attribut de l'entité « ESPÈCE ». Il obéit à la contrainte d'unicité 1. - Le champ « nom vernaculaire » était déjà un simple attribut de l'entité « ESPÈCE ». - OCCUPE (_#code espèce_, _#nom_, _#date naissance_, _num. enclos_, date début!, date fin!) - Les champs « code espèce », « nom » et « date naissance » font partie de la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité « ANIMAL ». - Le champ « num. enclos » fait partie de la clé primaire de la table. Sa table d'origine (« ENCLOS ») ayant été supprimée, il n'est pas considéré comme clé étrangère. - Les champs à saisie obligatoire « date début » et « date fin » sont de simples attributs. Ils ont migré directement à partir de l'entité « PÉRIODE » en perdant leur caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, ils ne sont pas considérés comme clés étrangères. - PEUT COHABITER AVEC (_#code espèce_, _#code espèce commensale_, nb. max. commensaux) - Les champs « code espèce » et « code espèce commensale » constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité « ESPÈCE ». - Le champ « nb. max. commensaux » était déjà un simple attribut de l'association « PEUT COHABITER AVEC ». - PEUT VIVRE DANS (_#code espèce_, _num. enclos_, nb. max. congénères) - Le champ « code espèce » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « ESPÈCE ». - Le champ « num. enclos » fait partie de la clé primaire de la table. Sa table d'origine (« ENCLOS ») ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ « nb. max. congénères » était déjà un simple attribut de l'association « PEUT VIVRE DANS ».
    -------------------------------------------------------------------------------- NB. Les tables « ENCLOS » et « PÉRIODE » ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/complex/rewritten/complex_rw_create_df_arrows=across.mcd ================================================ : PÉRIODE: date début, _date fin A MÈRE, 01 ANIMAL, 0N> [mère] ANIMAL : : ENCLOS: num. enclos OCCUPE, 1N ANIMAL, /1N PÉRIODE, 1N ENCLOS ANIMAL: nom, sexe, _date naissance, date décès /\\ ANIMAL <= CARNIVORE, HERBIVORE: type alimentation CARNIVORE: quantité viande PEUT VIVRE DANS, 1N ESPÈCE, 1N ENCLOS: nb. max. congénères ESPÈCE: code espèce, 1_nom latin, nom vernaculaire DF, 0N> ESPÈCE, _11 ANIMAL HERBIVORE: plante préférée : : PEUT COHABITER AVEC, 0N ESPÈCE, 0N [commensale] ESPÈCE: nb. max. commensaux : : : ================================================ FILE: test/zoo/complex/rewritten/complex_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/complex/rewritten/complex_rw_drown.mcd ================================================ : ENTITÉ 01_: at 01 1, _at 01 2 ASSOC 07_, 01 ENTITÉ 03_, 0N> [rôle 1] ENTITÉ 03_ : : ENTITÉ 02_: at 02 1 ASSOC 08_, 1N ENTITÉ 03_, /1N ENTITÉ 01_, 1N ENTITÉ 02_ ENTITÉ 03_: at 03 1, at 03 2, _at 03 3, at 03 4 /\\ ENTITÉ 03_ <= ENTITÉ 04_, ENTITÉ 06_: type alimentation ENTITÉ 04_: at 04 1 ASSOC 09_, 1N ENTITÉ 05_, 1N ENTITÉ 02_: at 09 1 ENTITÉ 05_: at 05 1, 1_at 05 2, at 05 3 DF, 0N ENTITÉ 05_, _11 ENTITÉ 03_ ENTITÉ 06_: at 06 1 : : ASSOC 11_, 0N ENTITÉ 05_, 0N [rôle 1] ENTITÉ 05_: at 11 1 : : : ================================================ FILE: test/zoo/complex/rewritten/complex_rw_explode_arity=2,weak.mcd ================================================ : : : DF, _11 PEUT COHABITER AVEC, 0N [commensale] ESPÈCE PEUT COHABITER AVEC: _nb. max. commensaux : : DF, _11 PEUT VIVRE DANS, 1N ESPÈCE ESPÈCE: code espèce, 1_nom latin, nom vernaculaire DF, _11 PEUT COHABITER AVEC, 0N ESPÈCE ENCLOS: num. enclos DF, _11 PEUT VIVRE DANS, 1N ENCLOS PEUT VIVRE DANS: _nb. max. congénères DF, 0N ESPÈCE, _11 ANIMAL : DF, _11 OCCUPE, 1N ENCLOS OCCUPE: DF, _11 OCCUPE, 1N ANIMAL ANIMAL: nom, sexe, _date naissance, date décès A MÈRE, 01 ANIMAL, 0N> [mère] ANIMAL PÉRIODE: date début, _date fin DF, 11 OCCUPE, 1N PÉRIODE CARNIVORE: quantité viande /\\ ANIMAL <= CARNIVORE, HERBIVORE: type alimentation HERBIVORE: plante préférée ================================================ FILE: test/zoo/complex/rewritten/complex_rw_explode_arity=2.5,weak.mcd ================================================ HERBIVORE: plante préférée /\\ ANIMAL <= CARNIVORE, HERBIVORE: type alimentation CARNIVORE: quantité viande DF, _11 PEUT COHABITER AVEC, 0N [commensale] ESPÈCE PEUT COHABITER AVEC: _nb. max. commensaux A MÈRE, 01 ANIMAL, 0N> [mère] ANIMAL ANIMAL: nom, sexe, _date naissance, date décès DF, 0N ESPÈCE, _11 ANIMAL ESPÈCE: code espèce, 1_nom latin, nom vernaculaire DF, _11 PEUT COHABITER AVEC, 0N ESPÈCE PÉRIODE: date début, _date fin OCCUPE, 1N ANIMAL, /1N PÉRIODE, 1N ENCLOS PEUT VIVRE DANS: _nb. max. congénères DF, _11 PEUT VIVRE DANS, 1N ESPÈCE : : ENCLOS: num. enclos DF, _11 PEUT VIVRE DANS, 1N ENCLOS : : ================================================ FILE: test/zoo/complex/rewritten/complex_rw_explode_arity=2.5.mcd ================================================ HERBIVORE: plante préférée /\\ ANIMAL <= CARNIVORE, HERBIVORE: type alimentation CARNIVORE: quantité viande DF, 11 PEUT COHABITER AVEC, 0N [commensale] ESPÈCE PEUT COHABITER AVEC: id. peut cohabiter avec, nb. max. commensaux A MÈRE, 01 ANIMAL, 0N> [mère] ANIMAL ANIMAL: nom, sexe, _date naissance, date décès DF, 0N ESPÈCE, _11 ANIMAL ESPÈCE: code espèce, 1_nom latin, nom vernaculaire DF, 11 PEUT COHABITER AVEC, 0N ESPÈCE PÉRIODE: date début, _date fin OCCUPE, 1N ANIMAL, /1N PÉRIODE, 1N ENCLOS PEUT VIVRE DANS: id. peut vivre dans, nb. max. congénères DF, 11 PEUT VIVRE DANS, 1N ESPÈCE : : ENCLOS: num. enclos DF, 11 PEUT VIVRE DANS, 1N ENCLOS : : ================================================ FILE: test/zoo/complex/rewritten/complex_rw_explode_arity=2.mcd ================================================ HERBIVORE: plante préférée /\\ ANIMAL <= CARNIVORE, HERBIVORE: type alimentation CARNIVORE: quantité viande DF, 11 PEUT COHABITER AVEC, 0N [commensale] ESPÈCE PEUT COHABITER AVEC: id. peut cohabiter avec, nb. max. commensaux A MÈRE, 01 ANIMAL, 0N> [mère] ANIMAL ANIMAL: nom, sexe, _date naissance, date décès DF, 0N ESPÈCE, _11 ANIMAL ESPÈCE: code espèce, 1_nom latin, nom vernaculaire DF, 11 PEUT COHABITER AVEC, 0N ESPÈCE PÉRIODE: date début, _date fin OCCUPE, 1N ANIMAL, /1N PÉRIODE, 1N ENCLOS PEUT VIVRE DANS: id. peut vivre dans, nb. max. congénères DF, 11 PEUT VIVRE DANS, 1N ESPÈCE : : ENCLOS: num. enclos DF, 11 PEUT VIVRE DANS, 1N ENCLOS : : ================================================ FILE: test/zoo/complex/rewritten/complex_rw_explode_arity=3,weak.mcd ================================================ : : : : A MÈRE, 01 ANIMAL, 0N> [mère] ANIMAL : : PÉRIODE: date début, _date fin DF, 11 OCCUPE, 1N PÉRIODE OCCUPE: DF, _11 OCCUPE, 1N ANIMAL ANIMAL: nom, sexe, _date naissance, date décès /\\ ANIMAL <= CARNIVORE, HERBIVORE: type alimentation HERBIVORE: plante préférée : : DF, _11 OCCUPE, 1N ENCLOS : DF, 0N ESPÈCE, _11 ANIMAL CARNIVORE: quantité viande : : : ENCLOS: num. enclos PEUT VIVRE DANS, 1N ESPÈCE, 1N ENCLOS: nb. max. congénères ESPÈCE: code espèce, 1_nom latin, nom vernaculaire : : : : : : PEUT COHABITER AVEC, 0N ESPÈCE, 0N [commensale] ESPÈCE: nb. max. commensaux : : ================================================ FILE: test/zoo/complex/rewritten/complex_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/complex/rewritten/complex_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/drain/_drain.mcd ================================================ Entreprise: nom entreprise, adresse, téléphone Proposer, 0N Entreprise, 11 Stage: date proposition Stage: num. stage, sujet Attribuer, 11 Étudiant, 01 Stage: date signature Étudiant: num étudiant, nom Soutenir, 01 Étudiant, 0N Date: note stage Date: date ================================================ FILE: test/zoo/drain/ddl/drain_ddl.d2 ================================================ "Entreprise": { shape: sql_table "nom entreprise": VARCHAR(42) {constraint: PK} "adresse": VARCHAR(42) "téléphone": VARCHAR(42) } "Étudiant": { shape: sql_table "num étudiant": VARCHAR(42) {constraint: PK} "nom": VARCHAR(42) "num. stage": VARCHAR(42) {constraint: [FK; NOT NULL]} "date signature": VARCHAR(42) "date": VARCHAR(42) {constraint: "NULL"} "note stage": VARCHAR(42) } "Stage": { shape: sql_table "num. stage": VARCHAR(42) {constraint: PK} "sujet": VARCHAR(42) "nom entreprise": VARCHAR(42) {constraint: [FK; NOT NULL]} "date proposition": VARCHAR(42) } "Étudiant"."num. stage" -> "Stage"."num. stage" "Stage"."nom entreprise" -> "Entreprise"."nom entreprise" ================================================ FILE: test/zoo/drain/ddl/drain_ddl.dbml ================================================ Table "Entreprise" { "nom entreprise" VARCHAR(42) [pk, NOT NULL] "adresse" VARCHAR(42) "téléphone" VARCHAR(42) } Table "Étudiant" { "num étudiant" VARCHAR(42) [pk, NOT NULL] "nom" VARCHAR(42) "num. stage" VARCHAR(42) [NOT NULL] "date signature" VARCHAR(42) "date" VARCHAR(42) ["NULL"] "note stage" VARCHAR(42) Indexes { "num. stage" [unique] } } Table "Stage" { "num. stage" VARCHAR(42) [pk, NOT NULL] "sujet" VARCHAR(42) "nom entreprise" VARCHAR(42) [NOT NULL] "date proposition" VARCHAR(42) } Ref:"Étudiant"."num. stage" > "Stage"."num. stage" Ref:"Stage"."nom entreprise" > "Entreprise"."nom entreprise" ================================================ FILE: test/zoo/drain/ddl/drain_ddl.sql ================================================ CREATE TABLE ENTREPRISE ( PRIMARY KEY (nom_entreprise), nom_entreprise VARCHAR(255) NOT NULL, adresse VARCHAR(30), telephone VARCHAR(20) ); CREATE TABLE ETUDIANT ( PRIMARY KEY (num_etudiant), num_etudiant VARCHAR(8) NOT NULL, nom VARCHAR(255), num_stage VARCHAR(8) NOT NULL, date_signature BINARY(64), date DATE NULL, note_stage TEXT, UNIQUE (num_stage) ); CREATE TABLE STAGE ( PRIMARY KEY (num_stage), num_stage VARCHAR(8) NOT NULL, sujet VARCHAR(42), nom_entreprise VARCHAR(255) NOT NULL, date_proposition DATE ); ALTER TABLE ETUDIANT ADD FOREIGN KEY (num_stage) REFERENCES STAGE (num_stage); ALTER TABLE STAGE ADD FOREIGN KEY (nom_entreprise) REFERENCES ENTREPRISE (nom_entreprise); ================================================ FILE: test/zoo/drain/exported/drain_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Entreprise"] 6 [label="Stage"] 11 [label="Étudiant"] 16 [label="Date"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="adresse"] 4 [label="téléphone"] 9 [label="sujet"] 14 [label="nom"] // Weak and strong entity attributes 2 [label=<nom
    entreprise
    >] 8 [label=<num.
    stage
    >] 13 [label=<num
    étudiant
    >] 18 [label=<date>] // Relationship attributes node [ fillcolor="#FFFFFF" ] 7 [label="date\nproposition"] 12 [label="date\nsignature"] 17 [label="note\nstage"] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 5 [label="Proposer"] 10 [label="Attribuer"] 15 [label="Soutenir"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 1 -- 4 6 -- 8 6 -- 9 11 -- 13 11 -- 14 16 -- 18 // Edges between relationships and attributes edge [color="#000000"] 5 -- 7 10 -- 12 15 -- 17 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 11 -- 10 1 -- 5 [color="#000000"] 6 -- 10 [color="#000000"] 16 -- 15 [color="#000000"] edge [headlabel=N] 6 -- 5 11 -- 15 [color="#000000"] } ================================================ FILE: test/zoo/drain/exported/drain_erd_chen.txt ================================================ [Date] --1-- [Entreprise] --1-- [Stage] --1-- [Stage] ==N== [Étudiant] --N-- [Étudiant] ==1== ================================================ FILE: test/zoo/drain/exported/drain_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    Entreprise
    PKnom entreprise
    adresse
    téléphone
    >] 2 [label=<
    Stage
    PKnum. stage
    sujet
    date proposition
    >] 3 [label=<
    Étudiant
    PKnum étudiant
    nom
    date signature
    >] 4 [label=<
    Date
    PKdate
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 1 -> 2 [arrowhead="crowodot" arrowtail="teetee" label="Proposer"] 3 -> 2 [arrowhead="teetee" arrowtail="teeodot" label="Attribuer"] 3 -> 4 [arrowhead="teeodot" arrowtail="crowodot" label="Soutenir"] } ================================================ FILE: test/zoo/drain/exported/drain_erd_crow.mmd ================================================ erDiagram Entreprise { TYPE nom_entreprise PK TYPE adresse TYPE telephone } Stage { TYPE num_stage PK TYPE sujet TYPE date_proposition } Etudiant { TYPE num_etudiant PK TYPE nom TYPE date_signature } Date { TYPE date PK } Entreprise ||--o{ Stage: Proposer Etudiant |o--|| Stage: Attribuer Etudiant }o--o| Date: Soutenir ================================================ FILE: test/zoo/drain/exported/drain_uml.puml ================================================ @startuml "drain" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Entreprise") { {field} + pk(nom entreprise) {field} + adresse {field} + téléphone } "Entreprise" "1" --- "*" "Stage": "Proposer" Table("Stage") { {field} + pk(num. stage) {field} + sujet {field} + date proposition } "Étudiant" "1..*" --- "1" "Stage": "Attribuer" Table("Étudiant") { {field} + pk(num étudiant) {field} + nom {field} + date signature } "Étudiant" "*" --- "1..*" "Date": "Soutenir" ("Étudiant", "Date") .. "Soutenir" Table("Soutenir") { {field} + note stage } Table("Date") { {field} + pk(date) } @enduml ================================================ FILE: test/zoo/drain/mld/drain_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Entreprise nom entreprise ! primary_key True Entreprise adresse normal_attribute False Entreprise téléphone normal_attribute False Étudiant num étudiant ! primary_key True Étudiant nom normal_attribute False Étudiant num. stage ! 1 foreign_key False Stage Stage Attribuer Étudiant date signature outer_attribute False Attribuer Étudiant date ? ex_foreign_key False Date Date Soutenir Étudiant note stage outer_attribute False Soutenir Stage num. stage ! primary_key True Stage sujet normal_attribute False Stage nom entreprise ! foreign_key False Entreprise Entreprise Proposer Stage date proposition outer_attribute False Proposer ================================================ FILE: test/zoo/drain/mld/drain_dependencies.gv ================================================ digraph { node [shape=box] "Stage" -> "Étudiant" "Entreprise" -> "Stage" } ================================================ FILE: test/zoo/drain/mld/drain_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD drain
    Entreprise ( nom entreprise, adresse, téléphone )
    • Le champ nom entreprise constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Entreprise.
    • Les champs adresse et téléphone étaient déjà de simples attributs de l'entité Entreprise.
    Étudiant ( num étudiant, nom, #num. stage u1, date signature, date?, note stage )
    • Le champ num étudiant constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Étudiant.
    • Le champ nom était déjà un simple attribut de l'entité Étudiant.
    • Le champ num. stage est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle Attribuer à partir de l'entité Stage en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1.
    • Le champ date signature a migré à partir de l'association de dépendance fonctionnelle Attribuer.
    • Le champ à saisie facultative date est un simple attribut. Il a migré par l'association de dépendance fonctionnelle Soutenir à partir de l'entité Date en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère.
    • Le champ note stage a migré à partir de l'association de dépendance fonctionnelle Soutenir.
    Stage ( num. stage, sujet, #nom entreprise!, date proposition )
    • Le champ num. stage constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Stage.
    • Le champ sujet était déjà un simple attribut de l'entité Stage.
    • Le champ à saisie obligatoire nom entreprise est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle Proposer à partir de l'entité Entreprise en perdant son caractère identifiant.
    • Le champ date proposition a migré à partir de l'association de dépendance fonctionnelle Proposer.


    NB. La table Date a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/drain/mld/drain_mld.mcd ================================================ %%mocodo : Entreprise: nom entreprise, adresse, téléphone : Stage: num. stage, sujet, #nom entreprise > Entreprise > nom entreprise, date proposition : Étudiant: num étudiant, nom, #num. stage > Stage > num. stage, date signature, date, note stage : ================================================ FILE: test/zoo/drain/mld/drain_mld.md ================================================ - **Entreprise** (nom entreprise, adresse, téléphone) - Le champ _nom entreprise_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Entreprise_. - Les champs _adresse_ et _téléphone_ étaient déjà de simples attributs de l'entité _Entreprise_. - **Étudiant** (num étudiant, nom, _#num. stage_ u1, date signature, date?, note stage) - Le champ _num étudiant_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Étudiant_. - Le champ _nom_ était déjà un simple attribut de l'entité _Étudiant_. - Le champ _num. stage_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _Attribuer_ à partir de l'entité _Stage_ en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. - Le champ _date signature_ a migré à partir de l'association de dépendance fonctionnelle _Attribuer_. - Le champ à saisie facultative _date_ est un simple attribut. Il a migré par l'association de dépendance fonctionnelle _Soutenir_ à partir de l'entité _Date_ en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. - Le champ _note stage_ a migré à partir de l'association de dépendance fonctionnelle _Soutenir_. - **Stage** (num. stage, sujet, _#nom entreprise!_, date proposition) - Le champ _num. stage_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Stage_. - Le champ _sujet_ était déjà un simple attribut de l'entité _Stage_. - Le champ à saisie obligatoire _nom entreprise_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _Proposer_ à partir de l'entité _Entreprise_ en perdant son caractère identifiant. - Le champ _date proposition_ a migré à partir de l'association de dépendance fonctionnelle _Proposer_.
    ---- **NB.** La table _Date_ a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/drain/mld/drain_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{drain}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Entreprise} (\prim{nom entreprise}, \attr{adresse}, \attr{téléphone}) \begin{itemize} \item Le champ \emph{nom entreprise} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Entreprise}. \item Les champs \emph{adresse} et \emph{téléphone} étaient déjà de simples attributs de l'entité \emph{Entreprise}. \end{itemize} \item \relat{Étudiant} (\prim{num étudiant}, \attr{nom}, \foreign{num. stage}$^{u\_1}$, \attr{date signature}, \attr{date?}, \attr{note stage}) \begin{itemize} \item Le champ \emph{num étudiant} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Étudiant}. \item Le champ \emph{nom} était déjà un simple attribut de l'entité \emph{Étudiant}. \item Le champ \emph{num. stage} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{Attribuer} à partir de l'entité \emph{Stage} en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. \item Le champ \emph{date signature} a migré à partir de l'association de dépendance fonctionnelle \emph{Attribuer}. \item Le champ à saisie facultative \emph{date} est un simple attribut. Il a migré par l'association de dépendance fonctionnelle \emph{Soutenir} à partir de l'entité \emph{Date} en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. \item Le champ \emph{note stage} a migré à partir de l'association de dépendance fonctionnelle \emph{Soutenir}. \end{itemize} \item \relat{Stage} (\prim{num. stage}, \attr{sujet}, \foreign{nom entreprise!}, \attr{date proposition}) \begin{itemize} \item Le champ \emph{num. stage} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Stage}. \item Le champ \emph{sujet} était déjà un simple attribut de l'entité \emph{Stage}. \item Le champ à saisie obligatoire \emph{nom entreprise} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{Proposer} à partir de l'entité \emph{Entreprise} en perdant son caractère identifiant. \item Le champ \emph{date proposition} a migré à partir de l'association de dépendance fonctionnelle \emph{Proposer}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/drain/mld/drain_mld.txt ================================================ - Entreprise (_nom entreprise_, adresse, téléphone) - Le champ « nom entreprise » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Entreprise ». - Les champs « adresse » et « téléphone » étaient déjà de simples attributs de l'entité « Entreprise ». - Étudiant (_num étudiant_, nom, #num. stage¹, date signature, date?, note stage) - Le champ « num étudiant » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Étudiant ». - Le champ « nom » était déjà un simple attribut de l'entité « Étudiant ». - Le champ « num. stage » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « Attribuer » à partir de l'entité « Stage » en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. - Le champ « date signature » a migré à partir de l'association de dépendance fonctionnelle « Attribuer ». - Le champ à saisie facultative « date » est un simple attribut. Il a migré par l'association de dépendance fonctionnelle « Soutenir » à partir de l'entité « Date » en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. - Le champ « note stage » a migré à partir de l'association de dépendance fonctionnelle « Soutenir ». - Stage (_num. stage_, sujet, #nom entreprise!, date proposition) - Le champ « num. stage » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Stage ». - Le champ « sujet » était déjà un simple attribut de l'entité « Stage ». - Le champ à saisie obligatoire « nom entreprise » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « Proposer » à partir de l'entité « Entreprise » en perdant son caractère identifiant. - Le champ « date proposition » a migré à partir de l'association de dépendance fonctionnelle « Proposer ».
    -------------------------------------------------------------------------------- NB. La table « Date » a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/drain/rewritten/drain_rw_create_df_arrows=across.mcd ================================================ Entreprise: nom entreprise, adresse, téléphone Proposer, 0N> Entreprise, 11 Stage: date proposition Stage: num. stage, sujet Attribuer, 11 Étudiant, 01> Stage: date signature Étudiant: num étudiant, nom Soutenir, 01 Étudiant, 0N Date: note stage Date: date ================================================ FILE: test/zoo/drain/rewritten/drain_rw_drain.mcd ================================================ Entreprise: nom entreprise, adresse, téléphone Proposer, 0N Entreprise, 11 Stage Stage: num. stage, sujet, date proposition Attribuer, 11 Étudiant, 01 Stage Étudiant: num étudiant, nom, date signature Soutenir, 01 Étudiant, 0N Date: note stage Date: date ================================================ FILE: test/zoo/drain/rewritten/drain_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2, at 1 3 ASSOC 5_, 0N ENTITÉ 1_, 11 ENTITÉ 2_: at 5 1 ENTITÉ 2_: at 2 1, at 2 2 ASSOC 6_, 11 ENTITÉ 3_, 01 ENTITÉ 2_: at 6 1 ENTITÉ 3_: at 3 1, at 3 2 ASSOC 7_, 01 ENTITÉ 3_, 0N ENTITÉ 4_: at 7 1 ENTITÉ 4_: at 4 1 ================================================ FILE: test/zoo/drain/rewritten/drain_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/drain/rewritten/drain_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/drain/rewritten/drain_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/drain/rewritten/drain_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/drain/rewritten/drain_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/drain/rewritten/drain_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/drain/rewritten/drain_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/empty_attrs/_empty_attrs_0.mcd ================================================ CLIENT: Réf. client, , , ================================================ FILE: test/zoo/empty_attrs/ddl/empty_attrs_0_ddl.d2 ================================================ ================================================ FILE: test/zoo/empty_attrs/ddl/empty_attrs_0_ddl.dbml ================================================ ================================================ FILE: test/zoo/empty_attrs/ddl/empty_attrs_0_ddl.sql ================================================ ================================================ FILE: test/zoo/empty_attrs/exported/empty_attrs_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] // Associative entities 1 [label="CLIENT",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] // Weak and strong entity attributes 2 [label=<Réf.
    client
    >] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 } ================================================ FILE: test/zoo/empty_attrs/exported/empty_attrs_0_erd_chen.txt ================================================ ================================================ FILE: test/zoo/empty_attrs/exported/empty_attrs_0_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    CLIENT
    PKRéf. client
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] } ================================================ FILE: test/zoo/empty_attrs/exported/empty_attrs_0_erd_crow.mmd ================================================ erDiagram CLIENT { TYPE Ref_client PK TYPE TYPE TYPE } ================================================ FILE: test/zoo/empty_attrs/exported/empty_attrs_0_uml.puml ================================================ @startuml "empty_attrs" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("CLIENT") { {field} + pk(Réf. client) } @enduml ================================================ FILE: test/zoo/empty_attrs/mld/empty_attrs_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note ================================================ FILE: test/zoo/empty_attrs/mld/empty_attrs_0_dependencies.gv ================================================ digraph { node [shape=box] } ================================================ FILE: test/zoo/empty_attrs/mld/empty_attrs_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD empty_attrs


    NB. La table CLIENT a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/empty_attrs/mld/empty_attrs_0_mld.mcd ================================================ %%mocodo : ================================================ FILE: test/zoo/empty_attrs/mld/empty_attrs_0_mld.md ================================================
    ---- **NB.** La table _CLIENT_ a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/empty_attrs/mld/empty_attrs_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{empty\_attrs}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/empty_attrs/mld/empty_attrs_0_mld.txt ================================================
    -------------------------------------------------------------------------------- NB. La table « CLIENT » a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/empty_attrs/rewritten/empty_attrs_0_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/empty_attrs/rewritten/empty_attrs_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/empty_attrs/rewritten/empty_attrs_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, , , ================================================ FILE: test/zoo/empty_attrs/rewritten/empty_attrs_0_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/empty_attrs/rewritten/empty_attrs_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/empty_attrs/rewritten/empty_attrs_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/empty_attrs/rewritten/empty_attrs_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/empty_attrs/rewritten/empty_attrs_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/empty_attrs/rewritten/empty_attrs_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/empty_attrs/rewritten/empty_attrs_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/_gerund_0.mcd ================================================ Produit: produit DF, _11 Ligne de commande, 0N Produit Ligne de commande: _quantité DF, _11 Ligne de commande, 1N Commande Commande: commande ================================================ FILE: test/zoo/gerund/_gerund_1.mcd ================================================ Produit: produit, libellé DF, _11 Ligne de commande, 0N Produit Ligne de commande: _quantité DF, _11 Ligne de commande, 1N Commande Commande: commande, date ================================================ FILE: test/zoo/gerund/ddl/gerund_0_ddl.d2 ================================================ "Ligne de commande": { shape: sql_table "commande": VARCHAR(42) {constraint: PK} "produit": VARCHAR(42) {constraint: PK} "quantité": VARCHAR(42) } ================================================ FILE: test/zoo/gerund/ddl/gerund_0_ddl.dbml ================================================ Table "Ligne de commande" { "commande" VARCHAR(42) [NOT NULL] "produit" VARCHAR(42) [NOT NULL] "quantité" VARCHAR(42) Indexes { ("commande", "produit") [pk] } } ================================================ FILE: test/zoo/gerund/ddl/gerund_0_ddl.sql ================================================ CREATE TABLE LIGNE_DE_COMMANDE ( PRIMARY KEY (commande, produit), commande VARCHAR(42) NOT NULL, produit VARCHAR(42) NOT NULL, quantite INTEGER ); ================================================ FILE: test/zoo/gerund/ddl/gerund_1_ddl.d2 ================================================ "Commande": { shape: sql_table "commande": VARCHAR(42) {constraint: PK} "date": VARCHAR(42) } "Ligne de commande": { shape: sql_table "commande": VARCHAR(42) {constraint: [PK; FK]} "produit": VARCHAR(42) {constraint: [PK; FK]} "quantité": VARCHAR(42) } "Produit": { shape: sql_table "produit": VARCHAR(42) {constraint: PK} "libellé": VARCHAR(42) } "Ligne de commande"."commande" -> "Commande"."commande" "Ligne de commande"."produit" -> "Produit"."produit" ================================================ FILE: test/zoo/gerund/ddl/gerund_1_ddl.dbml ================================================ Table "Commande" { "commande" VARCHAR(42) [pk, NOT NULL] "date" VARCHAR(42) } Table "Ligne de commande" { "commande" VARCHAR(42) [NOT NULL] "produit" VARCHAR(42) [NOT NULL] "quantité" VARCHAR(42) Indexes { ("commande", "produit") [pk] } } Table "Produit" { "produit" VARCHAR(42) [pk, NOT NULL] "libellé" VARCHAR(42) } Ref:"Ligne de commande"."commande" > "Commande"."commande" Ref:"Ligne de commande"."produit" > "Produit"."produit" ================================================ FILE: test/zoo/gerund/ddl/gerund_1_ddl.sql ================================================ CREATE TABLE COMMANDE ( PRIMARY KEY (commande), commande VARCHAR(42) NOT NULL, date DATE ); CREATE TABLE LIGNE_DE_COMMANDE ( PRIMARY KEY (commande, produit), commande VARCHAR(42) NOT NULL, produit VARCHAR(42) NOT NULL, quantite INTEGER ); CREATE TABLE PRODUIT ( PRIMARY KEY (produit), produit VARCHAR(42) NOT NULL, libelle VARCHAR(50) ); ALTER TABLE LIGNE_DE_COMMANDE ADD FOREIGN KEY (produit) REFERENCES PRODUIT (produit); ALTER TABLE LIGNE_DE_COMMANDE ADD FOREIGN KEY (commande) REFERENCES COMMANDE (commande); ================================================ FILE: test/zoo/gerund/exported/gerund_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Produit"] 7 [label="Commande"] // Associative entities 4 [label="Ligne de\ncommande",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] // Weak and strong entity attributes 2 [label=<produit>] 5 [label=<quantité> style="dashed,filled"] 8 [label=<commande>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="DF",peripheries=2] 6 [label="DF",peripheries=2] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 4 -- 5 7 -- 8 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 7 -- 6 1 -- 3 [color="#000000"] edge [headlabel=N] 4 -- 3 4 -- 6 } ================================================ FILE: test/zoo/gerund/exported/gerund_0_erd_chen.txt ================================================ [] ==N== <> [] ==N== <> [Commande] ==1== <> [Produit] --1-- <> ================================================ FILE: test/zoo/gerund/exported/gerund_0_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    Produit
    PKproduit
    >] 2 [label=<
    Ligne de commande
    PKquantité
    >] 3 [label=<
    Commande
    PKcommande
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="teetee" arrowtail="crowodot" label="DF" style=dotted] 2 -> 3 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] } ================================================ FILE: test/zoo/gerund/exported/gerund_0_erd_crow.mmd ================================================ erDiagram Produit { TYPE produit PK } Ligne_de_commande { TYPE quantite PK } Commande { TYPE commande PK } Ligne_de_commande }o..|| Produit: DF Ligne_de_commande }|..|| Commande: DF ================================================ FILE: test/zoo/gerund/exported/gerund_0_uml.puml ================================================ @startuml "gerund" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Produit") { {field} + pk(produit) } "Ligne de commande" "*" --* "1" "Produit" Table("Ligne de commande") { {field} + pk(quantité) } "Ligne de commande" "1..*" --* "1" "Commande" Table("Commande") { {field} + pk(commande) } @enduml ================================================ FILE: test/zoo/gerund/exported/gerund_1_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Produit"] 8 [label="Commande"] // Associative entities 5 [label="Ligne de\ncommande",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="libellé"] 10 [label="date"] // Weak and strong entity attributes 2 [label=<produit>] 6 [label=<quantité> style="dashed,filled"] 9 [label=<commande>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="DF",peripheries=2] 7 [label="DF",peripheries=2] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 6 8 -- 9 8 -- 10 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 8 -- 7 1 -- 4 [color="#000000"] edge [headlabel=N] 5 -- 4 5 -- 7 } ================================================ FILE: test/zoo/gerund/exported/gerund_1_erd_chen.txt ================================================ [] ==N== <> [] ==N== <> [Commande] ==1== <> [Produit] --1-- <> ================================================ FILE: test/zoo/gerund/exported/gerund_1_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    Produit
    PKproduit
    libellé
    >] 2 [label=<
    Ligne de commande
    PKquantité
    >] 3 [label=<
    Commande
    PKcommande
    date
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="teetee" arrowtail="crowodot" label="DF" style=dotted] 2 -> 3 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] } ================================================ FILE: test/zoo/gerund/exported/gerund_1_erd_crow.mmd ================================================ erDiagram Produit { TYPE produit PK TYPE libelle } Ligne_de_commande { TYPE quantite PK } Commande { TYPE commande PK TYPE date } Ligne_de_commande }o..|| Produit: DF Ligne_de_commande }|..|| Commande: DF ================================================ FILE: test/zoo/gerund/exported/gerund_1_uml.puml ================================================ @startuml "gerund" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Produit") { {field} + pk(produit) {field} + libellé } "Ligne de commande" "*" --* "1" "Produit" Table("Ligne de commande") { {field} + pk(quantité) } "Ligne de commande" "1..*" --* "1" "Commande" Table("Commande") { {field} + pk(commande) {field} + date } @enduml ================================================ FILE: test/zoo/gerund/mld/gerund_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Ligne de commande commande ! strengthening_primary_ex_foreign_key True Commande Commande DF Ligne de commande produit ! strengthening_primary_ex_foreign_key True Produit Produit DF Ligne de commande quantité normal_attribute False ================================================ FILE: test/zoo/gerund/mld/gerund_0_dependencies.gv ================================================ digraph { node [shape=box] } ================================================ FILE: test/zoo/gerund/mld/gerund_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD gerund
    Ligne de commande ( commande, produit, quantité )
    • Le champ commande fait partie de la clé primaire de la table. Il a migré à partir de l'entité Commande pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère.
    • Le champ produit fait partie de la clé primaire de la table. Il a migré à partir de l'entité Produit pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère.
    • Le champ quantité était déjà un simple attribut de l'entité Ligne de commande.


    NB. Les tables Commande et Produit ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/gerund/mld/gerund_0_mld.mcd ================================================ %%mocodo : Ligne de commande: commande, _produit, quantité : ================================================ FILE: test/zoo/gerund/mld/gerund_0_mld.md ================================================ - **Ligne de commande** (commande, produit, quantité) - Le champ _commande_ fait partie de la clé primaire de la table. Il a migré à partir de l'entité _Commande_ pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. - Le champ _produit_ fait partie de la clé primaire de la table. Il a migré à partir de l'entité _Produit_ pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. - Le champ _quantité_ était déjà un simple attribut de l'entité _Ligne de commande_.
    ---- **NB.** Les tables _Commande_ et _Produit_ ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/gerund/mld/gerund_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{gerund}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Ligne de commande} (\prim{commande}, \prim{produit}, \attr{quantité}) \begin{itemize} \item Le champ \emph{commande} fait partie de la clé primaire de la table. Il a migré à partir de l'entité \emph{Commande} pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. \item Le champ \emph{produit} fait partie de la clé primaire de la table. Il a migré à partir de l'entité \emph{Produit} pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. \item Le champ \emph{quantité} était déjà un simple attribut de l'entité \emph{Ligne de commande}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/gerund/mld/gerund_0_mld.txt ================================================ - Ligne de commande (_commande_, _produit_, quantité) - Le champ « commande » fait partie de la clé primaire de la table. Il a migré à partir de l'entité « Commande » pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. - Le champ « produit » fait partie de la clé primaire de la table. Il a migré à partir de l'entité « Produit » pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. - Le champ « quantité » était déjà un simple attribut de l'entité « Ligne de commande ».
    -------------------------------------------------------------------------------- NB. Les tables « Commande » et « Produit » ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/gerund/mld/gerund_1_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Commande commande ! primary_key True Commande date normal_attribute False Ligne de commande commande ! strengthening_primary_foreign_key True Commande Commande DF Ligne de commande produit ! strengthening_primary_foreign_key True Produit Produit DF Ligne de commande quantité normal_attribute False Produit produit ! primary_key True Produit libellé normal_attribute False ================================================ FILE: test/zoo/gerund/mld/gerund_1_dependencies.gv ================================================ digraph { node [shape=box] "Produit" -> "Ligne de commande" "Commande" -> "Ligne de commande" } ================================================ FILE: test/zoo/gerund/mld/gerund_1_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD gerund
    Commande ( commande, date )
    • Le champ commande constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Commande.
    • Le champ date était déjà un simple attribut de l'entité Commande.
    Ligne de commande ( #commande, #produit, quantité )
    • Le champ commande fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité Commande pour renforcer l'identifiant.
    • Le champ produit fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité Produit pour renforcer l'identifiant.
    • Le champ quantité était déjà un simple attribut de l'entité Ligne de commande.
    Produit ( produit, libellé )
    • Le champ produit constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Produit.
    • Le champ libellé était déjà un simple attribut de l'entité Produit.
    ================================================ FILE: test/zoo/gerund/mld/gerund_1_mld.mcd ================================================ %%mocodo : Produit: produit, libellé : Ligne de commande: #commande > Commande > commande, _#produit > Produit > produit, quantité : Commande: commande, date : ================================================ FILE: test/zoo/gerund/mld/gerund_1_mld.md ================================================ - **Commande** (commande, date) - Le champ _commande_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Commande_. - Le champ _date_ était déjà un simple attribut de l'entité _Commande_. - **Ligne de commande** (_#commande_, _#produit_, quantité) - Le champ _commande_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _Commande_ pour renforcer l'identifiant. - Le champ _produit_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _Produit_ pour renforcer l'identifiant. - Le champ _quantité_ était déjà un simple attribut de l'entité _Ligne de commande_. - **Produit** (produit, libellé) - Le champ _produit_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Produit_. - Le champ _libellé_ était déjà un simple attribut de l'entité _Produit_. ================================================ FILE: test/zoo/gerund/mld/gerund_1_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{gerund}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Commande} (\prim{commande}, \attr{date}) \begin{itemize} \item Le champ \emph{commande} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Commande}. \item Le champ \emph{date} était déjà un simple attribut de l'entité \emph{Commande}. \end{itemize} \item \relat{Ligne de commande} (\foreign{\prim{commande}}, \foreign{\prim{produit}}, \attr{quantité}) \begin{itemize} \item Le champ \emph{commande} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{Commande} pour renforcer l'identifiant. \item Le champ \emph{produit} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{Produit} pour renforcer l'identifiant. \item Le champ \emph{quantité} était déjà un simple attribut de l'entité \emph{Ligne de commande}. \end{itemize} \item \relat{Produit} (\prim{produit}, \attr{libellé}) \begin{itemize} \item Le champ \emph{produit} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Produit}. \item Le champ \emph{libellé} était déjà un simple attribut de l'entité \emph{Produit}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/gerund/mld/gerund_1_mld.txt ================================================ - Commande (_commande_, date) - Le champ « commande » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Commande ». - Le champ « date » était déjà un simple attribut de l'entité « Commande ». - Ligne de commande (_#commande_, _#produit_, quantité) - Le champ « commande » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « Commande » pour renforcer l'identifiant. - Le champ « produit » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « Produit » pour renforcer l'identifiant. - Le champ « quantité » était déjà un simple attribut de l'entité « Ligne de commande ». - Produit (_produit_, libellé) - Le champ « produit » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Produit ». - Le champ « libellé » était déjà un simple attribut de l'entité « Produit ». ================================================ FILE: test/zoo/gerund/rewritten/gerund_0_rw_create_df_arrows=across.mcd ================================================ Produit: produit DF, _11 Ligne de commande, 0N> Produit Ligne de commande: _quantité DF, _11 Ligne de commande, 1N> Commande Commande: commande ================================================ FILE: test/zoo/gerund/rewritten/gerund_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1 DF, _11 ENTITÉ 2_, 0N ENTITÉ 1_ ENTITÉ 2_: _at 2 1 DF, _11 ENTITÉ 2_, 1N ENTITÉ 3_ ENTITÉ 3_: at 3 1 ================================================ FILE: test/zoo/gerund/rewritten/gerund_0_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_1_rw_create_df_arrows=across.mcd ================================================ Produit: produit, libellé DF, _11 Ligne de commande, 0N> Produit Ligne de commande: _quantité DF, _11 Ligne de commande, 1N> Commande Commande: commande, date ================================================ FILE: test/zoo/gerund/rewritten/gerund_1_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_1_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2 DF, _11 ENTITÉ 2_, 0N ENTITÉ 1_ ENTITÉ 2_: _at 2 1 DF, _11 ENTITÉ 2_, 1N ENTITÉ 3_ ENTITÉ 3_: at 3 1, at 3 2 ================================================ FILE: test/zoo/gerund/rewritten/gerund_1_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_1_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_1_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_1_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_1_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_1_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/gerund/rewritten/gerund_1_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/_inheritance_0.mcd ================================================ SUSCIPIT: orci, lorem RHONCUS, 1N TRISTIS, 11 SUSCIPIT : : SODALES: convallis, ipsum VITAE, 11 QUAM, 1N SODALES QUAM: cras, sed CONSEQUAT: fermentum, dederit ELIT, 11 TRISTIS, 1N CONSEQUAT TRISTIS: magna, vestibulum /XT\ TRISTIS <- SODALES, NEC, LACUS: type NEC: pulvinar, audis MOLLIS, 1N CURABITUR, 11 NEC CURABITUR: gravida, amor DIGNISSIM: tellus, terra ALIQUET, 1N TRISTIS, 1N DIGNISSIM : : LACUS: tempor, fugit ULTRICES, 1N LIBERO, 1N LACUS LIBERO: posuere, lacrima ================================================ FILE: test/zoo/inheritance/_inheritance_1.mcd ================================================ SUSCIPIT: orci, lorem RHONCUS, 1N TRISTIS, 11 SUSCIPIT : : SODALES: convallis, ipsum VITAE, 11 QUAM, 1N SODALES QUAM: cras, sed CONSEQUAT: fermentum, dederit ELIT, 11 TRISTIS, 1N CONSEQUAT TRISTIS: magna, vestibulum /XT\ TRISTIS -> SODALES, NEC, LACUS: type NEC: pulvinar, audis MOLLIS, 1N CURABITUR, 11 NEC CURABITUR: gravida, amor DIGNISSIM: tellus, terra ALIQUET, 1N TRISTIS, 1N DIGNISSIM : : LACUS: tempor, fugit ULTRICES, 1N LIBERO, 1N LACUS LIBERO: posuere, lacrima ================================================ FILE: test/zoo/inheritance/_inheritance_2.mcd ================================================ SUSCIPIT: orci, lorem RHONCUS, 1N TRISTIS, 11 SUSCIPIT : : SODALES: convallis, ipsum VITAE, 11 QUAM, 1N SODALES QUAM: cras, sed CONSEQUAT: fermentum, dederit ELIT, 11 TRISTIS, 1N CONSEQUAT TRISTIS: magna, vestibulum /XT\ TRISTIS => SODALES, NEC, LACUS: type NEC: pulvinar, audis MOLLIS, 1N CURABITUR, 11 NEC CURABITUR: gravida, amor DIGNISSIM: tellus, terra ALIQUET, 1N TRISTIS, 1N DIGNISSIM : : LACUS: tempor, fugit ULTRICES, 1N LIBERO, 1N LACUS LIBERO: posuere, lacrima ================================================ FILE: test/zoo/inheritance/_inheritance_3.mcd ================================================ : MOLLIS, 1N [via_mollis] LACUS, 11 NEC NEC: pulvinar, audis : LACUS: tempor, fugit /XT\ TRISTIS <- SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum VITAE, 11 NEC, 1N [via_vitae] SODALES : ULTRICES, 1N [sodales] SODALES, 1N [lacus] LACUS SODALES: convallis, ipsum : ================================================ FILE: test/zoo/inheritance/_inheritance_4.mcd ================================================ : MOLLIS, 1N [via_mollis] LACUS, 11 NEC NEC: pulvinar, audis : LACUS: tempor, fugit /XT\ TRISTIS -> SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum VITAE, 11 NEC, 1N [via_vitae] SODALES : ULTRICES, 1N [sodales] SODALES, 1N [lacus] LACUS SODALES: convallis, ipsum : ================================================ FILE: test/zoo/inheritance/_inheritance_5.mcd ================================================ : MOLLIS, 1N [via_mollis] LACUS, 11 NEC NEC: pulvinar, audis : LACUS: tempor, fugit /XT\ TRISTIS => SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum VITAE, 11 NEC, 1N [via_vitae] SODALES : ULTRICES, 1N [sodales] SODALES, 1N [lacus] LACUS SODALES: convallis, ipsum : ================================================ FILE: test/zoo/inheritance/ddl/inheritance_0_ddl.d2 ================================================ "ALIQUET": { shape: sql_table "magna": VARCHAR(42) {constraint: [PK; FK]} "tellus": VARCHAR(42) {constraint: [PK; FK]} } "CONSEQUAT": { shape: sql_table "fermentum": VARCHAR(42) {constraint: PK} "dederit": VARCHAR(42) } "CURABITUR": { shape: sql_table "gravida": VARCHAR(42) {constraint: PK} "amor": VARCHAR(42) } "DIGNISSIM": { shape: sql_table "tellus": VARCHAR(42) {constraint: PK} "terra": VARCHAR(42) } "LIBERO": { shape: sql_table "posuere": VARCHAR(42) {constraint: PK} "lacrima": VARCHAR(42) } "QUAM": { shape: sql_table "cras": VARCHAR(42) {constraint: PK} "sed": VARCHAR(42) "magna": VARCHAR(42) {constraint: [FK; NOT NULL]} } "SUSCIPIT": { shape: sql_table "orci": VARCHAR(42) {constraint: PK} "lorem": VARCHAR(42) "magna": VARCHAR(42) {constraint: [FK; NOT NULL]} } "TRISTIS": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "vestibulum": VARCHAR(42) "fermentum": VARCHAR(42) {constraint: [FK; NOT NULL]} "type": UNSIGNED INT {constraint: NOT NULL} "convallis": VARCHAR(42) {constraint: "NULL"} "ipsum": VARCHAR(42) {constraint: "NULL"} "pulvinar": VARCHAR(42) {constraint: "NULL"} "audis": VARCHAR(42) {constraint: "NULL"} "gravida": VARCHAR(42) {constraint: [FK; "NULL"]} "tempor": VARCHAR(42) {constraint: "NULL"} "fugit": VARCHAR(42) {constraint: "NULL"} } "ULTRICES": { shape: sql_table "posuere": VARCHAR(42) {constraint: [PK; FK]} "magna": VARCHAR(42) {constraint: [PK; FK]} } "ALIQUET"."magna" -> "TRISTIS"."magna" "ALIQUET"."tellus" -> "DIGNISSIM"."tellus" "QUAM"."magna" -> "TRISTIS"."magna" "SUSCIPIT"."magna" -> "TRISTIS"."magna" "TRISTIS"."fermentum" -> "CONSEQUAT"."fermentum" "TRISTIS"."gravida" -> "CURABITUR"."gravida" "ULTRICES"."posuere" -> "LIBERO"."posuere" "ULTRICES"."magna" -> "TRISTIS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_0_ddl.dbml ================================================ Table "ALIQUET" { "magna" VARCHAR(42) [NOT NULL] "tellus" VARCHAR(42) [NOT NULL] Indexes { ("magna", "tellus") [pk] } } Table "CONSEQUAT" { "fermentum" VARCHAR(42) [pk, NOT NULL] "dederit" VARCHAR(42) } Table "CURABITUR" { "gravida" VARCHAR(42) [pk, NOT NULL] "amor" VARCHAR(42) } Table "DIGNISSIM" { "tellus" VARCHAR(42) [pk, NOT NULL] "terra" VARCHAR(42) } Table "LIBERO" { "posuere" VARCHAR(42) [pk, NOT NULL] "lacrima" VARCHAR(42) } Table "QUAM" { "cras" VARCHAR(42) [pk, NOT NULL] "sed" VARCHAR(42) "magna" VARCHAR(42) [NOT NULL] } Table "SUSCIPIT" { "orci" VARCHAR(42) [pk, NOT NULL] "lorem" VARCHAR(42) "magna" VARCHAR(42) [NOT NULL] } Table "TRISTIS" { "magna" VARCHAR(42) [pk, NOT NULL] "vestibulum" VARCHAR(42) "fermentum" VARCHAR(42) [NOT NULL] "type" UNSIGNED_INT [NOT NULL] "convallis" VARCHAR(42) ["NULL"] "ipsum" VARCHAR(42) ["NULL"] "pulvinar" VARCHAR(42) ["NULL"] "audis" VARCHAR(42) ["NULL"] "gravida" VARCHAR(42) ["NULL"] "tempor" VARCHAR(42) ["NULL"] "fugit" VARCHAR(42) ["NULL"] } Table "ULTRICES" { "posuere" VARCHAR(42) [NOT NULL] "magna" VARCHAR(42) [NOT NULL] Indexes { ("posuere", "magna") [pk] } } Ref:"ALIQUET"."magna" > "TRISTIS"."magna" Ref:"ALIQUET"."tellus" > "DIGNISSIM"."tellus" Ref:"QUAM"."magna" > "TRISTIS"."magna" Ref:"SUSCIPIT"."magna" > "TRISTIS"."magna" Ref:"TRISTIS"."fermentum" > "CONSEQUAT"."fermentum" Ref:"TRISTIS"."gravida" > "CURABITUR"."gravida" Ref:"ULTRICES"."posuere" > "LIBERO"."posuere" Ref:"ULTRICES"."magna" > "TRISTIS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_0_ddl.sql ================================================ CREATE TABLE ALIQUET ( PRIMARY KEY (magna, tellus), magna VARCHAR(42) NOT NULL, tellus VARCHAR(42) NOT NULL ); CREATE TABLE CONSEQUAT ( PRIMARY KEY (fermentum), fermentum VARCHAR(42) NOT NULL, dederit VARCHAR(42) ); CREATE TABLE CURABITUR ( PRIMARY KEY (gravida), gravida VARCHAR(42) NOT NULL, amor VARCHAR(42) ); CREATE TABLE DIGNISSIM ( PRIMARY KEY (tellus), tellus VARCHAR(42) NOT NULL, terra VARCHAR(42) ); CREATE TABLE LIBERO ( PRIMARY KEY (posuere), posuere VARCHAR(42) NOT NULL, lacrima VARCHAR(42) ); CREATE TABLE QUAM ( PRIMARY KEY (cras), cras VARCHAR(42) NOT NULL, sed VARCHAR(42), magna VARCHAR(42) NOT NULL ); CREATE TABLE SUSCIPIT ( PRIMARY KEY (orci), orci VARCHAR(42) NOT NULL, lorem VARCHAR(42), magna VARCHAR(42) NOT NULL ); CREATE TABLE TRISTIS ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, vestibulum VARCHAR(42), fermentum VARCHAR(42) NOT NULL, type UNSIGNED INT NOT NULL, convallis VARCHAR(42) NULL, ipsum VARCHAR(42) NULL, pulvinar VARCHAR(42) NULL, audis VARCHAR(42) NULL, gravida VARCHAR(42) NULL, tempor VARCHAR(42) NULL, fugit VARCHAR(42) NULL ); CREATE TABLE ULTRICES ( PRIMARY KEY (posuere, magna), posuere VARCHAR(42) NOT NULL, magna VARCHAR(42) NOT NULL ); ALTER TABLE ALIQUET ADD FOREIGN KEY (tellus) REFERENCES DIGNISSIM (tellus); ALTER TABLE ALIQUET ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE QUAM ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE SUSCIPIT ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE TRISTIS ADD FOREIGN KEY (gravida) REFERENCES CURABITUR (gravida); ALTER TABLE TRISTIS ADD FOREIGN KEY (fermentum) REFERENCES CONSEQUAT (fermentum); ALTER TABLE ULTRICES ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE ULTRICES ADD FOREIGN KEY (posuere) REFERENCES LIBERO (posuere); ================================================ FILE: test/zoo/inheritance/ddl/inheritance_1_ddl.d2 ================================================ "ALIQUET": { shape: sql_table "magna": VARCHAR(42) {constraint: [PK; FK]} "tellus": VARCHAR(42) {constraint: [PK; FK]} } "CONSEQUAT": { shape: sql_table "fermentum": VARCHAR(42) {constraint: PK} "dederit": VARCHAR(42) } "CURABITUR": { shape: sql_table "gravida": VARCHAR(42) {constraint: PK} "amor": VARCHAR(42) } "DIGNISSIM": { shape: sql_table "tellus": VARCHAR(42) {constraint: PK} "terra": VARCHAR(42) } "LACUS": { shape: sql_table "magna": VARCHAR(42) {constraint: [PK; FK]} "tempor": VARCHAR(42) "fugit": VARCHAR(42) } "LIBERO": { shape: sql_table "posuere": VARCHAR(42) {constraint: PK} "lacrima": VARCHAR(42) } "NEC": { shape: sql_table "magna": VARCHAR(42) {constraint: [PK; FK]} "pulvinar": VARCHAR(42) "audis": VARCHAR(42) "gravida": VARCHAR(42) {constraint: [FK; NOT NULL]} } "QUAM": { shape: sql_table "cras": VARCHAR(42) {constraint: PK} "sed": VARCHAR(42) "magna": VARCHAR(42) {constraint: [FK; NOT NULL]} } "SODALES": { shape: sql_table "magna": VARCHAR(42) {constraint: [PK; FK]} "convallis": VARCHAR(42) "ipsum": VARCHAR(42) } "SUSCIPIT": { shape: sql_table "orci": VARCHAR(42) {constraint: PK} "lorem": VARCHAR(42) "magna": VARCHAR(42) {constraint: [FK; NOT NULL]} } "TRISTIS": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "vestibulum": VARCHAR(42) "fermentum": VARCHAR(42) {constraint: [FK; NOT NULL]} "type": UNSIGNED INT {constraint: NOT NULL} } "ULTRICES": { shape: sql_table "posuere": VARCHAR(42) {constraint: [PK; FK]} "magna": VARCHAR(42) {constraint: [PK; FK]} } "ALIQUET"."magna" -> "TRISTIS"."magna" "ALIQUET"."tellus" -> "DIGNISSIM"."tellus" "LACUS"."magna" -> "TRISTIS"."magna" "NEC"."magna" -> "TRISTIS"."magna" "NEC"."gravida" -> "CURABITUR"."gravida" "QUAM"."magna" -> "SODALES"."magna" "SODALES"."magna" -> "TRISTIS"."magna" "SUSCIPIT"."magna" -> "TRISTIS"."magna" "TRISTIS"."fermentum" -> "CONSEQUAT"."fermentum" "ULTRICES"."posuere" -> "LIBERO"."posuere" "ULTRICES"."magna" -> "LACUS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_1_ddl.dbml ================================================ Table "ALIQUET" { "magna" VARCHAR(42) [NOT NULL] "tellus" VARCHAR(42) [NOT NULL] Indexes { ("magna", "tellus") [pk] } } Table "CONSEQUAT" { "fermentum" VARCHAR(42) [pk, NOT NULL] "dederit" VARCHAR(42) } Table "CURABITUR" { "gravida" VARCHAR(42) [pk, NOT NULL] "amor" VARCHAR(42) } Table "DIGNISSIM" { "tellus" VARCHAR(42) [pk, NOT NULL] "terra" VARCHAR(42) } Table "LACUS" { "magna" VARCHAR(42) [pk, NOT NULL] "tempor" VARCHAR(42) "fugit" VARCHAR(42) } Table "LIBERO" { "posuere" VARCHAR(42) [pk, NOT NULL] "lacrima" VARCHAR(42) } Table "NEC" { "magna" VARCHAR(42) [pk, NOT NULL] "pulvinar" VARCHAR(42) "audis" VARCHAR(42) "gravida" VARCHAR(42) [NOT NULL] } Table "QUAM" { "cras" VARCHAR(42) [pk, NOT NULL] "sed" VARCHAR(42) "magna" VARCHAR(42) [NOT NULL] } Table "SODALES" { "magna" VARCHAR(42) [pk, NOT NULL] "convallis" VARCHAR(42) "ipsum" VARCHAR(42) } Table "SUSCIPIT" { "orci" VARCHAR(42) [pk, NOT NULL] "lorem" VARCHAR(42) "magna" VARCHAR(42) [NOT NULL] } Table "TRISTIS" { "magna" VARCHAR(42) [pk, NOT NULL] "vestibulum" VARCHAR(42) "fermentum" VARCHAR(42) [NOT NULL] "type" UNSIGNED_INT [NOT NULL] } Table "ULTRICES" { "posuere" VARCHAR(42) [NOT NULL] "magna" VARCHAR(42) [NOT NULL] Indexes { ("posuere", "magna") [pk] } } Ref:"ALIQUET"."magna" > "TRISTIS"."magna" Ref:"ALIQUET"."tellus" > "DIGNISSIM"."tellus" Ref:"LACUS"."magna" > "TRISTIS"."magna" Ref:"NEC"."magna" > "TRISTIS"."magna" Ref:"NEC"."gravida" > "CURABITUR"."gravida" Ref:"QUAM"."magna" > "SODALES"."magna" Ref:"SODALES"."magna" > "TRISTIS"."magna" Ref:"SUSCIPIT"."magna" > "TRISTIS"."magna" Ref:"TRISTIS"."fermentum" > "CONSEQUAT"."fermentum" Ref:"ULTRICES"."posuere" > "LIBERO"."posuere" Ref:"ULTRICES"."magna" > "LACUS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_1_ddl.sql ================================================ CREATE TABLE ALIQUET ( PRIMARY KEY (magna, tellus), magna VARCHAR(42) NOT NULL, tellus VARCHAR(42) NOT NULL ); CREATE TABLE CONSEQUAT ( PRIMARY KEY (fermentum), fermentum VARCHAR(42) NOT NULL, dederit VARCHAR(42) ); CREATE TABLE CURABITUR ( PRIMARY KEY (gravida), gravida VARCHAR(42) NOT NULL, amor VARCHAR(42) ); CREATE TABLE DIGNISSIM ( PRIMARY KEY (tellus), tellus VARCHAR(42) NOT NULL, terra VARCHAR(42) ); CREATE TABLE LACUS ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, tempor VARCHAR(42), fugit VARCHAR(42) ); CREATE TABLE LIBERO ( PRIMARY KEY (posuere), posuere VARCHAR(42) NOT NULL, lacrima VARCHAR(42) ); CREATE TABLE NEC ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, pulvinar VARCHAR(42), audis VARCHAR(42), gravida VARCHAR(42) NOT NULL ); CREATE TABLE QUAM ( PRIMARY KEY (cras), cras VARCHAR(42) NOT NULL, sed VARCHAR(42), magna VARCHAR(42) NOT NULL ); CREATE TABLE SODALES ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, convallis VARCHAR(42), ipsum VARCHAR(42) ); CREATE TABLE SUSCIPIT ( PRIMARY KEY (orci), orci VARCHAR(42) NOT NULL, lorem VARCHAR(42), magna VARCHAR(42) NOT NULL ); CREATE TABLE TRISTIS ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, vestibulum VARCHAR(42), fermentum VARCHAR(42) NOT NULL, type UNSIGNED INT NOT NULL ); CREATE TABLE ULTRICES ( PRIMARY KEY (posuere, magna), posuere VARCHAR(42) NOT NULL, magna VARCHAR(42) NOT NULL ); ALTER TABLE ALIQUET ADD FOREIGN KEY (tellus) REFERENCES DIGNISSIM (tellus); ALTER TABLE ALIQUET ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE LACUS ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE NEC ADD FOREIGN KEY (gravida) REFERENCES CURABITUR (gravida); ALTER TABLE NEC ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE QUAM ADD FOREIGN KEY (magna) REFERENCES SODALES (magna); ALTER TABLE SODALES ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE SUSCIPIT ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE TRISTIS ADD FOREIGN KEY (fermentum) REFERENCES CONSEQUAT (fermentum); ALTER TABLE ULTRICES ADD FOREIGN KEY (magna) REFERENCES LACUS (magna); ALTER TABLE ULTRICES ADD FOREIGN KEY (posuere) REFERENCES LIBERO (posuere); ================================================ FILE: test/zoo/inheritance/ddl/inheritance_2_ddl.d2 ================================================ "ALIQUET": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "tellus": VARCHAR(42) {constraint: [PK; FK]} "type": UNSIGNED INT {constraint: NOT NULL} } "CONSEQUAT": { shape: sql_table "fermentum": VARCHAR(42) {constraint: PK} "dederit": VARCHAR(42) } "CURABITUR": { shape: sql_table "gravida": VARCHAR(42) {constraint: PK} "amor": VARCHAR(42) } "DIGNISSIM": { shape: sql_table "tellus": VARCHAR(42) {constraint: PK} "terra": VARCHAR(42) } "LACUS": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "vestibulum": VARCHAR(42) "fermentum": VARCHAR(42) {constraint: [FK; NOT NULL]} "tempor": VARCHAR(42) "fugit": VARCHAR(42) } "LIBERO": { shape: sql_table "posuere": VARCHAR(42) {constraint: PK} "lacrima": VARCHAR(42) } "NEC": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "vestibulum": VARCHAR(42) "fermentum": VARCHAR(42) {constraint: [FK; NOT NULL]} "pulvinar": VARCHAR(42) "audis": VARCHAR(42) "gravida": VARCHAR(42) {constraint: [FK; NOT NULL]} } "QUAM": { shape: sql_table "cras": VARCHAR(42) {constraint: PK} "sed": VARCHAR(42) "magna": VARCHAR(42) {constraint: [FK; NOT NULL]} } "SODALES": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "vestibulum": VARCHAR(42) "fermentum": VARCHAR(42) {constraint: [FK; NOT NULL]} "convallis": VARCHAR(42) "ipsum": VARCHAR(42) } "SUSCIPIT": { shape: sql_table "orci": VARCHAR(42) {constraint: PK} "lorem": VARCHAR(42) "magna": VARCHAR(42) {constraint: NOT NULL} "type": UNSIGNED INT {constraint: NOT NULL} } "ULTRICES": { shape: sql_table "posuere": VARCHAR(42) {constraint: [PK; FK]} "magna": VARCHAR(42) {constraint: [PK; FK]} } "ALIQUET"."tellus" -> "DIGNISSIM"."tellus" "LACUS"."fermentum" -> "CONSEQUAT"."fermentum" "NEC"."fermentum" -> "CONSEQUAT"."fermentum" "NEC"."gravida" -> "CURABITUR"."gravida" "QUAM"."magna" -> "SODALES"."magna" "SODALES"."fermentum" -> "CONSEQUAT"."fermentum" "ULTRICES"."posuere" -> "LIBERO"."posuere" "ULTRICES"."magna" -> "LACUS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_2_ddl.dbml ================================================ Table "ALIQUET" { "magna" VARCHAR(42) [NOT NULL] "tellus" VARCHAR(42) [NOT NULL] "type" UNSIGNED_INT [NOT NULL] Indexes { ("magna", "tellus") [pk] } } Table "CONSEQUAT" { "fermentum" VARCHAR(42) [pk, NOT NULL] "dederit" VARCHAR(42) } Table "CURABITUR" { "gravida" VARCHAR(42) [pk, NOT NULL] "amor" VARCHAR(42) } Table "DIGNISSIM" { "tellus" VARCHAR(42) [pk, NOT NULL] "terra" VARCHAR(42) } Table "LACUS" { "magna" VARCHAR(42) [pk, NOT NULL] "vestibulum" VARCHAR(42) "fermentum" VARCHAR(42) [NOT NULL] "tempor" VARCHAR(42) "fugit" VARCHAR(42) } Table "LIBERO" { "posuere" VARCHAR(42) [pk, NOT NULL] "lacrima" VARCHAR(42) } Table "NEC" { "magna" VARCHAR(42) [pk, NOT NULL] "vestibulum" VARCHAR(42) "fermentum" VARCHAR(42) [NOT NULL] "pulvinar" VARCHAR(42) "audis" VARCHAR(42) "gravida" VARCHAR(42) [NOT NULL] } Table "QUAM" { "cras" VARCHAR(42) [pk, NOT NULL] "sed" VARCHAR(42) "magna" VARCHAR(42) [NOT NULL] } Table "SODALES" { "magna" VARCHAR(42) [pk, NOT NULL] "vestibulum" VARCHAR(42) "fermentum" VARCHAR(42) [NOT NULL] "convallis" VARCHAR(42) "ipsum" VARCHAR(42) } Table "SUSCIPIT" { "orci" VARCHAR(42) [pk, NOT NULL] "lorem" VARCHAR(42) "magna" VARCHAR(42) [NOT NULL] "type" UNSIGNED_INT [NOT NULL] } Table "ULTRICES" { "posuere" VARCHAR(42) [NOT NULL] "magna" VARCHAR(42) [NOT NULL] Indexes { ("posuere", "magna") [pk] } } Ref:"ALIQUET"."tellus" > "DIGNISSIM"."tellus" Ref:"LACUS"."fermentum" > "CONSEQUAT"."fermentum" Ref:"NEC"."fermentum" > "CONSEQUAT"."fermentum" Ref:"NEC"."gravida" > "CURABITUR"."gravida" Ref:"QUAM"."magna" > "SODALES"."magna" Ref:"SODALES"."fermentum" > "CONSEQUAT"."fermentum" Ref:"ULTRICES"."posuere" > "LIBERO"."posuere" Ref:"ULTRICES"."magna" > "LACUS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_2_ddl.sql ================================================ CREATE TABLE ALIQUET ( PRIMARY KEY (magna, tellus), magna VARCHAR(42) NOT NULL, tellus VARCHAR(42) NOT NULL, type UNSIGNED INT NOT NULL ); CREATE TABLE CONSEQUAT ( PRIMARY KEY (fermentum), fermentum VARCHAR(42) NOT NULL, dederit VARCHAR(42) ); CREATE TABLE CURABITUR ( PRIMARY KEY (gravida), gravida VARCHAR(42) NOT NULL, amor VARCHAR(42) ); CREATE TABLE DIGNISSIM ( PRIMARY KEY (tellus), tellus VARCHAR(42) NOT NULL, terra VARCHAR(42) ); CREATE TABLE LACUS ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, vestibulum VARCHAR(42), fermentum VARCHAR(42) NOT NULL, tempor VARCHAR(42), fugit VARCHAR(42) ); CREATE TABLE LIBERO ( PRIMARY KEY (posuere), posuere VARCHAR(42) NOT NULL, lacrima VARCHAR(42) ); CREATE TABLE NEC ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, vestibulum VARCHAR(42), fermentum VARCHAR(42) NOT NULL, pulvinar VARCHAR(42), audis VARCHAR(42), gravida VARCHAR(42) NOT NULL ); CREATE TABLE QUAM ( PRIMARY KEY (cras), cras VARCHAR(42) NOT NULL, sed VARCHAR(42), magna VARCHAR(42) NOT NULL ); CREATE TABLE SODALES ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, vestibulum VARCHAR(42), fermentum VARCHAR(42) NOT NULL, convallis VARCHAR(42), ipsum VARCHAR(42) ); CREATE TABLE SUSCIPIT ( PRIMARY KEY (orci), orci VARCHAR(42) NOT NULL, lorem VARCHAR(42), magna VARCHAR(42) NOT NULL, type UNSIGNED INT NOT NULL ); CREATE TABLE ULTRICES ( PRIMARY KEY (posuere, magna), posuere VARCHAR(42) NOT NULL, magna VARCHAR(42) NOT NULL ); ALTER TABLE ALIQUET ADD FOREIGN KEY (tellus) REFERENCES DIGNISSIM (tellus); ALTER TABLE LACUS ADD FOREIGN KEY (fermentum) REFERENCES CONSEQUAT (fermentum); ALTER TABLE NEC ADD FOREIGN KEY (gravida) REFERENCES CURABITUR (gravida); ALTER TABLE NEC ADD FOREIGN KEY (fermentum) REFERENCES CONSEQUAT (fermentum); ALTER TABLE QUAM ADD FOREIGN KEY (magna) REFERENCES SODALES (magna); ALTER TABLE SODALES ADD FOREIGN KEY (fermentum) REFERENCES CONSEQUAT (fermentum); ALTER TABLE ULTRICES ADD FOREIGN KEY (magna) REFERENCES LACUS (magna); ALTER TABLE ULTRICES ADD FOREIGN KEY (posuere) REFERENCES LIBERO (posuere); ================================================ FILE: test/zoo/inheritance/ddl/inheritance_3_ddl.d2 ================================================ "TRISTIS": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "vestibulum": VARCHAR(42) "type": UNSIGNED INT {constraint: NOT NULL} "convallis": VARCHAR(42) {constraint: "NULL"} "ipsum": VARCHAR(42) {constraint: "NULL"} "pulvinar": VARCHAR(42) {constraint: "NULL"} "audis": VARCHAR(42) {constraint: "NULL"} "magna via_mollis": VARCHAR(42) {constraint: [FK; "NULL"]} "magna via_vitae": VARCHAR(42) {constraint: [FK; "NULL"]} "tempor": VARCHAR(42) {constraint: "NULL"} "fugit": VARCHAR(42) {constraint: "NULL"} } "ULTRICES": { shape: sql_table "magna sodales": VARCHAR(42) {constraint: [PK; FK]} "magna lacus": VARCHAR(42) {constraint: [PK; FK]} } "TRISTIS"."magna via_mollis" -> "TRISTIS"."magna" "TRISTIS"."magna via_vitae" -> "TRISTIS"."magna" "ULTRICES"."magna sodales" -> "TRISTIS"."magna" "ULTRICES"."magna lacus" -> "TRISTIS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_3_ddl.dbml ================================================ Table "TRISTIS" { "magna" VARCHAR(42) [pk, NOT NULL] "vestibulum" VARCHAR(42) "type" UNSIGNED_INT [NOT NULL] "convallis" VARCHAR(42) ["NULL"] "ipsum" VARCHAR(42) ["NULL"] "pulvinar" VARCHAR(42) ["NULL"] "audis" VARCHAR(42) ["NULL"] "magna via_mollis" VARCHAR(42) ["NULL"] "magna via_vitae" VARCHAR(42) ["NULL"] "tempor" VARCHAR(42) ["NULL"] "fugit" VARCHAR(42) ["NULL"] } Table "ULTRICES" { "magna sodales" VARCHAR(42) [NOT NULL] "magna lacus" VARCHAR(42) [NOT NULL] Indexes { ("magna sodales", "magna lacus") [pk] } } Ref:"TRISTIS".("magna via_mollis", "magna via_vitae") > "TRISTIS".("magna", "magna") Ref:"ULTRICES".("magna sodales", "magna lacus") > "TRISTIS".("magna", "magna") ================================================ FILE: test/zoo/inheritance/ddl/inheritance_3_ddl.sql ================================================ CREATE TABLE TRISTIS ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, vestibulum VARCHAR(42), type UNSIGNED INT NOT NULL, convallis VARCHAR(42) NULL, ipsum VARCHAR(42) NULL, pulvinar VARCHAR(42) NULL, audis VARCHAR(42) NULL, magna_via_mollis VARCHAR(42) NULL, magna_via_vitae VARCHAR(42) NULL, tempor VARCHAR(42) NULL, fugit VARCHAR(42) NULL ); CREATE TABLE ULTRICES ( PRIMARY KEY (magna_sodales, magna_lacus), magna_sodales VARCHAR(42) NOT NULL, magna_lacus VARCHAR(42) NOT NULL ); ALTER TABLE TRISTIS ADD FOREIGN KEY (magna_via_vitae) REFERENCES TRISTIS (magna); ALTER TABLE TRISTIS ADD FOREIGN KEY (magna_via_mollis) REFERENCES TRISTIS (magna); ALTER TABLE ULTRICES ADD FOREIGN KEY (magna_lacus) REFERENCES TRISTIS (magna); ALTER TABLE ULTRICES ADD FOREIGN KEY (magna_sodales) REFERENCES TRISTIS (magna); ================================================ FILE: test/zoo/inheritance/ddl/inheritance_4_ddl.d2 ================================================ "LACUS": { shape: sql_table "magna": VARCHAR(42) {constraint: [PK; FK]} "tempor": VARCHAR(42) "fugit": VARCHAR(42) } "NEC": { shape: sql_table "magna": VARCHAR(42) {constraint: [PK; FK]} "pulvinar": VARCHAR(42) "audis": VARCHAR(42) "magna via_mollis": VARCHAR(42) {constraint: [FK; NOT NULL]} "magna via_vitae": VARCHAR(42) {constraint: [FK; NOT NULL]} } "SODALES": { shape: sql_table "magna": VARCHAR(42) {constraint: [PK; FK]} "convallis": VARCHAR(42) "ipsum": VARCHAR(42) } "TRISTIS": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "vestibulum": VARCHAR(42) "type": UNSIGNED INT {constraint: NOT NULL} } "ULTRICES": { shape: sql_table "magna sodales": VARCHAR(42) {constraint: [PK; FK]} "magna lacus": VARCHAR(42) {constraint: [PK; FK]} } "LACUS"."magna" -> "TRISTIS"."magna" "NEC"."magna" -> "TRISTIS"."magna" "NEC"."magna via_mollis" -> "LACUS"."magna" "NEC"."magna via_vitae" -> "SODALES"."magna" "SODALES"."magna" -> "TRISTIS"."magna" "ULTRICES"."magna sodales" -> "SODALES"."magna" "ULTRICES"."magna lacus" -> "LACUS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_4_ddl.dbml ================================================ Table "LACUS" { "magna" VARCHAR(42) [pk, NOT NULL] "tempor" VARCHAR(42) "fugit" VARCHAR(42) } Table "NEC" { "magna" VARCHAR(42) [pk, NOT NULL] "pulvinar" VARCHAR(42) "audis" VARCHAR(42) "magna via_mollis" VARCHAR(42) [NOT NULL] "magna via_vitae" VARCHAR(42) [NOT NULL] } Table "SODALES" { "magna" VARCHAR(42) [pk, NOT NULL] "convallis" VARCHAR(42) "ipsum" VARCHAR(42) } Table "TRISTIS" { "magna" VARCHAR(42) [pk, NOT NULL] "vestibulum" VARCHAR(42) "type" UNSIGNED_INT [NOT NULL] } Table "ULTRICES" { "magna sodales" VARCHAR(42) [NOT NULL] "magna lacus" VARCHAR(42) [NOT NULL] Indexes { ("magna sodales", "magna lacus") [pk] } } Ref:"LACUS"."magna" > "TRISTIS"."magna" Ref:"NEC"."magna" > "TRISTIS"."magna" Ref:"NEC"."magna via_mollis" > "LACUS"."magna" Ref:"NEC"."magna via_vitae" > "SODALES"."magna" Ref:"SODALES"."magna" > "TRISTIS"."magna" Ref:"ULTRICES"."magna sodales" > "SODALES"."magna" Ref:"ULTRICES"."magna lacus" > "LACUS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_4_ddl.sql ================================================ CREATE TABLE LACUS ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, tempor VARCHAR(42), fugit VARCHAR(42) ); CREATE TABLE NEC ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, pulvinar VARCHAR(42), audis VARCHAR(42), magna_via_mollis VARCHAR(42) NOT NULL, magna_via_vitae VARCHAR(42) NOT NULL ); CREATE TABLE SODALES ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, convallis VARCHAR(42), ipsum VARCHAR(42) ); CREATE TABLE TRISTIS ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, vestibulum VARCHAR(42), type UNSIGNED INT NOT NULL ); CREATE TABLE ULTRICES ( PRIMARY KEY (magna_sodales, magna_lacus), magna_sodales VARCHAR(42) NOT NULL, magna_lacus VARCHAR(42) NOT NULL ); ALTER TABLE LACUS ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE NEC ADD FOREIGN KEY (magna_via_vitae) REFERENCES SODALES (magna); ALTER TABLE NEC ADD FOREIGN KEY (magna_via_mollis) REFERENCES LACUS (magna); ALTER TABLE NEC ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE SODALES ADD FOREIGN KEY (magna) REFERENCES TRISTIS (magna); ALTER TABLE ULTRICES ADD FOREIGN KEY (magna_lacus) REFERENCES LACUS (magna); ALTER TABLE ULTRICES ADD FOREIGN KEY (magna_sodales) REFERENCES SODALES (magna); ================================================ FILE: test/zoo/inheritance/ddl/inheritance_5_ddl.d2 ================================================ "LACUS": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "vestibulum": VARCHAR(42) "tempor": VARCHAR(42) "fugit": VARCHAR(42) } "NEC": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "vestibulum": VARCHAR(42) "pulvinar": VARCHAR(42) "audis": VARCHAR(42) "magna via_mollis": VARCHAR(42) {constraint: [FK; NOT NULL]} "magna via_vitae": VARCHAR(42) {constraint: [FK; NOT NULL]} } "SODALES": { shape: sql_table "magna": VARCHAR(42) {constraint: PK} "vestibulum": VARCHAR(42) "convallis": VARCHAR(42) "ipsum": VARCHAR(42) } "ULTRICES": { shape: sql_table "magna sodales": VARCHAR(42) {constraint: [PK; FK]} "magna lacus": VARCHAR(42) {constraint: [PK; FK]} } "NEC"."magna via_mollis" -> "LACUS"."magna" "NEC"."magna via_vitae" -> "SODALES"."magna" "ULTRICES"."magna sodales" -> "SODALES"."magna" "ULTRICES"."magna lacus" -> "LACUS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_5_ddl.dbml ================================================ Table "LACUS" { "magna" VARCHAR(42) [pk, NOT NULL] "vestibulum" VARCHAR(42) "tempor" VARCHAR(42) "fugit" VARCHAR(42) } Table "NEC" { "magna" VARCHAR(42) [pk, NOT NULL] "vestibulum" VARCHAR(42) "pulvinar" VARCHAR(42) "audis" VARCHAR(42) "magna via_mollis" VARCHAR(42) [NOT NULL] "magna via_vitae" VARCHAR(42) [NOT NULL] } Table "SODALES" { "magna" VARCHAR(42) [pk, NOT NULL] "vestibulum" VARCHAR(42) "convallis" VARCHAR(42) "ipsum" VARCHAR(42) } Table "ULTRICES" { "magna sodales" VARCHAR(42) [NOT NULL] "magna lacus" VARCHAR(42) [NOT NULL] Indexes { ("magna sodales", "magna lacus") [pk] } } Ref:"NEC"."magna via_mollis" > "LACUS"."magna" Ref:"NEC"."magna via_vitae" > "SODALES"."magna" Ref:"ULTRICES"."magna sodales" > "SODALES"."magna" Ref:"ULTRICES"."magna lacus" > "LACUS"."magna" ================================================ FILE: test/zoo/inheritance/ddl/inheritance_5_ddl.sql ================================================ CREATE TABLE LACUS ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, vestibulum VARCHAR(42), tempor VARCHAR(42), fugit VARCHAR(42) ); CREATE TABLE NEC ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, vestibulum VARCHAR(42), pulvinar VARCHAR(42), audis VARCHAR(42), magna_via_mollis VARCHAR(42) NOT NULL, magna_via_vitae VARCHAR(42) NOT NULL ); CREATE TABLE SODALES ( PRIMARY KEY (magna), magna VARCHAR(42) NOT NULL, vestibulum VARCHAR(42), convallis VARCHAR(42), ipsum VARCHAR(42) ); CREATE TABLE ULTRICES ( PRIMARY KEY (magna_sodales, magna_lacus), magna_sodales VARCHAR(42) NOT NULL, magna_lacus VARCHAR(42) NOT NULL ); ALTER TABLE NEC ADD FOREIGN KEY (magna_via_vitae) REFERENCES SODALES (magna); ALTER TABLE NEC ADD FOREIGN KEY (magna_via_mollis) REFERENCES LACUS (magna); ALTER TABLE ULTRICES ADD FOREIGN KEY (magna_lacus) REFERENCES LACUS (magna); ALTER TABLE ULTRICES ADD FOREIGN KEY (magna_sodales) REFERENCES SODALES (magna); ================================================ FILE: test/zoo/inheritance/exported/inheritance_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="SUSCIPIT"] 6 [label="SODALES"] 10 [label="QUAM"] 13 [label="CONSEQUAT"] 5 [label="TRISTIS"] 19 [label="NEC"] 23 [label="CURABITUR"] 26 [label="DIGNISSIM"] 30 [label="LACUS"] 34 [label="LIBERO"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="lorem"] 8 [label="ipsum"] 12 [label="sed"] 15 [label="dederit"] 18 [label="vestibulum"] 21 [label="audis"] 25 [label="amor"] 28 [label="terra"] 32 [label="fugit"] 36 [label="lacrima"] // Weak and strong entity attributes 2 [label=<orci>] 7 [label=<convallis>] 11 [label=<cras>] 14 [label=<fermentum>] 17 [label=<magna>] 20 [label=<pulvinar>] 24 [label=<gravida>] 27 [label=<tellus>] 31 [label=<tempor>] 35 [label=<posuere>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="RHONCUS"] 9 [label="VITAE"] 16 [label="ELIT"] 22 [label="MOLLIS"] 29 [label="ALIQUET"] 33 [label="ULTRICES"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 17 5 -- 18 6 -- 7 6 -- 8 10 -- 11 10 -- 12 13 -- 14 13 -- 15 19 -- 20 19 -- 21 23 -- 24 23 -- 25 26 -- 27 26 -- 28 30 -- 31 30 -- 32 34 -- 35 34 -- 36 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 5 -- 4 6 -- 9 13 -- 16 23 -- 22 edge [headlabel=M] 26 -- 29 30 -- 33 edge [headlabel=N] 1 -- 4 5 -- 16 5 -- 29 10 -- 9 19 -- 22 34 -- 33 } ================================================ FILE: test/zoo/inheritance/exported/inheritance_0_erd_chen.txt ================================================ [CONSEQUAT] ==1== [CURABITUR] ==1== [DIGNISSIM] ==M== [LACUS] ==M== [LIBERO] ==N== [NEC] ==N== [QUAM] ==N== [SODALES] ==1== [SUSCIPIT] ==N== [TRISTIS] ==1== [TRISTIS] ==N== [TRISTIS] ==N== ================================================ FILE: test/zoo/inheritance/exported/inheritance_0_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    SUSCIPIT
    PKorci
    lorem
    >] 2 [label=<
    SODALES
    PKconvallis
    ipsum
    >] 3 [label=<
    QUAM
    PKcras
    sed
    >] 4 [label=<
    CONSEQUAT
    PKfermentum
    dederit
    >] 5 [label=<
    TRISTIS
    PKmagna
    vestibulum
    >] 6 [label=<
    NEC
    PKpulvinar
    audis
    >] 7 [label=<
    CURABITUR
    PKgravida
    amor
    >] 8 [label=<
    DIGNISSIM
    PKtellus
    terra
    >] 9 [label=<
    LACUS
    PKtempor
    fugit
    >] 10 [label=<
    LIBERO
    PKposuere
    lacrima
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 5 -> 1 [arrowhead="crowtee" arrowtail="teetee" label="RHONCUS"] 3 -> 2 [arrowhead="teetee" arrowtail="crowtee" label="VITAE"] 5 -> 4 [arrowhead="teetee" arrowtail="crowtee" label="ELIT"] 7 -> 6 [arrowhead="crowtee" arrowtail="teetee" label="MOLLIS"] 5 -> 8 [arrowhead="crowtee" arrowtail="crowtee" label="ALIQUET"] 10 -> 9 [arrowhead="crowtee" arrowtail="crowtee" label="ULTRICES"] } ================================================ FILE: test/zoo/inheritance/exported/inheritance_0_erd_crow.mmd ================================================ erDiagram SUSCIPIT { TYPE orci PK TYPE lorem } SODALES { TYPE convallis PK TYPE ipsum } QUAM { TYPE cras PK TYPE sed } CONSEQUAT { TYPE fermentum PK TYPE dederit } TRISTIS { TYPE magna PK TYPE vestibulum } NEC { TYPE pulvinar PK TYPE audis } CURABITUR { TYPE gravida PK TYPE amor } DIGNISSIM { TYPE tellus PK TYPE terra } LACUS { TYPE tempor PK TYPE fugit } LIBERO { TYPE posuere PK TYPE lacrima } TRISTIS ||--|{ SUSCIPIT: RHONCUS QUAM }|--|| SODALES: VITAE TRISTIS }|--|| CONSEQUAT: ELIT CURABITUR ||--|{ NEC: MOLLIS TRISTIS }|--|{ DIGNISSIM: ALIQUET LIBERO }|--|{ LACUS: ULTRICES ================================================ FILE: test/zoo/inheritance/exported/inheritance_0_uml.puml ================================================ @startuml "inheritance" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("SUSCIPIT") { {field} + pk(orci) {field} + lorem } "TRISTIS" "1" --- "1..*" "SUSCIPIT": "RHONCUS" Table("SODALES") { {field} + pk(convallis) {field} + ipsum } "QUAM" "1..*" --- "1" "SODALES": "VITAE" Table("QUAM") { {field} + pk(cras) {field} + sed } Table("CONSEQUAT") { {field} + pk(fermentum) {field} + dederit } "TRISTIS" "1..*" --- "1" "CONSEQUAT": "ELIT" Table("TRISTIS") { {field} + pk(magna) {field} + vestibulum } note "{complete, disjoint}" as GENERALIZATION_0 GENERALIZATION_0 -[dotted]- LACUS GENERALIZATION_0 -[dotted]- NEC GENERALIZATION_0 -[dotted]- SODALES TRISTIS <|-- LACUS TRISTIS <|-- NEC TRISTIS <|-- SODALES Table("NEC") { {field} + pk(pulvinar) {field} + audis } "CURABITUR" "1" --- "1..*" "NEC": "MOLLIS" Table("CURABITUR") { {field} + pk(gravida) {field} + amor } Table("DIGNISSIM") { {field} + pk(tellus) {field} + terra } "TRISTIS" "1..*" --- "1..*" "DIGNISSIM": "ALIQUET" Table("LACUS") { {field} + pk(tempor) {field} + fugit } "LIBERO" "1..*" --- "1..*" "LACUS": "ULTRICES" Table("LIBERO") { {field} + pk(posuere) {field} + lacrima } @enduml ================================================ FILE: test/zoo/inheritance/exported/inheritance_1_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="SUSCIPIT"] 6 [label="SODALES"] 10 [label="QUAM"] 13 [label="CONSEQUAT"] 5 [label="TRISTIS"] 19 [label="NEC"] 23 [label="CURABITUR"] 26 [label="DIGNISSIM"] 30 [label="LACUS"] 34 [label="LIBERO"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="lorem"] 8 [label="ipsum"] 12 [label="sed"] 15 [label="dederit"] 18 [label="vestibulum"] 21 [label="audis"] 25 [label="amor"] 28 [label="terra"] 32 [label="fugit"] 36 [label="lacrima"] // Weak and strong entity attributes 2 [label=<orci>] 7 [label=<convallis>] 11 [label=<cras>] 14 [label=<fermentum>] 17 [label=<magna>] 20 [label=<pulvinar>] 24 [label=<gravida>] 27 [label=<tellus>] 31 [label=<tempor>] 35 [label=<posuere>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="RHONCUS"] 9 [label="VITAE"] 16 [label="ELIT"] 22 [label="MOLLIS"] 29 [label="ALIQUET"] 33 [label="ULTRICES"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 17 5 -- 18 6 -- 7 6 -- 8 10 -- 11 10 -- 12 13 -- 14 13 -- 15 19 -- 20 19 -- 21 23 -- 24 23 -- 25 26 -- 27 26 -- 28 30 -- 31 30 -- 32 34 -- 35 34 -- 36 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 5 -- 4 6 -- 9 13 -- 16 23 -- 22 edge [headlabel=M] 26 -- 29 30 -- 33 edge [headlabel=N] 1 -- 4 5 -- 16 5 -- 29 10 -- 9 19 -- 22 34 -- 33 } ================================================ FILE: test/zoo/inheritance/exported/inheritance_1_erd_chen.txt ================================================ [CONSEQUAT] ==1== [CURABITUR] ==1== [DIGNISSIM] ==M== [LACUS] ==M== [LIBERO] ==N== [NEC] ==N== [QUAM] ==N== [SODALES] ==1== [SUSCIPIT] ==N== [TRISTIS] ==1== [TRISTIS] ==N== [TRISTIS] ==N== ================================================ FILE: test/zoo/inheritance/exported/inheritance_1_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    SUSCIPIT
    PKorci
    lorem
    >] 2 [label=<
    SODALES
    PKconvallis
    ipsum
    >] 3 [label=<
    QUAM
    PKcras
    sed
    >] 4 [label=<
    CONSEQUAT
    PKfermentum
    dederit
    >] 5 [label=<
    TRISTIS
    PKmagna
    vestibulum
    >] 6 [label=<
    NEC
    PKpulvinar
    audis
    >] 7 [label=<
    CURABITUR
    PKgravida
    amor
    >] 8 [label=<
    DIGNISSIM
    PKtellus
    terra
    >] 9 [label=<
    LACUS
    PKtempor
    fugit
    >] 10 [label=<
    LIBERO
    PKposuere
    lacrima
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 5 -> 1 [arrowhead="crowtee" arrowtail="teetee" label="RHONCUS"] 3 -> 2 [arrowhead="teetee" arrowtail="crowtee" label="VITAE"] 5 -> 4 [arrowhead="teetee" arrowtail="crowtee" label="ELIT"] 7 -> 6 [arrowhead="crowtee" arrowtail="teetee" label="MOLLIS"] 5 -> 8 [arrowhead="crowtee" arrowtail="crowtee" label="ALIQUET"] 10 -> 9 [arrowhead="crowtee" arrowtail="crowtee" label="ULTRICES"] } ================================================ FILE: test/zoo/inheritance/exported/inheritance_1_erd_crow.mmd ================================================ erDiagram SUSCIPIT { TYPE orci PK TYPE lorem } SODALES { TYPE convallis PK TYPE ipsum } QUAM { TYPE cras PK TYPE sed } CONSEQUAT { TYPE fermentum PK TYPE dederit } TRISTIS { TYPE magna PK TYPE vestibulum } NEC { TYPE pulvinar PK TYPE audis } CURABITUR { TYPE gravida PK TYPE amor } DIGNISSIM { TYPE tellus PK TYPE terra } LACUS { TYPE tempor PK TYPE fugit } LIBERO { TYPE posuere PK TYPE lacrima } TRISTIS ||--|{ SUSCIPIT: RHONCUS QUAM }|--|| SODALES: VITAE TRISTIS }|--|| CONSEQUAT: ELIT CURABITUR ||--|{ NEC: MOLLIS TRISTIS }|--|{ DIGNISSIM: ALIQUET LIBERO }|--|{ LACUS: ULTRICES ================================================ FILE: test/zoo/inheritance/exported/inheritance_1_uml.puml ================================================ @startuml "inheritance" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("SUSCIPIT") { {field} + pk(orci) {field} + lorem } "TRISTIS" "1" --- "1..*" "SUSCIPIT": "RHONCUS" Table("SODALES") { {field} + pk(convallis) {field} + ipsum } "QUAM" "1..*" --- "1" "SODALES": "VITAE" Table("QUAM") { {field} + pk(cras) {field} + sed } Table("CONSEQUAT") { {field} + pk(fermentum) {field} + dederit } "TRISTIS" "1..*" --- "1" "CONSEQUAT": "ELIT" Table("TRISTIS") { {field} + pk(magna) {field} + vestibulum } note "{complete, disjoint}" as GENERALIZATION_0 GENERALIZATION_0 -[dotted]- LACUS GENERALIZATION_0 -[dotted]- NEC GENERALIZATION_0 -[dotted]- SODALES TRISTIS <|-- LACUS TRISTIS <|-- NEC TRISTIS <|-- SODALES Table("NEC") { {field} + pk(pulvinar) {field} + audis } "CURABITUR" "1" --- "1..*" "NEC": "MOLLIS" Table("CURABITUR") { {field} + pk(gravida) {field} + amor } Table("DIGNISSIM") { {field} + pk(tellus) {field} + terra } "TRISTIS" "1..*" --- "1..*" "DIGNISSIM": "ALIQUET" Table("LACUS") { {field} + pk(tempor) {field} + fugit } "LIBERO" "1..*" --- "1..*" "LACUS": "ULTRICES" Table("LIBERO") { {field} + pk(posuere) {field} + lacrima } @enduml ================================================ FILE: test/zoo/inheritance/exported/inheritance_2_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="SUSCIPIT"] 6 [label="SODALES"] 10 [label="QUAM"] 13 [label="CONSEQUAT"] 5 [label="TRISTIS"] 19 [label="NEC"] 23 [label="CURABITUR"] 26 [label="DIGNISSIM"] 30 [label="LACUS"] 34 [label="LIBERO"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="lorem"] 8 [label="ipsum"] 12 [label="sed"] 15 [label="dederit"] 18 [label="vestibulum"] 21 [label="audis"] 25 [label="amor"] 28 [label="terra"] 32 [label="fugit"] 36 [label="lacrima"] // Weak and strong entity attributes 2 [label=<orci>] 7 [label=<convallis>] 11 [label=<cras>] 14 [label=<fermentum>] 17 [label=<magna>] 20 [label=<pulvinar>] 24 [label=<gravida>] 27 [label=<tellus>] 31 [label=<tempor>] 35 [label=<posuere>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="RHONCUS"] 9 [label="VITAE"] 16 [label="ELIT"] 22 [label="MOLLIS"] 29 [label="ALIQUET"] 33 [label="ULTRICES"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 17 5 -- 18 6 -- 7 6 -- 8 10 -- 11 10 -- 12 13 -- 14 13 -- 15 19 -- 20 19 -- 21 23 -- 24 23 -- 25 26 -- 27 26 -- 28 30 -- 31 30 -- 32 34 -- 35 34 -- 36 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 5 -- 4 6 -- 9 13 -- 16 23 -- 22 edge [headlabel=M] 26 -- 29 30 -- 33 edge [headlabel=N] 1 -- 4 5 -- 16 5 -- 29 10 -- 9 19 -- 22 34 -- 33 } ================================================ FILE: test/zoo/inheritance/exported/inheritance_2_erd_chen.txt ================================================ [CONSEQUAT] ==1== [CURABITUR] ==1== [DIGNISSIM] ==M== [LACUS] ==M== [LIBERO] ==N== [NEC] ==N== [QUAM] ==N== [SODALES] ==1== [SUSCIPIT] ==N== [TRISTIS] ==1== [TRISTIS] ==N== [TRISTIS] ==N== ================================================ FILE: test/zoo/inheritance/exported/inheritance_2_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    SUSCIPIT
    PKorci
    lorem
    >] 2 [label=<
    SODALES
    PKconvallis
    ipsum
    >] 3 [label=<
    QUAM
    PKcras
    sed
    >] 4 [label=<
    CONSEQUAT
    PKfermentum
    dederit
    >] 5 [label=<
    TRISTIS
    PKmagna
    vestibulum
    >] 6 [label=<
    NEC
    PKpulvinar
    audis
    >] 7 [label=<
    CURABITUR
    PKgravida
    amor
    >] 8 [label=<
    DIGNISSIM
    PKtellus
    terra
    >] 9 [label=<
    LACUS
    PKtempor
    fugit
    >] 10 [label=<
    LIBERO
    PKposuere
    lacrima
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 5 -> 1 [arrowhead="crowtee" arrowtail="teetee" label="RHONCUS"] 3 -> 2 [arrowhead="teetee" arrowtail="crowtee" label="VITAE"] 5 -> 4 [arrowhead="teetee" arrowtail="crowtee" label="ELIT"] 7 -> 6 [arrowhead="crowtee" arrowtail="teetee" label="MOLLIS"] 5 -> 8 [arrowhead="crowtee" arrowtail="crowtee" label="ALIQUET"] 10 -> 9 [arrowhead="crowtee" arrowtail="crowtee" label="ULTRICES"] } ================================================ FILE: test/zoo/inheritance/exported/inheritance_2_erd_crow.mmd ================================================ erDiagram SUSCIPIT { TYPE orci PK TYPE lorem } SODALES { TYPE convallis PK TYPE ipsum } QUAM { TYPE cras PK TYPE sed } CONSEQUAT { TYPE fermentum PK TYPE dederit } TRISTIS { TYPE magna PK TYPE vestibulum } NEC { TYPE pulvinar PK TYPE audis } CURABITUR { TYPE gravida PK TYPE amor } DIGNISSIM { TYPE tellus PK TYPE terra } LACUS { TYPE tempor PK TYPE fugit } LIBERO { TYPE posuere PK TYPE lacrima } TRISTIS ||--|{ SUSCIPIT: RHONCUS QUAM }|--|| SODALES: VITAE TRISTIS }|--|| CONSEQUAT: ELIT CURABITUR ||--|{ NEC: MOLLIS TRISTIS }|--|{ DIGNISSIM: ALIQUET LIBERO }|--|{ LACUS: ULTRICES ================================================ FILE: test/zoo/inheritance/exported/inheritance_2_uml.puml ================================================ @startuml "inheritance" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("SUSCIPIT") { {field} + pk(orci) {field} + lorem } "TRISTIS" "1" --- "1..*" "SUSCIPIT": "RHONCUS" Table("SODALES") { {field} + pk(convallis) {field} + ipsum } "QUAM" "1..*" --- "1" "SODALES": "VITAE" Table("QUAM") { {field} + pk(cras) {field} + sed } Table("CONSEQUAT") { {field} + pk(fermentum) {field} + dederit } "TRISTIS" "1..*" --- "1" "CONSEQUAT": "ELIT" Table("TRISTIS") { {field} + pk(magna) {field} + vestibulum } note "{complete, disjoint}" as GENERALIZATION_0 GENERALIZATION_0 -[dotted]- LACUS GENERALIZATION_0 -[dotted]- NEC GENERALIZATION_0 -[dotted]- SODALES TRISTIS <|-- LACUS TRISTIS <|-- NEC TRISTIS <|-- SODALES Table("NEC") { {field} + pk(pulvinar) {field} + audis } "CURABITUR" "1" --- "1..*" "NEC": "MOLLIS" Table("CURABITUR") { {field} + pk(gravida) {field} + amor } Table("DIGNISSIM") { {field} + pk(tellus) {field} + terra } "TRISTIS" "1..*" --- "1..*" "DIGNISSIM": "ALIQUET" Table("LACUS") { {field} + pk(tempor) {field} + fugit } "LIBERO" "1..*" --- "1..*" "LACUS": "ULTRICES" Table("LIBERO") { {field} + pk(posuere) {field} + lacrima } @enduml ================================================ FILE: test/zoo/inheritance/exported/inheritance_3_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="NEC"] 2 [label="LACUS"] 12 [label="SODALES"] // Associative entities 8 [label="TRISTIS",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 5 [label="audis"] 7 [label="fugit"] 10 [label="vestibulum"] 15 [label="ipsum"] // Weak and strong entity attributes 4 [label=<pulvinar>] 6 [label=<tempor>] 9 [label=<magna>] 14 [label=<convallis>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="MOLLIS"] 11 [label="VITAE"] 13 [label="ULTRICES"] // Edges between entities and attributes edge [ penwidth=1.5 ] 2 -- 6 2 -- 7 3 -- 4 3 -- 5 8 -- 9 8 -- 10 12 -- 14 12 -- 15 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 2 -- 1 12 -- 11 edge [headlabel=M] 2 -- 13 edge [headlabel=N] 3 -- 1 3 -- 11 12 -- 13 } ================================================ FILE: test/zoo/inheritance/exported/inheritance_3_erd_chen.txt ================================================ [LACUS] ==1== [LACUS] ==M== [NEC] ==N== [NEC] ==N== [SODALES] ==1== [SODALES] ==N== ================================================ FILE: test/zoo/inheritance/exported/inheritance_3_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    NEC
    PKpulvinar
    audis
    >] 2 [label=<
    LACUS
    PKtempor
    fugit
    >] 3 [label=<
    TRISTIS
    PKmagna
    vestibulum
    >] 4 [label=<
    SODALES
    PKconvallis
    ipsum
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="crowtee" arrowtail="teetee" label="MOLLIS"] 1 -> 4 [arrowhead="teetee" arrowtail="crowtee" label="VITAE"] 4 -> 2 [arrowhead="crowtee" arrowtail="crowtee" label="ULTRICES"] } ================================================ FILE: test/zoo/inheritance/exported/inheritance_3_erd_crow.mmd ================================================ erDiagram NEC { TYPE pulvinar PK TYPE audis } LACUS { TYPE tempor PK TYPE fugit } TRISTIS { TYPE magna PK TYPE vestibulum } SODALES { TYPE convallis PK TYPE ipsum } LACUS ||--|{ NEC: MOLLIS NEC }|--|| SODALES: VITAE SODALES }|--|{ LACUS: ULTRICES ================================================ FILE: test/zoo/inheritance/exported/inheritance_3_uml.puml ================================================ @startuml "inheritance" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x "LACUS" "1" --- "1..*" "NEC": "MOLLIS" Table("NEC") { {field} + pk(pulvinar) {field} + audis } Table("LACUS") { {field} + pk(tempor) {field} + fugit } note "{complete, disjoint}" as GENERALIZATION_0 GENERALIZATION_0 -[dotted]- LACUS GENERALIZATION_0 -[dotted]- NEC GENERALIZATION_0 -[dotted]- SODALES TRISTIS <|-- LACUS TRISTIS <|-- NEC TRISTIS <|-- SODALES Table("TRISTIS") { {field} + pk(magna) {field} + vestibulum } "NEC" "1..*" --- "1" "SODALES": "VITAE" "SODALES" "1..*" --- "1..*" "LACUS": "ULTRICES" Table("SODALES") { {field} + pk(convallis) {field} + ipsum } @enduml ================================================ FILE: test/zoo/inheritance/exported/inheritance_4_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="NEC"] 2 [label="LACUS"] 12 [label="SODALES"] // Associative entities 8 [label="TRISTIS",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 5 [label="audis"] 7 [label="fugit"] 10 [label="vestibulum"] 15 [label="ipsum"] // Weak and strong entity attributes 4 [label=<pulvinar>] 6 [label=<tempor>] 9 [label=<magna>] 14 [label=<convallis>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="MOLLIS"] 11 [label="VITAE"] 13 [label="ULTRICES"] // Edges between entities and attributes edge [ penwidth=1.5 ] 2 -- 6 2 -- 7 3 -- 4 3 -- 5 8 -- 9 8 -- 10 12 -- 14 12 -- 15 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 2 -- 1 12 -- 11 edge [headlabel=M] 2 -- 13 edge [headlabel=N] 3 -- 1 3 -- 11 12 -- 13 } ================================================ FILE: test/zoo/inheritance/exported/inheritance_4_erd_chen.txt ================================================ [LACUS] ==1== [LACUS] ==M== [NEC] ==N== [NEC] ==N== [SODALES] ==1== [SODALES] ==N== ================================================ FILE: test/zoo/inheritance/exported/inheritance_4_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    NEC
    PKpulvinar
    audis
    >] 2 [label=<
    LACUS
    PKtempor
    fugit
    >] 3 [label=<
    TRISTIS
    PKmagna
    vestibulum
    >] 4 [label=<
    SODALES
    PKconvallis
    ipsum
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="crowtee" arrowtail="teetee" label="MOLLIS"] 1 -> 4 [arrowhead="teetee" arrowtail="crowtee" label="VITAE"] 4 -> 2 [arrowhead="crowtee" arrowtail="crowtee" label="ULTRICES"] } ================================================ FILE: test/zoo/inheritance/exported/inheritance_4_erd_crow.mmd ================================================ erDiagram NEC { TYPE pulvinar PK TYPE audis } LACUS { TYPE tempor PK TYPE fugit } TRISTIS { TYPE magna PK TYPE vestibulum } SODALES { TYPE convallis PK TYPE ipsum } LACUS ||--|{ NEC: MOLLIS NEC }|--|| SODALES: VITAE SODALES }|--|{ LACUS: ULTRICES ================================================ FILE: test/zoo/inheritance/exported/inheritance_4_uml.puml ================================================ @startuml "inheritance" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x "LACUS" "1" --- "1..*" "NEC": "MOLLIS" Table("NEC") { {field} + pk(pulvinar) {field} + audis } Table("LACUS") { {field} + pk(tempor) {field} + fugit } note "{complete, disjoint}" as GENERALIZATION_0 GENERALIZATION_0 -[dotted]- LACUS GENERALIZATION_0 -[dotted]- NEC GENERALIZATION_0 -[dotted]- SODALES TRISTIS <|-- LACUS TRISTIS <|-- NEC TRISTIS <|-- SODALES Table("TRISTIS") { {field} + pk(magna) {field} + vestibulum } "NEC" "1..*" --- "1" "SODALES": "VITAE" "SODALES" "1..*" --- "1..*" "LACUS": "ULTRICES" Table("SODALES") { {field} + pk(convallis) {field} + ipsum } @enduml ================================================ FILE: test/zoo/inheritance/exported/inheritance_5_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="NEC"] 2 [label="LACUS"] 12 [label="SODALES"] // Associative entities 8 [label="TRISTIS",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 5 [label="audis"] 7 [label="fugit"] 10 [label="vestibulum"] 15 [label="ipsum"] // Weak and strong entity attributes 4 [label=<pulvinar>] 6 [label=<tempor>] 9 [label=<magna>] 14 [label=<convallis>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="MOLLIS"] 11 [label="VITAE"] 13 [label="ULTRICES"] // Edges between entities and attributes edge [ penwidth=1.5 ] 2 -- 6 2 -- 7 3 -- 4 3 -- 5 8 -- 9 8 -- 10 12 -- 14 12 -- 15 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 2 -- 1 12 -- 11 edge [headlabel=M] 2 -- 13 edge [headlabel=N] 3 -- 1 3 -- 11 12 -- 13 } ================================================ FILE: test/zoo/inheritance/exported/inheritance_5_erd_chen.txt ================================================ [LACUS] ==1== [LACUS] ==M== [NEC] ==N== [NEC] ==N== [SODALES] ==1== [SODALES] ==N== ================================================ FILE: test/zoo/inheritance/exported/inheritance_5_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    NEC
    PKpulvinar
    audis
    >] 2 [label=<
    LACUS
    PKtempor
    fugit
    >] 3 [label=<
    TRISTIS
    PKmagna
    vestibulum
    >] 4 [label=<
    SODALES
    PKconvallis
    ipsum
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="crowtee" arrowtail="teetee" label="MOLLIS"] 1 -> 4 [arrowhead="teetee" arrowtail="crowtee" label="VITAE"] 4 -> 2 [arrowhead="crowtee" arrowtail="crowtee" label="ULTRICES"] } ================================================ FILE: test/zoo/inheritance/exported/inheritance_5_erd_crow.mmd ================================================ erDiagram NEC { TYPE pulvinar PK TYPE audis } LACUS { TYPE tempor PK TYPE fugit } TRISTIS { TYPE magna PK TYPE vestibulum } SODALES { TYPE convallis PK TYPE ipsum } LACUS ||--|{ NEC: MOLLIS NEC }|--|| SODALES: VITAE SODALES }|--|{ LACUS: ULTRICES ================================================ FILE: test/zoo/inheritance/exported/inheritance_5_uml.puml ================================================ @startuml "inheritance" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x "LACUS" "1" --- "1..*" "NEC": "MOLLIS" Table("NEC") { {field} + pk(pulvinar) {field} + audis } Table("LACUS") { {field} + pk(tempor) {field} + fugit } note "{complete, disjoint}" as GENERALIZATION_0 GENERALIZATION_0 -[dotted]- LACUS GENERALIZATION_0 -[dotted]- NEC GENERALIZATION_0 -[dotted]- SODALES TRISTIS <|-- LACUS TRISTIS <|-- NEC TRISTIS <|-- SODALES Table("TRISTIS") { {field} + pk(magna) {field} + vestibulum } "NEC" "1..*" --- "1" "SODALES": "VITAE" "SODALES" "1..*" --- "1..*" "LACUS": "ULTRICES" Table("SODALES") { {field} + pk(convallis) {field} + ipsum } @enduml ================================================ FILE: test/zoo/inheritance/mld/inheritance_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note ALIQUET magna ! primary_foreign_key True TRISTIS TRISTIS ALIQUET ALIQUET tellus ! primary_foreign_key True DIGNISSIM DIGNISSIM ALIQUET CONSEQUAT fermentum ! primary_key True CONSEQUAT dederit normal_attribute False CURABITUR gravida ! primary_key True CURABITUR amor normal_attribute False DIGNISSIM tellus ! primary_key True DIGNISSIM terra normal_attribute False LIBERO posuere ! primary_key True LIBERO lacrima normal_attribute False QUAM cras ! primary_key True QUAM sed normal_attribute False QUAM magna ! foreign_key False SODALES TRISTIS VITAE SUSCIPIT orci ! primary_key True SUSCIPIT lorem normal_attribute False SUSCIPIT magna ! foreign_key False TRISTIS TRISTIS RHONCUS TRISTIS magna ! primary_key True TRISTIS vestibulum normal_attribute False TRISTIS fermentum ! foreign_key False CONSEQUAT CONSEQUAT ELIT TRISTIS type ! deleted_child_discriminator_XT False XT UNSIGNED_INT_PLACEHOLDER TRISTIS convallis ? deleted_child_attribute False SODALES XT TRISTIS ipsum ? deleted_child_attribute False SODALES XT TRISTIS pulvinar ? deleted_child_attribute False NEC XT TRISTIS audis ? deleted_child_attribute False NEC XT TRISTIS gravida ? deleted_child_foreign_key False NEC CURABITUR XT TRISTIS tempor ? deleted_child_attribute False LACUS XT TRISTIS fugit ? deleted_child_attribute False LACUS XT ULTRICES posuere ! primary_foreign_key True LIBERO LIBERO ULTRICES ULTRICES magna ! primary_foreign_key True LACUS TRISTIS ULTRICES ================================================ FILE: test/zoo/inheritance/mld/inheritance_0_dependencies.gv ================================================ digraph { node [shape=box] "DIGNISSIM" -> "ALIQUET" "TRISTIS" -> "ALIQUET" "TRISTIS" -> "QUAM" "TRISTIS" -> "SUSCIPIT" "CURABITUR" -> "TRISTIS" "CONSEQUAT" -> "TRISTIS" "TRISTIS" -> "ULTRICES" "LIBERO" -> "ULTRICES" } ================================================ FILE: test/zoo/inheritance/mld/inheritance_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD inheritance
    ALIQUET ( #magna, #tellus )
    • Le champ magna fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité TRISTIS.
    • Le champ tellus fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité DIGNISSIM.
    CONSEQUAT ( fermentum, dederit )
    • Le champ fermentum constitue la clé primaire de la table. C'était déjà un identifiant de l'entité CONSEQUAT.
    • Le champ dederit était déjà un simple attribut de l'entité CONSEQUAT.
    CURABITUR ( gravida, amor )
    • Le champ gravida constitue la clé primaire de la table. C'était déjà un identifiant de l'entité CURABITUR.
    • Le champ amor était déjà un simple attribut de l'entité CURABITUR.
    DIGNISSIM ( tellus, terra )
    • Le champ tellus constitue la clé primaire de la table. C'était déjà un identifiant de l'entité DIGNISSIM.
    • Le champ terra était déjà un simple attribut de l'entité DIGNISSIM.
    LIBERO ( posuere, lacrima )
    • Le champ posuere constitue la clé primaire de la table. C'était déjà un identifiant de l'entité LIBERO.
    • Le champ lacrima était déjà un simple attribut de l'entité LIBERO.
    QUAM ( cras, sed, #magna! )
    • Le champ cras constitue la clé primaire de la table. C'était déjà un identifiant de l'entité QUAM.
    • Le champ sed était déjà un simple attribut de l'entité QUAM.
    • Le champ à saisie obligatoire magna est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle VITAE à partir de l'entité TRISTIS en perdant son caractère identifiant.
    SUSCIPIT ( orci, lorem, #magna! )
    • Le champ orci constitue la clé primaire de la table. C'était déjà un identifiant de l'entité SUSCIPIT.
    • Le champ lorem était déjà un simple attribut de l'entité SUSCIPIT.
    • Le champ à saisie obligatoire magna est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle RHONCUS à partir de l'entité TRISTIS en perdant son caractère identifiant.
    TRISTIS ( magna, vestibulum, #fermentum!, type!, convallis?, ipsum?, pulvinar?, audis?, #gravida?, tempor?, fugit? )
    • Le champ magna constitue la clé primaire de la table. C'était déjà un identifiant de l'entité TRISTIS.
    • Le champ vestibulum était déjà un simple attribut de l'entité TRISTIS.
    • Le champ à saisie obligatoire fermentum est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle ELIT à partir de l'entité CONSEQUAT en perdant son caractère identifiant.
    • Un discriminateur à saisie obligatoire type est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité.
    • Le champ à saisie facultative convallis a migré à partir de l'entité-fille SODALES (supprimée).
    • Le champ à saisie facultative ipsum a migré à partir de l'entité-fille SODALES (supprimée).
    • Le champ à saisie facultative pulvinar a migré à partir de l'entité-fille NEC (supprimée).
    • Le champ à saisie facultative audis a migré à partir de l'entité-fille NEC (supprimée).
    • Le champ à saisie facultative gravida est une clé étrangère. Il a migré à partir de l'entité-fille NEC (supprimée) dans laquelle il avait déjà migré à partir de l'entité CURABITUR.
    • Le champ à saisie facultative tempor a migré à partir de l'entité-fille LACUS (supprimée).
    • Le champ à saisie facultative fugit a migré à partir de l'entité-fille LACUS (supprimée).
    ULTRICES ( #posuere, #magna )
    • Le champ posuere fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité LIBERO.
    • Le champ magna fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité TRISTIS.
    ================================================ FILE: test/zoo/inheritance/mld/inheritance_0_mld.mcd ================================================ %%mocodo : SUSCIPIT: orci, lorem, #magna > TRISTIS > magna ::::::: QUAM: cras, sed, #magna > TRISTIS > magna : : CONSEQUAT: fermentum, dederit ::: TRISTIS: magna, vestibulum, #fermentum > CONSEQUAT > fermentum, type, convallis, ipsum, pulvinar, audis, #gravida > CURABITUR > gravida, tempor, fugit ::: CURABITUR: gravida, amor : : DIGNISSIM: tellus, terra : ALIQUET: #magna > TRISTIS > magna, _#tellus > DIGNISSIM > tellus ::: ULTRICES: #posuere > LIBERO > posuere, _#magna > TRISTIS > magna : LIBERO: posuere, lacrima : ================================================ FILE: test/zoo/inheritance/mld/inheritance_0_mld.md ================================================ - **ALIQUET** (_#magna_, _#tellus_) - Le champ _magna_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _TRISTIS_. - Le champ _tellus_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _DIGNISSIM_. - **CONSEQUAT** (fermentum, dederit) - Le champ _fermentum_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _CONSEQUAT_. - Le champ _dederit_ était déjà un simple attribut de l'entité _CONSEQUAT_. - **CURABITUR** (gravida, amor) - Le champ _gravida_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _CURABITUR_. - Le champ _amor_ était déjà un simple attribut de l'entité _CURABITUR_. - **DIGNISSIM** (tellus, terra) - Le champ _tellus_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _DIGNISSIM_. - Le champ _terra_ était déjà un simple attribut de l'entité _DIGNISSIM_. - **LIBERO** (posuere, lacrima) - Le champ _posuere_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _LIBERO_. - Le champ _lacrima_ était déjà un simple attribut de l'entité _LIBERO_. - **QUAM** (cras, sed, _#magna!_) - Le champ _cras_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _QUAM_. - Le champ _sed_ était déjà un simple attribut de l'entité _QUAM_. - Le champ à saisie obligatoire _magna_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _VITAE_ à partir de l'entité _TRISTIS_ en perdant son caractère identifiant. - **SUSCIPIT** (orci, lorem, _#magna!_) - Le champ _orci_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _SUSCIPIT_. - Le champ _lorem_ était déjà un simple attribut de l'entité _SUSCIPIT_. - Le champ à saisie obligatoire _magna_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _RHONCUS_ à partir de l'entité _TRISTIS_ en perdant son caractère identifiant. - **TRISTIS** (magna, vestibulum, _#fermentum!_, type!, convallis?, ipsum?, pulvinar?, audis?, _#gravida?_, tempor?, fugit?) - Le champ _magna_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _TRISTIS_. - Le champ _vestibulum_ était déjà un simple attribut de l'entité _TRISTIS_. - Le champ à saisie obligatoire _fermentum_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _ELIT_ à partir de l'entité _CONSEQUAT_ en perdant son caractère identifiant. - Un discriminateur à saisie obligatoire _type_ est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - Le champ à saisie facultative _convallis_ a migré à partir de l'entité-fille _SODALES_ (supprimée). - Le champ à saisie facultative _ipsum_ a migré à partir de l'entité-fille _SODALES_ (supprimée). - Le champ à saisie facultative _pulvinar_ a migré à partir de l'entité-fille _NEC_ (supprimée). - Le champ à saisie facultative _audis_ a migré à partir de l'entité-fille _NEC_ (supprimée). - Le champ à saisie facultative _gravida_ est une clé étrangère. Il a migré à partir de l'entité-fille _NEC_ (supprimée) dans laquelle il avait déjà migré à partir de l'entité _CURABITUR_. - Le champ à saisie facultative _tempor_ a migré à partir de l'entité-fille _LACUS_ (supprimée). - Le champ à saisie facultative _fugit_ a migré à partir de l'entité-fille _LACUS_ (supprimée). - **ULTRICES** (_#posuere_, _#magna_) - Le champ _posuere_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _LIBERO_. - Le champ _magna_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _TRISTIS_. ================================================ FILE: test/zoo/inheritance/mld/inheritance_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{inheritance}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{ALIQUET} (\foreign{\prim{magna}}, \foreign{\prim{tellus}}) \begin{itemize} \item Le champ \emph{magna} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{TRISTIS}. \item Le champ \emph{tellus} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{DIGNISSIM}. \end{itemize} \item \relat{CONSEQUAT} (\prim{fermentum}, \attr{dederit}) \begin{itemize} \item Le champ \emph{fermentum} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CONSEQUAT}. \item Le champ \emph{dederit} était déjà un simple attribut de l'entité \emph{CONSEQUAT}. \end{itemize} \item \relat{CURABITUR} (\prim{gravida}, \attr{amor}) \begin{itemize} \item Le champ \emph{gravida} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CURABITUR}. \item Le champ \emph{amor} était déjà un simple attribut de l'entité \emph{CURABITUR}. \end{itemize} \item \relat{DIGNISSIM} (\prim{tellus}, \attr{terra}) \begin{itemize} \item Le champ \emph{tellus} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{DIGNISSIM}. \item Le champ \emph{terra} était déjà un simple attribut de l'entité \emph{DIGNISSIM}. \end{itemize} \item \relat{LIBERO} (\prim{posuere}, \attr{lacrima}) \begin{itemize} \item Le champ \emph{posuere} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{LIBERO}. \item Le champ \emph{lacrima} était déjà un simple attribut de l'entité \emph{LIBERO}. \end{itemize} \item \relat{QUAM} (\prim{cras}, \attr{sed}, \foreign{magna!}) \begin{itemize} \item Le champ \emph{cras} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{QUAM}. \item Le champ \emph{sed} était déjà un simple attribut de l'entité \emph{QUAM}. \item Le champ à saisie obligatoire \emph{magna} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{VITAE} à partir de l'entité \emph{TRISTIS} en perdant son caractère identifiant. \end{itemize} \item \relat{SUSCIPIT} (\prim{orci}, \attr{lorem}, \foreign{magna!}) \begin{itemize} \item Le champ \emph{orci} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{SUSCIPIT}. \item Le champ \emph{lorem} était déjà un simple attribut de l'entité \emph{SUSCIPIT}. \item Le champ à saisie obligatoire \emph{magna} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{RHONCUS} à partir de l'entité \emph{TRISTIS} en perdant son caractère identifiant. \end{itemize} \item \relat{TRISTIS} (\prim{magna}, \attr{vestibulum}, \foreign{fermentum!}, \attr{type!}, \attr{convallis?}, \attr{ipsum?}, \attr{pulvinar?}, \attr{audis?}, \foreign{gravida?}, \attr{tempor?}, \attr{fugit?}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{TRISTIS}. \item Le champ \emph{vestibulum} était déjà un simple attribut de l'entité \emph{TRISTIS}. \item Le champ à saisie obligatoire \emph{fermentum} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{ELIT} à partir de l'entité \emph{CONSEQUAT} en perdant son caractère identifiant. \item Un discriminateur à saisie obligatoire \emph{type} est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. \item Le champ à saisie facultative \emph{convallis} a migré à partir de l'entité-fille \emph{SODALES} (supprimée). \item Le champ à saisie facultative \emph{ipsum} a migré à partir de l'entité-fille \emph{SODALES} (supprimée). \item Le champ à saisie facultative \emph{pulvinar} a migré à partir de l'entité-fille \emph{NEC} (supprimée). \item Le champ à saisie facultative \emph{audis} a migré à partir de l'entité-fille \emph{NEC} (supprimée). \item Le champ à saisie facultative \emph{gravida} est une clé étrangère. Il a migré à partir de l'entité-fille \emph{NEC} (supprimée) dans laquelle il avait déjà migré à partir de l'entité \emph{CURABITUR}. \item Le champ à saisie facultative \emph{tempor} a migré à partir de l'entité-fille \emph{LACUS} (supprimée). \item Le champ à saisie facultative \emph{fugit} a migré à partir de l'entité-fille \emph{LACUS} (supprimée). \end{itemize} \item \relat{ULTRICES} (\foreign{\prim{posuere}}, \foreign{\prim{magna}}) \begin{itemize} \item Le champ \emph{posuere} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{LIBERO}. \item Le champ \emph{magna} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{TRISTIS}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/inheritance/mld/inheritance_0_mld.txt ================================================ - ALIQUET (_#magna_, _#tellus_) - Le champ « magna » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « TRISTIS ». - Le champ « tellus » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « DIGNISSIM ». - CONSEQUAT (_fermentum_, dederit) - Le champ « fermentum » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « CONSEQUAT ». - Le champ « dederit » était déjà un simple attribut de l'entité « CONSEQUAT ». - CURABITUR (_gravida_, amor) - Le champ « gravida » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « CURABITUR ». - Le champ « amor » était déjà un simple attribut de l'entité « CURABITUR ». - DIGNISSIM (_tellus_, terra) - Le champ « tellus » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « DIGNISSIM ». - Le champ « terra » était déjà un simple attribut de l'entité « DIGNISSIM ». - LIBERO (_posuere_, lacrima) - Le champ « posuere » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « LIBERO ». - Le champ « lacrima » était déjà un simple attribut de l'entité « LIBERO ». - QUAM (_cras_, sed, #magna!) - Le champ « cras » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « QUAM ». - Le champ « sed » était déjà un simple attribut de l'entité « QUAM ». - Le champ à saisie obligatoire « magna » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « VITAE » à partir de l'entité « TRISTIS » en perdant son caractère identifiant. - SUSCIPIT (_orci_, lorem, #magna!) - Le champ « orci » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « SUSCIPIT ». - Le champ « lorem » était déjà un simple attribut de l'entité « SUSCIPIT ». - Le champ à saisie obligatoire « magna » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « RHONCUS » à partir de l'entité « TRISTIS » en perdant son caractère identifiant. - TRISTIS (_magna_, vestibulum, #fermentum!, type!, convallis?, ipsum?, pulvinar?, audis?, #gravida?, tempor?, fugit?) - Le champ « magna » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « TRISTIS ». - Le champ « vestibulum » était déjà un simple attribut de l'entité « TRISTIS ». - Le champ à saisie obligatoire « fermentum » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « ELIT » à partir de l'entité « CONSEQUAT » en perdant son caractère identifiant. - Un discriminateur à saisie obligatoire « type » est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - Le champ à saisie facultative « convallis » a migré à partir de l'entité-fille « SODALES » (supprimée). - Le champ à saisie facultative « ipsum » a migré à partir de l'entité-fille « SODALES » (supprimée). - Le champ à saisie facultative « pulvinar » a migré à partir de l'entité-fille « NEC » (supprimée). - Le champ à saisie facultative « audis » a migré à partir de l'entité-fille « NEC » (supprimée). - Le champ à saisie facultative « gravida » est une clé étrangère. Il a migré à partir de l'entité-fille « NEC » (supprimée) dans laquelle il avait déjà migré à partir de l'entité « CURABITUR ». - Le champ à saisie facultative « tempor » a migré à partir de l'entité-fille « LACUS » (supprimée). - Le champ à saisie facultative « fugit » a migré à partir de l'entité-fille « LACUS » (supprimée). - ULTRICES (_#posuere_, _#magna_) - Le champ « posuere » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « LIBERO ». - Le champ « magna » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « TRISTIS ». ================================================ FILE: test/zoo/inheritance/mld/inheritance_1_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note ALIQUET magna ! primary_foreign_key True TRISTIS TRISTIS ALIQUET ALIQUET tellus ! primary_foreign_key True DIGNISSIM DIGNISSIM ALIQUET CONSEQUAT fermentum ! primary_key True CONSEQUAT dederit normal_attribute False CURABITUR gravida ! primary_key True CURABITUR amor normal_attribute False DIGNISSIM tellus ! primary_key True DIGNISSIM terra normal_attribute False LACUS magna ! parent_primary_key True TRISTIS TRISTIS XT LACUS tempor normal_attribute False LACUS fugit normal_attribute False LIBERO posuere ! primary_key True LIBERO lacrima normal_attribute False NEC magna ! parent_primary_key True TRISTIS TRISTIS XT NEC pulvinar normal_attribute False NEC audis normal_attribute False NEC gravida ! foreign_key False CURABITUR CURABITUR MOLLIS QUAM cras ! primary_key True QUAM sed normal_attribute False QUAM magna ! foreign_key False SODALES SODALES VITAE SODALES magna ! parent_primary_key True TRISTIS TRISTIS XT SODALES convallis normal_attribute False SODALES ipsum normal_attribute False SUSCIPIT orci ! primary_key True SUSCIPIT lorem normal_attribute False SUSCIPIT magna ! foreign_key False TRISTIS TRISTIS RHONCUS TRISTIS magna ! primary_key True TRISTIS vestibulum normal_attribute False TRISTIS fermentum ! foreign_key False CONSEQUAT CONSEQUAT ELIT TRISTIS type ! deleted_child_discriminator_XT False XT UNSIGNED_INT_PLACEHOLDER ULTRICES posuere ! primary_foreign_key True LIBERO LIBERO ULTRICES ULTRICES magna ! primary_foreign_key True LACUS LACUS ULTRICES ================================================ FILE: test/zoo/inheritance/mld/inheritance_1_dependencies.gv ================================================ digraph { node [shape=box] "DIGNISSIM" -> "ALIQUET" "TRISTIS" -> "ALIQUET" "TRISTIS" -> "LACUS" "CURABITUR" -> "NEC" "TRISTIS" -> "NEC" "SODALES" -> "QUAM" "TRISTIS" -> "SODALES" "TRISTIS" -> "SUSCIPIT" "CONSEQUAT" -> "TRISTIS" "LACUS" -> "ULTRICES" "LIBERO" -> "ULTRICES" } ================================================ FILE: test/zoo/inheritance/mld/inheritance_1_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD inheritance
    ALIQUET ( #magna, #tellus )
    • Le champ magna fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité TRISTIS.
    • Le champ tellus fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité DIGNISSIM.
    CONSEQUAT ( fermentum, dederit )
    • Le champ fermentum constitue la clé primaire de la table. C'était déjà un identifiant de l'entité CONSEQUAT.
    • Le champ dederit était déjà un simple attribut de l'entité CONSEQUAT.
    CURABITUR ( gravida, amor )
    • Le champ gravida constitue la clé primaire de la table. C'était déjà un identifiant de l'entité CURABITUR.
    • Le champ amor était déjà un simple attribut de l'entité CURABITUR.
    DIGNISSIM ( tellus, terra )
    • Le champ tellus constitue la clé primaire de la table. C'était déjà un identifiant de l'entité DIGNISSIM.
    • Le champ terra était déjà un simple attribut de l'entité DIGNISSIM.
    LACUS ( #magna, tempor, fugit )
    • Le champ magna constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère TRISTIS.
    • Les champs tempor et fugit étaient déjà de simples attributs de l'entité LACUS.
    LIBERO ( posuere, lacrima )
    • Le champ posuere constitue la clé primaire de la table. C'était déjà un identifiant de l'entité LIBERO.
    • Le champ lacrima était déjà un simple attribut de l'entité LIBERO.
    NEC ( #magna, pulvinar, audis, #gravida! )
    • Le champ magna constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère TRISTIS.
    • Les champs pulvinar et audis étaient déjà de simples attributs de l'entité NEC.
    • Le champ à saisie obligatoire gravida est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle MOLLIS à partir de l'entité CURABITUR en perdant son caractère identifiant.
    QUAM ( cras, sed, #magna! )
    • Le champ cras constitue la clé primaire de la table. C'était déjà un identifiant de l'entité QUAM.
    • Le champ sed était déjà un simple attribut de l'entité QUAM.
    • Le champ à saisie obligatoire magna est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle VITAE à partir de l'entité SODALES en perdant son caractère identifiant.
    SODALES ( #magna, convallis, ipsum )
    • Le champ magna constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère TRISTIS.
    • Les champs convallis et ipsum étaient déjà de simples attributs de l'entité SODALES.
    SUSCIPIT ( orci, lorem, #magna! )
    • Le champ orci constitue la clé primaire de la table. C'était déjà un identifiant de l'entité SUSCIPIT.
    • Le champ lorem était déjà un simple attribut de l'entité SUSCIPIT.
    • Le champ à saisie obligatoire magna est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle RHONCUS à partir de l'entité TRISTIS en perdant son caractère identifiant.
    TRISTIS ( magna, vestibulum, #fermentum!, type! )
    • Le champ magna constitue la clé primaire de la table. C'était déjà un identifiant de l'entité TRISTIS.
    • Le champ vestibulum était déjà un simple attribut de l'entité TRISTIS.
    • Le champ à saisie obligatoire fermentum est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle ELIT à partir de l'entité CONSEQUAT en perdant son caractère identifiant.
    • Un discriminateur à saisie obligatoire type est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité.
    ULTRICES ( #posuere, #magna )
    • Le champ posuere fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité LIBERO.
    • Le champ magna fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité LACUS.
    ================================================ FILE: test/zoo/inheritance/mld/inheritance_1_mld.mcd ================================================ %%mocodo : SUSCIPIT: orci, lorem, #magna > TRISTIS > magna ::::: SODALES: #magna > TRISTIS > magna, convallis, ipsum ::: QUAM: cras, sed, #magna > SODALES > magna : : CONSEQUAT: fermentum, dederit ::: TRISTIS: magna, vestibulum, #fermentum > CONSEQUAT > fermentum, type : NEC: #magna > TRISTIS > magna, pulvinar, audis, #gravida > CURABITUR > gravida ::: CURABITUR: gravida, amor : : DIGNISSIM: tellus, terra : ALIQUET: #magna > TRISTIS > magna, _#tellus > DIGNISSIM > tellus ::: LACUS: #magna > TRISTIS > magna, tempor, fugit : ULTRICES: #posuere > LIBERO > posuere, _#magna > LACUS > magna : LIBERO: posuere, lacrima : ================================================ FILE: test/zoo/inheritance/mld/inheritance_1_mld.md ================================================ - **ALIQUET** (_#magna_, _#tellus_) - Le champ _magna_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _TRISTIS_. - Le champ _tellus_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _DIGNISSIM_. - **CONSEQUAT** (fermentum, dederit) - Le champ _fermentum_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _CONSEQUAT_. - Le champ _dederit_ était déjà un simple attribut de l'entité _CONSEQUAT_. - **CURABITUR** (gravida, amor) - Le champ _gravida_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _CURABITUR_. - Le champ _amor_ était déjà un simple attribut de l'entité _CURABITUR_. - **DIGNISSIM** (tellus, terra) - Le champ _tellus_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _DIGNISSIM_. - Le champ _terra_ était déjà un simple attribut de l'entité _DIGNISSIM_. - **LACUS** (_#magna_, tempor, fugit) - Le champ _magna_ constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère _TRISTIS_. - Les champs _tempor_ et _fugit_ étaient déjà de simples attributs de l'entité _LACUS_. - **LIBERO** (posuere, lacrima) - Le champ _posuere_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _LIBERO_. - Le champ _lacrima_ était déjà un simple attribut de l'entité _LIBERO_. - **NEC** (_#magna_, pulvinar, audis, _#gravida!_) - Le champ _magna_ constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère _TRISTIS_. - Les champs _pulvinar_ et _audis_ étaient déjà de simples attributs de l'entité _NEC_. - Le champ à saisie obligatoire _gravida_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _MOLLIS_ à partir de l'entité _CURABITUR_ en perdant son caractère identifiant. - **QUAM** (cras, sed, _#magna!_) - Le champ _cras_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _QUAM_. - Le champ _sed_ était déjà un simple attribut de l'entité _QUAM_. - Le champ à saisie obligatoire _magna_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _VITAE_ à partir de l'entité _SODALES_ en perdant son caractère identifiant. - **SODALES** (_#magna_, convallis, ipsum) - Le champ _magna_ constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère _TRISTIS_. - Les champs _convallis_ et _ipsum_ étaient déjà de simples attributs de l'entité _SODALES_. - **SUSCIPIT** (orci, lorem, _#magna!_) - Le champ _orci_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _SUSCIPIT_. - Le champ _lorem_ était déjà un simple attribut de l'entité _SUSCIPIT_. - Le champ à saisie obligatoire _magna_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _RHONCUS_ à partir de l'entité _TRISTIS_ en perdant son caractère identifiant. - **TRISTIS** (magna, vestibulum, _#fermentum!_, type!) - Le champ _magna_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _TRISTIS_. - Le champ _vestibulum_ était déjà un simple attribut de l'entité _TRISTIS_. - Le champ à saisie obligatoire _fermentum_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _ELIT_ à partir de l'entité _CONSEQUAT_ en perdant son caractère identifiant. - Un discriminateur à saisie obligatoire _type_ est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - **ULTRICES** (_#posuere_, _#magna_) - Le champ _posuere_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _LIBERO_. - Le champ _magna_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _LACUS_. ================================================ FILE: test/zoo/inheritance/mld/inheritance_1_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{inheritance}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{ALIQUET} (\foreign{\prim{magna}}, \foreign{\prim{tellus}}) \begin{itemize} \item Le champ \emph{magna} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{TRISTIS}. \item Le champ \emph{tellus} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{DIGNISSIM}. \end{itemize} \item \relat{CONSEQUAT} (\prim{fermentum}, \attr{dederit}) \begin{itemize} \item Le champ \emph{fermentum} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CONSEQUAT}. \item Le champ \emph{dederit} était déjà un simple attribut de l'entité \emph{CONSEQUAT}. \end{itemize} \item \relat{CURABITUR} (\prim{gravida}, \attr{amor}) \begin{itemize} \item Le champ \emph{gravida} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CURABITUR}. \item Le champ \emph{amor} était déjà un simple attribut de l'entité \emph{CURABITUR}. \end{itemize} \item \relat{DIGNISSIM} (\prim{tellus}, \attr{terra}) \begin{itemize} \item Le champ \emph{tellus} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{DIGNISSIM}. \item Le champ \emph{terra} était déjà un simple attribut de l'entité \emph{DIGNISSIM}. \end{itemize} \item \relat{LACUS} (\foreign{\prim{magna}}, \attr{tempor}, \attr{fugit}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère \emph{TRISTIS}. \item Les champs \emph{tempor} et \emph{fugit} étaient déjà de simples attributs de l'entité \emph{LACUS}. \end{itemize} \item \relat{LIBERO} (\prim{posuere}, \attr{lacrima}) \begin{itemize} \item Le champ \emph{posuere} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{LIBERO}. \item Le champ \emph{lacrima} était déjà un simple attribut de l'entité \emph{LIBERO}. \end{itemize} \item \relat{NEC} (\foreign{\prim{magna}}, \attr{pulvinar}, \attr{audis}, \foreign{gravida!}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère \emph{TRISTIS}. \item Les champs \emph{pulvinar} et \emph{audis} étaient déjà de simples attributs de l'entité \emph{NEC}. \item Le champ à saisie obligatoire \emph{gravida} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{MOLLIS} à partir de l'entité \emph{CURABITUR} en perdant son caractère identifiant. \end{itemize} \item \relat{QUAM} (\prim{cras}, \attr{sed}, \foreign{magna!}) \begin{itemize} \item Le champ \emph{cras} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{QUAM}. \item Le champ \emph{sed} était déjà un simple attribut de l'entité \emph{QUAM}. \item Le champ à saisie obligatoire \emph{magna} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{VITAE} à partir de l'entité \emph{SODALES} en perdant son caractère identifiant. \end{itemize} \item \relat{SODALES} (\foreign{\prim{magna}}, \attr{convallis}, \attr{ipsum}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère \emph{TRISTIS}. \item Les champs \emph{convallis} et \emph{ipsum} étaient déjà de simples attributs de l'entité \emph{SODALES}. \end{itemize} \item \relat{SUSCIPIT} (\prim{orci}, \attr{lorem}, \foreign{magna!}) \begin{itemize} \item Le champ \emph{orci} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{SUSCIPIT}. \item Le champ \emph{lorem} était déjà un simple attribut de l'entité \emph{SUSCIPIT}. \item Le champ à saisie obligatoire \emph{magna} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{RHONCUS} à partir de l'entité \emph{TRISTIS} en perdant son caractère identifiant. \end{itemize} \item \relat{TRISTIS} (\prim{magna}, \attr{vestibulum}, \foreign{fermentum!}, \attr{type!}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{TRISTIS}. \item Le champ \emph{vestibulum} était déjà un simple attribut de l'entité \emph{TRISTIS}. \item Le champ à saisie obligatoire \emph{fermentum} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{ELIT} à partir de l'entité \emph{CONSEQUAT} en perdant son caractère identifiant. \item Un discriminateur à saisie obligatoire \emph{type} est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. \end{itemize} \item \relat{ULTRICES} (\foreign{\prim{posuere}}, \foreign{\prim{magna}}) \begin{itemize} \item Le champ \emph{posuere} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{LIBERO}. \item Le champ \emph{magna} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{LACUS}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/inheritance/mld/inheritance_1_mld.txt ================================================ - ALIQUET (_#magna_, _#tellus_) - Le champ « magna » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « TRISTIS ». - Le champ « tellus » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « DIGNISSIM ». - CONSEQUAT (_fermentum_, dederit) - Le champ « fermentum » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « CONSEQUAT ». - Le champ « dederit » était déjà un simple attribut de l'entité « CONSEQUAT ». - CURABITUR (_gravida_, amor) - Le champ « gravida » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « CURABITUR ». - Le champ « amor » était déjà un simple attribut de l'entité « CURABITUR ». - DIGNISSIM (_tellus_, terra) - Le champ « tellus » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « DIGNISSIM ». - Le champ « terra » était déjà un simple attribut de l'entité « DIGNISSIM ». - LACUS (_#magna_, tempor, fugit) - Le champ « magna » constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère « TRISTIS ». - Les champs « tempor » et « fugit » étaient déjà de simples attributs de l'entité « LACUS ». - LIBERO (_posuere_, lacrima) - Le champ « posuere » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « LIBERO ». - Le champ « lacrima » était déjà un simple attribut de l'entité « LIBERO ». - NEC (_#magna_, pulvinar, audis, #gravida!) - Le champ « magna » constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère « TRISTIS ». - Les champs « pulvinar » et « audis » étaient déjà de simples attributs de l'entité « NEC ». - Le champ à saisie obligatoire « gravida » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « MOLLIS » à partir de l'entité « CURABITUR » en perdant son caractère identifiant. - QUAM (_cras_, sed, #magna!) - Le champ « cras » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « QUAM ». - Le champ « sed » était déjà un simple attribut de l'entité « QUAM ». - Le champ à saisie obligatoire « magna » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « VITAE » à partir de l'entité « SODALES » en perdant son caractère identifiant. - SODALES (_#magna_, convallis, ipsum) - Le champ « magna » constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère « TRISTIS ». - Les champs « convallis » et « ipsum » étaient déjà de simples attributs de l'entité « SODALES ». - SUSCIPIT (_orci_, lorem, #magna!) - Le champ « orci » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « SUSCIPIT ». - Le champ « lorem » était déjà un simple attribut de l'entité « SUSCIPIT ». - Le champ à saisie obligatoire « magna » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « RHONCUS » à partir de l'entité « TRISTIS » en perdant son caractère identifiant. - TRISTIS (_magna_, vestibulum, #fermentum!, type!) - Le champ « magna » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « TRISTIS ». - Le champ « vestibulum » était déjà un simple attribut de l'entité « TRISTIS ». - Le champ à saisie obligatoire « fermentum » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « ELIT » à partir de l'entité « CONSEQUAT » en perdant son caractère identifiant. - Un discriminateur à saisie obligatoire « type » est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - ULTRICES (_#posuere_, _#magna_) - Le champ « posuere » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « LIBERO ». - Le champ « magna » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « LACUS ». ================================================ FILE: test/zoo/inheritance/mld/inheritance_2_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note ALIQUET magna ! unsourced_primary_foreign_key True TRISTIS ALIQUET ALIQUET tellus ! primary_foreign_key True DIGNISSIM DIGNISSIM ALIQUET ALIQUET type ! deleted_parent_discriminator_XT False TRISTIS ALIQUET UNSIGNED_INT_PLACEHOLDER CONSEQUAT fermentum ! primary_key True CONSEQUAT dederit normal_attribute False CURABITUR gravida ! primary_key True CURABITUR amor normal_attribute False DIGNISSIM tellus ! primary_key True DIGNISSIM terra normal_attribute False LACUS magna ! deleted_parent_primary_key True TRISTIS TRISTIS XT LACUS vestibulum deleted_parent_attribute False TRISTIS XT LACUS fermentum ! deleted_parent_foreign_key False TRISTIS CONSEQUAT XT LACUS tempor normal_attribute False LACUS fugit normal_attribute False LIBERO posuere ! primary_key True LIBERO lacrima normal_attribute False NEC magna ! deleted_parent_primary_key True TRISTIS TRISTIS XT NEC vestibulum deleted_parent_attribute False TRISTIS XT NEC fermentum ! deleted_parent_foreign_key False TRISTIS CONSEQUAT XT NEC pulvinar normal_attribute False NEC audis normal_attribute False NEC gravida ! foreign_key False CURABITUR CURABITUR MOLLIS QUAM cras ! primary_key True QUAM sed normal_attribute False QUAM magna ! foreign_key False SODALES SODALES VITAE SODALES magna ! deleted_parent_primary_key True TRISTIS TRISTIS XT SODALES vestibulum deleted_parent_attribute False TRISTIS XT SODALES fermentum ! deleted_parent_foreign_key False TRISTIS CONSEQUAT XT SODALES convallis normal_attribute False SODALES ipsum normal_attribute False SUSCIPIT orci ! primary_key True SUSCIPIT lorem normal_attribute False SUSCIPIT magna ! unsourced_foreign_key False TRISTIS RHONCUS SUSCIPIT type ! deleted_parent_discriminator_XT False TRISTIS RHONCUS UNSIGNED_INT_PLACEHOLDER ULTRICES posuere ! primary_foreign_key True LIBERO LIBERO ULTRICES ULTRICES magna ! primary_foreign_key True LACUS LACUS ULTRICES ================================================ FILE: test/zoo/inheritance/mld/inheritance_2_dependencies.gv ================================================ digraph { node [shape=box] "DIGNISSIM" -> "ALIQUET" "CONSEQUAT" -> "LACUS" "CURABITUR" -> "NEC" "CONSEQUAT" -> "NEC" "SODALES" -> "QUAM" "CONSEQUAT" -> "SODALES" "LACUS" -> "ULTRICES" "LIBERO" -> "ULTRICES" } ================================================ FILE: test/zoo/inheritance/mld/inheritance_2_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD inheritance
    ALIQUET ( magna, #tellus, type! )
    • Le champ magna fait partie de la clé primaire de la table. Il a migré par l'association de dépendance fonctionnelle ALIQUET à partir de l'entité TRISTIS (supprimée). Attention : aucune contrainte d'intégrité référentielle n'est plus assurée.
    • Le champ tellus fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité DIGNISSIM.
    • Un discriminateur à saisie obligatoire type est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité.
    CONSEQUAT ( fermentum, dederit )
    • Le champ fermentum constitue la clé primaire de la table. C'était déjà un identifiant de l'entité CONSEQUAT.
    • Le champ dederit était déjà un simple attribut de l'entité CONSEQUAT.
    CURABITUR ( gravida, amor )
    • Le champ gravida constitue la clé primaire de la table. C'était déjà un identifiant de l'entité CURABITUR.
    • Le champ amor était déjà un simple attribut de l'entité CURABITUR.
    DIGNISSIM ( tellus, terra )
    • Le champ tellus constitue la clé primaire de la table. C'était déjà un identifiant de l'entité DIGNISSIM.
    • Le champ terra était déjà un simple attribut de l'entité DIGNISSIM.
    LACUS ( magna, vestibulum, #fermentum!, tempor, fugit )
    • Le champ magna constitue la clé primaire de la table. Il était clé primaire de l'entité-mère TRISTIS (supprimée).
    • Le champ vestibulum est un simple attribut. Il était simple attribut de l'entité-mère TRISTIS (supprimée).
    • Le champ à saisie obligatoire fermentum est une clé étrangère. Il a migré à travers l'entité-mère TRISTIS (supprimée), et réfère maintenant directement à l'entité CONSEQUAT.
    • Les champs tempor et fugit étaient déjà de simples attributs de l'entité LACUS.
    LIBERO ( posuere, lacrima )
    • Le champ posuere constitue la clé primaire de la table. C'était déjà un identifiant de l'entité LIBERO.
    • Le champ lacrima était déjà un simple attribut de l'entité LIBERO.
    NEC ( magna, vestibulum, #fermentum!, pulvinar, audis, #gravida! )
    • Le champ magna constitue la clé primaire de la table. Il était clé primaire de l'entité-mère TRISTIS (supprimée).
    • Le champ vestibulum est un simple attribut. Il était simple attribut de l'entité-mère TRISTIS (supprimée).
    • Le champ à saisie obligatoire fermentum est une clé étrangère. Il a migré à travers l'entité-mère TRISTIS (supprimée), et réfère maintenant directement à l'entité CONSEQUAT.
    • Les champs pulvinar et audis étaient déjà de simples attributs de l'entité NEC.
    • Le champ à saisie obligatoire gravida est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle MOLLIS à partir de l'entité CURABITUR en perdant son caractère identifiant.
    QUAM ( cras, sed, #magna! )
    • Le champ cras constitue la clé primaire de la table. C'était déjà un identifiant de l'entité QUAM.
    • Le champ sed était déjà un simple attribut de l'entité QUAM.
    • Le champ à saisie obligatoire magna est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle VITAE à partir de l'entité SODALES en perdant son caractère identifiant.
    SODALES ( magna, vestibulum, #fermentum!, convallis, ipsum )
    • Le champ magna constitue la clé primaire de la table. Il était clé primaire de l'entité-mère TRISTIS (supprimée).
    • Le champ vestibulum est un simple attribut. Il était simple attribut de l'entité-mère TRISTIS (supprimée).
    • Le champ à saisie obligatoire fermentum est une clé étrangère. Il a migré à travers l'entité-mère TRISTIS (supprimée), et réfère maintenant directement à l'entité CONSEQUAT.
    • Les champs convallis et ipsum étaient déjà de simples attributs de l'entité SODALES.
    SUSCIPIT ( orci, lorem, magna!, type! )
    • Le champ orci constitue la clé primaire de la table. C'était déjà un identifiant de l'entité SUSCIPIT.
    • Le champ lorem était déjà un simple attribut de l'entité SUSCIPIT.
    • Le champ à saisie obligatoire magna a migré par l'association de dépendance fonctionnelle RHONCUS à partir de l'entité TRISTIS (supprimée). Attention : aucune contrainte d'intégrité référentielle n'est plus assurée.
    • Un discriminateur à saisie obligatoire type est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité.
    ULTRICES ( #posuere, #magna )
    • Le champ posuere fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité LIBERO.
    • Le champ magna fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité LACUS.
    ================================================ FILE: test/zoo/inheritance/mld/inheritance_2_mld.mcd ================================================ %%mocodo : SUSCIPIT: orci, lorem, magna, type ::: SODALES: magna, vestibulum, #fermentum > CONSEQUAT > fermentum, convallis, ipsum ::: QUAM: cras, sed, #magna > SODALES > magna : : CONSEQUAT: fermentum, dederit ::: NEC: magna, vestibulum, #fermentum > CONSEQUAT > fermentum, pulvinar, audis, #gravida > CURABITUR > gravida ::: CURABITUR: gravida, amor : : DIGNISSIM: tellus, terra : ALIQUET: magna, _#tellus > DIGNISSIM > tellus, type : LACUS: magna, vestibulum, #fermentum > CONSEQUAT > fermentum, tempor, fugit : ULTRICES: #posuere > LIBERO > posuere, _#magna > LACUS > magna : LIBERO: posuere, lacrima : ================================================ FILE: test/zoo/inheritance/mld/inheritance_2_mld.md ================================================ - **ALIQUET** (magna, _#tellus_, type!) - Le champ _magna_ fait partie de la clé primaire de la table. Il a migré par l'association de dépendance fonctionnelle _ALIQUET_ à partir de l'entité _TRISTIS_ (supprimée). **Attention** : aucune contrainte d'intégrité référentielle n'est plus assurée. - Le champ _tellus_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _DIGNISSIM_. - Un discriminateur à saisie obligatoire _type_ est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - **CONSEQUAT** (fermentum, dederit) - Le champ _fermentum_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _CONSEQUAT_. - Le champ _dederit_ était déjà un simple attribut de l'entité _CONSEQUAT_. - **CURABITUR** (gravida, amor) - Le champ _gravida_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _CURABITUR_. - Le champ _amor_ était déjà un simple attribut de l'entité _CURABITUR_. - **DIGNISSIM** (tellus, terra) - Le champ _tellus_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _DIGNISSIM_. - Le champ _terra_ était déjà un simple attribut de l'entité _DIGNISSIM_. - **LACUS** (magna, vestibulum, _#fermentum!_, tempor, fugit) - Le champ _magna_ constitue la clé primaire de la table. Il était clé primaire de l'entité-mère _TRISTIS_ (supprimée). - Le champ _vestibulum_ est un simple attribut. Il était simple attribut de l'entité-mère _TRISTIS_ (supprimée). - Le champ à saisie obligatoire _fermentum_ est une clé étrangère. Il a migré à travers l'entité-mère _TRISTIS_ (supprimée), et réfère maintenant directement à l'entité _CONSEQUAT_. - Les champs _tempor_ et _fugit_ étaient déjà de simples attributs de l'entité _LACUS_. - **LIBERO** (posuere, lacrima) - Le champ _posuere_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _LIBERO_. - Le champ _lacrima_ était déjà un simple attribut de l'entité _LIBERO_. - **NEC** (magna, vestibulum, _#fermentum!_, pulvinar, audis, _#gravida!_) - Le champ _magna_ constitue la clé primaire de la table. Il était clé primaire de l'entité-mère _TRISTIS_ (supprimée). - Le champ _vestibulum_ est un simple attribut. Il était simple attribut de l'entité-mère _TRISTIS_ (supprimée). - Le champ à saisie obligatoire _fermentum_ est une clé étrangère. Il a migré à travers l'entité-mère _TRISTIS_ (supprimée), et réfère maintenant directement à l'entité _CONSEQUAT_. - Les champs _pulvinar_ et _audis_ étaient déjà de simples attributs de l'entité _NEC_. - Le champ à saisie obligatoire _gravida_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _MOLLIS_ à partir de l'entité _CURABITUR_ en perdant son caractère identifiant. - **QUAM** (cras, sed, _#magna!_) - Le champ _cras_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _QUAM_. - Le champ _sed_ était déjà un simple attribut de l'entité _QUAM_. - Le champ à saisie obligatoire _magna_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _VITAE_ à partir de l'entité _SODALES_ en perdant son caractère identifiant. - **SODALES** (magna, vestibulum, _#fermentum!_, convallis, ipsum) - Le champ _magna_ constitue la clé primaire de la table. Il était clé primaire de l'entité-mère _TRISTIS_ (supprimée). - Le champ _vestibulum_ est un simple attribut. Il était simple attribut de l'entité-mère _TRISTIS_ (supprimée). - Le champ à saisie obligatoire _fermentum_ est une clé étrangère. Il a migré à travers l'entité-mère _TRISTIS_ (supprimée), et réfère maintenant directement à l'entité _CONSEQUAT_. - Les champs _convallis_ et _ipsum_ étaient déjà de simples attributs de l'entité _SODALES_. - **SUSCIPIT** (orci, lorem, magna!, type!) - Le champ _orci_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _SUSCIPIT_. - Le champ _lorem_ était déjà un simple attribut de l'entité _SUSCIPIT_. - Le champ à saisie obligatoire _magna_ a migré par l'association de dépendance fonctionnelle _RHONCUS_ à partir de l'entité _TRISTIS_ (supprimée). **Attention** : aucune contrainte d'intégrité référentielle n'est plus assurée. - Un discriminateur à saisie obligatoire _type_ est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - **ULTRICES** (_#posuere_, _#magna_) - Le champ _posuere_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _LIBERO_. - Le champ _magna_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _LACUS_. ================================================ FILE: test/zoo/inheritance/mld/inheritance_2_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{inheritance}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{ALIQUET} (\prim{magna}, \foreign{\prim{tellus}}, \attr{type!}) \begin{itemize} \item Le champ \emph{magna} fait partie de la clé primaire de la table. Il a migré par l'association de dépendance fonctionnelle \emph{ALIQUET} à partir de l'entité \emph{TRISTIS} (supprimée). \paragraph{Attention} : aucune contrainte d'intégrité référentielle n'est plus assurée. \item Le champ \emph{tellus} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{DIGNISSIM}. \item Un discriminateur à saisie obligatoire \emph{type} est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. \end{itemize} \item \relat{CONSEQUAT} (\prim{fermentum}, \attr{dederit}) \begin{itemize} \item Le champ \emph{fermentum} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CONSEQUAT}. \item Le champ \emph{dederit} était déjà un simple attribut de l'entité \emph{CONSEQUAT}. \end{itemize} \item \relat{CURABITUR} (\prim{gravida}, \attr{amor}) \begin{itemize} \item Le champ \emph{gravida} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CURABITUR}. \item Le champ \emph{amor} était déjà un simple attribut de l'entité \emph{CURABITUR}. \end{itemize} \item \relat{DIGNISSIM} (\prim{tellus}, \attr{terra}) \begin{itemize} \item Le champ \emph{tellus} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{DIGNISSIM}. \item Le champ \emph{terra} était déjà un simple attribut de l'entité \emph{DIGNISSIM}. \end{itemize} \item \relat{LACUS} (\prim{magna}, \attr{vestibulum}, \foreign{fermentum!}, \attr{tempor}, \attr{fugit}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. Il était clé primaire de l'entité-mère \emph{TRISTIS} (supprimée). \item Le champ \emph{vestibulum} est un simple attribut. Il était simple attribut de l'entité-mère \emph{TRISTIS} (supprimée). \item Le champ à saisie obligatoire \emph{fermentum} est une clé étrangère. Il a migré à travers l'entité-mère \emph{TRISTIS} (supprimée), et réfère maintenant directement à l'entité \emph{CONSEQUAT}. \item Les champs \emph{tempor} et \emph{fugit} étaient déjà de simples attributs de l'entité \emph{LACUS}. \end{itemize} \item \relat{LIBERO} (\prim{posuere}, \attr{lacrima}) \begin{itemize} \item Le champ \emph{posuere} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{LIBERO}. \item Le champ \emph{lacrima} était déjà un simple attribut de l'entité \emph{LIBERO}. \end{itemize} \item \relat{NEC} (\prim{magna}, \attr{vestibulum}, \foreign{fermentum!}, \attr{pulvinar}, \attr{audis}, \foreign{gravida!}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. Il était clé primaire de l'entité-mère \emph{TRISTIS} (supprimée). \item Le champ \emph{vestibulum} est un simple attribut. Il était simple attribut de l'entité-mère \emph{TRISTIS} (supprimée). \item Le champ à saisie obligatoire \emph{fermentum} est une clé étrangère. Il a migré à travers l'entité-mère \emph{TRISTIS} (supprimée), et réfère maintenant directement à l'entité \emph{CONSEQUAT}. \item Les champs \emph{pulvinar} et \emph{audis} étaient déjà de simples attributs de l'entité \emph{NEC}. \item Le champ à saisie obligatoire \emph{gravida} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{MOLLIS} à partir de l'entité \emph{CURABITUR} en perdant son caractère identifiant. \end{itemize} \item \relat{QUAM} (\prim{cras}, \attr{sed}, \foreign{magna!}) \begin{itemize} \item Le champ \emph{cras} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{QUAM}. \item Le champ \emph{sed} était déjà un simple attribut de l'entité \emph{QUAM}. \item Le champ à saisie obligatoire \emph{magna} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{VITAE} à partir de l'entité \emph{SODALES} en perdant son caractère identifiant. \end{itemize} \item \relat{SODALES} (\prim{magna}, \attr{vestibulum}, \foreign{fermentum!}, \attr{convallis}, \attr{ipsum}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. Il était clé primaire de l'entité-mère \emph{TRISTIS} (supprimée). \item Le champ \emph{vestibulum} est un simple attribut. Il était simple attribut de l'entité-mère \emph{TRISTIS} (supprimée). \item Le champ à saisie obligatoire \emph{fermentum} est une clé étrangère. Il a migré à travers l'entité-mère \emph{TRISTIS} (supprimée), et réfère maintenant directement à l'entité \emph{CONSEQUAT}. \item Les champs \emph{convallis} et \emph{ipsum} étaient déjà de simples attributs de l'entité \emph{SODALES}. \end{itemize} \item \relat{SUSCIPIT} (\prim{orci}, \attr{lorem}, \attr{magna!}, \attr{type!}) \begin{itemize} \item Le champ \emph{orci} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{SUSCIPIT}. \item Le champ \emph{lorem} était déjà un simple attribut de l'entité \emph{SUSCIPIT}. \item Le champ à saisie obligatoire \emph{magna} a migré par l'association de dépendance fonctionnelle \emph{RHONCUS} à partir de l'entité \emph{TRISTIS} (supprimée). \paragraph{Attention} : aucune contrainte d'intégrité référentielle n'est plus assurée. \item Un discriminateur à saisie obligatoire \emph{type} est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. \end{itemize} \item \relat{ULTRICES} (\foreign{\prim{posuere}}, \foreign{\prim{magna}}) \begin{itemize} \item Le champ \emph{posuere} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{LIBERO}. \item Le champ \emph{magna} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{LACUS}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/inheritance/mld/inheritance_2_mld.txt ================================================ - ALIQUET (_magna_, _#tellus_, type!) - Le champ « magna » fait partie de la clé primaire de la table. Il a migré par l'association de dépendance fonctionnelle « ALIQUET » à partir de l'entité « TRISTIS » (supprimée). Attention : aucune contrainte d'intégrité référentielle n'est plus assurée. - Le champ « tellus » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « DIGNISSIM ». - Un discriminateur à saisie obligatoire « type » est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - CONSEQUAT (_fermentum_, dederit) - Le champ « fermentum » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « CONSEQUAT ». - Le champ « dederit » était déjà un simple attribut de l'entité « CONSEQUAT ». - CURABITUR (_gravida_, amor) - Le champ « gravida » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « CURABITUR ». - Le champ « amor » était déjà un simple attribut de l'entité « CURABITUR ». - DIGNISSIM (_tellus_, terra) - Le champ « tellus » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « DIGNISSIM ». - Le champ « terra » était déjà un simple attribut de l'entité « DIGNISSIM ». - LACUS (_magna_, vestibulum, #fermentum!, tempor, fugit) - Le champ « magna » constitue la clé primaire de la table. Il était clé primaire de l'entité-mère « TRISTIS » (supprimée). - Le champ « vestibulum » est un simple attribut. Il était simple attribut de l'entité-mère « TRISTIS » (supprimée). - Le champ à saisie obligatoire « fermentum » est une clé étrangère. Il a migré à travers l'entité-mère « TRISTIS » (supprimée), et réfère maintenant directement à l'entité « CONSEQUAT ». - Les champs « tempor » et « fugit » étaient déjà de simples attributs de l'entité « LACUS ». - LIBERO (_posuere_, lacrima) - Le champ « posuere » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « LIBERO ». - Le champ « lacrima » était déjà un simple attribut de l'entité « LIBERO ». - NEC (_magna_, vestibulum, #fermentum!, pulvinar, audis, #gravida!) - Le champ « magna » constitue la clé primaire de la table. Il était clé primaire de l'entité-mère « TRISTIS » (supprimée). - Le champ « vestibulum » est un simple attribut. Il était simple attribut de l'entité-mère « TRISTIS » (supprimée). - Le champ à saisie obligatoire « fermentum » est une clé étrangère. Il a migré à travers l'entité-mère « TRISTIS » (supprimée), et réfère maintenant directement à l'entité « CONSEQUAT ». - Les champs « pulvinar » et « audis » étaient déjà de simples attributs de l'entité « NEC ». - Le champ à saisie obligatoire « gravida » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « MOLLIS » à partir de l'entité « CURABITUR » en perdant son caractère identifiant. - QUAM (_cras_, sed, #magna!) - Le champ « cras » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « QUAM ». - Le champ « sed » était déjà un simple attribut de l'entité « QUAM ». - Le champ à saisie obligatoire « magna » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « VITAE » à partir de l'entité « SODALES » en perdant son caractère identifiant. - SODALES (_magna_, vestibulum, #fermentum!, convallis, ipsum) - Le champ « magna » constitue la clé primaire de la table. Il était clé primaire de l'entité-mère « TRISTIS » (supprimée). - Le champ « vestibulum » est un simple attribut. Il était simple attribut de l'entité-mère « TRISTIS » (supprimée). - Le champ à saisie obligatoire « fermentum » est une clé étrangère. Il a migré à travers l'entité-mère « TRISTIS » (supprimée), et réfère maintenant directement à l'entité « CONSEQUAT ». - Les champs « convallis » et « ipsum » étaient déjà de simples attributs de l'entité « SODALES ». - SUSCIPIT (_orci_, lorem, magna!, type!) - Le champ « orci » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « SUSCIPIT ». - Le champ « lorem » était déjà un simple attribut de l'entité « SUSCIPIT ». - Le champ à saisie obligatoire « magna » a migré par l'association de dépendance fonctionnelle « RHONCUS » à partir de l'entité « TRISTIS » (supprimée). Attention : aucune contrainte d'intégrité référentielle n'est plus assurée. - Un discriminateur à saisie obligatoire « type » est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - ULTRICES (_#posuere_, _#magna_) - Le champ « posuere » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « LIBERO ». - Le champ « magna » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « LACUS ». ================================================ FILE: test/zoo/inheritance/mld/inheritance_3_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note TRISTIS magna ! primary_key True TRISTIS vestibulum normal_attribute False TRISTIS type ! deleted_child_discriminator_XT False XT UNSIGNED_INT_PLACEHOLDER TRISTIS convallis ? deleted_child_attribute False SODALES XT TRISTIS ipsum ? deleted_child_attribute False SODALES XT TRISTIS pulvinar ? deleted_child_attribute False NEC XT TRISTIS audis ? deleted_child_attribute False NEC XT TRISTIS magna ? deleted_child_foreign_key False NEC TRISTIS XT via_mollis TRISTIS magna ? deleted_child_foreign_key False NEC TRISTIS XT via_vitae TRISTIS tempor ? deleted_child_attribute False LACUS XT TRISTIS fugit ? deleted_child_attribute False LACUS XT ULTRICES magna ! primary_foreign_key True SODALES TRISTIS ULTRICES sodales ULTRICES magna ! primary_foreign_key True LACUS TRISTIS ULTRICES lacus ================================================ FILE: test/zoo/inheritance/mld/inheritance_3_dependencies.gv ================================================ digraph { node [shape=box] "TRISTIS" -> "TRISTIS" "TRISTIS" -> "ULTRICES" } ================================================ FILE: test/zoo/inheritance/mld/inheritance_3_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD inheritance
    TRISTIS ( magna, vestibulum, type!, convallis?, ipsum?, pulvinar?, audis?, #magna via_mollis?, #magna via_vitae?, tempor?, fugit? )
    • Le champ magna constitue la clé primaire de la table. C'était déjà un identifiant de l'entité TRISTIS.
    • Le champ vestibulum était déjà un simple attribut de l'entité TRISTIS.
    • Un discriminateur à saisie obligatoire type est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité.
    • Le champ à saisie facultative convallis a migré à partir de l'entité-fille SODALES (supprimée).
    • Le champ à saisie facultative ipsum a migré à partir de l'entité-fille SODALES (supprimée).
    • Le champ à saisie facultative pulvinar a migré à partir de l'entité-fille NEC (supprimée).
    • Le champ à saisie facultative audis a migré à partir de l'entité-fille NEC (supprimée).
    • Le champ à saisie facultative magna via_mollis est une clé étrangère. Il a migré à partir de l'entité-fille NEC (supprimée) dans laquelle il avait déjà migré à partir de l'entité TRISTIS.
    • Le champ à saisie facultative magna via_vitae est une clé étrangère. Il a migré à partir de l'entité-fille NEC (supprimée) dans laquelle il avait déjà migré à partir de l'entité TRISTIS.
    • Le champ à saisie facultative tempor a migré à partir de l'entité-fille LACUS (supprimée).
    • Le champ à saisie facultative fugit a migré à partir de l'entité-fille LACUS (supprimée).
    ULTRICES ( #magna sodales, #magna lacus )
    • Les champs magna sodales et magna lacus constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité TRISTIS.
    ================================================ FILE: test/zoo/inheritance/mld/inheritance_3_mld.mcd ================================================ ::: TRISTIS: magna, vestibulum, type, convallis, ipsum, pulvinar, audis, #magna via_mollis > TRISTIS > magna, #magna via_vitae > TRISTIS > magna, tempor, fugit : : ULTRICES: #magna sodales > TRISTIS > magna, _#magna lacus > TRISTIS > magna ::: ================================================ FILE: test/zoo/inheritance/mld/inheritance_3_mld.md ================================================ - **TRISTIS** (magna, vestibulum, type!, convallis?, ipsum?, pulvinar?, audis?, _#magna via_mollis?_, _#magna via_vitae?_, tempor?, fugit?) - Le champ _magna_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _TRISTIS_. - Le champ _vestibulum_ était déjà un simple attribut de l'entité _TRISTIS_. - Un discriminateur à saisie obligatoire _type_ est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - Le champ à saisie facultative _convallis_ a migré à partir de l'entité-fille _SODALES_ (supprimée). - Le champ à saisie facultative _ipsum_ a migré à partir de l'entité-fille _SODALES_ (supprimée). - Le champ à saisie facultative _pulvinar_ a migré à partir de l'entité-fille _NEC_ (supprimée). - Le champ à saisie facultative _audis_ a migré à partir de l'entité-fille _NEC_ (supprimée). - Le champ à saisie facultative _magna via_mollis_ est une clé étrangère. Il a migré à partir de l'entité-fille _NEC_ (supprimée) dans laquelle il avait déjà migré à partir de l'entité _TRISTIS_. - Le champ à saisie facultative _magna via_vitae_ est une clé étrangère. Il a migré à partir de l'entité-fille _NEC_ (supprimée) dans laquelle il avait déjà migré à partir de l'entité _TRISTIS_. - Le champ à saisie facultative _tempor_ a migré à partir de l'entité-fille _LACUS_ (supprimée). - Le champ à saisie facultative _fugit_ a migré à partir de l'entité-fille _LACUS_ (supprimée). - **ULTRICES** (_#magna sodales_, _#magna lacus_) - Les champs _magna sodales_ et _magna lacus_ constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité _TRISTIS_. ================================================ FILE: test/zoo/inheritance/mld/inheritance_3_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{inheritance}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{TRISTIS} (\prim{magna}, \attr{vestibulum}, \attr{type!}, \attr{convallis?}, \attr{ipsum?}, \attr{pulvinar?}, \attr{audis?}, \foreign{magna via\_mollis?}, \foreign{magna via\_vitae?}, \attr{tempor?}, \attr{fugit?}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{TRISTIS}. \item Le champ \emph{vestibulum} était déjà un simple attribut de l'entité \emph{TRISTIS}. \item Un discriminateur à saisie obligatoire \emph{type} est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. \item Le champ à saisie facultative \emph{convallis} a migré à partir de l'entité-fille \emph{SODALES} (supprimée). \item Le champ à saisie facultative \emph{ipsum} a migré à partir de l'entité-fille \emph{SODALES} (supprimée). \item Le champ à saisie facultative \emph{pulvinar} a migré à partir de l'entité-fille \emph{NEC} (supprimée). \item Le champ à saisie facultative \emph{audis} a migré à partir de l'entité-fille \emph{NEC} (supprimée). \item Le champ à saisie facultative \emph{magna via\_mollis} est une clé étrangère. Il a migré à partir de l'entité-fille \emph{NEC} (supprimée) dans laquelle il avait déjà migré à partir de l'entité \emph{TRISTIS}. \item Le champ à saisie facultative \emph{magna via\_vitae} est une clé étrangère. Il a migré à partir de l'entité-fille \emph{NEC} (supprimée) dans laquelle il avait déjà migré à partir de l'entité \emph{TRISTIS}. \item Le champ à saisie facultative \emph{tempor} a migré à partir de l'entité-fille \emph{LACUS} (supprimée). \item Le champ à saisie facultative \emph{fugit} a migré à partir de l'entité-fille \emph{LACUS} (supprimée). \end{itemize} \item \relat{ULTRICES} (\foreign{\prim{magna sodales}}, \foreign{\prim{magna lacus}}) \begin{itemize} \item Les champs \emph{magna sodales} et \emph{magna lacus} constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité \emph{TRISTIS}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/inheritance/mld/inheritance_3_mld.txt ================================================ - TRISTIS (_magna_, vestibulum, type!, convallis?, ipsum?, pulvinar?, audis?, #magna via_mollis?, #magna via_vitae?, tempor?, fugit?) - Le champ « magna » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « TRISTIS ». - Le champ « vestibulum » était déjà un simple attribut de l'entité « TRISTIS ». - Un discriminateur à saisie obligatoire « type » est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - Le champ à saisie facultative « convallis » a migré à partir de l'entité-fille « SODALES » (supprimée). - Le champ à saisie facultative « ipsum » a migré à partir de l'entité-fille « SODALES » (supprimée). - Le champ à saisie facultative « pulvinar » a migré à partir de l'entité-fille « NEC » (supprimée). - Le champ à saisie facultative « audis » a migré à partir de l'entité-fille « NEC » (supprimée). - Le champ à saisie facultative « magna via_mollis » est une clé étrangère. Il a migré à partir de l'entité-fille « NEC » (supprimée) dans laquelle il avait déjà migré à partir de l'entité « TRISTIS ». - Le champ à saisie facultative « magna via_vitae » est une clé étrangère. Il a migré à partir de l'entité-fille « NEC » (supprimée) dans laquelle il avait déjà migré à partir de l'entité « TRISTIS ». - Le champ à saisie facultative « tempor » a migré à partir de l'entité-fille « LACUS » (supprimée). - Le champ à saisie facultative « fugit » a migré à partir de l'entité-fille « LACUS » (supprimée). - ULTRICES (_#magna sodales_, _#magna lacus_) - Les champs « magna sodales » et « magna lacus » constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité « TRISTIS ». ================================================ FILE: test/zoo/inheritance/mld/inheritance_4_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note LACUS magna ! parent_primary_key True TRISTIS TRISTIS XT LACUS tempor normal_attribute False LACUS fugit normal_attribute False NEC magna ! parent_primary_key True TRISTIS TRISTIS XT NEC pulvinar normal_attribute False NEC audis normal_attribute False NEC magna ! foreign_key False LACUS LACUS MOLLIS via_mollis NEC magna ! foreign_key False SODALES SODALES VITAE via_vitae SODALES magna ! parent_primary_key True TRISTIS TRISTIS XT SODALES convallis normal_attribute False SODALES ipsum normal_attribute False TRISTIS magna ! primary_key True TRISTIS vestibulum normal_attribute False TRISTIS type ! deleted_child_discriminator_XT False XT UNSIGNED_INT_PLACEHOLDER ULTRICES magna ! primary_foreign_key True SODALES SODALES ULTRICES sodales ULTRICES magna ! primary_foreign_key True LACUS LACUS ULTRICES lacus ================================================ FILE: test/zoo/inheritance/mld/inheritance_4_dependencies.gv ================================================ digraph { node [shape=box] "TRISTIS" -> "LACUS" "SODALES" -> "NEC" "LACUS" -> "NEC" "TRISTIS" -> "NEC" "TRISTIS" -> "SODALES" "LACUS" -> "ULTRICES" "SODALES" -> "ULTRICES" } ================================================ FILE: test/zoo/inheritance/mld/inheritance_4_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD inheritance
    LACUS ( #magna, tempor, fugit )
    • Le champ magna constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère TRISTIS.
    • Les champs tempor et fugit étaient déjà de simples attributs de l'entité LACUS.
    NEC ( #magna, pulvinar, audis, #magna via_mollis!, #magna via_vitae! )
    • Le champ magna constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère TRISTIS.
    • Les champs pulvinar et audis étaient déjà de simples attributs de l'entité NEC.
    • Le champ à saisie obligatoire magna via_mollis est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle MOLLIS à partir de l'entité LACUS en perdant son caractère identifiant.
    • Le champ à saisie obligatoire magna via_vitae est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle VITAE à partir de l'entité SODALES en perdant son caractère identifiant.
    SODALES ( #magna, convallis, ipsum )
    • Le champ magna constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère TRISTIS.
    • Les champs convallis et ipsum étaient déjà de simples attributs de l'entité SODALES.
    TRISTIS ( magna, vestibulum, type! )
    • Le champ magna constitue la clé primaire de la table. C'était déjà un identifiant de l'entité TRISTIS.
    • Le champ vestibulum était déjà un simple attribut de l'entité TRISTIS.
    • Un discriminateur à saisie obligatoire type est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité.
    ULTRICES ( #magna sodales, #magna lacus )
    • Le champ magna sodales fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité SODALES.
    • Le champ magna lacus fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité LACUS.
    ================================================ FILE: test/zoo/inheritance/mld/inheritance_4_mld.mcd ================================================ %%mocodo ::::: NEC: #magna > TRISTIS > magna, pulvinar, audis, #magna via_mollis > LACUS > magna, #magna via_vitae > SODALES > magna : : LACUS: #magna > TRISTIS > magna, tempor, fugit ::: TRISTIS: magna, vestibulum, type : ::: ULTRICES: #magna sodales > SODALES > magna, _#magna lacus > LACUS > magna : SODALES: #magna > TRISTIS > magna, convallis, ipsum : ================================================ FILE: test/zoo/inheritance/mld/inheritance_4_mld.md ================================================ - **LACUS** (_#magna_, tempor, fugit) - Le champ _magna_ constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère _TRISTIS_. - Les champs _tempor_ et _fugit_ étaient déjà de simples attributs de l'entité _LACUS_. - **NEC** (_#magna_, pulvinar, audis, _#magna via_mollis!_, _#magna via_vitae!_) - Le champ _magna_ constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère _TRISTIS_. - Les champs _pulvinar_ et _audis_ étaient déjà de simples attributs de l'entité _NEC_. - Le champ à saisie obligatoire _magna via_mollis_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _MOLLIS_ à partir de l'entité _LACUS_ en perdant son caractère identifiant. - Le champ à saisie obligatoire _magna via_vitae_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _VITAE_ à partir de l'entité _SODALES_ en perdant son caractère identifiant. - **SODALES** (_#magna_, convallis, ipsum) - Le champ _magna_ constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère _TRISTIS_. - Les champs _convallis_ et _ipsum_ étaient déjà de simples attributs de l'entité _SODALES_. - **TRISTIS** (magna, vestibulum, type!) - Le champ _magna_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _TRISTIS_. - Le champ _vestibulum_ était déjà un simple attribut de l'entité _TRISTIS_. - Un discriminateur à saisie obligatoire _type_ est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - **ULTRICES** (_#magna sodales_, _#magna lacus_) - Le champ _magna sodales_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _SODALES_. - Le champ _magna lacus_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _LACUS_. ================================================ FILE: test/zoo/inheritance/mld/inheritance_4_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{inheritance}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{LACUS} (\foreign{\prim{magna}}, \attr{tempor}, \attr{fugit}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère \emph{TRISTIS}. \item Les champs \emph{tempor} et \emph{fugit} étaient déjà de simples attributs de l'entité \emph{LACUS}. \end{itemize} \item \relat{NEC} (\foreign{\prim{magna}}, \attr{pulvinar}, \attr{audis}, \foreign{magna via\_mollis!}, \foreign{magna via\_vitae!}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère \emph{TRISTIS}. \item Les champs \emph{pulvinar} et \emph{audis} étaient déjà de simples attributs de l'entité \emph{NEC}. \item Le champ à saisie obligatoire \emph{magna via\_mollis} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{MOLLIS} à partir de l'entité \emph{LACUS} en perdant son caractère identifiant. \item Le champ à saisie obligatoire \emph{magna via\_vitae} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{VITAE} à partir de l'entité \emph{SODALES} en perdant son caractère identifiant. \end{itemize} \item \relat{SODALES} (\foreign{\prim{magna}}, \attr{convallis}, \attr{ipsum}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère \emph{TRISTIS}. \item Les champs \emph{convallis} et \emph{ipsum} étaient déjà de simples attributs de l'entité \emph{SODALES}. \end{itemize} \item \relat{TRISTIS} (\prim{magna}, \attr{vestibulum}, \attr{type!}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{TRISTIS}. \item Le champ \emph{vestibulum} était déjà un simple attribut de l'entité \emph{TRISTIS}. \item Un discriminateur à saisie obligatoire \emph{type} est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. \end{itemize} \item \relat{ULTRICES} (\foreign{\prim{magna sodales}}, \foreign{\prim{magna lacus}}) \begin{itemize} \item Le champ \emph{magna sodales} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{SODALES}. \item Le champ \emph{magna lacus} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{LACUS}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/inheritance/mld/inheritance_4_mld.txt ================================================ - LACUS (_#magna_, tempor, fugit) - Le champ « magna » constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère « TRISTIS ». - Les champs « tempor » et « fugit » étaient déjà de simples attributs de l'entité « LACUS ». - NEC (_#magna_, pulvinar, audis, #magna via_mollis!, #magna via_vitae!) - Le champ « magna » constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère « TRISTIS ». - Les champs « pulvinar » et « audis » étaient déjà de simples attributs de l'entité « NEC ». - Le champ à saisie obligatoire « magna via_mollis » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « MOLLIS » à partir de l'entité « LACUS » en perdant son caractère identifiant. - Le champ à saisie obligatoire « magna via_vitae » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « VITAE » à partir de l'entité « SODALES » en perdant son caractère identifiant. - SODALES (_#magna_, convallis, ipsum) - Le champ « magna » constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère « TRISTIS ». - Les champs « convallis » et « ipsum » étaient déjà de simples attributs de l'entité « SODALES ». - TRISTIS (_magna_, vestibulum, type!) - Le champ « magna » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « TRISTIS ». - Le champ « vestibulum » était déjà un simple attribut de l'entité « TRISTIS ». - Un discriminateur à saisie obligatoire « type » est ajouté pour indiquer la nature de la spécialisation. Jamais vide, du fait de la contrainte de totalité. - ULTRICES (_#magna sodales_, _#magna lacus_) - Le champ « magna sodales » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « SODALES ». - Le champ « magna lacus » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « LACUS ». ================================================ FILE: test/zoo/inheritance/mld/inheritance_5_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note LACUS magna ! deleted_parent_primary_key True TRISTIS TRISTIS XT LACUS vestibulum deleted_parent_attribute False TRISTIS XT LACUS tempor normal_attribute False LACUS fugit normal_attribute False NEC magna ! deleted_parent_primary_key True TRISTIS TRISTIS XT NEC vestibulum deleted_parent_attribute False TRISTIS XT NEC pulvinar normal_attribute False NEC audis normal_attribute False NEC magna ! foreign_key False LACUS LACUS MOLLIS via_mollis NEC magna ! foreign_key False SODALES SODALES VITAE via_vitae SODALES magna ! deleted_parent_primary_key True TRISTIS TRISTIS XT SODALES vestibulum deleted_parent_attribute False TRISTIS XT SODALES convallis normal_attribute False SODALES ipsum normal_attribute False ULTRICES magna ! primary_foreign_key True SODALES SODALES ULTRICES sodales ULTRICES magna ! primary_foreign_key True LACUS LACUS ULTRICES lacus ================================================ FILE: test/zoo/inheritance/mld/inheritance_5_dependencies.gv ================================================ digraph { node [shape=box] "SODALES" -> "NEC" "LACUS" -> "NEC" "LACUS" -> "ULTRICES" "SODALES" -> "ULTRICES" } ================================================ FILE: test/zoo/inheritance/mld/inheritance_5_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD inheritance
    LACUS ( magna, vestibulum, tempor, fugit )
    • Le champ magna constitue la clé primaire de la table. Il était clé primaire de l'entité-mère TRISTIS (supprimée).
    • Le champ vestibulum est un simple attribut. Il était simple attribut de l'entité-mère TRISTIS (supprimée).
    • Les champs tempor et fugit étaient déjà de simples attributs de l'entité LACUS.
    NEC ( magna, vestibulum, pulvinar, audis, #magna via_mollis!, #magna via_vitae! )
    • Le champ magna constitue la clé primaire de la table. Il était clé primaire de l'entité-mère TRISTIS (supprimée).
    • Le champ vestibulum est un simple attribut. Il était simple attribut de l'entité-mère TRISTIS (supprimée).
    • Les champs pulvinar et audis étaient déjà de simples attributs de l'entité NEC.
    • Le champ à saisie obligatoire magna via_mollis est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle MOLLIS à partir de l'entité LACUS en perdant son caractère identifiant.
    • Le champ à saisie obligatoire magna via_vitae est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle VITAE à partir de l'entité SODALES en perdant son caractère identifiant.
    SODALES ( magna, vestibulum, convallis, ipsum )
    • Le champ magna constitue la clé primaire de la table. Il était clé primaire de l'entité-mère TRISTIS (supprimée).
    • Le champ vestibulum est un simple attribut. Il était simple attribut de l'entité-mère TRISTIS (supprimée).
    • Les champs convallis et ipsum étaient déjà de simples attributs de l'entité SODALES.
    ULTRICES ( #magna sodales, #magna lacus )
    • Le champ magna sodales fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité SODALES.
    • Le champ magna lacus fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité LACUS.
    ================================================ FILE: test/zoo/inheritance/mld/inheritance_5_mld.mcd ================================================ %%mocodo ::::: NEC: magna, vestibulum, pulvinar, audis, #magna via_mollis > LACUS > magna, #magna via_vitae > SODALES > magna : : LACUS: magna, vestibulum, tempor, fugit ::::: ::: ULTRICES: #magna sodales > SODALES > magna, _#magna lacus > LACUS > magna : SODALES: magna, vestibulum, convallis, ipsum : ================================================ FILE: test/zoo/inheritance/mld/inheritance_5_mld.md ================================================ - **LACUS** (magna, vestibulum, tempor, fugit) - Le champ _magna_ constitue la clé primaire de la table. Il était clé primaire de l'entité-mère _TRISTIS_ (supprimée). - Le champ _vestibulum_ est un simple attribut. Il était simple attribut de l'entité-mère _TRISTIS_ (supprimée). - Les champs _tempor_ et _fugit_ étaient déjà de simples attributs de l'entité _LACUS_. - **NEC** (magna, vestibulum, pulvinar, audis, _#magna via_mollis!_, _#magna via_vitae!_) - Le champ _magna_ constitue la clé primaire de la table. Il était clé primaire de l'entité-mère _TRISTIS_ (supprimée). - Le champ _vestibulum_ est un simple attribut. Il était simple attribut de l'entité-mère _TRISTIS_ (supprimée). - Les champs _pulvinar_ et _audis_ étaient déjà de simples attributs de l'entité _NEC_. - Le champ à saisie obligatoire _magna via_mollis_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _MOLLIS_ à partir de l'entité _LACUS_ en perdant son caractère identifiant. - Le champ à saisie obligatoire _magna via_vitae_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _VITAE_ à partir de l'entité _SODALES_ en perdant son caractère identifiant. - **SODALES** (magna, vestibulum, convallis, ipsum) - Le champ _magna_ constitue la clé primaire de la table. Il était clé primaire de l'entité-mère _TRISTIS_ (supprimée). - Le champ _vestibulum_ est un simple attribut. Il était simple attribut de l'entité-mère _TRISTIS_ (supprimée). - Les champs _convallis_ et _ipsum_ étaient déjà de simples attributs de l'entité _SODALES_. - **ULTRICES** (_#magna sodales_, _#magna lacus_) - Le champ _magna sodales_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _SODALES_. - Le champ _magna lacus_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _LACUS_. ================================================ FILE: test/zoo/inheritance/mld/inheritance_5_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{inheritance}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{LACUS} (\prim{magna}, \attr{vestibulum}, \attr{tempor}, \attr{fugit}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. Il était clé primaire de l'entité-mère \emph{TRISTIS} (supprimée). \item Le champ \emph{vestibulum} est un simple attribut. Il était simple attribut de l'entité-mère \emph{TRISTIS} (supprimée). \item Les champs \emph{tempor} et \emph{fugit} étaient déjà de simples attributs de l'entité \emph{LACUS}. \end{itemize} \item \relat{NEC} (\prim{magna}, \attr{vestibulum}, \attr{pulvinar}, \attr{audis}, \foreign{magna via\_mollis!}, \foreign{magna via\_vitae!}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. Il était clé primaire de l'entité-mère \emph{TRISTIS} (supprimée). \item Le champ \emph{vestibulum} est un simple attribut. Il était simple attribut de l'entité-mère \emph{TRISTIS} (supprimée). \item Les champs \emph{pulvinar} et \emph{audis} étaient déjà de simples attributs de l'entité \emph{NEC}. \item Le champ à saisie obligatoire \emph{magna via\_mollis} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{MOLLIS} à partir de l'entité \emph{LACUS} en perdant son caractère identifiant. \item Le champ à saisie obligatoire \emph{magna via\_vitae} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{VITAE} à partir de l'entité \emph{SODALES} en perdant son caractère identifiant. \end{itemize} \item \relat{SODALES} (\prim{magna}, \attr{vestibulum}, \attr{convallis}, \attr{ipsum}) \begin{itemize} \item Le champ \emph{magna} constitue la clé primaire de la table. Il était clé primaire de l'entité-mère \emph{TRISTIS} (supprimée). \item Le champ \emph{vestibulum} est un simple attribut. Il était simple attribut de l'entité-mère \emph{TRISTIS} (supprimée). \item Les champs \emph{convallis} et \emph{ipsum} étaient déjà de simples attributs de l'entité \emph{SODALES}. \end{itemize} \item \relat{ULTRICES} (\foreign{\prim{magna sodales}}, \foreign{\prim{magna lacus}}) \begin{itemize} \item Le champ \emph{magna sodales} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{SODALES}. \item Le champ \emph{magna lacus} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{LACUS}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/inheritance/mld/inheritance_5_mld.txt ================================================ - LACUS (_magna_, vestibulum, tempor, fugit) - Le champ « magna » constitue la clé primaire de la table. Il était clé primaire de l'entité-mère « TRISTIS » (supprimée). - Le champ « vestibulum » est un simple attribut. Il était simple attribut de l'entité-mère « TRISTIS » (supprimée). - Les champs « tempor » et « fugit » étaient déjà de simples attributs de l'entité « LACUS ». - NEC (_magna_, vestibulum, pulvinar, audis, #magna via_mollis!, #magna via_vitae!) - Le champ « magna » constitue la clé primaire de la table. Il était clé primaire de l'entité-mère « TRISTIS » (supprimée). - Le champ « vestibulum » est un simple attribut. Il était simple attribut de l'entité-mère « TRISTIS » (supprimée). - Les champs « pulvinar » et « audis » étaient déjà de simples attributs de l'entité « NEC ». - Le champ à saisie obligatoire « magna via_mollis » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « MOLLIS » à partir de l'entité « LACUS » en perdant son caractère identifiant. - Le champ à saisie obligatoire « magna via_vitae » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « VITAE » à partir de l'entité « SODALES » en perdant son caractère identifiant. - SODALES (_magna_, vestibulum, convallis, ipsum) - Le champ « magna » constitue la clé primaire de la table. Il était clé primaire de l'entité-mère « TRISTIS » (supprimée). - Le champ « vestibulum » est un simple attribut. Il était simple attribut de l'entité-mère « TRISTIS » (supprimée). - Les champs « convallis » et « ipsum » étaient déjà de simples attributs de l'entité « SODALES ». - ULTRICES (_#magna sodales_, _#magna lacus_) - Le champ « magna sodales » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « SODALES ». - Le champ « magna lacus » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « LACUS ». ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_0_rw_create_df_arrows=across.mcd ================================================ SUSCIPIT: orci, lorem RHONCUS, 1N> TRISTIS, 11 SUSCIPIT : : SODALES: convallis, ipsum VITAE, 11 QUAM, 1N> SODALES QUAM: cras, sed CONSEQUAT: fermentum, dederit ELIT, 11 TRISTIS, 1N> CONSEQUAT TRISTIS: magna, vestibulum /XT\ TRISTIS <- SODALES, NEC, LACUS: type NEC: pulvinar, audis MOLLIS, 1N> CURABITUR, 11 NEC CURABITUR: gravida, amor DIGNISSIM: tellus, terra ALIQUET, 1N TRISTIS, 1N DIGNISSIM : : LACUS: tempor, fugit ULTRICES, 1N LIBERO, 1N LACUS LIBERO: posuere, lacrima ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_0_rw_drown.mcd ================================================ ENTITÉ 01_: at 01 1, at 01 2 ASSOC 11_, 1N ENTITÉ 05_, 11 ENTITÉ 01_ : : ENTITÉ 02_: at 02 1, at 02 2 ASSOC 12_, 11 ENTITÉ 03_, 1N ENTITÉ 02_ ENTITÉ 03_: at 03 1, at 03 2 ENTITÉ 04_: at 04 1, at 04 2 ASSOC 13_, 11 ENTITÉ 05_, 1N ENTITÉ 04_ ENTITÉ 05_: at 05 1, at 05 2 /XT\ ENTITÉ 05_ <- ENTITÉ 02_, ENTITÉ 06_, ENTITÉ 09_: type ENTITÉ 06_: at 06 1, at 06 2 ASSOC 14_, 1N ENTITÉ 07_, 11 ENTITÉ 06_ ENTITÉ 07_: at 07 1, at 07 2 ENTITÉ 08_: at 08 1, at 08 2 ASSOC 15_, 1N ENTITÉ 05_, 1N ENTITÉ 08_ : : ENTITÉ 09_: at 09 1, at 09 2 ASSOC 16_, 1N ENTITÉ 10_, 1N ENTITÉ 09_ ENTITÉ 10_: at 10 1, at 10 2 ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_0_rw_explode_arity=2,weak.mcd ================================================ LIBERO: posuere, lacrima : : : : : : DF, _11 ULTRICES, 1N LIBERO : MOLLIS, 1N CURABITUR, 11 NEC CURABITUR: gravida, amor : : : ULTRICES: DF, _11 ULTRICES, 1N LACUS NEC: pulvinar, audis RHONCUS, 1N TRISTIS, 11 SUSCIPIT SUSCIPIT: orci, lorem : : : LACUS: tempor, fugit /XT\ TRISTIS <- SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum DF, _11 ALIQUET, 1N TRISTIS ALIQUET: DF, _11 ALIQUET, 1N DIGNISSIM : : SODALES: convallis, ipsum ELIT, 11 TRISTIS, 1N CONSEQUAT CONSEQUAT: fermentum, dederit : DIGNISSIM: tellus, terra : QUAM: cras, sed VITAE, 11 QUAM, 1N SODALES : : : : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_0_rw_explode_arity=2.mcd ================================================ LIBERO: posuere, lacrima : : : : : : DF, 11 ULTRICES, 1N LIBERO : MOLLIS, 1N CURABITUR, 11 NEC CURABITUR: gravida, amor : : : ULTRICES: id. ultrices DF, 11 ULTRICES, 1N LACUS NEC: pulvinar, audis RHONCUS, 1N TRISTIS, 11 SUSCIPIT SUSCIPIT: orci, lorem : : : LACUS: tempor, fugit /XT\ TRISTIS <- SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum DF, 11 ALIQUET, 1N TRISTIS ALIQUET: id. aliquet DF, 11 ALIQUET, 1N DIGNISSIM : : SODALES: convallis, ipsum ELIT, 11 TRISTIS, 1N CONSEQUAT CONSEQUAT: fermentum, dederit : DIGNISSIM: tellus, terra : QUAM: cras, sed VITAE, 11 QUAM, 1N SODALES : : : : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_1_rw_create_df_arrows=across.mcd ================================================ SUSCIPIT: orci, lorem RHONCUS, 1N> TRISTIS, 11 SUSCIPIT : : SODALES: convallis, ipsum VITAE, 11 QUAM, 1N> SODALES QUAM: cras, sed CONSEQUAT: fermentum, dederit ELIT, 11 TRISTIS, 1N> CONSEQUAT TRISTIS: magna, vestibulum /XT\ TRISTIS -> SODALES, NEC, LACUS: type NEC: pulvinar, audis MOLLIS, 1N> CURABITUR, 11 NEC CURABITUR: gravida, amor DIGNISSIM: tellus, terra ALIQUET, 1N TRISTIS, 1N DIGNISSIM : : LACUS: tempor, fugit ULTRICES, 1N LIBERO, 1N LACUS LIBERO: posuere, lacrima ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_1_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_1_rw_drown.mcd ================================================ ENTITÉ 01_: at 01 1, at 01 2 ASSOC 11_, 1N ENTITÉ 05_, 11 ENTITÉ 01_ : : ENTITÉ 02_: at 02 1, at 02 2 ASSOC 12_, 11 ENTITÉ 03_, 1N ENTITÉ 02_ ENTITÉ 03_: at 03 1, at 03 2 ENTITÉ 04_: at 04 1, at 04 2 ASSOC 13_, 11 ENTITÉ 05_, 1N ENTITÉ 04_ ENTITÉ 05_: at 05 1, at 05 2 /XT\ ENTITÉ 05_ -> ENTITÉ 02_, ENTITÉ 06_, ENTITÉ 09_: type ENTITÉ 06_: at 06 1, at 06 2 ASSOC 14_, 1N ENTITÉ 07_, 11 ENTITÉ 06_ ENTITÉ 07_: at 07 1, at 07 2 ENTITÉ 08_: at 08 1, at 08 2 ASSOC 15_, 1N ENTITÉ 05_, 1N ENTITÉ 08_ : : ENTITÉ 09_: at 09 1, at 09 2 ASSOC 16_, 1N ENTITÉ 10_, 1N ENTITÉ 09_ ENTITÉ 10_: at 10 1, at 10 2 ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_1_rw_explode_arity=2,weak.mcd ================================================ LIBERO: posuere, lacrima : : : : : : DF, _11 ULTRICES, 1N LIBERO : MOLLIS, 1N CURABITUR, 11 NEC CURABITUR: gravida, amor : : : ULTRICES: DF, _11 ULTRICES, 1N LACUS NEC: pulvinar, audis RHONCUS, 1N TRISTIS, 11 SUSCIPIT SUSCIPIT: orci, lorem : : : LACUS: tempor, fugit /XT\ TRISTIS -> SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum DF, _11 ALIQUET, 1N TRISTIS ALIQUET: DF, _11 ALIQUET, 1N DIGNISSIM : : SODALES: convallis, ipsum ELIT, 11 TRISTIS, 1N CONSEQUAT CONSEQUAT: fermentum, dederit : DIGNISSIM: tellus, terra : QUAM: cras, sed VITAE, 11 QUAM, 1N SODALES : : : : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_1_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_1_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_1_rw_explode_arity=2.mcd ================================================ LIBERO: posuere, lacrima : : : : : : DF, 11 ULTRICES, 1N LIBERO : MOLLIS, 1N CURABITUR, 11 NEC CURABITUR: gravida, amor : : : ULTRICES: id. ultrices DF, 11 ULTRICES, 1N LACUS NEC: pulvinar, audis RHONCUS, 1N TRISTIS, 11 SUSCIPIT SUSCIPIT: orci, lorem : : : LACUS: tempor, fugit /XT\ TRISTIS -> SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum DF, 11 ALIQUET, 1N TRISTIS ALIQUET: id. aliquet DF, 11 ALIQUET, 1N DIGNISSIM : : SODALES: convallis, ipsum ELIT, 11 TRISTIS, 1N CONSEQUAT CONSEQUAT: fermentum, dederit : DIGNISSIM: tellus, terra : QUAM: cras, sed VITAE, 11 QUAM, 1N SODALES : : : : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_1_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_1_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_1_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_2_rw_create_df_arrows=across.mcd ================================================ SUSCIPIT: orci, lorem RHONCUS, 1N> TRISTIS, 11 SUSCIPIT : : SODALES: convallis, ipsum VITAE, 11 QUAM, 1N> SODALES QUAM: cras, sed CONSEQUAT: fermentum, dederit ELIT, 11 TRISTIS, 1N> CONSEQUAT TRISTIS: magna, vestibulum /XT\ TRISTIS => SODALES, NEC, LACUS: type NEC: pulvinar, audis MOLLIS, 1N> CURABITUR, 11 NEC CURABITUR: gravida, amor DIGNISSIM: tellus, terra ALIQUET, 1N TRISTIS, 1N DIGNISSIM : : LACUS: tempor, fugit ULTRICES, 1N LIBERO, 1N LACUS LIBERO: posuere, lacrima ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_2_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_2_rw_drown.mcd ================================================ ENTITÉ 01_: at 01 1, at 01 2 ASSOC 11_, 1N ENTITÉ 05_, 11 ENTITÉ 01_ : : ENTITÉ 02_: at 02 1, at 02 2 ASSOC 12_, 11 ENTITÉ 03_, 1N ENTITÉ 02_ ENTITÉ 03_: at 03 1, at 03 2 ENTITÉ 04_: at 04 1, at 04 2 ASSOC 13_, 11 ENTITÉ 05_, 1N ENTITÉ 04_ ENTITÉ 05_: at 05 1, at 05 2 /XT\ ENTITÉ 05_ => ENTITÉ 02_, ENTITÉ 06_, ENTITÉ 09_: type ENTITÉ 06_: at 06 1, at 06 2 ASSOC 14_, 1N ENTITÉ 07_, 11 ENTITÉ 06_ ENTITÉ 07_: at 07 1, at 07 2 ENTITÉ 08_: at 08 1, at 08 2 ASSOC 15_, 1N ENTITÉ 05_, 1N ENTITÉ 08_ : : ENTITÉ 09_: at 09 1, at 09 2 ASSOC 16_, 1N ENTITÉ 10_, 1N ENTITÉ 09_ ENTITÉ 10_: at 10 1, at 10 2 ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_2_rw_explode_arity=2,weak.mcd ================================================ LIBERO: posuere, lacrima : : : : : : DF, _11 ULTRICES, 1N LIBERO : MOLLIS, 1N CURABITUR, 11 NEC CURABITUR: gravida, amor : : : ULTRICES: DF, _11 ULTRICES, 1N LACUS NEC: pulvinar, audis RHONCUS, 1N TRISTIS, 11 SUSCIPIT SUSCIPIT: orci, lorem : : : LACUS: tempor, fugit /XT\ TRISTIS => SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum DF, _11 ALIQUET, 1N TRISTIS ALIQUET: DF, _11 ALIQUET, 1N DIGNISSIM : : SODALES: convallis, ipsum ELIT, 11 TRISTIS, 1N CONSEQUAT CONSEQUAT: fermentum, dederit : DIGNISSIM: tellus, terra : QUAM: cras, sed VITAE, 11 QUAM, 1N SODALES : : : : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_2_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_2_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_2_rw_explode_arity=2.mcd ================================================ LIBERO: posuere, lacrima : : : : : : DF, 11 ULTRICES, 1N LIBERO : MOLLIS, 1N CURABITUR, 11 NEC CURABITUR: gravida, amor : : : ULTRICES: id. ultrices DF, 11 ULTRICES, 1N LACUS NEC: pulvinar, audis RHONCUS, 1N TRISTIS, 11 SUSCIPIT SUSCIPIT: orci, lorem : : : LACUS: tempor, fugit /XT\ TRISTIS => SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum DF, 11 ALIQUET, 1N TRISTIS ALIQUET: id. aliquet DF, 11 ALIQUET, 1N DIGNISSIM : : SODALES: convallis, ipsum ELIT, 11 TRISTIS, 1N CONSEQUAT CONSEQUAT: fermentum, dederit : DIGNISSIM: tellus, terra : QUAM: cras, sed VITAE, 11 QUAM, 1N SODALES : : : : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_2_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_2_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_2_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_3_rw_create_df_arrows=across.mcd ================================================ : MOLLIS, 1N> [via_mollis] LACUS, 11 NEC NEC: pulvinar, audis : LACUS: tempor, fugit /XT\ TRISTIS <- SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum VITAE, 11 NEC, 1N> [via_vitae] SODALES : ULTRICES, 1N [sodales] SODALES, 1N [lacus] LACUS SODALES: convallis, ipsum : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_3_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_3_rw_drown.mcd ================================================ : ASSOC 5_, 1N [rôle 1] ENTITÉ 2_, 11 ENTITÉ 1_ ENTITÉ 1_: at 1 1, at 1 2 : ENTITÉ 2_: at 2 1, at 2 2 /XT\ ENTITÉ 3_ <- ENTITÉ 4_, ENTITÉ 1_, ENTITÉ 2_: type ENTITÉ 3_: at 3 1, at 3 2 ASSOC 6_, 11 ENTITÉ 1_, 1N [rôle 1] ENTITÉ 4_ : ASSOC 7_, 1N [rôle 1] ENTITÉ 4_, 1N [rôle 2] ENTITÉ 2_ ENTITÉ 4_: at 4 1, at 4 2 : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_3_rw_explode_arity=2,weak.mcd ================================================ : DF, _11 ULTRICES, 1N [sodales] SODALES SODALES: convallis, ipsum VITAE, 11 NEC, 1N [via_vitae] SODALES ULTRICES: TRISTIS: magna, vestibulum /XT\ TRISTIS <- SODALES, NEC, LACUS: type NEC: pulvinar, audis : DF, _11 ULTRICES, 1N [lacus] LACUS LACUS: tempor, fugit MOLLIS, 1N [via_mollis] LACUS, 11 NEC ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_3_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_3_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_3_rw_explode_arity=2.mcd ================================================ : DF, 11 ULTRICES, 1N [sodales] SODALES SODALES: convallis, ipsum VITAE, 11 NEC, 1N [via_vitae] SODALES ULTRICES: id. ultrices TRISTIS: magna, vestibulum /XT\ TRISTIS <- SODALES, NEC, LACUS: type NEC: pulvinar, audis : DF, 11 ULTRICES, 1N [lacus] LACUS LACUS: tempor, fugit MOLLIS, 1N [via_mollis] LACUS, 11 NEC ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_3_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_3_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_3_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_4_rw_create_df_arrows=across.mcd ================================================ : MOLLIS, 1N> [via_mollis] LACUS, 11 NEC NEC: pulvinar, audis : LACUS: tempor, fugit /XT\ TRISTIS -> SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum VITAE, 11 NEC, 1N> [via_vitae] SODALES : ULTRICES, 1N [sodales] SODALES, 1N [lacus] LACUS SODALES: convallis, ipsum : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_4_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_4_rw_drown.mcd ================================================ : ASSOC 5_, 1N [rôle 1] ENTITÉ 2_, 11 ENTITÉ 1_ ENTITÉ 1_: at 1 1, at 1 2 : ENTITÉ 2_: at 2 1, at 2 2 /XT\ ENTITÉ 3_ -> ENTITÉ 4_, ENTITÉ 1_, ENTITÉ 2_: type ENTITÉ 3_: at 3 1, at 3 2 ASSOC 6_, 11 ENTITÉ 1_, 1N [rôle 1] ENTITÉ 4_ : ASSOC 7_, 1N [rôle 1] ENTITÉ 4_, 1N [rôle 2] ENTITÉ 2_ ENTITÉ 4_: at 4 1, at 4 2 : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_4_rw_explode_arity=2,weak.mcd ================================================ : DF, _11 ULTRICES, 1N [sodales] SODALES SODALES: convallis, ipsum VITAE, 11 NEC, 1N [via_vitae] SODALES ULTRICES: TRISTIS: magna, vestibulum /XT\ TRISTIS -> SODALES, NEC, LACUS: type NEC: pulvinar, audis : DF, _11 ULTRICES, 1N [lacus] LACUS LACUS: tempor, fugit MOLLIS, 1N [via_mollis] LACUS, 11 NEC ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_4_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_4_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_4_rw_explode_arity=2.mcd ================================================ : DF, 11 ULTRICES, 1N [sodales] SODALES SODALES: convallis, ipsum VITAE, 11 NEC, 1N [via_vitae] SODALES ULTRICES: id. ultrices TRISTIS: magna, vestibulum /XT\ TRISTIS -> SODALES, NEC, LACUS: type NEC: pulvinar, audis : DF, 11 ULTRICES, 1N [lacus] LACUS LACUS: tempor, fugit MOLLIS, 1N [via_mollis] LACUS, 11 NEC ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_4_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_4_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_4_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_5_rw_create_df_arrows=across.mcd ================================================ : MOLLIS, 1N> [via_mollis] LACUS, 11 NEC NEC: pulvinar, audis : LACUS: tempor, fugit /XT\ TRISTIS => SODALES, NEC, LACUS: type TRISTIS: magna, vestibulum VITAE, 11 NEC, 1N> [via_vitae] SODALES : ULTRICES, 1N [sodales] SODALES, 1N [lacus] LACUS SODALES: convallis, ipsum : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_5_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_5_rw_drown.mcd ================================================ : ASSOC 5_, 1N [rôle 1] ENTITÉ 2_, 11 ENTITÉ 1_ ENTITÉ 1_: at 1 1, at 1 2 : ENTITÉ 2_: at 2 1, at 2 2 /XT\ ENTITÉ 3_ => ENTITÉ 4_, ENTITÉ 1_, ENTITÉ 2_: type ENTITÉ 3_: at 3 1, at 3 2 ASSOC 6_, 11 ENTITÉ 1_, 1N [rôle 1] ENTITÉ 4_ : ASSOC 7_, 1N [rôle 1] ENTITÉ 4_, 1N [rôle 2] ENTITÉ 2_ ENTITÉ 4_: at 4 1, at 4 2 : ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_5_rw_explode_arity=2,weak.mcd ================================================ : DF, _11 ULTRICES, 1N [sodales] SODALES SODALES: convallis, ipsum VITAE, 11 NEC, 1N [via_vitae] SODALES ULTRICES: TRISTIS: magna, vestibulum /XT\ TRISTIS => SODALES, NEC, LACUS: type NEC: pulvinar, audis : DF, _11 ULTRICES, 1N [lacus] LACUS LACUS: tempor, fugit MOLLIS, 1N [via_mollis] LACUS, 11 NEC ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_5_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_5_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_5_rw_explode_arity=2.mcd ================================================ : DF, 11 ULTRICES, 1N [sodales] SODALES SODALES: convallis, ipsum VITAE, 11 NEC, 1N [via_vitae] SODALES ULTRICES: id. ultrices TRISTIS: magna, vestibulum /XT\ TRISTIS => SODALES, NEC, LACUS: type NEC: pulvinar, audis : DF, 11 ULTRICES, 1N [lacus] LACUS LACUS: tempor, fugit MOLLIS, 1N [via_mollis] LACUS, 11 NEC ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_5_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_5_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance/rewritten/inheritance_5_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/_strong_child_0.mcd ================================================ % https://github.com/laowantong/mocodo/issues/118 % Class table inheritance: all entities are preserved DF, _11 CONTRAT, 1N VACATAIRE VACATAIRE: statut vacataire CONTRAT: date contrat, salaire horaire contrat /XT\ PROFESSEUR -> VACATAIRE, SALARIÉ SALARIÉ: date embauche salarié, échelon salarié, salaire salarié PROFESSEUR: num prof, nom prof, prénom prof, téléphone prof ================================================ FILE: test/zoo/inheritance_weak/_strong_child_1.mcd ================================================ % https://github.com/laowantong/mocodo/issues/118 % Single table inheritance: children are merged into the parent table DF, _11 CONTRAT, 1N VACATAIRE VACATAIRE: statut vacataire CONTRAT: date contrat, salaire horaire contrat /XT\ PROFESSEUR <- VACATAIRE, SALARIÉ SALARIÉ: date embauche salarié, échelon salarié, salaire salarié PROFESSEUR: num prof, nom prof, prénom prof, téléphone prof ================================================ FILE: test/zoo/inheritance_weak/_strong_child_2.mcd ================================================ % https://github.com/laowantong/mocodo/issues/118 % Concrete table inheritance: parent disappears DF, _11 CONTRAT, 1N VACATAIRE VACATAIRE: statut vacataire CONTRAT: date contrat, salaire horaire contrat /XT\ PROFESSEUR => VACATAIRE, SALARIÉ SALARIÉ: date embauche salarié, échelon salarié, salaire salarié PROFESSEUR: num prof, nom prof, prénom prof, téléphone prof ================================================ FILE: test/zoo/inheritance_weak/ddl/strong_child_0_ddl.d2 ================================================ "CONTRAT": { shape: sql_table "num prof": VARCHAR(42) {constraint: [PK; FK]} "date contrat": VARCHAR(42) {constraint: PK} "salaire horaire contrat": VARCHAR(42) } "PROFESSEUR": { shape: sql_table "num prof": VARCHAR(42) {constraint: PK} "nom prof": VARCHAR(42) "prénom prof": VARCHAR(42) "téléphone prof": VARCHAR(42) } "SALARIÉ": { shape: sql_table "num prof": VARCHAR(42) {constraint: [PK; FK]} "date embauche salarié": VARCHAR(42) "échelon salarié": VARCHAR(42) "salaire salarié": VARCHAR(42) } "VACATAIRE": { shape: sql_table "num prof": VARCHAR(42) {constraint: [PK; FK]} "statut vacataire": VARCHAR(42) } "CONTRAT"."num prof" -> "VACATAIRE"."num prof" "SALARIÉ"."num prof" -> "PROFESSEUR"."num prof" "VACATAIRE"."num prof" -> "PROFESSEUR"."num prof" ================================================ FILE: test/zoo/inheritance_weak/ddl/strong_child_0_ddl.dbml ================================================ Table "CONTRAT" { "num prof" VARCHAR(42) [NOT NULL] "date contrat" VARCHAR(42) [NOT NULL] "salaire horaire contrat" VARCHAR(42) Indexes { ("num prof", "date contrat") [pk] } } Table "PROFESSEUR" { "num prof" VARCHAR(42) [pk, NOT NULL] "nom prof" VARCHAR(42) "prénom prof" VARCHAR(42) "téléphone prof" VARCHAR(42) } Table "SALARIÉ" { "num prof" VARCHAR(42) [pk, NOT NULL] "date embauche salarié" VARCHAR(42) "échelon salarié" VARCHAR(42) "salaire salarié" VARCHAR(42) } Table "VACATAIRE" { "num prof" VARCHAR(42) [pk, NOT NULL] "statut vacataire" VARCHAR(42) } Ref:"CONTRAT"."num prof" > "VACATAIRE"."num prof" Ref:"SALARIÉ"."num prof" > "PROFESSEUR"."num prof" Ref:"VACATAIRE"."num prof" > "PROFESSEUR"."num prof" ================================================ FILE: test/zoo/inheritance_weak/ddl/strong_child_0_ddl.sql ================================================ CREATE TABLE CONTRAT ( PRIMARY KEY (num_prof, date_contrat), num_prof VARCHAR(8) NOT NULL, date_contrat DATE NOT NULL, salaire_horaire_contrat VARCHAR(42) ); CREATE TABLE PROFESSEUR ( PRIMARY KEY (num_prof), num_prof VARCHAR(8) NOT NULL, nom_prof VARCHAR(255), prenom_prof VARCHAR(255), telephone_prof VARCHAR(20) ); CREATE TABLE SALARIE ( PRIMARY KEY (num_prof), num_prof VARCHAR(8) NOT NULL, date_embauche_salarie DATE, echelon_salarie VARCHAR(42), salaire_salarie VARCHAR(42) ); CREATE TABLE VACATAIRE ( PRIMARY KEY (num_prof), num_prof VARCHAR(8) NOT NULL, statut_vacataire VARCHAR(20) ); ALTER TABLE CONTRAT ADD FOREIGN KEY (num_prof) REFERENCES VACATAIRE (num_prof); ALTER TABLE SALARIE ADD FOREIGN KEY (num_prof) REFERENCES PROFESSEUR (num_prof); ALTER TABLE VACATAIRE ADD FOREIGN KEY (num_prof) REFERENCES PROFESSEUR (num_prof); ================================================ FILE: test/zoo/inheritance_weak/ddl/strong_child_1_ddl.d2 ================================================ "CONTRAT": { shape: sql_table "num prof": VARCHAR(42) {constraint: [PK; FK]} "date contrat": VARCHAR(42) {constraint: PK} "salaire horaire contrat": VARCHAR(42) } "PROFESSEUR": { shape: sql_table "num prof": VARCHAR(42) {constraint: PK} "nom prof": VARCHAR(42) "prénom prof": VARCHAR(42) "téléphone prof": VARCHAR(42) "statut vacataire": VARCHAR(42) {constraint: "NULL"} "date embauche salarié": VARCHAR(42) {constraint: "NULL"} "échelon salarié": VARCHAR(42) {constraint: "NULL"} "salaire salarié": VARCHAR(42) {constraint: "NULL"} } "CONTRAT"."num prof" -> "PROFESSEUR"."num prof" ================================================ FILE: test/zoo/inheritance_weak/ddl/strong_child_1_ddl.dbml ================================================ Table "CONTRAT" { "num prof" VARCHAR(42) [NOT NULL] "date contrat" VARCHAR(42) [NOT NULL] "salaire horaire contrat" VARCHAR(42) Indexes { ("num prof", "date contrat") [pk] } } Table "PROFESSEUR" { "num prof" VARCHAR(42) [pk, NOT NULL] "nom prof" VARCHAR(42) "prénom prof" VARCHAR(42) "téléphone prof" VARCHAR(42) "statut vacataire" VARCHAR(42) ["NULL"] "date embauche salarié" VARCHAR(42) ["NULL"] "échelon salarié" VARCHAR(42) ["NULL"] "salaire salarié" VARCHAR(42) ["NULL"] } Ref:"CONTRAT"."num prof" > "PROFESSEUR"."num prof" ================================================ FILE: test/zoo/inheritance_weak/ddl/strong_child_1_ddl.sql ================================================ CREATE TABLE CONTRAT ( PRIMARY KEY (num_prof, date_contrat), num_prof VARCHAR(8) NOT NULL, date_contrat DATE NOT NULL, salaire_horaire_contrat VARCHAR(42) ); CREATE TABLE PROFESSEUR ( PRIMARY KEY (num_prof), num_prof VARCHAR(8) NOT NULL, nom_prof VARCHAR(255), prenom_prof VARCHAR(255), telephone_prof VARCHAR(20), statut_vacataire VARCHAR(20) NULL, date_embauche_salarie DATE NULL, echelon_salarie VARCHAR(42) NULL, salaire_salarie VARCHAR(42) NULL ); ALTER TABLE CONTRAT ADD FOREIGN KEY (num_prof) REFERENCES PROFESSEUR (num_prof); ================================================ FILE: test/zoo/inheritance_weak/ddl/strong_child_2_ddl.d2 ================================================ "CONTRAT": { shape: sql_table "num prof": VARCHAR(42) {constraint: [PK; FK]} "date contrat": VARCHAR(42) {constraint: PK} "salaire horaire contrat": VARCHAR(42) } "SALARIÉ": { shape: sql_table "num prof": VARCHAR(42) {constraint: PK} "nom prof": VARCHAR(42) "prénom prof": VARCHAR(42) "téléphone prof": VARCHAR(42) "date embauche salarié": VARCHAR(42) "échelon salarié": VARCHAR(42) "salaire salarié": VARCHAR(42) } "VACATAIRE": { shape: sql_table "num prof": VARCHAR(42) {constraint: PK} "nom prof": VARCHAR(42) "prénom prof": VARCHAR(42) "téléphone prof": VARCHAR(42) "statut vacataire": VARCHAR(42) } "CONTRAT"."num prof" -> "VACATAIRE"."num prof" ================================================ FILE: test/zoo/inheritance_weak/ddl/strong_child_2_ddl.dbml ================================================ Table "CONTRAT" { "num prof" VARCHAR(42) [NOT NULL] "date contrat" VARCHAR(42) [NOT NULL] "salaire horaire contrat" VARCHAR(42) Indexes { ("num prof", "date contrat") [pk] } } Table "SALARIÉ" { "num prof" VARCHAR(42) [pk, NOT NULL] "nom prof" VARCHAR(42) "prénom prof" VARCHAR(42) "téléphone prof" VARCHAR(42) "date embauche salarié" VARCHAR(42) "échelon salarié" VARCHAR(42) "salaire salarié" VARCHAR(42) } Table "VACATAIRE" { "num prof" VARCHAR(42) [pk, NOT NULL] "nom prof" VARCHAR(42) "prénom prof" VARCHAR(42) "téléphone prof" VARCHAR(42) "statut vacataire" VARCHAR(42) } Ref:"CONTRAT"."num prof" > "VACATAIRE"."num prof" ================================================ FILE: test/zoo/inheritance_weak/ddl/strong_child_2_ddl.sql ================================================ CREATE TABLE CONTRAT ( PRIMARY KEY (num_prof, date_contrat), num_prof VARCHAR(8) NOT NULL, date_contrat DATE NOT NULL, salaire_horaire_contrat VARCHAR(42) ); CREATE TABLE SALARIE ( PRIMARY KEY (num_prof), num_prof VARCHAR(8) NOT NULL, nom_prof VARCHAR(255), prenom_prof VARCHAR(255), telephone_prof VARCHAR(20), date_embauche_salarie DATE, echelon_salarie VARCHAR(42), salaire_salarie VARCHAR(42) ); CREATE TABLE VACATAIRE ( PRIMARY KEY (num_prof), num_prof VARCHAR(8) NOT NULL, nom_prof VARCHAR(255), prenom_prof VARCHAR(255), telephone_prof VARCHAR(20), statut_vacataire VARCHAR(20) ); ALTER TABLE CONTRAT ADD FOREIGN KEY (num_prof) REFERENCES VACATAIRE (num_prof); ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="VACATAIRE"] 2 [label="CONTRAT",peripheries=2] // Associative entities 7 [label="SALARIÉ",shape=Mdiamond] 11 [label="PROFESSEUR",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 6 [label="salaire\nhoraire\ncontrat"] 9 [label="échelon\nsalarié"] 10 [label="salaire\nsalarié"] 13 [label="nom prof"] 14 [label="prénom\nprof"] 15 [label="téléphone\nprof"] // Weak and strong entity attributes 4 [label=<statut
    vacataire
    >] 5 [label=<date
    contrat
    > style="dashed,filled"] 8 [label=<date
    embauche
    salarié
    >] 12 [label=<num prof>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="DF",peripheries=2] // Edges between entities and attributes edge [ penwidth=1.5 ] 2 -- 5 2 -- 6 3 -- 4 7 -- 8 7 -- 9 7 -- 10 11 -- 12 11 -- 13 11 -- 14 11 -- 15 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 3 -- 1 edge [headlabel=N] 2 -- 1 } ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_0_erd_chen.txt ================================================ [VACATAIRE] ==1== <> [[CONTRAT]] ==N== <> ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_0_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    VACATAIRE
    PKstatut vacataire
    >] 2 [label=<
    CONTRAT
    PKdate contrat
    salaire horaire contrat
    >] 3 [label=<
    SALARIÉ
    PKdate embauche salarié
    échelon salarié
    salaire salarié
    >] 4 [label=<
    PROFESSEUR
    PKnum prof
    nom prof
    prénom prof
    téléphone prof
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] } ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_0_erd_crow.mmd ================================================ erDiagram VACATAIRE { TYPE statut_vacataire PK } CONTRAT { TYPE date_contrat PK TYPE salaire_horaire_contrat } SALARIE { TYPE date_embauche_salarie PK TYPE echelon_salarie TYPE salaire_salarie } PROFESSEUR { TYPE num_prof PK TYPE nom_prof TYPE prenom_prof TYPE telephone_prof } CONTRAT }|..|| VACATAIRE: DF ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_0_uml.puml ================================================ @startuml "inheritance_weak" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x "CONTRAT" "1..*" --* "1" "VACATAIRE" Table("VACATAIRE") { {field} + pk(statut vacataire) } Table("CONTRAT") { {field} + pk(date contrat) {field} + salaire horaire contrat } note "{complete, disjoint}" as GENERALIZATION_0 GENERALIZATION_0 -[dotted]- SALARIÉ GENERALIZATION_0 -[dotted]- VACATAIRE PROFESSEUR <|-- SALARIÉ PROFESSEUR <|-- VACATAIRE Table("SALARIÉ") { {field} + pk(date embauche salarié) {field} + échelon salarié {field} + salaire salarié } Table("PROFESSEUR") { {field} + pk(num prof) {field} + nom prof {field} + prénom prof {field} + téléphone prof } @enduml ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_1_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="VACATAIRE"] 2 [label="CONTRAT",peripheries=2] // Associative entities 7 [label="SALARIÉ",shape=Mdiamond] 11 [label="PROFESSEUR",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 6 [label="salaire\nhoraire\ncontrat"] 9 [label="échelon\nsalarié"] 10 [label="salaire\nsalarié"] 13 [label="nom prof"] 14 [label="prénom\nprof"] 15 [label="téléphone\nprof"] // Weak and strong entity attributes 4 [label=<statut
    vacataire
    >] 5 [label=<date
    contrat
    > style="dashed,filled"] 8 [label=<date
    embauche
    salarié
    >] 12 [label=<num prof>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="DF",peripheries=2] // Edges between entities and attributes edge [ penwidth=1.5 ] 2 -- 5 2 -- 6 3 -- 4 7 -- 8 7 -- 9 7 -- 10 11 -- 12 11 -- 13 11 -- 14 11 -- 15 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 3 -- 1 edge [headlabel=N] 2 -- 1 } ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_1_erd_chen.txt ================================================ [VACATAIRE] ==1== <> [[CONTRAT]] ==N== <> ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_1_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    VACATAIRE
    PKstatut vacataire
    >] 2 [label=<
    CONTRAT
    PKdate contrat
    salaire horaire contrat
    >] 3 [label=<
    SALARIÉ
    PKdate embauche salarié
    échelon salarié
    salaire salarié
    >] 4 [label=<
    PROFESSEUR
    PKnum prof
    nom prof
    prénom prof
    téléphone prof
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] } ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_1_erd_crow.mmd ================================================ erDiagram VACATAIRE { TYPE statut_vacataire PK } CONTRAT { TYPE date_contrat PK TYPE salaire_horaire_contrat } SALARIE { TYPE date_embauche_salarie PK TYPE echelon_salarie TYPE salaire_salarie } PROFESSEUR { TYPE num_prof PK TYPE nom_prof TYPE prenom_prof TYPE telephone_prof } CONTRAT }|..|| VACATAIRE: DF ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_1_uml.puml ================================================ @startuml "inheritance_weak" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x "CONTRAT" "1..*" --* "1" "VACATAIRE" Table("VACATAIRE") { {field} + pk(statut vacataire) } Table("CONTRAT") { {field} + pk(date contrat) {field} + salaire horaire contrat } note "{complete, disjoint}" as GENERALIZATION_0 GENERALIZATION_0 -[dotted]- SALARIÉ GENERALIZATION_0 -[dotted]- VACATAIRE PROFESSEUR <|-- SALARIÉ PROFESSEUR <|-- VACATAIRE Table("SALARIÉ") { {field} + pk(date embauche salarié) {field} + échelon salarié {field} + salaire salarié } Table("PROFESSEUR") { {field} + pk(num prof) {field} + nom prof {field} + prénom prof {field} + téléphone prof } @enduml ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_2_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="VACATAIRE"] 2 [label="CONTRAT",peripheries=2] // Associative entities 7 [label="SALARIÉ",shape=Mdiamond] 11 [label="PROFESSEUR",shape=Mdiamond] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 6 [label="salaire\nhoraire\ncontrat"] 9 [label="échelon\nsalarié"] 10 [label="salaire\nsalarié"] 13 [label="nom prof"] 14 [label="prénom\nprof"] 15 [label="téléphone\nprof"] // Weak and strong entity attributes 4 [label=<statut
    vacataire
    >] 5 [label=<date
    contrat
    > style="dashed,filled"] 8 [label=<date
    embauche
    salarié
    >] 12 [label=<num prof>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="DF",peripheries=2] // Edges between entities and attributes edge [ penwidth=1.5 ] 2 -- 5 2 -- 6 3 -- 4 7 -- 8 7 -- 9 7 -- 10 11 -- 12 11 -- 13 11 -- 14 11 -- 15 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 3 -- 1 edge [headlabel=N] 2 -- 1 } ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_2_erd_chen.txt ================================================ [VACATAIRE] ==1== <> [[CONTRAT]] ==N== <> ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_2_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    VACATAIRE
    PKstatut vacataire
    >] 2 [label=<
    CONTRAT
    PKdate contrat
    salaire horaire contrat
    >] 3 [label=<
    SALARIÉ
    PKdate embauche salarié
    échelon salarié
    salaire salarié
    >] 4 [label=<
    PROFESSEUR
    PKnum prof
    nom prof
    prénom prof
    téléphone prof
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] } ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_2_erd_crow.mmd ================================================ erDiagram VACATAIRE { TYPE statut_vacataire PK } CONTRAT { TYPE date_contrat PK TYPE salaire_horaire_contrat } SALARIE { TYPE date_embauche_salarie PK TYPE echelon_salarie TYPE salaire_salarie } PROFESSEUR { TYPE num_prof PK TYPE nom_prof TYPE prenom_prof TYPE telephone_prof } CONTRAT }|..|| VACATAIRE: DF ================================================ FILE: test/zoo/inheritance_weak/exported/strong_child_2_uml.puml ================================================ @startuml "inheritance_weak" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x "CONTRAT" "1..*" --* "1" "VACATAIRE" Table("VACATAIRE") { {field} + pk(statut vacataire) } Table("CONTRAT") { {field} + pk(date contrat) {field} + salaire horaire contrat } note "{complete, disjoint}" as GENERALIZATION_0 GENERALIZATION_0 -[dotted]- SALARIÉ GENERALIZATION_0 -[dotted]- VACATAIRE PROFESSEUR <|-- SALARIÉ PROFESSEUR <|-- VACATAIRE Table("SALARIÉ") { {field} + pk(date embauche salarié) {field} + échelon salarié {field} + salaire salarié } Table("PROFESSEUR") { {field} + pk(num prof) {field} + nom prof {field} + prénom prof {field} + téléphone prof } @enduml ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note CONTRAT num prof ! strengthening_primary_foreign_key True VACATAIRE VACATAIRE DF CONTRAT date contrat ! primary_key True CONTRAT salaire horaire contrat normal_attribute False PROFESSEUR num prof ! primary_key True PROFESSEUR nom prof normal_attribute False PROFESSEUR prénom prof normal_attribute False PROFESSEUR téléphone prof normal_attribute False SALARIÉ num prof ! parent_primary_key True PROFESSEUR PROFESSEUR XT SALARIÉ date embauche salarié normal_attribute False SALARIÉ échelon salarié normal_attribute False SALARIÉ salaire salarié normal_attribute False VACATAIRE num prof ! parent_primary_key True PROFESSEUR PROFESSEUR XT VACATAIRE statut vacataire normal_attribute False ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_0_dependencies.gv ================================================ digraph { node [shape=box] "VACATAIRE" -> "CONTRAT" "PROFESSEUR" -> "SALARIÉ" "PROFESSEUR" -> "VACATAIRE" } ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD inheritance_weak
    CONTRAT ( #num prof, date contrat, salaire horaire contrat )
    • Le champ num prof fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité VACATAIRE pour renforcer l'identifiant.
    • Le champ date contrat fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité CONTRAT.
    • Le champ salaire horaire contrat était déjà un simple attribut de l'entité CONTRAT.
    PROFESSEUR ( num prof, nom prof, prénom prof, téléphone prof )
    • Le champ num prof constitue la clé primaire de la table. C'était déjà un identifiant de l'entité PROFESSEUR.
    • Les champs nom prof, prénom prof et téléphone prof étaient déjà de simples attributs de l'entité PROFESSEUR.
    SALARIÉ ( #num prof, date embauche salarié, échelon salarié, salaire salarié )
    • Le champ num prof constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère PROFESSEUR.
    • Les champs date embauche salarié, échelon salarié et salaire salarié étaient déjà de simples attributs de l'entité SALARIÉ.
    VACATAIRE ( #num prof, statut vacataire )
    • Le champ num prof constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère PROFESSEUR.
    • Le champ statut vacataire était déjà un simple attribut de l'entité VACATAIRE.
    ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_0_mld.mcd ================================================ %%mocodo ::: VACATAIRE: #num prof > PROFESSEUR > num prof, statut vacataire ::: : CONTRAT: #num prof > VACATAIRE > num prof, _date contrat, salaire horaire contrat ::: SALARIÉ: #num prof > PROFESSEUR > num prof, date embauche salarié, échelon salarié, salaire salarié : ::: PROFESSEUR: num prof, nom prof, prénom prof, téléphone prof ::: ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_0_mld.md ================================================ - **CONTRAT** (_#num prof_, date contrat, salaire horaire contrat) - Le champ _num prof_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _VACATAIRE_ pour renforcer l'identifiant. - Le champ _date contrat_ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _CONTRAT_. - Le champ _salaire horaire contrat_ était déjà un simple attribut de l'entité _CONTRAT_. - **PROFESSEUR** (num prof, nom prof, prénom prof, téléphone prof) - Le champ _num prof_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _PROFESSEUR_. - Les champs _nom prof_, _prénom prof_ et _téléphone prof_ étaient déjà de simples attributs de l'entité _PROFESSEUR_. - **SALARIÉ** (_#num prof_, date embauche salarié, échelon salarié, salaire salarié) - Le champ _num prof_ constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère _PROFESSEUR_. - Les champs _date embauche salarié_, _échelon salarié_ et _salaire salarié_ étaient déjà de simples attributs de l'entité _SALARIÉ_. - **VACATAIRE** (_#num prof_, statut vacataire) - Le champ _num prof_ constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère _PROFESSEUR_. - Le champ _statut vacataire_ était déjà un simple attribut de l'entité _VACATAIRE_. ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{inheritance\_weak}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{CONTRAT} (\foreign{\prim{num prof}}, \prim{date contrat}, \attr{salaire horaire contrat}) \begin{itemize} \item Le champ \emph{num prof} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{VACATAIRE} pour renforcer l'identifiant. \item Le champ \emph{date contrat} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CONTRAT}. \item Le champ \emph{salaire horaire contrat} était déjà un simple attribut de l'entité \emph{CONTRAT}. \end{itemize} \item \relat{PROFESSEUR} (\prim{num prof}, \attr{nom prof}, \attr{prénom prof}, \attr{téléphone prof}) \begin{itemize} \item Le champ \emph{num prof} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{PROFESSEUR}. \item Les champs \emph{nom prof}, \emph{prénom prof} et \emph{téléphone prof} étaient déjà de simples attributs de l'entité \emph{PROFESSEUR}. \end{itemize} \item \relat{SALARIÉ} (\foreign{\prim{num prof}}, \attr{date embauche salarié}, \attr{échelon salarié}, \attr{salaire salarié}) \begin{itemize} \item Le champ \emph{num prof} constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère \emph{PROFESSEUR}. \item Les champs \emph{date embauche salarié}, \emph{échelon salarié} et \emph{salaire salarié} étaient déjà de simples attributs de l'entité \emph{SALARIÉ}. \end{itemize} \item \relat{VACATAIRE} (\foreign{\prim{num prof}}, \attr{statut vacataire}) \begin{itemize} \item Le champ \emph{num prof} constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère \emph{PROFESSEUR}. \item Le champ \emph{statut vacataire} était déjà un simple attribut de l'entité \emph{VACATAIRE}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_0_mld.txt ================================================ - CONTRAT (_#num prof_, _date contrat_, salaire horaire contrat) - Le champ « num prof » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « VACATAIRE » pour renforcer l'identifiant. - Le champ « date contrat » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « CONTRAT ». - Le champ « salaire horaire contrat » était déjà un simple attribut de l'entité « CONTRAT ». - PROFESSEUR (_num prof_, nom prof, prénom prof, téléphone prof) - Le champ « num prof » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « PROFESSEUR ». - Les champs « nom prof », « prénom prof » et « téléphone prof » étaient déjà de simples attributs de l'entité « PROFESSEUR ». - SALARIÉ (_#num prof_, date embauche salarié, échelon salarié, salaire salarié) - Le champ « num prof » constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère « PROFESSEUR ». - Les champs « date embauche salarié », « échelon salarié » et « salaire salarié » étaient déjà de simples attributs de l'entité « SALARIÉ ». - VACATAIRE (_#num prof_, statut vacataire) - Le champ « num prof » constitue la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité-mère « PROFESSEUR ». - Le champ « statut vacataire » était déjà un simple attribut de l'entité « VACATAIRE ». ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_1_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note CONTRAT num prof ! strengthening_primary_foreign_key True VACATAIRE PROFESSEUR DF CONTRAT date contrat ! primary_key True CONTRAT salaire horaire contrat normal_attribute False PROFESSEUR num prof ! primary_key True PROFESSEUR nom prof normal_attribute False PROFESSEUR prénom prof normal_attribute False PROFESSEUR téléphone prof normal_attribute False PROFESSEUR statut vacataire ? deleted_child_attribute False VACATAIRE XT PROFESSEUR date embauche salarié ? deleted_child_attribute False SALARIÉ XT PROFESSEUR échelon salarié ? deleted_child_attribute False SALARIÉ XT PROFESSEUR salaire salarié ? deleted_child_attribute False SALARIÉ XT ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_1_dependencies.gv ================================================ digraph { node [shape=box] "PROFESSEUR" -> "CONTRAT" } ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_1_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD inheritance_weak
    CONTRAT ( #num prof, date contrat, salaire horaire contrat )
    • Le champ num prof fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité PROFESSEUR pour renforcer l'identifiant.
    • Le champ date contrat fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité CONTRAT.
    • Le champ salaire horaire contrat était déjà un simple attribut de l'entité CONTRAT.
    PROFESSEUR ( num prof, nom prof, prénom prof, téléphone prof, statut vacataire?, date embauche salarié?, échelon salarié?, salaire salarié? )
    • Le champ num prof constitue la clé primaire de la table. C'était déjà un identifiant de l'entité PROFESSEUR.
    • Les champs nom prof, prénom prof et téléphone prof étaient déjà de simples attributs de l'entité PROFESSEUR.
    • Le champ à saisie facultative statut vacataire a migré à partir de l'entité-fille VACATAIRE (supprimée).
    • Le champ à saisie facultative date embauche salarié a migré à partir de l'entité-fille SALARIÉ (supprimée).
    • Le champ à saisie facultative échelon salarié a migré à partir de l'entité-fille SALARIÉ (supprimée).
    • Le champ à saisie facultative salaire salarié a migré à partir de l'entité-fille SALARIÉ (supprimée).
    ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_1_mld.mcd ================================================ : CONTRAT: #num prof > PROFESSEUR > num prof, _date contrat, salaire horaire contrat ::: ::: PROFESSEUR: num prof, nom prof, prénom prof, téléphone prof, statut vacataire, date embauche salarié, échelon salarié, salaire salarié : ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_1_mld.md ================================================ - **CONTRAT** (_#num prof_, date contrat, salaire horaire contrat) - Le champ _num prof_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _PROFESSEUR_ pour renforcer l'identifiant. - Le champ _date contrat_ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _CONTRAT_. - Le champ _salaire horaire contrat_ était déjà un simple attribut de l'entité _CONTRAT_. - **PROFESSEUR** (num prof, nom prof, prénom prof, téléphone prof, statut vacataire?, date embauche salarié?, échelon salarié?, salaire salarié?) - Le champ _num prof_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _PROFESSEUR_. - Les champs _nom prof_, _prénom prof_ et _téléphone prof_ étaient déjà de simples attributs de l'entité _PROFESSEUR_. - Le champ à saisie facultative _statut vacataire_ a migré à partir de l'entité-fille _VACATAIRE_ (supprimée). - Le champ à saisie facultative _date embauche salarié_ a migré à partir de l'entité-fille _SALARIÉ_ (supprimée). - Le champ à saisie facultative _échelon salarié_ a migré à partir de l'entité-fille _SALARIÉ_ (supprimée). - Le champ à saisie facultative _salaire salarié_ a migré à partir de l'entité-fille _SALARIÉ_ (supprimée). ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_1_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{inheritance\_weak}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{CONTRAT} (\foreign{\prim{num prof}}, \prim{date contrat}, \attr{salaire horaire contrat}) \begin{itemize} \item Le champ \emph{num prof} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{PROFESSEUR} pour renforcer l'identifiant. \item Le champ \emph{date contrat} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CONTRAT}. \item Le champ \emph{salaire horaire contrat} était déjà un simple attribut de l'entité \emph{CONTRAT}. \end{itemize} \item \relat{PROFESSEUR} (\prim{num prof}, \attr{nom prof}, \attr{prénom prof}, \attr{téléphone prof}, \attr{statut vacataire?}, \attr{date embauche salarié?}, \attr{échelon salarié?}, \attr{salaire salarié?}) \begin{itemize} \item Le champ \emph{num prof} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{PROFESSEUR}. \item Les champs \emph{nom prof}, \emph{prénom prof} et \emph{téléphone prof} étaient déjà de simples attributs de l'entité \emph{PROFESSEUR}. \item Le champ à saisie facultative \emph{statut vacataire} a migré à partir de l'entité-fille \emph{VACATAIRE} (supprimée). \item Le champ à saisie facultative \emph{date embauche salarié} a migré à partir de l'entité-fille \emph{SALARIÉ} (supprimée). \item Le champ à saisie facultative \emph{échelon salarié} a migré à partir de l'entité-fille \emph{SALARIÉ} (supprimée). \item Le champ à saisie facultative \emph{salaire salarié} a migré à partir de l'entité-fille \emph{SALARIÉ} (supprimée). \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_1_mld.txt ================================================ - CONTRAT (_#num prof_, _date contrat_, salaire horaire contrat) - Le champ « num prof » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « PROFESSEUR » pour renforcer l'identifiant. - Le champ « date contrat » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « CONTRAT ». - Le champ « salaire horaire contrat » était déjà un simple attribut de l'entité « CONTRAT ». - PROFESSEUR (_num prof_, nom prof, prénom prof, téléphone prof, statut vacataire?, date embauche salarié?, échelon salarié?, salaire salarié?) - Le champ « num prof » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « PROFESSEUR ». - Les champs « nom prof », « prénom prof » et « téléphone prof » étaient déjà de simples attributs de l'entité « PROFESSEUR ». - Le champ à saisie facultative « statut vacataire » a migré à partir de l'entité-fille « VACATAIRE » (supprimée). - Le champ à saisie facultative « date embauche salarié » a migré à partir de l'entité-fille « SALARIÉ » (supprimée). - Le champ à saisie facultative « échelon salarié » a migré à partir de l'entité-fille « SALARIÉ » (supprimée). - Le champ à saisie facultative « salaire salarié » a migré à partir de l'entité-fille « SALARIÉ » (supprimée). ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_2_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note CONTRAT num prof ! strengthening_primary_foreign_key True VACATAIRE VACATAIRE DF CONTRAT date contrat ! primary_key True CONTRAT salaire horaire contrat normal_attribute False SALARIÉ num prof ! deleted_parent_primary_key True PROFESSEUR PROFESSEUR XT SALARIÉ nom prof deleted_parent_attribute False PROFESSEUR XT SALARIÉ prénom prof deleted_parent_attribute False PROFESSEUR XT SALARIÉ téléphone prof deleted_parent_attribute False PROFESSEUR XT SALARIÉ date embauche salarié normal_attribute False SALARIÉ échelon salarié normal_attribute False SALARIÉ salaire salarié normal_attribute False VACATAIRE num prof ! deleted_parent_primary_key True PROFESSEUR PROFESSEUR XT VACATAIRE nom prof deleted_parent_attribute False PROFESSEUR XT VACATAIRE prénom prof deleted_parent_attribute False PROFESSEUR XT VACATAIRE téléphone prof deleted_parent_attribute False PROFESSEUR XT VACATAIRE statut vacataire normal_attribute False ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_2_dependencies.gv ================================================ digraph { node [shape=box] "VACATAIRE" -> "CONTRAT" } ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_2_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD inheritance_weak
    CONTRAT ( #num prof, date contrat, salaire horaire contrat )
    • Le champ num prof fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité VACATAIRE pour renforcer l'identifiant.
    • Le champ date contrat fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité CONTRAT.
    • Le champ salaire horaire contrat était déjà un simple attribut de l'entité CONTRAT.
    SALARIÉ ( num prof, nom prof, prénom prof, téléphone prof, date embauche salarié, échelon salarié, salaire salarié )
    • Le champ num prof constitue la clé primaire de la table. Il était clé primaire de l'entité-mère PROFESSEUR (supprimée).
    • Le champ nom prof est un simple attribut. Il était simple attribut de l'entité-mère PROFESSEUR (supprimée).
    • Le champ prénom prof est un simple attribut. Il était simple attribut de l'entité-mère PROFESSEUR (supprimée).
    • Le champ téléphone prof est un simple attribut. Il était simple attribut de l'entité-mère PROFESSEUR (supprimée).
    • Les champs date embauche salarié, échelon salarié et salaire salarié étaient déjà de simples attributs de l'entité SALARIÉ.
    VACATAIRE ( num prof, nom prof, prénom prof, téléphone prof, statut vacataire )
    • Le champ num prof constitue la clé primaire de la table. Il était clé primaire de l'entité-mère PROFESSEUR (supprimée).
    • Le champ nom prof est un simple attribut. Il était simple attribut de l'entité-mère PROFESSEUR (supprimée).
    • Le champ prénom prof est un simple attribut. Il était simple attribut de l'entité-mère PROFESSEUR (supprimée).
    • Le champ téléphone prof est un simple attribut. Il était simple attribut de l'entité-mère PROFESSEUR (supprimée).
    • Le champ statut vacataire était déjà un simple attribut de l'entité VACATAIRE.
    ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_2_mld.mcd ================================================ %%mocodo ::: VACATAIRE: num prof, nom prof, prénom prof, téléphone prof, statut vacataire ::: : CONTRAT: #num prof > VACATAIRE > num prof, _date contrat, salaire horaire contrat ::: SALARIÉ: num prof, nom prof, prénom prof, téléphone prof, date embauche salarié, échelon salarié, salaire salarié : ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_2_mld.md ================================================ - **CONTRAT** (_#num prof_, date contrat, salaire horaire contrat) - Le champ _num prof_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _VACATAIRE_ pour renforcer l'identifiant. - Le champ _date contrat_ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _CONTRAT_. - Le champ _salaire horaire contrat_ était déjà un simple attribut de l'entité _CONTRAT_. - **SALARIÉ** (num prof, nom prof, prénom prof, téléphone prof, date embauche salarié, échelon salarié, salaire salarié) - Le champ _num prof_ constitue la clé primaire de la table. Il était clé primaire de l'entité-mère _PROFESSEUR_ (supprimée). - Le champ _nom prof_ est un simple attribut. Il était simple attribut de l'entité-mère _PROFESSEUR_ (supprimée). - Le champ _prénom prof_ est un simple attribut. Il était simple attribut de l'entité-mère _PROFESSEUR_ (supprimée). - Le champ _téléphone prof_ est un simple attribut. Il était simple attribut de l'entité-mère _PROFESSEUR_ (supprimée). - Les champs _date embauche salarié_, _échelon salarié_ et _salaire salarié_ étaient déjà de simples attributs de l'entité _SALARIÉ_. - **VACATAIRE** (num prof, nom prof, prénom prof, téléphone prof, statut vacataire) - Le champ _num prof_ constitue la clé primaire de la table. Il était clé primaire de l'entité-mère _PROFESSEUR_ (supprimée). - Le champ _nom prof_ est un simple attribut. Il était simple attribut de l'entité-mère _PROFESSEUR_ (supprimée). - Le champ _prénom prof_ est un simple attribut. Il était simple attribut de l'entité-mère _PROFESSEUR_ (supprimée). - Le champ _téléphone prof_ est un simple attribut. Il était simple attribut de l'entité-mère _PROFESSEUR_ (supprimée). - Le champ _statut vacataire_ était déjà un simple attribut de l'entité _VACATAIRE_. ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_2_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{inheritance\_weak}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{CONTRAT} (\foreign{\prim{num prof}}, \prim{date contrat}, \attr{salaire horaire contrat}) \begin{itemize} \item Le champ \emph{num prof} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{VACATAIRE} pour renforcer l'identifiant. \item Le champ \emph{date contrat} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{CONTRAT}. \item Le champ \emph{salaire horaire contrat} était déjà un simple attribut de l'entité \emph{CONTRAT}. \end{itemize} \item \relat{SALARIÉ} (\prim{num prof}, \attr{nom prof}, \attr{prénom prof}, \attr{téléphone prof}, \attr{date embauche salarié}, \attr{échelon salarié}, \attr{salaire salarié}) \begin{itemize} \item Le champ \emph{num prof} constitue la clé primaire de la table. Il était clé primaire de l'entité-mère \emph{PROFESSEUR} (supprimée). \item Le champ \emph{nom prof} est un simple attribut. Il était simple attribut de l'entité-mère \emph{PROFESSEUR} (supprimée). \item Le champ \emph{prénom prof} est un simple attribut. Il était simple attribut de l'entité-mère \emph{PROFESSEUR} (supprimée). \item Le champ \emph{téléphone prof} est un simple attribut. Il était simple attribut de l'entité-mère \emph{PROFESSEUR} (supprimée). \item Les champs \emph{date embauche salarié}, \emph{échelon salarié} et \emph{salaire salarié} étaient déjà de simples attributs de l'entité \emph{SALARIÉ}. \end{itemize} \item \relat{VACATAIRE} (\prim{num prof}, \attr{nom prof}, \attr{prénom prof}, \attr{téléphone prof}, \attr{statut vacataire}) \begin{itemize} \item Le champ \emph{num prof} constitue la clé primaire de la table. Il était clé primaire de l'entité-mère \emph{PROFESSEUR} (supprimée). \item Le champ \emph{nom prof} est un simple attribut. Il était simple attribut de l'entité-mère \emph{PROFESSEUR} (supprimée). \item Le champ \emph{prénom prof} est un simple attribut. Il était simple attribut de l'entité-mère \emph{PROFESSEUR} (supprimée). \item Le champ \emph{téléphone prof} est un simple attribut. Il était simple attribut de l'entité-mère \emph{PROFESSEUR} (supprimée). \item Le champ \emph{statut vacataire} était déjà un simple attribut de l'entité \emph{VACATAIRE}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/inheritance_weak/mld/strong_child_2_mld.txt ================================================ - CONTRAT (_#num prof_, _date contrat_, salaire horaire contrat) - Le champ « num prof » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « VACATAIRE » pour renforcer l'identifiant. - Le champ « date contrat » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « CONTRAT ». - Le champ « salaire horaire contrat » était déjà un simple attribut de l'entité « CONTRAT ». - SALARIÉ (_num prof_, nom prof, prénom prof, téléphone prof, date embauche salarié, échelon salarié, salaire salarié) - Le champ « num prof » constitue la clé primaire de la table. Il était clé primaire de l'entité-mère « PROFESSEUR » (supprimée). - Le champ « nom prof » est un simple attribut. Il était simple attribut de l'entité-mère « PROFESSEUR » (supprimée). - Le champ « prénom prof » est un simple attribut. Il était simple attribut de l'entité-mère « PROFESSEUR » (supprimée). - Le champ « téléphone prof » est un simple attribut. Il était simple attribut de l'entité-mère « PROFESSEUR » (supprimée). - Les champs « date embauche salarié », « échelon salarié » et « salaire salarié » étaient déjà de simples attributs de l'entité « SALARIÉ ». - VACATAIRE (_num prof_, nom prof, prénom prof, téléphone prof, statut vacataire) - Le champ « num prof » constitue la clé primaire de la table. Il était clé primaire de l'entité-mère « PROFESSEUR » (supprimée). - Le champ « nom prof » est un simple attribut. Il était simple attribut de l'entité-mère « PROFESSEUR » (supprimée). - Le champ « prénom prof » est un simple attribut. Il était simple attribut de l'entité-mère « PROFESSEUR » (supprimée). - Le champ « téléphone prof » est un simple attribut. Il était simple attribut de l'entité-mère « PROFESSEUR » (supprimée). - Le champ « statut vacataire » était déjà un simple attribut de l'entité « VACATAIRE ». ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_0_rw_create_df_arrows=across.mcd ================================================ % https://github.com/laowantong/mocodo/issues/118 % Class table inheritance: all entities are preserved DF, _11 CONTRAT, 1N> VACATAIRE VACATAIRE: statut vacataire CONTRAT: date contrat, salaire horaire contrat /XT\ PROFESSEUR -> VACATAIRE, SALARIÉ SALARIÉ: date embauche salarié, échelon salarié, salaire salarié PROFESSEUR: num prof, nom prof, prénom prof, téléphone prof ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_0_rw_drown.mcd ================================================ % https://github.com/laowantong/mocodo/issues/118 % Class table inheritance: all entities are preserved DF, _11 ENTITÉ 2_, 1N ENTITÉ 1_ ENTITÉ 1_: at 1 1 ENTITÉ 2_: at 2 1, at 2 2 /XT\ ENTITÉ 4_ -> ENTITÉ 1_, ENTITÉ 3_ ENTITÉ 3_: at 3 1, at 3 2, at 3 3 ENTITÉ 4_: at 4 1, at 4 2, at 4 3, at 4 4 ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_0_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_1_rw_create_df_arrows=across.mcd ================================================ % https://github.com/laowantong/mocodo/issues/118 % Single table inheritance: children are merged into the parent table DF, _11 CONTRAT, 1N> VACATAIRE VACATAIRE: statut vacataire CONTRAT: date contrat, salaire horaire contrat /XT\ PROFESSEUR <- VACATAIRE, SALARIÉ SALARIÉ: date embauche salarié, échelon salarié, salaire salarié PROFESSEUR: num prof, nom prof, prénom prof, téléphone prof ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_1_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_1_rw_drown.mcd ================================================ % https://github.com/laowantong/mocodo/issues/118 % Single table inheritance: children are merged into the parent table DF, _11 ENTITÉ 2_, 1N ENTITÉ 1_ ENTITÉ 1_: at 1 1 ENTITÉ 2_: at 2 1, at 2 2 /XT\ ENTITÉ 4_ <- ENTITÉ 1_, ENTITÉ 3_ ENTITÉ 3_: at 3 1, at 3 2, at 3 3 ENTITÉ 4_: at 4 1, at 4 2, at 4 3, at 4 4 ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_1_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_1_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_1_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_1_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_1_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_1_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_1_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_2_rw_create_df_arrows=across.mcd ================================================ % https://github.com/laowantong/mocodo/issues/118 % Concrete table inheritance: parent disappears DF, _11 CONTRAT, 1N> VACATAIRE VACATAIRE: statut vacataire CONTRAT: date contrat, salaire horaire contrat /XT\ PROFESSEUR => VACATAIRE, SALARIÉ SALARIÉ: date embauche salarié, échelon salarié, salaire salarié PROFESSEUR: num prof, nom prof, prénom prof, téléphone prof ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_2_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_2_rw_drown.mcd ================================================ % https://github.com/laowantong/mocodo/issues/118 % Concrete table inheritance: parent disappears DF, _11 ENTITÉ 2_, 1N ENTITÉ 1_ ENTITÉ 1_: at 1 1 ENTITÉ 2_: at 2 1, at 2 2 /XT\ ENTITÉ 4_ => ENTITÉ 1_, ENTITÉ 3_ ENTITÉ 3_: at 3 1, at 3 2, at 3 3 ENTITÉ 4_: at 4 1, at 4 2, at 4 3, at 4 4 ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_2_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_2_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_2_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_2_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_2_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_2_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/inheritance_weak/rewritten/strong_child_2_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/landing/_landing.mcd ================================================ AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise PIÈCE: réf. pièce, libellé pièce COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, _11 AYANT-DROIT, 0N EMPLOYÉ EMPLOYÉ: matricule, nom employé PROJET: num. projet, nom projet FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT TRAVAILLER, 0N EMPLOYÉ, 1N PROJET SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET ================================================ FILE: test/zoo/landing/ddl/landing_ddl.d2 ================================================ "AYANT-DROIT": { shape: sql_table "matricule": VARCHAR(42) {constraint: [PK; FK]} "nom ayant-droit": VARCHAR(42) {constraint: PK} "lien": VARCHAR(42) } "COMPOSER": { shape: sql_table "réf. pièce composée": VARCHAR(42) {constraint: [PK; FK]} "réf. pièce composante": VARCHAR(42) {constraint: [PK; FK]} "quantité": VARCHAR(42) } "DÉPARTEMENT": { shape: sql_table "num. département": VARCHAR(42) {constraint: PK} "nom département": VARCHAR(42) } "EMPLOYÉ": { shape: sql_table "matricule": VARCHAR(42) {constraint: PK} "nom employé": VARCHAR(42) "num. département": VARCHAR(42) {constraint: [FK; NOT NULL]} } "FOURNIR": { shape: sql_table "num. projet": VARCHAR(42) {constraint: [PK; FK]} "réf. pièce": VARCHAR(42) {constraint: [PK; FK]} "num. société": VARCHAR(42) {constraint: [PK; FK]} "qté fournie": VARCHAR(42) } "PIÈCE": { shape: sql_table "réf. pièce": VARCHAR(42) {constraint: PK} "libellé pièce": VARCHAR(42) } "PROJET": { shape: sql_table "num. projet": VARCHAR(42) {constraint: PK} "nom projet": VARCHAR(42) "matricule responsable": VARCHAR(42) {constraint: [FK; "NULL"]} } "REQUÉRIR": { shape: sql_table "num. projet": VARCHAR(42) {constraint: [PK; FK]} "réf. pièce": VARCHAR(42) {constraint: [PK; FK]} "qté requise": VARCHAR(42) } "SOCIÉTÉ": { shape: sql_table "num. société": VARCHAR(42) {constraint: PK} "raison sociale": VARCHAR(42) "num. société mère": VARCHAR(42) {constraint: [FK; "NULL"]} } "TRAVAILLER": { shape: sql_table "matricule": VARCHAR(42) {constraint: [PK; FK]} "num. projet": VARCHAR(42) {constraint: [PK; FK]} } "AYANT-DROIT"."matricule" -> "EMPLOYÉ"."matricule" "COMPOSER"."réf. pièce composée" -> "PIÈCE"."réf. pièce" "COMPOSER"."réf. pièce composante" -> "PIÈCE"."réf. pièce" "EMPLOYÉ"."num. département" -> "DÉPARTEMENT"."num. département" "FOURNIR"."num. projet" -> "PROJET"."num. projet" "FOURNIR"."réf. pièce" -> "PIÈCE"."réf. pièce" "FOURNIR"."num. société" -> "SOCIÉTÉ"."num. société" "PROJET"."matricule responsable" -> "EMPLOYÉ"."matricule" "REQUÉRIR"."num. projet" -> "PROJET"."num. projet" "REQUÉRIR"."réf. pièce" -> "PIÈCE"."réf. pièce" "SOCIÉTÉ"."num. société mère" -> "SOCIÉTÉ"."num. société" "TRAVAILLER"."matricule" -> "EMPLOYÉ"."matricule" "TRAVAILLER"."num. projet" -> "PROJET"."num. projet" ================================================ FILE: test/zoo/landing/ddl/landing_ddl.dbml ================================================ Table "AYANT-DROIT" { "matricule" VARCHAR(42) [NOT NULL] "nom ayant-droit" VARCHAR(42) [NOT NULL] "lien" VARCHAR(42) Indexes { ("matricule", "nom ayant-droit") [pk] } } Table "COMPOSER" { "réf. pièce composée" VARCHAR(42) [NOT NULL] "réf. pièce composante" VARCHAR(42) [NOT NULL] "quantité" VARCHAR(42) Indexes { ("réf. pièce composée", "réf. pièce composante") [pk] } } Table "DÉPARTEMENT" { "num. département" VARCHAR(42) [pk, NOT NULL] "nom département" VARCHAR(42) } Table "EMPLOYÉ" { "matricule" VARCHAR(42) [pk, NOT NULL] "nom employé" VARCHAR(42) "num. département" VARCHAR(42) [NOT NULL] } Table "FOURNIR" { "num. projet" VARCHAR(42) [NOT NULL] "réf. pièce" VARCHAR(42) [NOT NULL] "num. société" VARCHAR(42) [NOT NULL] "qté fournie" VARCHAR(42) Indexes { ("num. projet", "réf. pièce", "num. société") [pk] } } Table "PIÈCE" { "réf. pièce" VARCHAR(42) [pk, NOT NULL] "libellé pièce" VARCHAR(42) } Table "PROJET" { "num. projet" VARCHAR(42) [pk, NOT NULL] "nom projet" VARCHAR(42) "matricule responsable" VARCHAR(42) ["NULL"] } Table "REQUÉRIR" { "num. projet" VARCHAR(42) [NOT NULL] "réf. pièce" VARCHAR(42) [NOT NULL] "qté requise" VARCHAR(42) Indexes { ("num. projet", "réf. pièce") [pk] } } Table "SOCIÉTÉ" { "num. société" VARCHAR(42) [pk, NOT NULL] "raison sociale" VARCHAR(42) "num. société mère" VARCHAR(42) ["NULL"] } Table "TRAVAILLER" { "matricule" VARCHAR(42) [NOT NULL] "num. projet" VARCHAR(42) [NOT NULL] Indexes { ("matricule", "num. projet") [pk] } } Ref:"AYANT-DROIT"."matricule" > "EMPLOYÉ"."matricule" Ref:"COMPOSER".("réf. pièce composée", "réf. pièce composante") > "PIÈCE".("réf. pièce", "réf. pièce") Ref:"EMPLOYÉ"."num. département" > "DÉPARTEMENT"."num. département" Ref:"FOURNIR"."num. projet" > "PROJET"."num. projet" Ref:"FOURNIR"."réf. pièce" > "PIÈCE"."réf. pièce" Ref:"FOURNIR"."num. société" > "SOCIÉTÉ"."num. société" Ref:"PROJET"."matricule responsable" > "EMPLOYÉ"."matricule" Ref:"REQUÉRIR"."num. projet" > "PROJET"."num. projet" Ref:"REQUÉRIR"."réf. pièce" > "PIÈCE"."réf. pièce" Ref:"SOCIÉTÉ"."num. société mère" > "SOCIÉTÉ"."num. société" Ref:"TRAVAILLER"."matricule" > "EMPLOYÉ"."matricule" Ref:"TRAVAILLER"."num. projet" > "PROJET"."num. projet" ================================================ FILE: test/zoo/landing/ddl/landing_ddl.sql ================================================ CREATE TABLE AYANT_DROIT ( PRIMARY KEY (matricule, nom_ayant_droit), matricule VARCHAR(42) NOT NULL, nom_ayant_droit VARCHAR(255) NOT NULL, lien VARCHAR(42) ); CREATE TABLE COMPOSER ( PRIMARY KEY (ref_piece_composee, ref_piece_composante), ref_piece_composee VARCHAR(8) NOT NULL, ref_piece_composante VARCHAR(8) NOT NULL, quantite INTEGER ); CREATE TABLE DEPARTEMENT ( PRIMARY KEY (num_departement), num_departement VARCHAR(8) NOT NULL, nom_departement VARCHAR(255) ); CREATE TABLE EMPLOYE ( PRIMARY KEY (matricule), matricule VARCHAR(42) NOT NULL, nom_employe VARCHAR(255), num_departement VARCHAR(8) NOT NULL ); CREATE TABLE FOURNIR ( PRIMARY KEY (num_projet, ref_piece, num_societe), num_projet VARCHAR(8) NOT NULL, ref_piece VARCHAR(8) NOT NULL, num_societe VARCHAR(8) NOT NULL, qte_fournie INTEGER ); CREATE TABLE PIECE ( PRIMARY KEY (ref_piece), ref_piece VARCHAR(8) NOT NULL, libelle_piece VARCHAR(42) ); CREATE TABLE PROJET ( PRIMARY KEY (num_projet), num_projet VARCHAR(8) NOT NULL, nom_projet VARCHAR(255), matricule_responsable VARCHAR(42) NULL ); CREATE TABLE REQUERIR ( PRIMARY KEY (num_projet, ref_piece), num_projet VARCHAR(8) NOT NULL, ref_piece VARCHAR(8) NOT NULL, qte_requise INTEGER ); CREATE TABLE SOCIETE ( PRIMARY KEY (num_societe), num_societe VARCHAR(8) NOT NULL, raison_sociale VARCHAR(42), num_societe_mere VARCHAR(8) NULL ); CREATE TABLE TRAVAILLER ( PRIMARY KEY (matricule, num_projet), matricule VARCHAR(42) NOT NULL, num_projet VARCHAR(8) NOT NULL ); ALTER TABLE AYANT_DROIT ADD FOREIGN KEY (matricule) REFERENCES EMPLOYE (matricule); ALTER TABLE COMPOSER ADD FOREIGN KEY (ref_piece_composante) REFERENCES PIECE (ref_piece); ALTER TABLE COMPOSER ADD FOREIGN KEY (ref_piece_composee) REFERENCES PIECE (ref_piece); ALTER TABLE EMPLOYE ADD FOREIGN KEY (num_departement) REFERENCES DEPARTEMENT (num_departement); ALTER TABLE FOURNIR ADD FOREIGN KEY (num_societe) REFERENCES SOCIETE (num_societe); ALTER TABLE FOURNIR ADD FOREIGN KEY (ref_piece) REFERENCES PIECE (ref_piece); ALTER TABLE FOURNIR ADD FOREIGN KEY (num_projet) REFERENCES PROJET (num_projet); ALTER TABLE PROJET ADD FOREIGN KEY (matricule_responsable) REFERENCES EMPLOYE (matricule); ALTER TABLE REQUERIR ADD FOREIGN KEY (ref_piece) REFERENCES PIECE (ref_piece); ALTER TABLE REQUERIR ADD FOREIGN KEY (num_projet) REFERENCES PROJET (num_projet); ALTER TABLE SOCIETE ADD FOREIGN KEY (num_societe_mere) REFERENCES SOCIETE (num_societe); ALTER TABLE TRAVAILLER ADD FOREIGN KEY (num_projet) REFERENCES PROJET (num_projet); ALTER TABLE TRAVAILLER ADD FOREIGN KEY (matricule) REFERENCES EMPLOYE (matricule); ================================================ FILE: test/zoo/landing/exported/landing_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="AYANT-\nDROIT",peripheries=2] 8 [label="PIÈCE"] 5 [label="EMPLOYÉ"] 6 [label="PROJET"] 22 [label="DÉPARTEMENT"] 20 [label="SOCIÉTÉ"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="lien"] 11 [label="libellé\npièce"] 16 [label="nom\nemployé"] 18 [label="nom\nprojet"] 24 [label="nom\ndépartement"] 28 [label="raison\nsociale"] // Weak and strong entity attributes 2 [label=<nom ayant-
    droit
    > style="dashed,filled"] 10 [label=<réf.
    pièce
    >] 15 [label=<matricule>] 17 [label=<num.
    projet
    >] 23 [label=<num.
    département
    >] 27 [label=<num.
    société
    >] // Relationship attributes node [ fillcolor="#FFFFFF" ] 9 [label="qté\nrequise"] 13 [label="quantité"] 21 [label="qté\nfournie"] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="DIRIGER"] 7 [label="REQUÉRIR"] 12 [label="COMPOSER"] 14 [label="DF",peripheries=2] 19 [label="FOURNIR"] 25 [label="EMPLOYER"] 26 [label="TRAVAILLER"] 29 [label="CONTRÔLER"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 15 5 -- 16 6 -- 17 6 -- 18 8 -- 10 8 -- 11 20 -- 27 20 -- 28 22 -- 23 22 -- 24 // Edges between relationships and attributes edge [color="#000000"] 7 -- 9 12 -- 13 19 -- 21 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 22 -- 25 5 -- 4 [color="#000000"] 5 -- 14 [color="#000000"] 20 -- 29 [color="#000000"] edge [headlabel=M] 6 -- 26 8 -- 7 [color="#000000"] 8 -- 12 [color="#000000"] edge [headlabel=N] 1 -- 14 5 -- 25 6 -- 7 6 -- 19 8 -- 19 20 -- 19 5 -- 26 [color="#000000"] 6 -- 4 [color="#000000"] 12 -- 8 [color="#000000"] 29 -- 20 [color="#000000"] } ================================================ FILE: test/zoo/landing/exported/landing_erd_chen.txt ================================================ [DÉPARTEMENT] ==1== [EMPLOYÉ] --1-- <> [EMPLOYÉ] --1-- [EMPLOYÉ] --N-- [EMPLOYÉ] ==N== [PIÈCE] --M-- [PIÈCE] --M-- [PIÈCE] --N-- [PIÈCE] ==N== [PROJET] --N-- [PROJET] ==M== [PROJET] ==N== [PROJET] ==N== [SOCIÉTÉ] --1-- [SOCIÉTÉ] --N-- [SOCIÉTÉ] ==N== [[AYANT-DROIT]] ==N== <> ================================================ FILE: test/zoo/landing/exported/landing_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    AYANT-DROIT
    PKnom ayant-droit
    lien
    >] 2 [label=<
    REQUÉRIR
    PKqté requise
    >] 3 [label=<
    PIÈCE
    PKréf. pièce
    libellé pièce
    >] 4 [label=<
    COMPOSER
    PKquantité
    >] 5 [label=<
    EMPLOYÉ
    PKmatricule
    nom employé
    >] 6 [label=<
    PROJET
    PKnum. projet
    nom projet
    >] 7 [label=<
    FOURNIR
    PKqté fournie
    >] 8 [label=<
    DÉPARTEMENT
    PKnum. département
    nom département
    >] 9 [label=<
    SOCIÉTÉ
    PKnum. société
    raison sociale
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 5 -> 6 [arrowhead="crowodot" arrowtail="teeodot" label="DIRIGER"] 2 -> 6 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] 2 -> 3 [arrowhead="teetee" arrowtail="crowodot" label="DF" style=dotted] 4 -> 3 [arrowhead="teetee" arrowtail="crowodot" label="DF" style=dotted] 4 -> 3 [arrowhead="teetee" arrowtail="crowodot" label="DF" style=dotted] 1 -> 5 [arrowhead="teetee" arrowtail="crowodot" label="DF" style=dotted] 7 -> 6 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] 7 -> 3 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] 7 -> 9 [arrowhead="teetee" arrowtail="crowtee" label="DF" style=dotted] 5 -> 8 [arrowhead="teetee" arrowtail="crowtee" label="EMPLOYER"] 5 -> 6 [arrowhead="crowodot" arrowtail="crowtee" label="TRAVAILLER"] 9 -> 9 [arrowhead="crowodot" arrowtail="teeodot" label="CONTRÔLER"] } ================================================ FILE: test/zoo/landing/exported/landing_erd_crow.mmd ================================================ erDiagram AYANT_DROIT { TYPE nom_ayant_droit PK TYPE lien } REQUERIR { TYPE qte_requise PK } PIECE { TYPE ref_piece PK TYPE libelle_piece } COMPOSER { TYPE quantite PK } EMPLOYE { TYPE matricule PK TYPE nom_employe } PROJET { TYPE num_projet PK TYPE nom_projet } FOURNIR { TYPE qte_fournie PK } DEPARTEMENT { TYPE num_departement PK TYPE nom_departement } SOCIETE { TYPE num_societe PK TYPE raison_sociale } EMPLOYE |o--o{ PROJET: DIRIGER REQUERIR }|..|| PROJET: DF REQUERIR }o..|| PIECE: DF COMPOSER }o..|| PIECE: DF COMPOSER }o..|| PIECE: DF AYANT_DROIT }o..|| EMPLOYE: DF FOURNIR }|..|| PROJET: DF FOURNIR }|..|| PIECE: DF FOURNIR }|..|| SOCIETE: DF EMPLOYE }|--|| DEPARTEMENT: EMPLOYER EMPLOYE }|--o{ PROJET: TRAVAILLER SOCIETE |o--o{ SOCIETE: CONTROLER ================================================ FILE: test/zoo/landing/exported/landing_uml.puml ================================================ @startuml "landing" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("AYANT-DROIT") { {field} + pk(nom ayant-droit) {field} + lien } "EMPLOYÉ" "1..*" --- "*" "PROJET": "DIRIGER" "PROJET" "*" --- "1..*" "PIÈCE": "REQUÉRIR" ("PROJET", "PIÈCE") .. "REQUÉRIR" Table("REQUÉRIR") { {field} + qté requise } Table("PIÈCE") { {field} + pk(réf. pièce) {field} + libellé pièce } "PIÈCE" "*" --- "*" "PIÈCE": "COMPOSER" ("PIÈCE", "PIÈCE") .. "COMPOSER" Table("COMPOSER") { {field} + quantité } "AYANT-DROIT" "*" --* "1" "EMPLOYÉ" Table("EMPLOYÉ") { {field} + pk(matricule) {field} + nom employé } Table("PROJET") { {field} + pk(num. projet) {field} + nom projet } diamond N_ARY_0 N_ARY_0 -- "1..*" "PROJET" N_ARY_0 -- "1..*" "PIÈCE" N_ARY_0 -- "1..*" "SOCIÉTÉ" N_ARY_0 "FOURNIR" .. "FOURNIR" Table("FOURNIR") { {field} + qté fournie } Table("DÉPARTEMENT") { {field} + pk(num. département) {field} + nom département } "EMPLOYÉ" "1..*" --- "1" "DÉPARTEMENT": "EMPLOYER" "EMPLOYÉ" "1..*" --- "*" "PROJET": "TRAVAILLER" Table("SOCIÉTÉ") { {field} + pk(num. société) {field} + raison sociale } "SOCIÉTÉ" "1..*" --- "*" "SOCIÉTÉ": "CONTRÔLER" @enduml ================================================ FILE: test/zoo/landing/mld/landing_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note AYANT-DROIT matricule ! strengthening_primary_foreign_key True EMPLOYÉ EMPLOYÉ DF AYANT-DROIT nom ayant-droit ! primary_key True AYANT-DROIT lien normal_attribute False COMPOSER réf. pièce ! primary_foreign_key True PIÈCE PIÈCE COMPOSER composée COMPOSER réf. pièce ! primary_foreign_key True PIÈCE PIÈCE COMPOSER composante COMPOSER quantité association_attribute False COMPOSER DÉPARTEMENT num. département ! primary_key True DÉPARTEMENT nom département normal_attribute False EMPLOYÉ matricule ! primary_key True EMPLOYÉ nom employé normal_attribute False EMPLOYÉ num. département ! foreign_key False DÉPARTEMENT DÉPARTEMENT EMPLOYER FOURNIR num. projet ! primary_foreign_key True PROJET PROJET FOURNIR FOURNIR réf. pièce ! primary_foreign_key True PIÈCE PIÈCE FOURNIR FOURNIR num. société ! primary_foreign_key True SOCIÉTÉ SOCIÉTÉ FOURNIR FOURNIR qté fournie association_attribute False FOURNIR PIÈCE réf. pièce ! primary_key True PIÈCE libellé pièce normal_attribute False PROJET num. projet ! primary_key True PROJET nom projet normal_attribute False PROJET matricule ? foreign_key False EMPLOYÉ EMPLOYÉ DIRIGER responsable REQUÉRIR num. projet ! primary_foreign_key True PROJET PROJET REQUÉRIR REQUÉRIR réf. pièce ! primary_foreign_key True PIÈCE PIÈCE REQUÉRIR REQUÉRIR qté requise association_attribute False REQUÉRIR SOCIÉTÉ num. société ! primary_key True SOCIÉTÉ raison sociale normal_attribute False SOCIÉTÉ num. société ? foreign_key False SOCIÉTÉ SOCIÉTÉ CONTRÔLER mère TRAVAILLER matricule ! primary_foreign_key True EMPLOYÉ EMPLOYÉ TRAVAILLER TRAVAILLER num. projet ! primary_foreign_key True PROJET PROJET TRAVAILLER ================================================ FILE: test/zoo/landing/mld/landing_dependencies.gv ================================================ digraph { node [shape=box] "EMPLOYÉ" -> "AYANT-DROIT" "PIÈCE" -> "COMPOSER" "DÉPARTEMENT" -> "EMPLOYÉ" "SOCIÉTÉ" -> "FOURNIR" "PIÈCE" -> "FOURNIR" "PROJET" -> "FOURNIR" "EMPLOYÉ" -> "PROJET" "PIÈCE" -> "REQUÉRIR" "PROJET" -> "REQUÉRIR" "SOCIÉTÉ" -> "SOCIÉTÉ" "PROJET" -> "TRAVAILLER" "EMPLOYÉ" -> "TRAVAILLER" } ================================================ FILE: test/zoo/landing/mld/landing_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD landing
    AYANT-DROIT ( #matricule, nom ayant-droit, lien )
    • Le champ matricule fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité EMPLOYÉ pour renforcer l'identifiant.
    • Le champ nom ayant-droit fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité AYANT-DROIT.
    • Le champ lien était déjà un simple attribut de l'entité AYANT-DROIT.
    COMPOSER ( #réf. pièce composée, #réf. pièce composante, quantité )
    • Les champs réf. pièce composée et réf. pièce composante constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité PIÈCE.
    • Le champ quantité était déjà un simple attribut de l'association COMPOSER.
    DÉPARTEMENT ( num. département, nom département )
    • Le champ num. département constitue la clé primaire de la table. C'était déjà un identifiant de l'entité DÉPARTEMENT.
    • Le champ nom département était déjà un simple attribut de l'entité DÉPARTEMENT.
    EMPLOYÉ ( matricule, nom employé, #num. département! )
    • Le champ matricule constitue la clé primaire de la table. C'était déjà un identifiant de l'entité EMPLOYÉ.
    • Le champ nom employé était déjà un simple attribut de l'entité EMPLOYÉ.
    • Le champ à saisie obligatoire num. département est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle EMPLOYER à partir de l'entité DÉPARTEMENT en perdant son caractère identifiant.
    FOURNIR ( #num. projet, #réf. pièce, #num. société, qté fournie )
    • Le champ num. projet fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité PROJET.
    • Le champ réf. pièce fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité PIÈCE.
    • Le champ num. société fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité SOCIÉTÉ.
    • Le champ qté fournie était déjà un simple attribut de l'association FOURNIR.
    PIÈCE ( réf. pièce, libellé pièce )
    • Le champ réf. pièce constitue la clé primaire de la table. C'était déjà un identifiant de l'entité PIÈCE.
    • Le champ libellé pièce était déjà un simple attribut de l'entité PIÈCE.
    PROJET ( num. projet, nom projet, #matricule responsable? )
    • Le champ num. projet constitue la clé primaire de la table. C'était déjà un identifiant de l'entité PROJET.
    • Le champ nom projet était déjà un simple attribut de l'entité PROJET.
    • Le champ à saisie facultative matricule responsable est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle DIRIGER à partir de l'entité EMPLOYÉ en perdant son caractère identifiant.
    REQUÉRIR ( #num. projet, #réf. pièce, qté requise )
    • Le champ num. projet fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité PROJET.
    • Le champ réf. pièce fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité PIÈCE.
    • Le champ qté requise était déjà un simple attribut de l'association REQUÉRIR.
    SOCIÉTÉ ( num. société, raison sociale, #num. société mère? )
    • Le champ num. société constitue la clé primaire de la table. C'était déjà un identifiant de l'entité SOCIÉTÉ.
    • Le champ raison sociale était déjà un simple attribut de l'entité SOCIÉTÉ.
    • Le champ à saisie facultative num. société mère est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle CONTRÔLER à partir de l'entité SOCIÉTÉ en perdant son caractère identifiant.
    TRAVAILLER ( #matricule, #num. projet )
    • Le champ matricule fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité EMPLOYÉ.
    • Le champ num. projet fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité PROJET.
    ================================================ FILE: test/zoo/landing/mld/landing_mld.mcd ================================================ %%mocodo : AYANT-DROIT: #matricule > EMPLOYÉ > matricule, _nom ayant-droit, lien ::: REQUÉRIR: #num. projet > PROJET > num. projet, _#réf. pièce > PIÈCE > réf. pièce, qté requise : PIÈCE: réf. pièce, libellé pièce : COMPOSER: #réf. pièce composée > PIÈCE > réf. pièce, _#réf. pièce composante > PIÈCE > réf. pièce, quantité : ::: EMPLOYÉ: matricule, nom employé, #num. département > DÉPARTEMENT > num. département : PROJET: num. projet, nom projet, #matricule responsable > EMPLOYÉ > matricule : FOURNIR: #num. projet > PROJET > num. projet, _#réf. pièce > PIÈCE > réf. pièce, _#num. société > SOCIÉTÉ > num. société, qté fournie ::: : DÉPARTEMENT: num. département, nom département ::: TRAVAILLER: #matricule > EMPLOYÉ > matricule, _#num. projet > PROJET > num. projet : SOCIÉTÉ: num. société, raison sociale, #num. société mère > SOCIÉTÉ > num. société ::: ================================================ FILE: test/zoo/landing/mld/landing_mld.md ================================================ - **AYANT-DROIT** (_#matricule_, nom ayant-droit, lien) - Le champ _matricule_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _EMPLOYÉ_ pour renforcer l'identifiant. - Le champ _nom ayant-droit_ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _AYANT-DROIT_. - Le champ _lien_ était déjà un simple attribut de l'entité _AYANT-DROIT_. - **COMPOSER** (_#réf. pièce composée_, _#réf. pièce composante_, quantité) - Les champs _réf. pièce composée_ et _réf. pièce composante_ constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité _PIÈCE_. - Le champ _quantité_ était déjà un simple attribut de l'association _COMPOSER_. - **DÉPARTEMENT** (num. département, nom département) - Le champ _num. département_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _DÉPARTEMENT_. - Le champ _nom département_ était déjà un simple attribut de l'entité _DÉPARTEMENT_. - **EMPLOYÉ** (matricule, nom employé, _#num. département!_) - Le champ _matricule_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _EMPLOYÉ_. - Le champ _nom employé_ était déjà un simple attribut de l'entité _EMPLOYÉ_. - Le champ à saisie obligatoire _num. département_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _EMPLOYER_ à partir de l'entité _DÉPARTEMENT_ en perdant son caractère identifiant. - **FOURNIR** (_#num. projet_, _#réf. pièce_, _#num. société_, qté fournie) - Le champ _num. projet_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _PROJET_. - Le champ _réf. pièce_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _PIÈCE_. - Le champ _num. société_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _SOCIÉTÉ_. - Le champ _qté fournie_ était déjà un simple attribut de l'association _FOURNIR_. - **PIÈCE** (réf. pièce, libellé pièce) - Le champ _réf. pièce_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _PIÈCE_. - Le champ _libellé pièce_ était déjà un simple attribut de l'entité _PIÈCE_. - **PROJET** (num. projet, nom projet, _#matricule responsable?_) - Le champ _num. projet_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _PROJET_. - Le champ _nom projet_ était déjà un simple attribut de l'entité _PROJET_. - Le champ à saisie facultative _matricule responsable_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _DIRIGER_ à partir de l'entité _EMPLOYÉ_ en perdant son caractère identifiant. - **REQUÉRIR** (_#num. projet_, _#réf. pièce_, qté requise) - Le champ _num. projet_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _PROJET_. - Le champ _réf. pièce_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _PIÈCE_. - Le champ _qté requise_ était déjà un simple attribut de l'association _REQUÉRIR_. - **SOCIÉTÉ** (num. société, raison sociale, _#num. société mère?_) - Le champ _num. société_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _SOCIÉTÉ_. - Le champ _raison sociale_ était déjà un simple attribut de l'entité _SOCIÉTÉ_. - Le champ à saisie facultative _num. société mère_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _CONTRÔLER_ à partir de l'entité _SOCIÉTÉ_ en perdant son caractère identifiant. - **TRAVAILLER** (_#matricule_, _#num. projet_) - Le champ _matricule_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _EMPLOYÉ_. - Le champ _num. projet_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _PROJET_. ================================================ FILE: test/zoo/landing/mld/landing_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{landing}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{AYANT-DROIT} (\foreign{\prim{matricule}}, \prim{nom ayant-droit}, \attr{lien}) \begin{itemize} \item Le champ \emph{matricule} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{EMPLOYÉ} pour renforcer l'identifiant. \item Le champ \emph{nom ayant-droit} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{AYANT-DROIT}. \item Le champ \emph{lien} était déjà un simple attribut de l'entité \emph{AYANT-DROIT}. \end{itemize} \item \relat{COMPOSER} (\foreign{\prim{réf. pièce composée}}, \foreign{\prim{réf. pièce composante}}, \attr{quantité}) \begin{itemize} \item Les champs \emph{réf. pièce composée} et \emph{réf. pièce composante} constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité \emph{PIÈCE}. \item Le champ \emph{quantité} était déjà un simple attribut de l'association \emph{COMPOSER}. \end{itemize} \item \relat{DÉPARTEMENT} (\prim{num. département}, \attr{nom département}) \begin{itemize} \item Le champ \emph{num. département} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{DÉPARTEMENT}. \item Le champ \emph{nom département} était déjà un simple attribut de l'entité \emph{DÉPARTEMENT}. \end{itemize} \item \relat{EMPLOYÉ} (\prim{matricule}, \attr{nom employé}, \foreign{num. département!}) \begin{itemize} \item Le champ \emph{matricule} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{EMPLOYÉ}. \item Le champ \emph{nom employé} était déjà un simple attribut de l'entité \emph{EMPLOYÉ}. \item Le champ à saisie obligatoire \emph{num. département} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{EMPLOYER} à partir de l'entité \emph{DÉPARTEMENT} en perdant son caractère identifiant. \end{itemize} \item \relat{FOURNIR} (\foreign{\prim{num. projet}}, \foreign{\prim{réf. pièce}}, \foreign{\prim{num. société}}, \attr{qté fournie}) \begin{itemize} \item Le champ \emph{num. projet} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{PROJET}. \item Le champ \emph{réf. pièce} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{PIÈCE}. \item Le champ \emph{num. société} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{SOCIÉTÉ}. \item Le champ \emph{qté fournie} était déjà un simple attribut de l'association \emph{FOURNIR}. \end{itemize} \item \relat{PIÈCE} (\prim{réf. pièce}, \attr{libellé pièce}) \begin{itemize} \item Le champ \emph{réf. pièce} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{PIÈCE}. \item Le champ \emph{libellé pièce} était déjà un simple attribut de l'entité \emph{PIÈCE}. \end{itemize} \item \relat{PROJET} (\prim{num. projet}, \attr{nom projet}, \foreign{matricule responsable?}) \begin{itemize} \item Le champ \emph{num. projet} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{PROJET}. \item Le champ \emph{nom projet} était déjà un simple attribut de l'entité \emph{PROJET}. \item Le champ à saisie facultative \emph{matricule responsable} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{DIRIGER} à partir de l'entité \emph{EMPLOYÉ} en perdant son caractère identifiant. \end{itemize} \item \relat{REQUÉRIR} (\foreign{\prim{num. projet}}, \foreign{\prim{réf. pièce}}, \attr{qté requise}) \begin{itemize} \item Le champ \emph{num. projet} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{PROJET}. \item Le champ \emph{réf. pièce} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{PIÈCE}. \item Le champ \emph{qté requise} était déjà un simple attribut de l'association \emph{REQUÉRIR}. \end{itemize} \item \relat{SOCIÉTÉ} (\prim{num. société}, \attr{raison sociale}, \foreign{num. société mère?}) \begin{itemize} \item Le champ \emph{num. société} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{SOCIÉTÉ}. \item Le champ \emph{raison sociale} était déjà un simple attribut de l'entité \emph{SOCIÉTÉ}. \item Le champ à saisie facultative \emph{num. société mère} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{CONTRÔLER} à partir de l'entité \emph{SOCIÉTÉ} en perdant son caractère identifiant. \end{itemize} \item \relat{TRAVAILLER} (\foreign{\prim{matricule}}, \foreign{\prim{num. projet}}) \begin{itemize} \item Le champ \emph{matricule} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{EMPLOYÉ}. \item Le champ \emph{num. projet} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{PROJET}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/landing/mld/landing_mld.txt ================================================ - AYANT-DROIT (_#matricule_, _nom ayant-droit_, lien) - Le champ « matricule » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « EMPLOYÉ » pour renforcer l'identifiant. - Le champ « nom ayant-droit » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « AYANT-DROIT ». - Le champ « lien » était déjà un simple attribut de l'entité « AYANT-DROIT ». - COMPOSER (_#réf. pièce composée_, _#réf. pièce composante_, quantité) - Les champs « réf. pièce composée » et « réf. pièce composante » constituent la clé primaire de la table. Ce sont des clés étrangères qui ont migré directement à partir de l'entité « PIÈCE ». - Le champ « quantité » était déjà un simple attribut de l'association « COMPOSER ». - DÉPARTEMENT (_num. département_, nom département) - Le champ « num. département » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « DÉPARTEMENT ». - Le champ « nom département » était déjà un simple attribut de l'entité « DÉPARTEMENT ». - EMPLOYÉ (_matricule_, nom employé, #num. département!) - Le champ « matricule » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « EMPLOYÉ ». - Le champ « nom employé » était déjà un simple attribut de l'entité « EMPLOYÉ ». - Le champ à saisie obligatoire « num. département » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « EMPLOYER » à partir de l'entité « DÉPARTEMENT » en perdant son caractère identifiant. - FOURNIR (_#num. projet_, _#réf. pièce_, _#num. société_, qté fournie) - Le champ « num. projet » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « PROJET ». - Le champ « réf. pièce » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « PIÈCE ». - Le champ « num. société » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « SOCIÉTÉ ». - Le champ « qté fournie » était déjà un simple attribut de l'association « FOURNIR ». - PIÈCE (_réf. pièce_, libellé pièce) - Le champ « réf. pièce » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « PIÈCE ». - Le champ « libellé pièce » était déjà un simple attribut de l'entité « PIÈCE ». - PROJET (_num. projet_, nom projet, #matricule responsable?) - Le champ « num. projet » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « PROJET ». - Le champ « nom projet » était déjà un simple attribut de l'entité « PROJET ». - Le champ à saisie facultative « matricule responsable » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « DIRIGER » à partir de l'entité « EMPLOYÉ » en perdant son caractère identifiant. - REQUÉRIR (_#num. projet_, _#réf. pièce_, qté requise) - Le champ « num. projet » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « PROJET ». - Le champ « réf. pièce » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « PIÈCE ». - Le champ « qté requise » était déjà un simple attribut de l'association « REQUÉRIR ». - SOCIÉTÉ (_num. société_, raison sociale, #num. société mère?) - Le champ « num. société » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « SOCIÉTÉ ». - Le champ « raison sociale » était déjà un simple attribut de l'entité « SOCIÉTÉ ». - Le champ à saisie facultative « num. société mère » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « CONTRÔLER » à partir de l'entité « SOCIÉTÉ » en perdant son caractère identifiant. - TRAVAILLER (_#matricule_, _#num. projet_) - Le champ « matricule » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « EMPLOYÉ ». - Le champ « num. projet » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « PROJET ». ================================================ FILE: test/zoo/landing/rewritten/landing_rw_create_df_arrows=across.mcd ================================================ AYANT-DROIT: nom ayant-droit, lien DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise PIÈCE: réf. pièce, libellé pièce COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité DF, _11 AYANT-DROIT, 0N> EMPLOYÉ EMPLOYÉ: matricule, nom employé PROJET: num. projet, nom projet FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N> DÉPARTEMENT TRAVAILLER, 0N EMPLOYÉ, 1N PROJET SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET ================================================ FILE: test/zoo/landing/rewritten/landing_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/landing/rewritten/landing_rw_drown.mcd ================================================ ENTITÉ 01_: at 01 1, at 01 2 ASSOC 07_, 0N [rôle 1] ENTITÉ 03_, 01 ENTITÉ 04_ ASSOC 08_, 1N ENTITÉ 04_, 0N ENTITÉ 02_: at 08 1 ENTITÉ 02_: at 02 1, at 02 2 ASSOC 09_, 0N [rôle 1] ENTITÉ 02_, 0N [rôle 2] ENTITÉ 02_: at 09 1 DF, _11 ENTITÉ 01_, 0N ENTITÉ 03_ ENTITÉ 03_: at 03 1, at 03 2 ENTITÉ 04_: at 04 1, at 04 2 ASSOC 11_, 1N ENTITÉ 04_, 1N ENTITÉ 02_, 1N ENTITÉ 06_: at 11 1 ENTITÉ 05_: at 05 1, at 05 2 ASSOC 12_, 11 ENTITÉ 03_, 1N ENTITÉ 05_ ASSOC 13_, 0N ENTITÉ 03_, 1N ENTITÉ 04_ ENTITÉ 06_: at 06 1, at 06 2 ASSOC 14_, 0N< [rôle 1] ENTITÉ 06_, 01 [rôle 2] ENTITÉ 06_ (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..ENTITÉ 02_, ->ASSOC 08_, --ASSOC 11_, ENTITÉ 04_ ================================================ FILE: test/zoo/landing/rewritten/landing_rw_explode_arity=2,weak.mcd ================================================ : : : : : : CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ DÉPARTEMENT: num. département, nom département DF, _11 TRAVAILLER, 0N EMPLOYÉ TRAVAILLER: DF, _11 TRAVAILLER, 1N PROJET : DF, _11 FOURNIR, 1N SOCIÉTÉ SOCIÉTÉ: num. société, raison sociale EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT EMPLOYÉ: matricule, nom employé DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET PROJET: num. projet, nom projet DF, _11 FOURNIR, 1N PROJET FOURNIR: _qté fournie : : DF, _11 AYANT-DROIT, 0N EMPLOYÉ : DF, _11 REQUÉRIR, 1N PROJET REQUÉRIR: _qté requise DF, _11 FOURNIR, 1N PIÈCE : : AYANT-DROIT: nom ayant-droit, lien : : DF, _11 REQUÉRIR, 0N PIÈCE PIÈCE: réf. pièce, libellé pièce DF, _11 COMPOSER, 0N [composée] PIÈCE : : : : : DF, _11 COMPOSER, 0N [composante] PIÈCE COMPOSER: _quantité (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET ================================================ FILE: test/zoo/landing/rewritten/landing_rw_explode_arity=2.5,weak.mcd ================================================ COMPOSER: _quantité DF, _11 COMPOSER, 0N [composée] PIÈCE : DF, _11 FOURNIR, 1N SOCIÉTÉ SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ : DF, _11 COMPOSER, 0N [composante] PIÈCE PIÈCE: réf. pièce, libellé pièce DF, _11 FOURNIR, 1N PIÈCE FOURNIR: _qté fournie : : : : DF, _11 REQUÉRIR, 0N PIÈCE REQUÉRIR: _qté requise DF, _11 FOURNIR, 1N PROJET : : : : : DF, _11 REQUÉRIR, 1N PROJET PROJET: num. projet, nom projet DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET : : : : : TRAVAILLER, 0N EMPLOYÉ, 1N PROJET EMPLOYÉ: matricule, nom employé EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT DÉPARTEMENT: num. département, nom département : : : : DF, _11 AYANT-DROIT, 0N EMPLOYÉ AYANT-DROIT: nom ayant-droit, lien : (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET ================================================ FILE: test/zoo/landing/rewritten/landing_rw_explode_arity=2.5.mcd ================================================ COMPOSER: id. composer, quantité DF, 11 COMPOSER, 0N [composée] PIÈCE : DF, 11 FOURNIR, 1N SOCIÉTÉ SOCIÉTÉ: num. société, raison sociale CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ : DF, 11 COMPOSER, 0N [composante] PIÈCE PIÈCE: réf. pièce, libellé pièce DF, 11 FOURNIR, 1N PIÈCE FOURNIR: id. fournir, qté fournie : : : : DF, 11 REQUÉRIR, 0N PIÈCE REQUÉRIR: id. requérir, qté requise DF, 11 FOURNIR, 1N PROJET : : : : : DF, 11 REQUÉRIR, 1N PROJET PROJET: num. projet, nom projet DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET : : : : : TRAVAILLER, 0N EMPLOYÉ, 1N PROJET EMPLOYÉ: matricule, nom employé EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT DÉPARTEMENT: num. département, nom département : : : : DF, _11 AYANT-DROIT, 0N EMPLOYÉ AYANT-DROIT: nom ayant-droit, lien : (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET ================================================ FILE: test/zoo/landing/rewritten/landing_rw_explode_arity=2.mcd ================================================ : : : : : : CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ DÉPARTEMENT: num. département, nom département DF, 11 TRAVAILLER, 0N EMPLOYÉ TRAVAILLER: id. travailler DF, 11 TRAVAILLER, 1N PROJET : DF, 11 FOURNIR, 1N SOCIÉTÉ SOCIÉTÉ: num. société, raison sociale EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT EMPLOYÉ: matricule, nom employé DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET PROJET: num. projet, nom projet DF, 11 FOURNIR, 1N PROJET FOURNIR: id. fournir, qté fournie : : DF, _11 AYANT-DROIT, 0N EMPLOYÉ : DF, 11 REQUÉRIR, 1N PROJET REQUÉRIR: id. requérir, qté requise DF, 11 FOURNIR, 1N PIÈCE : : AYANT-DROIT: nom ayant-droit, lien : : DF, 11 REQUÉRIR, 0N PIÈCE PIÈCE: réf. pièce, libellé pièce DF, 11 COMPOSER, 0N [composée] PIÈCE : : : : : DF, 11 COMPOSER, 0N [composante] PIÈCE COMPOSER: id. composer, quantité (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET ================================================ FILE: test/zoo/landing/rewritten/landing_rw_explode_arity=3,weak.mcd ================================================ : : COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité PIÈCE: réf. pièce, libellé pièce DF, _11 FOURNIR, 1N PIÈCE : : : : : REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise FOURNIR: _qté fournie DF, _11 FOURNIR, 1N SOCIÉTÉ SOCIÉTÉ: num. société, raison sociale : : TRAVAILLER, 0N EMPLOYÉ, 1N PROJET PROJET: num. projet, nom projet DF, _11 FOURNIR, 1N PROJET : CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT EMPLOYÉ: matricule, nom employé DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET : : : : : DF, _11 AYANT-DROIT, 0N EMPLOYÉ : : : : : : AYANT-DROIT: nom ayant-droit, lien : : : : (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET ================================================ FILE: test/zoo/landing/rewritten/landing_rw_explode_arity=3.mcd ================================================ : : COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité PIÈCE: réf. pièce, libellé pièce DF, 11 FOURNIR, 1N PIÈCE : : : : : REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise FOURNIR: id. fournir, qté fournie DF, 11 FOURNIR, 1N SOCIÉTÉ SOCIÉTÉ: num. société, raison sociale : : TRAVAILLER, 0N EMPLOYÉ, 1N PROJET PROJET: num. projet, nom projet DF, 11 FOURNIR, 1N PROJET : CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ DÉPARTEMENT: num. département, nom département EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT EMPLOYÉ: matricule, nom employé DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET : : : : : DF, _11 AYANT-DROIT, 0N EMPLOYÉ : : : : : : AYANT-DROIT: nom ayant-droit, lien : : : : (I) [Les pièces fournies par une société pour un projet font partie de celles qu'il requiert.] ..PIÈCE, ->REQUÉRIR, --FOURNIR, PROJET ================================================ FILE: test/zoo/landing/rewritten/landing_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/minimal/_minimal.mcd ================================================ A: ================================================ FILE: test/zoo/minimal/ddl/minimal_ddl.d2 ================================================ ================================================ FILE: test/zoo/minimal/ddl/minimal_ddl.dbml ================================================ ================================================ FILE: test/zoo/minimal/ddl/minimal_ddl.sql ================================================ ================================================ FILE: test/zoo/minimal/exported/minimal_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] // Associative entities 1 [label="A",shape=Mdiamond] } ================================================ FILE: test/zoo/minimal/exported/minimal_erd_chen.txt ================================================ ================================================ FILE: test/zoo/minimal/exported/minimal_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    A
    >] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] } ================================================ FILE: test/zoo/minimal/exported/minimal_erd_crow.mmd ================================================ erDiagram A { } ================================================ FILE: test/zoo/minimal/exported/minimal_uml.puml ================================================ @startuml "minimal" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("A") { } @enduml ================================================ FILE: test/zoo/minimal/mld/minimal_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note ================================================ FILE: test/zoo/minimal/mld/minimal_dependencies.gv ================================================ digraph { node [shape=box] } ================================================ FILE: test/zoo/minimal/mld/minimal_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD minimal


    NB. La table A a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/minimal/mld/minimal_mld.mcd ================================================ %%mocodo : ================================================ FILE: test/zoo/minimal/mld/minimal_mld.md ================================================
    ---- **NB.** La table _A_ a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/minimal/mld/minimal_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{minimal}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/minimal/mld/minimal_mld.txt ================================================
    -------------------------------------------------------------------------------- NB. La table « A » a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/minimal/rewritten/minimal_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/minimal/rewritten/minimal_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/minimal/rewritten/minimal_rw_drown.mcd ================================================ ENTITÉ 1_: ================================================ FILE: test/zoo/minimal/rewritten/minimal_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/minimal/rewritten/minimal_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/minimal/rewritten/minimal_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/minimal/rewritten/minimal_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/minimal/rewritten/minimal_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/minimal/rewritten/minimal_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/minimal/rewritten/minimal_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/protected/_protected_0.mcd ================================================ % source: https://www.developpez.net/forums/d962823/general-developpement/alm/methodes/merise/regles-passage-mcd-mld-associations-binaires/ Agence: id. agence, nom agence +Superviser, 1N Direction régionale, 01 Agence +Direction régionale: id. dir., nom dir. ================================================ FILE: test/zoo/protected/ddl/protected_0_ddl.d2 ================================================ "Agence": { shape: sql_table "id. agence": VARCHAR(42) {constraint: PK} "nom agence": VARCHAR(42) } "Direction régionale": { shape: sql_table "id. dir.": VARCHAR(42) {constraint: PK} "nom dir.": VARCHAR(42) } "Superviser": { shape: sql_table "id. agence": VARCHAR(42) {constraint: [PK; FK]} "id. dir.": VARCHAR(42) {constraint: [FK; NOT NULL]} } "Superviser"."id. agence" -> "Agence"."id. agence" "Superviser"."id. dir." -> "Direction régionale"."id. dir." ================================================ FILE: test/zoo/protected/ddl/protected_0_ddl.dbml ================================================ Table "Agence" { "id. agence" VARCHAR(42) [pk, NOT NULL] "nom agence" VARCHAR(42) } Table "Direction régionale" { "id. dir." VARCHAR(42) [pk, NOT NULL] "nom dir." VARCHAR(42) } Table "Superviser" { "id. agence" VARCHAR(42) [pk, NOT NULL] "id. dir." VARCHAR(42) [NOT NULL] } Ref:"Superviser"."id. agence" > "Agence"."id. agence" Ref:"Superviser"."id. dir." > "Direction régionale"."id. dir." ================================================ FILE: test/zoo/protected/ddl/protected_0_ddl.sql ================================================ CREATE TABLE AGENCE ( PRIMARY KEY (id_agence), id_agence VARCHAR(8) NOT NULL, nom_agence VARCHAR(255) ); CREATE TABLE DIRECTION_REGIONALE ( PRIMARY KEY (id_dir), id_dir VARCHAR(8) NOT NULL, nom_dir VARCHAR(255) ); CREATE TABLE SUPERVISER ( PRIMARY KEY (id_agence), id_agence VARCHAR(8) NOT NULL, id_dir VARCHAR(8) NOT NULL ); ALTER TABLE SUPERVISER ADD FOREIGN KEY (id_dir) REFERENCES DIRECTION_REGIONALE (id_dir); ALTER TABLE SUPERVISER ADD FOREIGN KEY (id_agence) REFERENCES AGENCE (id_agence); ================================================ FILE: test/zoo/protected/exported/protected_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Agence"] 5 [label="Direction\nrégionale"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="nom\nagence"] 7 [label="nom dir."] // Weak and strong entity attributes 2 [label=<id.
    agence
    >] 6 [label=<id. dir.>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="Superviser"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 6 5 -- 7 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 5 -- 4 edge [headlabel=N] 1 -- 4 [color="#000000"] } ================================================ FILE: test/zoo/protected/exported/protected_0_erd_chen.txt ================================================ [Agence] --N-- [Direction régionale] ==1== ================================================ FILE: test/zoo/protected/exported/protected_0_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    Agence
    PKid. agence
    nom agence
    >] 2 [label=<
    Direction régionale
    PKid. dir.
    nom dir.
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="crowtee" arrowtail="teeodot" label="Superviser"] } ================================================ FILE: test/zoo/protected/exported/protected_0_erd_crow.mmd ================================================ erDiagram Agence { TYPE id_agence PK TYPE nom_agence } Direction_regionale { TYPE id_dir PK TYPE nom_dir } Direction_regionale |o--|{ Agence: Superviser ================================================ FILE: test/zoo/protected/exported/protected_0_uml.puml ================================================ @startuml "protected" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Agence") { {field} + pk(id. agence) {field} + nom agence } "Direction régionale" "1..*" --- "1..*" "Agence": "Superviser" Table("Direction régionale") { {field} + pk(id. dir.) {field} + nom dir. } @enduml ================================================ FILE: test/zoo/protected/mld/protected_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Agence id. agence ! primary_key True Agence nom agence normal_attribute False Direction régionale id. dir. ! primary_key True Direction régionale nom dir. normal_attribute False Superviser id. agence ! primary_foreign_key True Agence Agence Superviser Superviser id. dir. ! stopped_foreign_key False Direction régionale Direction régionale Superviser ================================================ FILE: test/zoo/protected/mld/protected_0_dependencies.gv ================================================ digraph { node [shape=box] "Direction régionale" -> "Superviser" "Agence" -> "Superviser" } ================================================ FILE: test/zoo/protected/mld/protected_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD protected
    Agence ( id. agence, nom agence )
    • Le champ id. agence constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Agence.
    • Le champ nom agence était déjà un simple attribut de l'entité Agence.
    Direction régionale ( id. dir., nom dir. )
    • Le champ id. dir. constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Direction régionale.
    • Le champ nom dir. était déjà un simple attribut de l'entité Direction régionale.
    Superviser ( #id. agence, #id. dir.! )
    • Avertissement. Table résultant de la conversion forcée d'une association DF.
    • Le champ id. agence constitue la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Agence.
    • Le champ à saisie obligatoire id. dir. est une clé étrangère. Il a migré directement à partir de l'entité Direction régionale en perdant son caractère identifiant.
    ================================================ FILE: test/zoo/protected/mld/protected_0_mld.mcd ================================================ %%mocodo : Agence: id. agence, nom agence : Superviser: #id. agence > Agence > id. agence, #id. dir. > Direction régionale > id. dir. : Direction régionale: id. dir., nom dir. : ================================================ FILE: test/zoo/protected/mld/protected_0_mld.md ================================================ - **Agence** (id. agence, nom agence) - Le champ _id. agence_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Agence_. - Le champ _nom agence_ était déjà un simple attribut de l'entité _Agence_. - **Direction régionale** (id. dir., nom dir.) - Le champ _id. dir._ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Direction régionale_. - Le champ _nom dir._ était déjà un simple attribut de l'entité _Direction régionale_. - **Superviser** (_#id. agence_, _#id. dir.!_) - **Avertissement.** Table résultant de la conversion forcée d'une association DF. - Le champ _id. agence_ constitue la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Agence_. - Le champ à saisie obligatoire _id. dir._ est une clé étrangère. Il a migré directement à partir de l'entité _Direction régionale_ en perdant son caractère identifiant. ================================================ FILE: test/zoo/protected/mld/protected_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{protected}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Agence} (\prim{id. agence}, \attr{nom agence}) \begin{itemize} \item Le champ \emph{id. agence} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Agence}. \item Le champ \emph{nom agence} était déjà un simple attribut de l'entité \emph{Agence}. \end{itemize} \item \relat{Direction régionale} (\prim{id. dir.}, \attr{nom dir.}) \begin{itemize} \item Le champ \emph{id. dir.} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Direction régionale}. \item Le champ \emph{nom dir.} était déjà un simple attribut de l'entité \emph{Direction régionale}. \end{itemize} \item \relat{Superviser} (\foreign{\prim{id. agence}}, \foreign{id. dir.!}) \begin{itemize} \item \paragraph{Avertissement.} Table résultant de la conversion forcée d'une association DF. \item Le champ \emph{id. agence} constitue la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{Agence}. \item Le champ à saisie obligatoire \emph{id. dir.} est une clé étrangère. Il a migré directement à partir de l'entité \emph{Direction régionale} en perdant son caractère identifiant. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/protected/mld/protected_0_mld.txt ================================================ - Agence (_id. agence_, nom agence) - Le champ « id. agence » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Agence ». - Le champ « nom agence » était déjà un simple attribut de l'entité « Agence ». - Direction régionale (_id. dir._, nom dir.) - Le champ « id. dir. » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Direction régionale ». - Le champ « nom dir. » était déjà un simple attribut de l'entité « Direction régionale ». - Superviser (_#id. agence_, #id. dir.!) - Avertissement. Table résultant de la conversion forcée d'une association DF. - Le champ « id. agence » constitue la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « Agence ». - Le champ à saisie obligatoire « id. dir. » est une clé étrangère. Il a migré directement à partir de l'entité « Direction régionale » en perdant son caractère identifiant. ================================================ FILE: test/zoo/protected/rewritten/protected_0_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/protected/rewritten/protected_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/protected/rewritten/protected_0_rw_drown.mcd ================================================ % source: https://www.developpez.net/forums/d962823/general-developpement/alm/methodes/merise/regles-passage-mcd-mld-associations-binaires/ ENTITÉ 1_: at 1 1, at 1 2 +ASSOC 3_, 1N ENTITÉ 2_, 01 ENTITÉ 1_ +ENTITÉ 2_: at 2 1, at 2 2 ================================================ FILE: test/zoo/protected/rewritten/protected_0_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/protected/rewritten/protected_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/protected/rewritten/protected_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/protected/rewritten/protected_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/protected/rewritten/protected_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/protected/rewritten/protected_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/protected/rewritten/protected_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/reflexive/_reflexive_0.mcd ================================================ PIÈCE: pièce COMPOSER, 0N [composante] PIÈCE, 0N [composée] PIÈCE HOMME: Num. SS, Nom, Prénom ENGENDRER, 0N [père] HOMME, 01 [fils] HOMME ================================================ FILE: test/zoo/reflexive/ddl/reflexive_0_ddl.d2 ================================================ "COMPOSER": { shape: sql_table "pièce composante": VARCHAR(42) {constraint: PK} "pièce composée": VARCHAR(42) {constraint: PK} } "HOMME": { shape: sql_table "Num. SS": VARCHAR(42) {constraint: PK} "Nom": VARCHAR(42) "Prénom": VARCHAR(42) "Num. SS père": VARCHAR(42) {constraint: [FK; "NULL"]} } "HOMME"."Num. SS père" -> "HOMME"."Num. SS" ================================================ FILE: test/zoo/reflexive/ddl/reflexive_0_ddl.dbml ================================================ Table "COMPOSER" { "pièce composante" VARCHAR(42) [NOT NULL] "pièce composée" VARCHAR(42) [NOT NULL] Indexes { ("pièce composante", "pièce composée") [pk] } } Table "HOMME" { "Num. SS" VARCHAR(42) [pk, NOT NULL] "Nom" VARCHAR(42) "Prénom" VARCHAR(42) "Num. SS père" VARCHAR(42) ["NULL"] } Ref:"HOMME"."Num. SS père" > "HOMME"."Num. SS" ================================================ FILE: test/zoo/reflexive/ddl/reflexive_0_ddl.sql ================================================ CREATE TABLE COMPOSER ( PRIMARY KEY (piece_composante, piece_composee), piece_composante VARCHAR(42) NOT NULL, piece_composee VARCHAR(42) NOT NULL ); CREATE TABLE HOMME ( PRIMARY KEY (num_ss), num_ss VARCHAR(8) NOT NULL, nom VARCHAR(255), prenom VARCHAR(255), num_ss_pere VARCHAR(8) NULL ); ALTER TABLE HOMME ADD FOREIGN KEY (num_ss_pere) REFERENCES HOMME (num_ss); ================================================ FILE: test/zoo/reflexive/exported/reflexive_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="PIÈCE"] 4 [label="HOMME"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 6 [label="Nom"] 7 [label="Prénom"] // Weak and strong entity attributes 2 [label=<pièce>] 5 [label=<Num. SS>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="COMPOSER"] 8 [label="ENGENDRER"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 4 -- 5 4 -- 6 4 -- 7 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 4 -- 8 [color="#000000"] edge [headlabel=M] 1 -- 3 [color="#000000"] edge [headlabel=N] 3 -- 1 [color="#000000"] 8 -- 4 [color="#000000"] } ================================================ FILE: test/zoo/reflexive/exported/reflexive_0_erd_chen.txt ================================================ [HOMME] --1-- [HOMME] --N-- [PIÈCE] --M-- [PIÈCE] --N-- ================================================ FILE: test/zoo/reflexive/exported/reflexive_0_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    PIÈCE
    PKpièce
    >] 2 [label=<
    HOMME
    PKNum. SS
    Nom
    Prénom
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 1 -> 1 [arrowhead="crowodot" arrowtail="crowodot" label="COMPOSER"] 2 -> 2 [arrowhead="crowodot" arrowtail="teeodot" label="ENGENDRER"] } ================================================ FILE: test/zoo/reflexive/exported/reflexive_0_erd_crow.mmd ================================================ erDiagram PIECE { TYPE piece PK } HOMME { TYPE Num_SS PK TYPE Nom TYPE Prenom } PIECE }o--o{ PIECE: COMPOSER HOMME |o--o{ HOMME: ENGENDRER ================================================ FILE: test/zoo/reflexive/exported/reflexive_0_uml.puml ================================================ @startuml "reflexive" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("PIÈCE") { {field} + pk(pièce) } "PIÈCE" "*" --- "*" "PIÈCE": "COMPOSER" Table("HOMME") { {field} + pk(Num. SS) {field} + Nom {field} + Prénom } "HOMME" "1..*" --- "*" "HOMME": "ENGENDRER" @enduml ================================================ FILE: test/zoo/reflexive/mld/reflexive_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note COMPOSER pièce ! primary_ex_foreign_key True PIÈCE PIÈCE COMPOSER composante COMPOSER pièce ! primary_ex_foreign_key True PIÈCE PIÈCE COMPOSER composée HOMME Num. SS ! primary_key True HOMME Nom normal_attribute False HOMME Prénom normal_attribute False HOMME Num. SS ? foreign_key False HOMME HOMME ENGENDRER père ================================================ FILE: test/zoo/reflexive/mld/reflexive_0_dependencies.gv ================================================ digraph { node [shape=box] "HOMME" -> "HOMME" } ================================================ FILE: test/zoo/reflexive/mld/reflexive_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD reflexive
    COMPOSER ( pièce composante, pièce composée )
    • Les champs pièce composante et pièce composée constituent la clé primaire de la table. Leur table d'origine (PIÈCE) ayant été supprimée, ils ne sont pas considérés comme clés étrangères.
    HOMME ( Num. SS, Nom, Prénom, #Num. SS père? )
    • Le champ Num. SS constitue la clé primaire de la table. C'était déjà un identifiant de l'entité HOMME.
    • Les champs Nom et Prénom étaient déjà de simples attributs de l'entité HOMME.
    • Le champ à saisie facultative Num. SS père est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle ENGENDRER à partir de l'entité HOMME en perdant son caractère identifiant.


    NB. La table PIÈCE a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/reflexive/mld/reflexive_0_mld.mcd ================================================ %%mocodo : COMPOSER: pièce composante, _pièce composée : HOMME: Num. SS, Nom, Prénom, #Num. SS père > HOMME > Num. SS : ================================================ FILE: test/zoo/reflexive/mld/reflexive_0_mld.md ================================================ - **COMPOSER** (pièce composante, pièce composée) - Les champs _pièce composante_ et _pièce composée_ constituent la clé primaire de la table. Leur table d'origine (_PIÈCE_) ayant été supprimée, ils ne sont pas considérés comme clés étrangères. - **HOMME** (Num. SS, Nom, Prénom, _#Num. SS père?_) - Le champ _Num. SS_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _HOMME_. - Les champs _Nom_ et _Prénom_ étaient déjà de simples attributs de l'entité _HOMME_. - Le champ à saisie facultative _Num. SS père_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _ENGENDRER_ à partir de l'entité _HOMME_ en perdant son caractère identifiant.
    ---- **NB.** La table _PIÈCE_ a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/reflexive/mld/reflexive_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{reflexive}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{COMPOSER} (\prim{pièce composante}, \prim{pièce composée}) \begin{itemize} \item Les champs \emph{pièce composante} et \emph{pièce composée} constituent la clé primaire de la table. Leur table d'origine (\emph{PIÈCE}) ayant été supprimée, ils ne sont pas considérés comme clés étrangères. \end{itemize} \item \relat{HOMME} (\prim{Num. SS}, \attr{Nom}, \attr{Prénom}, \foreign{Num. SS père?}) \begin{itemize} \item Le champ \emph{Num. SS} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{HOMME}. \item Les champs \emph{Nom} et \emph{Prénom} étaient déjà de simples attributs de l'entité \emph{HOMME}. \item Le champ à saisie facultative \emph{Num. SS père} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{ENGENDRER} à partir de l'entité \emph{HOMME} en perdant son caractère identifiant. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/reflexive/mld/reflexive_0_mld.txt ================================================ - COMPOSER (_pièce composante_, _pièce composée_) - Les champs « pièce composante » et « pièce composée » constituent la clé primaire de la table. Leur table d'origine (« PIÈCE ») ayant été supprimée, ils ne sont pas considérés comme clés étrangères. - HOMME (_Num. SS_, Nom, Prénom, #Num. SS père?) - Le champ « Num. SS » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « HOMME ». - Les champs « Nom » et « Prénom » étaient déjà de simples attributs de l'entité « HOMME ». - Le champ à saisie facultative « Num. SS père » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « ENGENDRER » à partir de l'entité « HOMME » en perdant son caractère identifiant.
    -------------------------------------------------------------------------------- NB. La table « PIÈCE » a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/reflexive/rewritten/reflexive_0_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/reflexive/rewritten/reflexive_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/reflexive/rewritten/reflexive_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1 ASSOC 3_, 0N [rôle 1] ENTITÉ 1_, 0N [rôle 2] ENTITÉ 1_ ENTITÉ 2_: at 2 1, at 2 2, at 2 3 ASSOC 4_, 0N [rôle 1] ENTITÉ 2_, 01 [rôle 2] ENTITÉ 2_ ================================================ FILE: test/zoo/reflexive/rewritten/reflexive_0_rw_explode_arity=2,weak.mcd ================================================ PIÈCE: pièce DF, _11 COMPOSER, 0N [composée] PIÈCE ENGENDRER, 0N [père] HOMME, 01 [fils] HOMME DF, _11 COMPOSER, 0N [composante] PIÈCE COMPOSER: HOMME: Num. SS, Nom, Prénom ================================================ FILE: test/zoo/reflexive/rewritten/reflexive_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/reflexive/rewritten/reflexive_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/reflexive/rewritten/reflexive_0_rw_explode_arity=2.mcd ================================================ PIÈCE: pièce DF, 11 COMPOSER, 0N [composée] PIÈCE ENGENDRER, 0N [père] HOMME, 01 [fils] HOMME DF, 11 COMPOSER, 0N [composante] PIÈCE COMPOSER: id. composer HOMME: Num. SS, Nom, Prénom ================================================ FILE: test/zoo/reflexive/rewritten/reflexive_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/reflexive/rewritten/reflexive_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/reflexive/rewritten/reflexive_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/split/_split.mcd ================================================ Bataille: nom bataille, lieu, date Récolter, 0N Villageois, 0N Bataille, 11 Trophée Villageois: nom villageois, adresse, fonction Trophée: numéro, type, état ================================================ FILE: test/zoo/split/ddl/split_ddl.d2 ================================================ "Bataille": { shape: sql_table "nom bataille": VARCHAR(42) {constraint: PK} "lieu": VARCHAR(42) "date": VARCHAR(42) } "Trophée": { shape: sql_table "numéro": VARCHAR(42) {constraint: PK} "type": VARCHAR(42) "état": VARCHAR(42) "nom villageois": VARCHAR(42) {constraint: [FK; NOT NULL]} "nom bataille": VARCHAR(42) {constraint: [FK; NOT NULL]} } "Villageois": { shape: sql_table "nom villageois": VARCHAR(42) {constraint: PK} "adresse": VARCHAR(42) "fonction": VARCHAR(42) } "Trophée"."nom villageois" -> "Villageois"."nom villageois" "Trophée"."nom bataille" -> "Bataille"."nom bataille" ================================================ FILE: test/zoo/split/ddl/split_ddl.dbml ================================================ Table "Bataille" { "nom bataille" VARCHAR(42) [pk, NOT NULL] "lieu" VARCHAR(42) "date" VARCHAR(42) } Table "Trophée" { "numéro" VARCHAR(42) [pk, NOT NULL] "type" VARCHAR(42) "état" VARCHAR(42) "nom villageois" VARCHAR(42) [NOT NULL] "nom bataille" VARCHAR(42) [NOT NULL] } Table "Villageois" { "nom villageois" VARCHAR(42) [pk, NOT NULL] "adresse" VARCHAR(42) "fonction" VARCHAR(42) } Ref:"Trophée"."nom villageois" > "Villageois"."nom villageois" Ref:"Trophée"."nom bataille" > "Bataille"."nom bataille" ================================================ FILE: test/zoo/split/ddl/split_ddl.sql ================================================ CREATE TABLE BATAILLE ( PRIMARY KEY (nom_bataille), nom_bataille VARCHAR(255) NOT NULL, lieu VARCHAR(42), date DATE ); CREATE TABLE TROPHEE ( PRIMARY KEY (numero), numero VARCHAR(8) NOT NULL, type VARCHAR(42), etat VARCHAR(42), nom_villageois VARCHAR(255) NOT NULL, nom_bataille VARCHAR(255) NOT NULL ); CREATE TABLE VILLAGEOIS ( PRIMARY KEY (nom_villageois), nom_villageois VARCHAR(255) NOT NULL, adresse VARCHAR(30), fonction VARCHAR(42) ); ALTER TABLE TROPHEE ADD FOREIGN KEY (nom_bataille) REFERENCES BATAILLE (nom_bataille); ALTER TABLE TROPHEE ADD FOREIGN KEY (nom_villageois) REFERENCES VILLAGEOIS (nom_villageois); ================================================ FILE: test/zoo/split/exported/split_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Bataille"] 7 [label="Villageois"] 6 [label="Trophée"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="lieu"] 4 [label="date"] 10 [label="adresse"] 11 [label="fonction"] 13 [label="type"] 14 [label="état"] // Weak and strong entity attributes 2 [label=<nom
    bataille
    >] 9 [label=<nom
    villageois
    >] 12 [label=<numéro>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 5 [label="Récolter"] 8 [label="Récolter"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 1 -- 4 6 -- 12 6 -- 13 6 -- 14 7 -- 9 7 -- 10 7 -- 11 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 8 [color="#000000"] 7 -- 5 [color="#000000"] edge [headlabel=N] 6 -- 5 6 -- 8 } ================================================ FILE: test/zoo/split/exported/split_erd_chen.txt ================================================ [Bataille] --1-- [Trophée] ==N== [Trophée] ==N== [Villageois] --1-- ================================================ FILE: test/zoo/split/exported/split_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    Bataille
    PKnom bataille
    lieu
    date
    >] 2 [label=<
    Villageois
    PKnom villageois
    adresse
    fonction
    >] 3 [label=<
    Trophée
    PKnuméro
    type
    état
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 3 -> 2 [arrowhead="teetee" arrowtail="crowodot" label="Récolter"] 3 -> 1 [arrowhead="teetee" arrowtail="crowodot" label="Récolter"] } ================================================ FILE: test/zoo/split/exported/split_erd_crow.mmd ================================================ erDiagram Bataille { TYPE nom_bataille PK TYPE lieu TYPE date } Villageois { TYPE nom_villageois PK TYPE adresse TYPE fonction } Trophee { TYPE numero PK TYPE type TYPE etat } Trophee }o--|| Villageois: Recolter Trophee }o--|| Bataille: Recolter ================================================ FILE: test/zoo/split/exported/split_uml.puml ================================================ @startuml "split" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Bataille") { {field} + pk(nom bataille) {field} + lieu {field} + date } "Trophée" "*" --- "1" "Villageois": "Récolter0" "Trophée" "*" --- "1" "Bataille": "Récolter1" Table("Villageois") { {field} + pk(nom villageois) {field} + adresse {field} + fonction } Table("Trophée") { {field} + pk(numéro) {field} + type {field} + état } @enduml ================================================ FILE: test/zoo/split/mld/split_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Bataille nom bataille ! primary_key True Bataille lieu normal_attribute False Bataille date normal_attribute False Trophée numéro ! primary_key True Trophée type normal_attribute False Trophée état normal_attribute False Trophée nom villageois ! foreign_key False Villageois Villageois Récolter Trophée nom bataille ! foreign_key False Bataille Bataille Récolter Villageois nom villageois ! primary_key True Villageois adresse normal_attribute False Villageois fonction normal_attribute False ================================================ FILE: test/zoo/split/mld/split_dependencies.gv ================================================ digraph { node [shape=box] "Bataille" -> "Trophée" "Villageois" -> "Trophée" } ================================================ FILE: test/zoo/split/mld/split_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD split
    Bataille ( nom bataille, lieu, date )
    • Le champ nom bataille constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Bataille.
    • Les champs lieu et date étaient déjà de simples attributs de l'entité Bataille.
    Trophée ( numéro, type, état, #nom villageois!, #nom bataille! )
    • Le champ numéro constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Trophée.
    • Les champs type et état étaient déjà de simples attributs de l'entité Trophée.
    • Le champ à saisie obligatoire nom villageois est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle Récolter à partir de l'entité Villageois en perdant son caractère identifiant.
    • Le champ à saisie obligatoire nom bataille est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle Récolter à partir de l'entité Bataille en perdant son caractère identifiant.
    Villageois ( nom villageois, adresse, fonction )
    • Le champ nom villageois constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Villageois.
    • Les champs adresse et fonction étaient déjà de simples attributs de l'entité Villageois.
    ================================================ FILE: test/zoo/split/mld/split_mld.mcd ================================================ %%mocodo : Bataille: nom bataille, lieu, date ::: Villageois: nom villageois, adresse, fonction : ::: Trophée: numéro, type, état, #nom villageois > Villageois > nom villageois, #nom bataille > Bataille > nom bataille ::: ================================================ FILE: test/zoo/split/mld/split_mld.md ================================================ - **Bataille** (nom bataille, lieu, date) - Le champ _nom bataille_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Bataille_. - Les champs _lieu_ et _date_ étaient déjà de simples attributs de l'entité _Bataille_. - **Trophée** (numéro, type, état, _#nom villageois!_, _#nom bataille!_) - Le champ _numéro_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Trophée_. - Les champs _type_ et _état_ étaient déjà de simples attributs de l'entité _Trophée_. - Le champ à saisie obligatoire _nom villageois_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _Récolter_ à partir de l'entité _Villageois_ en perdant son caractère identifiant. - Le champ à saisie obligatoire _nom bataille_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _Récolter_ à partir de l'entité _Bataille_ en perdant son caractère identifiant. - **Villageois** (nom villageois, adresse, fonction) - Le champ _nom villageois_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Villageois_. - Les champs _adresse_ et _fonction_ étaient déjà de simples attributs de l'entité _Villageois_. ================================================ FILE: test/zoo/split/mld/split_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{split}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Bataille} (\prim{nom bataille}, \attr{lieu}, \attr{date}) \begin{itemize} \item Le champ \emph{nom bataille} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Bataille}. \item Les champs \emph{lieu} et \emph{date} étaient déjà de simples attributs de l'entité \emph{Bataille}. \end{itemize} \item \relat{Trophée} (\prim{numéro}, \attr{type}, \attr{état}, \foreign{nom villageois!}, \foreign{nom bataille!}) \begin{itemize} \item Le champ \emph{numéro} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Trophée}. \item Les champs \emph{type} et \emph{état} étaient déjà de simples attributs de l'entité \emph{Trophée}. \item Le champ à saisie obligatoire \emph{nom villageois} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{Récolter} à partir de l'entité \emph{Villageois} en perdant son caractère identifiant. \item Le champ à saisie obligatoire \emph{nom bataille} est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle \emph{Récolter} à partir de l'entité \emph{Bataille} en perdant son caractère identifiant. \end{itemize} \item \relat{Villageois} (\prim{nom villageois}, \attr{adresse}, \attr{fonction}) \begin{itemize} \item Le champ \emph{nom villageois} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Villageois}. \item Les champs \emph{adresse} et \emph{fonction} étaient déjà de simples attributs de l'entité \emph{Villageois}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/split/mld/split_mld.txt ================================================ - Bataille (_nom bataille_, lieu, date) - Le champ « nom bataille » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Bataille ». - Les champs « lieu » et « date » étaient déjà de simples attributs de l'entité « Bataille ». - Trophée (_numéro_, type, état, #nom villageois!, #nom bataille!) - Le champ « numéro » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Trophée ». - Les champs « type » et « état » étaient déjà de simples attributs de l'entité « Trophée ». - Le champ à saisie obligatoire « nom villageois » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « Récolter » à partir de l'entité « Villageois » en perdant son caractère identifiant. - Le champ à saisie obligatoire « nom bataille » est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle « Récolter » à partir de l'entité « Bataille » en perdant son caractère identifiant. - Villageois (_nom villageois_, adresse, fonction) - Le champ « nom villageois » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Villageois ». - Les champs « adresse » et « fonction » étaient déjà de simples attributs de l'entité « Villageois ». ================================================ FILE: test/zoo/split/rewritten/split_rw_create_df_arrows=across.mcd ================================================ Bataille: nom bataille, lieu, date Récolter, 0N> Villageois, 0N> Bataille, 11 Trophée Villageois: nom villageois, adresse, fonction Trophée: numéro, type, état ================================================ FILE: test/zoo/split/rewritten/split_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/split/rewritten/split_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2, at 1 3 ASSOC 4_, 0N ENTITÉ 2_, 0N ENTITÉ 1_, 11 ENTITÉ 3_ ENTITÉ 2_: at 2 1, at 2 2, at 2 3 ENTITÉ 3_: at 3 1, at 3 2, at 3 3 ================================================ FILE: test/zoo/split/rewritten/split_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/split/rewritten/split_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/split/rewritten/split_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/split/rewritten/split_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/split/rewritten/split_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/split/rewritten/split_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/split/rewritten/split_rw_split.mcd ================================================ Bataille: nom bataille, lieu, date Récolter1, 11 Trophée, 0N Bataille : : Trophée: numéro, type, état Récolter0, 11 Trophée, 0N Villageois : : Villageois: nom villageois, adresse, fonction ================================================ FILE: test/zoo/ternary_unicity/_ternary_unicity_0.mcd ================================================ Voilier: voilier DF, _11 Disponibilité, 1N Voilier Disponibilité: DF, _11 Disponibilité, 1N Semaine Semaine: semaine, 1_date début DF, 11 Réservation, 01 Disponibilité Réservation: id résa, 1_num résa, arrhes, date réservation ================================================ FILE: test/zoo/ternary_unicity/ddl/ternary_unicity_0_ddl.d2 ================================================ "Disponibilité": { shape: sql_table "semaine": VARCHAR(42) {constraint: [PK; FK]} "voilier": VARCHAR(42) {constraint: PK} } "Réservation": { shape: sql_table "id résa": VARCHAR(42) {constraint: PK} "num résa": VARCHAR(42) {constraint: UNQ1} "arrhes": VARCHAR(42) "date réservation": VARCHAR(42) "semaine": VARCHAR(42) {constraint: [FK; NOT NULL]} "voilier": VARCHAR(42) {constraint: [FK; NOT NULL]} } "Semaine": { shape: sql_table "semaine": VARCHAR(42) {constraint: PK} "date début": VARCHAR(42) {constraint: UNQ1} } "Disponibilité"."semaine" -> "Semaine"."semaine" "Réservation"."semaine" -> "Disponibilité"."semaine" "Réservation"."voilier" -> "Disponibilité"."voilier" ================================================ FILE: test/zoo/ternary_unicity/ddl/ternary_unicity_0_ddl.dbml ================================================ Table "Disponibilité" { "semaine" VARCHAR(42) [NOT NULL] "voilier" VARCHAR(42) [NOT NULL] Indexes { ("semaine", "voilier") [pk] } } Table "Réservation" { "id résa" VARCHAR(42) [pk, NOT NULL] "num résa" VARCHAR(42) "arrhes" VARCHAR(42) "date réservation" VARCHAR(42) "semaine" VARCHAR(42) [NOT NULL] "voilier" VARCHAR(42) [NOT NULL] Indexes { "num résa" [unique] ("semaine", "voilier") [unique] } } Table "Semaine" { "semaine" VARCHAR(42) [pk, NOT NULL] "date début" VARCHAR(42) Indexes { "date début" [unique] } } Ref:"Disponibilité"."semaine" > "Semaine"."semaine" Ref:"Réservation".("semaine", "voilier") > "Disponibilité".("semaine", "voilier") ================================================ FILE: test/zoo/ternary_unicity/ddl/ternary_unicity_0_ddl.sql ================================================ CREATE TABLE DISPONIBILITE ( PRIMARY KEY (semaine, voilier), semaine VARCHAR(42) NOT NULL, voilier VARCHAR(42) NOT NULL ); CREATE TABLE RESERVATION ( PRIMARY KEY (id_resa), id_resa VARCHAR(8) NOT NULL, num_resa VARCHAR(8), arrhes VARCHAR(42), date_reservation DATE, semaine VARCHAR(42) NOT NULL, voilier VARCHAR(42) NOT NULL, UNIQUE (num_resa), UNIQUE (semaine, voilier) ); CREATE TABLE SEMAINE ( PRIMARY KEY (semaine), semaine VARCHAR(42) NOT NULL, date_debut DATE, UNIQUE (date_debut) ); ALTER TABLE DISPONIBILITE ADD FOREIGN KEY (semaine) REFERENCES SEMAINE (semaine); ALTER TABLE RESERVATION ADD FOREIGN KEY (semaine, voilier) REFERENCES DISPONIBILITE (semaine, voilier); ================================================ FILE: test/zoo/ternary_unicity/exported/ternary_unicity_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Voilier"] 4 [label="Disponibilité",peripheries=2] 6 [label="Semaine"] 10 [label="Réservation"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 8 [label="date\ndébut"] 12 [label="num résa"] 13 [label="arrhes"] 14 [label="date\nréservation"] // Weak and strong entity attributes 2 [label=<voilier>] 7 [label=<semaine>] 11 [label=<id résa>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="DF",peripheries=2] 5 [label="DF",peripheries=2] 9 [label="DF"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 6 -- 7 6 -- 8 10 -- 11 10 -- 12 10 -- 13 10 -- 14 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 3 6 -- 5 10 -- 9 4 -- 9 [color="#000000"] edge [headlabel=N] 4 -- 3 4 -- 5 } ================================================ FILE: test/zoo/ternary_unicity/exported/ternary_unicity_0_erd_chen.txt ================================================ [Réservation] ==1== [Semaine] ==1== <> [Voilier] ==1== <> [[Disponibilité]] --1-- [[Disponibilité]] ==N== <> [[Disponibilité]] ==N== <> ================================================ FILE: test/zoo/ternary_unicity/exported/ternary_unicity_0_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 2 [label=<
    Disponibilité
    >] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    Voilier
    PKvoilier
    >] 3 [label=<
    Semaine
    PKsemaine
    date début
    >] 4 [label=<
    Réservation
    PKid résa
    num résa
    arrhes
    date réservation
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="teetee" arrowtail="crowtee" style=dotted] 2 -> 3 [arrowhead="teetee" arrowtail="crowtee" style=dotted] 4 -> 2 [arrowhead="teetee" arrowtail="teeodot"] } ================================================ FILE: test/zoo/ternary_unicity/exported/ternary_unicity_0_erd_crow.mmd ================================================ erDiagram Voilier { TYPE voilier PK } Disponibilite { } Semaine { TYPE semaine PK TYPE date_debut } Reservation { TYPE id_resa PK TYPE num_resa TYPE arrhes TYPE date_reservation } Disponibilite }|..|| Voilier: DF Disponibilite }|..|| Semaine: DF Reservation |o--|| Disponibilite: DF ================================================ FILE: test/zoo/ternary_unicity/exported/ternary_unicity_0_uml.puml ================================================ @startuml "ternary_unicity" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Voilier") { {field} + pk(voilier) } "Disponibilité" "1..*" --* "1" "Voilier" Table("Disponibilité") { } "Disponibilité" "1..*" --* "1" "Semaine" Table("Semaine") { {field} + pk(semaine) {field} + date début } "Réservation" "1..*" --- "1" "Disponibilité" Table("Réservation") { {field} + pk(id résa) {field} + num résa {field} + arrhes {field} + date réservation } @enduml ================================================ FILE: test/zoo/ternary_unicity/mld/ternary_unicity_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Disponibilité semaine ! strengthening_primary_foreign_key True Semaine Semaine DF Disponibilité voilier ! strengthening_primary_ex_foreign_key True Voilier Voilier DF Réservation id résa ! primary_key True Réservation num résa 1 normal_attribute False Réservation arrhes normal_attribute False Réservation date réservation normal_attribute False Réservation semaine ! 2 foreign_key False Disponibilité Disponibilité DF Réservation voilier ! 2 foreign_key False Disponibilité Disponibilité DF Semaine semaine ! primary_key True Semaine date début 1 normal_attribute False ================================================ FILE: test/zoo/ternary_unicity/mld/ternary_unicity_0_dependencies.gv ================================================ digraph { node [shape=box] "Semaine" -> "Disponibilité" "Disponibilité" -> "Réservation" } ================================================ FILE: test/zoo/ternary_unicity/mld/ternary_unicity_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD ternary_unicity
    Disponibilité ( #semaine, voilier )
    • Le champ semaine fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité Semaine pour renforcer l'identifiant.
    • Le champ voilier fait partie de la clé primaire de la table. Il a migré à partir de l'entité Voilier pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère.
    Réservation ( id résa, num résa u1, arrhes, date réservation, #semaine u2, #voilier u2 )
    • Le champ id résa constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Réservation.
    • Le champ num résa était déjà un simple attribut de l'entité Réservation. Il obéit à la contrainte d'unicité 1.
    • Les champs arrhes et date réservation étaient déjà de simples attributs de l'entité Réservation.
    • Les champs semaine et voilier sont des clés étrangères. Ils ont migré par l'association de dépendance fonctionnelle DF à partir de l'entité Disponibilité en perdant leur caractère identifiant. Ils obéissent en outre à la contrainte d'unicité 2.
    Semaine ( semaine, date début u1 )
    • Le champ semaine constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Semaine.
    • Le champ date début était déjà un simple attribut de l'entité Semaine. Il obéit à la contrainte d'unicité 1.


    NB. La table Voilier a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/ternary_unicity/mld/ternary_unicity_0_mld.mcd ================================================ %%mocodo : Disponibilité: #semaine > Semaine > semaine, _voilier : Semaine: semaine, date début : : Réservation: id résa, num résa, arrhes, date réservation, #semaine > Disponibilité > semaine, #voilier > Disponibilité > voilier ::: ================================================ FILE: test/zoo/ternary_unicity/mld/ternary_unicity_0_mld.md ================================================ - **Disponibilité** (_#semaine_, voilier) - Le champ _semaine_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _Semaine_ pour renforcer l'identifiant. - Le champ _voilier_ fait partie de la clé primaire de la table. Il a migré à partir de l'entité _Voilier_ pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. - **Réservation** (id résa, num résa u1, arrhes, date réservation, _#semaine_ u2, _#voilier_ u2) - Le champ _id résa_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Réservation_. - Le champ _num résa_ était déjà un simple attribut de l'entité _Réservation_. Il obéit à la contrainte d'unicité 1. - Les champs _arrhes_ et _date réservation_ étaient déjà de simples attributs de l'entité _Réservation_. - Les champs _semaine_ et _voilier_ sont des clés étrangères. Ils ont migré par l'association de dépendance fonctionnelle _DF_ à partir de l'entité _Disponibilité_ en perdant leur caractère identifiant. Ils obéissent en outre à la contrainte d'unicité 2. - **Semaine** (semaine, date début u1) - Le champ _semaine_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Semaine_. - Le champ _date début_ était déjà un simple attribut de l'entité _Semaine_. Il obéit à la contrainte d'unicité 1.
    ---- **NB.** La table _Voilier_ a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/ternary_unicity/mld/ternary_unicity_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{ternary\_unicity}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Disponibilité} (\foreign{\prim{semaine}}, \prim{voilier}) \begin{itemize} \item Le champ \emph{semaine} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{Semaine} pour renforcer l'identifiant. \item Le champ \emph{voilier} fait partie de la clé primaire de la table. Il a migré à partir de l'entité \emph{Voilier} pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. \end{itemize} \item \relat{Réservation} (\prim{id résa}, \attr{num résa}$^{u\_1}$, \attr{arrhes}, \attr{date réservation}, \foreign{semaine}$^{u\_2}$, \foreign{voilier}$^{u\_2}$) \begin{itemize} \item Le champ \emph{id résa} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Réservation}. \item Le champ \emph{num résa} était déjà un simple attribut de l'entité \emph{Réservation}. Il obéit à la contrainte d'unicité 1. \item Les champs \emph{arrhes} et \emph{date réservation} étaient déjà de simples attributs de l'entité \emph{Réservation}. \item Les champs \emph{semaine} et \emph{voilier} sont des clés étrangères. Ils ont migré par l'association de dépendance fonctionnelle \emph{DF} à partir de l'entité \emph{Disponibilité} en perdant leur caractère identifiant. Ils obéissent en outre à la contrainte d'unicité 2. \end{itemize} \item \relat{Semaine} (\prim{semaine}, \attr{date début}$^{u\_1}$) \begin{itemize} \item Le champ \emph{semaine} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Semaine}. \item Le champ \emph{date début} était déjà un simple attribut de l'entité \emph{Semaine}. Il obéit à la contrainte d'unicité 1. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/ternary_unicity/mld/ternary_unicity_0_mld.txt ================================================ - Disponibilité (_#semaine_, _voilier_) - Le champ « semaine » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « Semaine » pour renforcer l'identifiant. - Le champ « voilier » fait partie de la clé primaire de la table. Il a migré à partir de l'entité « Voilier » pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. - Réservation (_id résa_, num résa¹, arrhes, date réservation, #semaine², #voilier²) - Le champ « id résa » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Réservation ». - Le champ « num résa » était déjà un simple attribut de l'entité « Réservation ». Il obéit à la contrainte d'unicité 1. - Les champs « arrhes » et « date réservation » étaient déjà de simples attributs de l'entité « Réservation ». - Les champs « semaine » et « voilier » sont des clés étrangères. Ils ont migré par l'association de dépendance fonctionnelle « DF » à partir de l'entité « Disponibilité » en perdant leur caractère identifiant. Ils obéissent en outre à la contrainte d'unicité 2. - Semaine (_semaine_, date début¹) - Le champ « semaine » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Semaine ». - Le champ « date début » était déjà un simple attribut de l'entité « Semaine ». Il obéit à la contrainte d'unicité 1.
    -------------------------------------------------------------------------------- NB. La table « Voilier » a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/ternary_unicity/rewritten/ternary_unicity_0_rw_create_df_arrows=across.mcd ================================================ Voilier: voilier DF, _11 Disponibilité, 1N> Voilier Disponibilité: DF, _11 Disponibilité, 1N> Semaine Semaine: semaine, 1_date début DF, 11 Réservation, 01> Disponibilité Réservation: id résa, 1_num résa, arrhes, date réservation ================================================ FILE: test/zoo/ternary_unicity/rewritten/ternary_unicity_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/ternary_unicity/rewritten/ternary_unicity_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1 DF, _11 ENTITÉ 2_, 1N ENTITÉ 1_ ENTITÉ 2_: DF, _11 ENTITÉ 2_, 1N ENTITÉ 3_ ENTITÉ 3_: at 3 1, 1_at 3 2 DF, 11 ENTITÉ 4_, 01 ENTITÉ 2_ ENTITÉ 4_: at 4 1, 1_at 4 2, at 4 3, at 4 4 ================================================ FILE: test/zoo/ternary_unicity/rewritten/ternary_unicity_0_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/ternary_unicity/rewritten/ternary_unicity_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/ternary_unicity/rewritten/ternary_unicity_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/ternary_unicity/rewritten/ternary_unicity_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/ternary_unicity/rewritten/ternary_unicity_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/ternary_unicity/rewritten/ternary_unicity_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/ternary_unicity/rewritten/ternary_unicity_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/_triple_111_0.mcd ================================================ % Non-orthogonal cluster: not implemented Technicien: technicien :: : Utiliser, /1N Technicien, /1N Carnet, /1N Projet Projet: projet : Carnet: carnet ================================================ FILE: test/zoo/triple_111/_triple_111_1.mcd ================================================ Technicien: technicien, nom technicien Utiliser, /1N Technicien, /1N Carnet, /1N Projet Projet: projet, libellé Carnet: carnet ================================================ FILE: test/zoo/triple_111/ddl/triple_111_0_ddl.d2 ================================================ "Utiliser": { shape: sql_table "carnet": VARCHAR(42) {constraint: [PK; UNQ1]} "projet": VARCHAR(42) {constraint: [PK; UNQ2]} "technicien": VARCHAR(42) {constraint: [NOT NULL; UNQ1; UNQ2]} } ================================================ FILE: test/zoo/triple_111/ddl/triple_111_0_ddl.dbml ================================================ Table "Utiliser" { "carnet" VARCHAR(42) [NOT NULL] "projet" VARCHAR(42) [NOT NULL] "technicien" VARCHAR(42) [NOT NULL] Indexes { ("carnet", "projet") [pk] ("technicien", "projet") [unique] ("carnet", "technicien") [unique] } } ================================================ FILE: test/zoo/triple_111/ddl/triple_111_0_ddl.sql ================================================ CREATE TABLE UTILISER ( PRIMARY KEY (carnet, projet), carnet VARCHAR(42) NOT NULL, projet VARCHAR(42) NOT NULL, technicien VARCHAR(42) NOT NULL, UNIQUE (carnet, technicien), UNIQUE (projet, technicien) ); ================================================ FILE: test/zoo/triple_111/ddl/triple_111_1_ddl.d2 ================================================ "Projet": { shape: sql_table "projet": VARCHAR(42) {constraint: PK} "libellé": VARCHAR(42) } "Technicien": { shape: sql_table "technicien": VARCHAR(42) {constraint: PK} "nom technicien": VARCHAR(42) } "Utiliser": { shape: sql_table "carnet": VARCHAR(42) {constraint: [PK; UNQ1]} "projet": VARCHAR(42) {constraint: [PK; FK]} "technicien": VARCHAR(42) {constraint: [FK; NOT NULL]} } "Utiliser"."projet" -> "Projet"."projet" "Utiliser"."technicien" -> "Technicien"."technicien" ================================================ FILE: test/zoo/triple_111/ddl/triple_111_1_ddl.dbml ================================================ Table "Projet" { "projet" VARCHAR(42) [pk, NOT NULL] "libellé" VARCHAR(42) } Table "Technicien" { "technicien" VARCHAR(42) [pk, NOT NULL] "nom technicien" VARCHAR(42) } Table "Utiliser" { "carnet" VARCHAR(42) [NOT NULL] "projet" VARCHAR(42) [NOT NULL] "technicien" VARCHAR(42) [NOT NULL] Indexes { ("carnet", "projet") [pk] ("technicien", "projet") [unique] ("carnet", "technicien") [unique] } } Ref:"Utiliser"."projet" > "Projet"."projet" Ref:"Utiliser"."technicien" > "Technicien"."technicien" ================================================ FILE: test/zoo/triple_111/ddl/triple_111_1_ddl.sql ================================================ CREATE TABLE PROJET ( PRIMARY KEY (projet), projet VARCHAR(42) NOT NULL, libelle VARCHAR(50) ); CREATE TABLE TECHNICIEN ( PRIMARY KEY (technicien), technicien VARCHAR(42) NOT NULL, nom_technicien VARCHAR(255) ); CREATE TABLE UTILISER ( PRIMARY KEY (carnet, projet), carnet VARCHAR(42) NOT NULL, projet VARCHAR(42) NOT NULL, technicien VARCHAR(42) NOT NULL, UNIQUE (carnet, technicien), UNIQUE (projet, technicien) ); ALTER TABLE UTILISER ADD FOREIGN KEY (technicien) REFERENCES TECHNICIEN (technicien); ALTER TABLE UTILISER ADD FOREIGN KEY (projet) REFERENCES PROJET (projet); ================================================ FILE: test/zoo/triple_111/exported/triple_111_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Technicien"] 5 [label="Projet"] 4 [label="Carnet"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] // Weak and strong entity attributes 2 [label=<technicien>] 6 [label=<projet>] 7 [label=<carnet>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="Utiliser"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 4 -- 7 5 -- 6 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 3 4 -- 3 5 -- 3 } ================================================ FILE: test/zoo/triple_111/exported/triple_111_0_erd_chen.txt ================================================ [Carnet] ==1== [Projet] ==1== [Technicien] ==1== ================================================ FILE: test/zoo/triple_111/exported/triple_111_0_erd_crow.mmd ================================================ Not implemented for n-ary associations. ================================================ FILE: test/zoo/triple_111/exported/triple_111_0_uml.puml ================================================ @startuml "triple_111" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Technicien") { {field} + pk(technicien) } diamond N_ARY_0 N_ARY_0 -- "1" "Technicien" N_ARY_0 -- "1" "Carnet" N_ARY_0 -- "1" "Projet" Table("Projet") { {field} + pk(projet) } Table("Carnet") { {field} + pk(carnet) } @enduml ================================================ FILE: test/zoo/triple_111/exported/triple_111_1_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Technicien"] 6 [label="Projet"] 5 [label="Carnet"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="nom\ntechnicien"] 8 [label="libellé"] // Weak and strong entity attributes 2 [label=<technicien>] 7 [label=<projet>] 9 [label=<carnet>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="Utiliser"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 9 6 -- 7 6 -- 8 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 4 5 -- 4 6 -- 4 } ================================================ FILE: test/zoo/triple_111/exported/triple_111_1_erd_chen.txt ================================================ [Carnet] ==1== [Projet] ==1== [Technicien] ==1== ================================================ FILE: test/zoo/triple_111/exported/triple_111_1_erd_crow.mmd ================================================ Not implemented for n-ary associations. ================================================ FILE: test/zoo/triple_111/exported/triple_111_1_uml.puml ================================================ @startuml "triple_111" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Technicien") { {field} + pk(technicien) {field} + nom technicien } diamond N_ARY_0 N_ARY_0 -- "1" "Technicien" N_ARY_0 -- "1" "Carnet" N_ARY_0 -- "1" "Projet" Table("Projet") { {field} + pk(projet) {field} + libellé } Table("Carnet") { {field} + pk(carnet) } @enduml ================================================ FILE: test/zoo/triple_111/mld/triple_111_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Utiliser carnet ! 1 primary_ex_foreign_key True Carnet Carnet Utiliser Utiliser projet ! 2 primary_ex_foreign_key True Projet Projet Utiliser Utiliser technicien ! 12 stopped_ex_foreign_key False Technicien Technicien Utiliser ================================================ FILE: test/zoo/triple_111/mld/triple_111_0_dependencies.gv ================================================ digraph { node [shape=box] } ================================================ FILE: test/zoo/triple_111/mld/triple_111_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD triple_111
    Utiliser ( carnet u1, projet u2, technicien u1 u2 )
    • Le champ carnet fait partie de la clé primaire de la table. Sa table d'origine (Carnet) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1.
    • Le champ projet fait partie de la clé primaire de la table. Sa table d'origine (Projet) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 2.
    • Le champ technicien est un simple attribut. Il a migré directement à partir de l'entité Technicien en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre aux contraintes d'unicité 1 et 2.


    NB. Les tables Carnet, Projet et Technicien ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/triple_111/mld/triple_111_0_mld.mcd ================================================ : Utiliser: carnet, _projet, technicien : ================================================ FILE: test/zoo/triple_111/mld/triple_111_0_mld.md ================================================ - **Utiliser** (carnet u1, projet u2, technicien u1 u2) - Le champ _carnet_ fait partie de la clé primaire de la table. Sa table d'origine (_Carnet_) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1. - Le champ _projet_ fait partie de la clé primaire de la table. Sa table d'origine (_Projet_) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 2. - Le champ _technicien_ est un simple attribut. Il a migré directement à partir de l'entité _Technicien_ en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre aux contraintes d'unicité 1 et 2.
    ---- **NB.** Les tables _Carnet_, _Projet_ et _Technicien_ ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/triple_111/mld/triple_111_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{triple\_111}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Utiliser} (\prim{carnet}$^{u\_1}$, \prim{projet}$^{u\_2}$, \attr{technicien}$^{u\_1 u\_2}$) \begin{itemize} \item Le champ \emph{carnet} fait partie de la clé primaire de la table. Sa table d'origine (\emph{Carnet}) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1. \item Le champ \emph{projet} fait partie de la clé primaire de la table. Sa table d'origine (\emph{Projet}) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 2. \item Le champ \emph{technicien} est un simple attribut. Il a migré directement à partir de l'entité \emph{Technicien} en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre aux contraintes d'unicité 1 et 2. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/triple_111/mld/triple_111_0_mld.txt ================================================ - Utiliser (_carnet_¹, _projet_², technicien¹²) - Le champ « carnet » fait partie de la clé primaire de la table. Sa table d'origine (« Carnet ») ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1. - Le champ « projet » fait partie de la clé primaire de la table. Sa table d'origine (« Projet ») ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 2. - Le champ « technicien » est un simple attribut. Il a migré directement à partir de l'entité « Technicien » en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre aux contraintes d'unicité 1 et 2.
    -------------------------------------------------------------------------------- NB. Les tables « Carnet », « Projet » et « Technicien » ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/triple_111/mld/triple_111_1_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Projet projet ! primary_key True Projet libellé normal_attribute False Technicien technicien ! primary_key True Technicien nom technicien normal_attribute False Utiliser carnet ! 1 primary_ex_foreign_key True Carnet Carnet Utiliser Utiliser projet ! 2 primary_foreign_key True Projet Projet Utiliser Utiliser technicien ! 12 stopped_foreign_key False Technicien Technicien Utiliser ================================================ FILE: test/zoo/triple_111/mld/triple_111_1_dependencies.gv ================================================ digraph { node [shape=box] "Technicien" -> "Utiliser" "Projet" -> "Utiliser" } ================================================ FILE: test/zoo/triple_111/mld/triple_111_1_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD triple_111
    Projet ( projet, libellé )
    • Le champ projet constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Projet.
    • Le champ libellé était déjà un simple attribut de l'entité Projet.
    Technicien ( technicien, nom technicien )
    • Le champ technicien constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Technicien.
    • Le champ nom technicien était déjà un simple attribut de l'entité Technicien.
    Utiliser ( carnet u1, #projet u2, #technicien u1 u2 )
    • Le champ carnet fait partie de la clé primaire de la table. Sa table d'origine (Carnet) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1.
    • Le champ projet fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Projet. Il obéit en outre à la contrainte d'unicité 2.
    • Le champ technicien est une clé étrangère. Il a migré directement à partir de l'entité Technicien en perdant son caractère identifiant. Il obéit en outre aux contraintes d'unicité 1 et 2.


    NB. La table Carnet a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/triple_111/mld/triple_111_1_mld.mcd ================================================ %%mocodo : Technicien: technicien, nom technicien : Utiliser: carnet, _#projet > Projet > projet, #technicien > Technicien > technicien : Projet: projet, libellé : ================================================ FILE: test/zoo/triple_111/mld/triple_111_1_mld.md ================================================ - **Projet** (projet, libellé) - Le champ _projet_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Projet_. - Le champ _libellé_ était déjà un simple attribut de l'entité _Projet_. - **Technicien** (technicien, nom technicien) - Le champ _technicien_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Technicien_. - Le champ _nom technicien_ était déjà un simple attribut de l'entité _Technicien_. - **Utiliser** (carnet u1, _#projet_ u2, _#technicien_ u1 u2) - Le champ _carnet_ fait partie de la clé primaire de la table. Sa table d'origine (_Carnet_) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1. - Le champ _projet_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Projet_. Il obéit en outre à la contrainte d'unicité 2. - Le champ _technicien_ est une clé étrangère. Il a migré directement à partir de l'entité _Technicien_ en perdant son caractère identifiant. Il obéit en outre aux contraintes d'unicité 1 et 2.
    ---- **NB.** La table _Carnet_ a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/triple_111/mld/triple_111_1_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{triple\_111}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Projet} (\prim{projet}, \attr{libellé}) \begin{itemize} \item Le champ \emph{projet} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Projet}. \item Le champ \emph{libellé} était déjà un simple attribut de l'entité \emph{Projet}. \end{itemize} \item \relat{Technicien} (\prim{technicien}, \attr{nom technicien}) \begin{itemize} \item Le champ \emph{technicien} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Technicien}. \item Le champ \emph{nom technicien} était déjà un simple attribut de l'entité \emph{Technicien}. \end{itemize} \item \relat{Utiliser} (\prim{carnet}$^{u\_1}$, \foreign{\prim{projet}}$^{u\_2}$, \foreign{technicien}$^{u\_1 u\_2}$) \begin{itemize} \item Le champ \emph{carnet} fait partie de la clé primaire de la table. Sa table d'origine (\emph{Carnet}) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1. \item Le champ \emph{projet} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{Projet}. Il obéit en outre à la contrainte d'unicité 2. \item Le champ \emph{technicien} est une clé étrangère. Il a migré directement à partir de l'entité \emph{Technicien} en perdant son caractère identifiant. Il obéit en outre aux contraintes d'unicité 1 et 2. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/triple_111/mld/triple_111_1_mld.txt ================================================ - Projet (_projet_, libellé) - Le champ « projet » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Projet ». - Le champ « libellé » était déjà un simple attribut de l'entité « Projet ». - Technicien (_technicien_, nom technicien) - Le champ « technicien » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Technicien ». - Le champ « nom technicien » était déjà un simple attribut de l'entité « Technicien ». - Utiliser (_carnet_¹, _#projet_², #technicien¹²) - Le champ « carnet » fait partie de la clé primaire de la table. Sa table d'origine (« Carnet ») ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1. - Le champ « projet » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « Projet ». Il obéit en outre à la contrainte d'unicité 2. - Le champ « technicien » est une clé étrangère. Il a migré directement à partir de l'entité « Technicien » en perdant son caractère identifiant. Il obéit en outre aux contraintes d'unicité 1 et 2.
    -------------------------------------------------------------------------------- NB. La table « Carnet » a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_0_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_0_rw_drown.mcd ================================================ % Non-orthogonal cluster: not implemented ENTITÉ 1_: at 1 1 :: : ASSOC 4_, /1N ENTITÉ 1_, /1N ENTITÉ 3_, /1N ENTITÉ 2_ ENTITÉ 2_: at 2 1 : ENTITÉ 3_: at 3 1 ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_0_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_1_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_1_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_1_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2 ASSOC 4_, /1N ENTITÉ 1_, /1N ENTITÉ 3_, /1N ENTITÉ 2_ ENTITÉ 2_: at 2 1, at 2 2 ENTITÉ 3_: at 3 1 ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_1_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_1_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_1_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_1_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_1_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_1_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_111/rewritten/triple_111_1_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/_triple_N11_0.mcd ================================================ Projet: projet Affecter, /1N Site, /1N Projet, 0N Employé Site: site Employé: employé ================================================ FILE: test/zoo/triple_N11/_triple_N11_1.mcd ================================================ Projet: projet, libellé Affecter, /1N Site, /1N Projet, 0N Employé Site: site, position Employé: employé, nom employé (CIF) --Projet, --Employé, ..Affecter, ->Site: Projet, Employé (CIF) ..Affecter, ->Projet, Employé, Site: Site, Employé ================================================ FILE: test/zoo/triple_N11/ddl/triple_N11_0_ddl.d2 ================================================ "Affecter": { shape: sql_table "projet": VARCHAR(42) {constraint: PK} "employé": VARCHAR(42) {constraint: [PK; UNQ1]} "site": VARCHAR(42) {constraint: [NOT NULL; UNQ1]} } ================================================ FILE: test/zoo/triple_N11/ddl/triple_N11_0_ddl.dbml ================================================ Table "Affecter" { "projet" VARCHAR(42) [NOT NULL] "employé" VARCHAR(42) [NOT NULL] "site" VARCHAR(42) [NOT NULL] Indexes { ("projet", "employé") [pk] ("employé", "site") [unique] } } ================================================ FILE: test/zoo/triple_N11/ddl/triple_N11_0_ddl.sql ================================================ CREATE TABLE AFFECTER ( PRIMARY KEY (projet, employe), projet VARCHAR(42) NOT NULL, employe VARCHAR(42) NOT NULL, site VARCHAR(42) NOT NULL, UNIQUE (employe, site) ); ================================================ FILE: test/zoo/triple_N11/ddl/triple_N11_1_ddl.d2 ================================================ "Affecter": { shape: sql_table "projet": VARCHAR(42) {constraint: [PK; FK]} "employé": VARCHAR(42) {constraint: [PK; FK]} "site": VARCHAR(42) {constraint: [FK; NOT NULL]} } "Employé": { shape: sql_table "employé": VARCHAR(42) {constraint: PK} "nom employé": VARCHAR(42) } "Projet": { shape: sql_table "projet": VARCHAR(42) {constraint: PK} "libellé": VARCHAR(42) } "Site": { shape: sql_table "site": VARCHAR(42) {constraint: PK} "position": VARCHAR(42) } "Affecter"."projet" -> "Projet"."projet" "Affecter"."employé" -> "Employé"."employé" "Affecter"."site" -> "Site"."site" ================================================ FILE: test/zoo/triple_N11/ddl/triple_N11_1_ddl.dbml ================================================ Table "Affecter" { "projet" VARCHAR(42) [NOT NULL] "employé" VARCHAR(42) [NOT NULL] "site" VARCHAR(42) [NOT NULL] Indexes { ("projet", "employé") [pk] ("employé", "site") [unique] } } Table "Employé" { "employé" VARCHAR(42) [pk, NOT NULL] "nom employé" VARCHAR(42) } Table "Projet" { "projet" VARCHAR(42) [pk, NOT NULL] "libellé" VARCHAR(42) } Table "Site" { "site" VARCHAR(42) [pk, NOT NULL] "position" VARCHAR(42) } Ref:"Affecter"."projet" > "Projet"."projet" Ref:"Affecter"."employé" > "Employé"."employé" Ref:"Affecter"."site" > "Site"."site" ================================================ FILE: test/zoo/triple_N11/ddl/triple_N11_1_ddl.sql ================================================ CREATE TABLE AFFECTER ( PRIMARY KEY (projet, employe), projet VARCHAR(42) NOT NULL, employe VARCHAR(42) NOT NULL, site VARCHAR(42) NOT NULL, UNIQUE (employe, site) ); CREATE TABLE EMPLOYE ( PRIMARY KEY (employe), employe VARCHAR(42) NOT NULL, nom_employe VARCHAR(255) ); CREATE TABLE PROJET ( PRIMARY KEY (projet), projet VARCHAR(42) NOT NULL, libelle VARCHAR(50) ); CREATE TABLE SITE ( PRIMARY KEY (site), site VARCHAR(42) NOT NULL, position POINT ); ALTER TABLE AFFECTER ADD FOREIGN KEY (site) REFERENCES SITE (site); ALTER TABLE AFFECTER ADD FOREIGN KEY (employe) REFERENCES EMPLOYE (employe); ALTER TABLE AFFECTER ADD FOREIGN KEY (projet) REFERENCES PROJET (projet); ================================================ FILE: test/zoo/triple_N11/exported/triple_N11_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Projet"] 4 [label="Site"] 5 [label="Employé"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] // Weak and strong entity attributes 2 [label=<projet>] 6 [label=<site>] 7 [label=<employé>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="Affecter"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 4 -- 6 5 -- 7 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 3 4 -- 3 edge [headlabel=N] 5 -- 3 [color="#000000"] } ================================================ FILE: test/zoo/triple_N11/exported/triple_N11_0_erd_chen.txt ================================================ [Employé] --N-- [Projet] ==1== [Site] ==1== ================================================ FILE: test/zoo/triple_N11/exported/triple_N11_0_erd_crow.mmd ================================================ Not implemented for n-ary associations. ================================================ FILE: test/zoo/triple_N11/exported/triple_N11_0_uml.puml ================================================ @startuml "triple_N11" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Projet") { {field} + pk(projet) } diamond N_ARY_0 N_ARY_0 -- "1" "Site" N_ARY_0 -- "1" "Projet" N_ARY_0 -- "*" "Employé" Table("Site") { {field} + pk(site) } Table("Employé") { {field} + pk(employé) } @enduml ================================================ FILE: test/zoo/triple_N11/exported/triple_N11_1_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Projet"] 5 [label="Site"] 6 [label="Employé"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="libellé"] 8 [label="position"] 10 [label="nom\nemployé"] // Weak and strong entity attributes 2 [label=<projet>] 7 [label=<site>] 9 [label=<employé>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="Affecter"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 7 5 -- 8 6 -- 9 6 -- 10 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 4 5 -- 4 edge [headlabel=N] 6 -- 4 [color="#000000"] } ================================================ FILE: test/zoo/triple_N11/exported/triple_N11_1_erd_chen.txt ================================================ [Employé] --N-- [Projet] ==1== [Site] ==1== ================================================ FILE: test/zoo/triple_N11/exported/triple_N11_1_erd_crow.mmd ================================================ Not implemented for n-ary associations. ================================================ FILE: test/zoo/triple_N11/exported/triple_N11_1_uml.puml ================================================ @startuml "triple_N11" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Projet") { {field} + pk(projet) {field} + libellé } diamond N_ARY_0 N_ARY_0 -- "1" "Site" N_ARY_0 -- "1" "Projet" N_ARY_0 -- "*" "Employé" Table("Site") { {field} + pk(site) {field} + position } Table("Employé") { {field} + pk(employé) {field} + nom employé } @enduml ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Affecter projet ! primary_ex_foreign_key True Projet Projet Affecter Affecter employé ! 1 primary_ex_foreign_key True Employé Employé Affecter Affecter site ! 1 stopped_ex_foreign_key False Site Site Affecter ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_0_dependencies.gv ================================================ digraph { node [shape=box] } ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD triple_N11
    Affecter ( projet, employé u1, site u1 )
    • Le champ projet fait partie de la clé primaire de la table. Sa table d'origine (Projet) ayant été supprimée, il n'est pas considéré comme clé étrangère.
    • Le champ employé fait partie de la clé primaire de la table. Sa table d'origine (Employé) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1.
    • Le champ site est un simple attribut. Il a migré directement à partir de l'entité Site en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1.


    NB. Les tables Employé, Projet et Site ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_0_mld.mcd ================================================ %%mocodo : Affecter: projet, _employé, site : ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_0_mld.md ================================================ - **Affecter** (projet, employé u1, site u1) - Le champ _projet_ fait partie de la clé primaire de la table. Sa table d'origine (_Projet_) ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ _employé_ fait partie de la clé primaire de la table. Sa table d'origine (_Employé_) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1. - Le champ _site_ est un simple attribut. Il a migré directement à partir de l'entité _Site_ en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1.
    ---- **NB.** Les tables _Employé_, _Projet_ et _Site_ ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{triple\_N11}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Affecter} (\prim{projet}, \prim{employé}$^{u\_1}$, \attr{site}$^{u\_1}$) \begin{itemize} \item Le champ \emph{projet} fait partie de la clé primaire de la table. Sa table d'origine (\emph{Projet}) ayant été supprimée, il n'est pas considéré comme clé étrangère. \item Le champ \emph{employé} fait partie de la clé primaire de la table. Sa table d'origine (\emph{Employé}) ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1. \item Le champ \emph{site} est un simple attribut. Il a migré directement à partir de l'entité \emph{Site} en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_0_mld.txt ================================================ - Affecter (_projet_, _employé_¹, site¹) - Le champ « projet » fait partie de la clé primaire de la table. Sa table d'origine (« Projet ») ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ « employé » fait partie de la clé primaire de la table. Sa table d'origine (« Employé ») ayant été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1. - Le champ « site » est un simple attribut. Il a migré directement à partir de l'entité « Site » en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. Il obéit par contre à la contrainte d'unicité 1.
    -------------------------------------------------------------------------------- NB. Les tables « Employé », « Projet » et « Site » ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_1_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Affecter projet ! primary_foreign_key True Projet Projet Affecter Affecter employé ! 1 primary_foreign_key True Employé Employé Affecter Affecter site ! 1 stopped_foreign_key False Site Site Affecter Employé employé ! primary_key True Employé nom employé normal_attribute False Projet projet ! primary_key True Projet libellé normal_attribute False Site site ! primary_key True Site position normal_attribute False ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_1_dependencies.gv ================================================ digraph { node [shape=box] "Site" -> "Affecter" "Employé" -> "Affecter" "Projet" -> "Affecter" } ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_1_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD triple_N11
    Affecter ( #projet, #employé u1, #site u1 )
    • Le champ projet fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Projet.
    • Le champ employé fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Employé. Il obéit en outre à la contrainte d'unicité 1.
    • Le champ site est une clé étrangère. Il a migré directement à partir de l'entité Site en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1.
    Employé ( employé, nom employé )
    • Le champ employé constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Employé.
    • Le champ nom employé était déjà un simple attribut de l'entité Employé.
    Projet ( projet, libellé )
    • Le champ projet constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Projet.
    • Le champ libellé était déjà un simple attribut de l'entité Projet.
    Site ( site, position )
    • Le champ site constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Site.
    • Le champ position était déjà un simple attribut de l'entité Site.
    ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_1_mld.mcd ================================================ %%mocodo : Projet: projet, libellé : Affecter: #projet > Projet > projet, _#employé > Employé > employé, #site > Site > site : Site: site, position : ::: Employé: employé, nom employé ::: ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_1_mld.md ================================================ - **Affecter** (_#projet_, _#employé_ u1, _#site_ u1) - Le champ _projet_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Projet_. - Le champ _employé_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Employé_. Il obéit en outre à la contrainte d'unicité 1. - Le champ _site_ est une clé étrangère. Il a migré directement à partir de l'entité _Site_ en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. - **Employé** (employé, nom employé) - Le champ _employé_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Employé_. - Le champ _nom employé_ était déjà un simple attribut de l'entité _Employé_. - **Projet** (projet, libellé) - Le champ _projet_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Projet_. - Le champ _libellé_ était déjà un simple attribut de l'entité _Projet_. - **Site** (site, position) - Le champ _site_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Site_. - Le champ _position_ était déjà un simple attribut de l'entité _Site_. ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_1_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{triple\_N11}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Affecter} (\foreign{\prim{projet}}, \foreign{\prim{employé}}$^{u\_1}$, \foreign{site}$^{u\_1}$) \begin{itemize} \item Le champ \emph{projet} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{Projet}. \item Le champ \emph{employé} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{Employé}. Il obéit en outre à la contrainte d'unicité 1. \item Le champ \emph{site} est une clé étrangère. Il a migré directement à partir de l'entité \emph{Site} en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. \end{itemize} \item \relat{Employé} (\prim{employé}, \attr{nom employé}) \begin{itemize} \item Le champ \emph{employé} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Employé}. \item Le champ \emph{nom employé} était déjà un simple attribut de l'entité \emph{Employé}. \end{itemize} \item \relat{Projet} (\prim{projet}, \attr{libellé}) \begin{itemize} \item Le champ \emph{projet} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Projet}. \item Le champ \emph{libellé} était déjà un simple attribut de l'entité \emph{Projet}. \end{itemize} \item \relat{Site} (\prim{site}, \attr{position}) \begin{itemize} \item Le champ \emph{site} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Site}. \item Le champ \emph{position} était déjà un simple attribut de l'entité \emph{Site}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/triple_N11/mld/triple_N11_1_mld.txt ================================================ - Affecter (_#projet_, _#employé_¹, #site¹) - Le champ « projet » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « Projet ». - Le champ « employé » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « Employé ». Il obéit en outre à la contrainte d'unicité 1. - Le champ « site » est une clé étrangère. Il a migré directement à partir de l'entité « Site » en perdant son caractère identifiant. Il obéit en outre à la contrainte d'unicité 1. - Employé (_employé_, nom employé) - Le champ « employé » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Employé ». - Le champ « nom employé » était déjà un simple attribut de l'entité « Employé ». - Projet (_projet_, libellé) - Le champ « projet » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Projet ». - Le champ « libellé » était déjà un simple attribut de l'entité « Projet ». - Site (_site_, position) - Le champ « site » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Site ». - Le champ « position » était déjà un simple attribut de l'entité « Site ». ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_create_cifs.mcd ================================================ Projet: projet Affecter, /1N Site, /1N Projet, 0N Employé Site: site Employé: employé -INVISIBLE_1, XX Projet, XX Projet -INVISIBLE_2, XX Site, XX Site (CIF) ..Affecter, ->Projet, --Employé, --Site: INVISIBLE_1, INVISIBLE_1 (CIF) ..Affecter, ->Site, --Employé, --Projet: INVISIBLE_2, INVISIBLE_2 ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1 ASSOC 4_, /1N ENTITÉ 2_, /1N ENTITÉ 1_, 0N ENTITÉ 3_ ENTITÉ 2_: at 2 1 ENTITÉ 3_: at 3 1 ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_create_cifs.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2 ASSOC 4_, /1N ENTITÉ 2_, /1N ENTITÉ 1_, 0N ENTITÉ 3_ ENTITÉ 2_: at 2 1, at 2 2 ENTITÉ 3_: at 3 1, at 3 2 (CIF) --ENTITÉ 1_, --ENTITÉ 3_, ..ASSOC 4_, ->ENTITÉ 2_: ENTITÉ 1_, ENTITÉ 3_ (CIF) ..ASSOC 4_, ->ENTITÉ 1_, ENTITÉ 3_, ENTITÉ 2_: ENTITÉ 2_, ENTITÉ 3_ ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_N11/rewritten/triple_N11_1_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/_triple_NN1_0.mcd ================================================ Ingénieur: ingénieur Gérer, /1N Responsable, 1N Ingénieur, 1N Projet Projet: projet Responsable: responsable ================================================ FILE: test/zoo/triple_NN1/_triple_NN1_1.mcd ================================================ Ingénieur: ingénieur, nom ingénieur Gérer, /1N Responsable, 1N Ingénieur, 1N Projet Projet: projet, libellé projet Responsable: responsable, nom responsable ================================================ FILE: test/zoo/triple_NN1/ddl/triple_NN1_0_ddl.d2 ================================================ "Gérer": { shape: sql_table "ingénieur": VARCHAR(42) {constraint: PK} "projet": VARCHAR(42) {constraint: PK} "responsable": VARCHAR(42) {constraint: NOT NULL} } ================================================ FILE: test/zoo/triple_NN1/ddl/triple_NN1_0_ddl.dbml ================================================ Table "Gérer" { "ingénieur" VARCHAR(42) [NOT NULL] "projet" VARCHAR(42) [NOT NULL] "responsable" VARCHAR(42) [NOT NULL] Indexes { ("ingénieur", "projet") [pk] } } ================================================ FILE: test/zoo/triple_NN1/ddl/triple_NN1_0_ddl.sql ================================================ CREATE TABLE GERER ( PRIMARY KEY (ingenieur, projet), ingenieur VARCHAR(42) NOT NULL, projet VARCHAR(42) NOT NULL, responsable VARCHAR(42) NOT NULL ); ================================================ FILE: test/zoo/triple_NN1/ddl/triple_NN1_1_ddl.d2 ================================================ "Gérer": { shape: sql_table "ingénieur": VARCHAR(42) {constraint: [PK; FK]} "projet": VARCHAR(42) {constraint: [PK; FK]} "responsable": VARCHAR(42) {constraint: [FK; NOT NULL]} } "Ingénieur": { shape: sql_table "ingénieur": VARCHAR(42) {constraint: PK} "nom ingénieur": VARCHAR(42) } "Projet": { shape: sql_table "projet": VARCHAR(42) {constraint: PK} "libellé projet": VARCHAR(42) } "Responsable": { shape: sql_table "responsable": VARCHAR(42) {constraint: PK} "nom responsable": VARCHAR(42) } "Gérer"."ingénieur" -> "Ingénieur"."ingénieur" "Gérer"."projet" -> "Projet"."projet" "Gérer"."responsable" -> "Responsable"."responsable" ================================================ FILE: test/zoo/triple_NN1/ddl/triple_NN1_1_ddl.dbml ================================================ Table "Gérer" { "ingénieur" VARCHAR(42) [NOT NULL] "projet" VARCHAR(42) [NOT NULL] "responsable" VARCHAR(42) [NOT NULL] Indexes { ("ingénieur", "projet") [pk] } } Table "Ingénieur" { "ingénieur" VARCHAR(42) [pk, NOT NULL] "nom ingénieur" VARCHAR(42) } Table "Projet" { "projet" VARCHAR(42) [pk, NOT NULL] "libellé projet" VARCHAR(42) } Table "Responsable" { "responsable" VARCHAR(42) [pk, NOT NULL] "nom responsable" VARCHAR(42) } Ref:"Gérer"."ingénieur" > "Ingénieur"."ingénieur" Ref:"Gérer"."projet" > "Projet"."projet" Ref:"Gérer"."responsable" > "Responsable"."responsable" ================================================ FILE: test/zoo/triple_NN1/ddl/triple_NN1_1_ddl.sql ================================================ CREATE TABLE GERER ( PRIMARY KEY (ingenieur, projet), ingenieur VARCHAR(42) NOT NULL, projet VARCHAR(42) NOT NULL, responsable VARCHAR(42) NOT NULL ); CREATE TABLE INGENIEUR ( PRIMARY KEY (ingenieur), ingenieur VARCHAR(42) NOT NULL, nom_ingenieur VARCHAR(255) ); CREATE TABLE PROJET ( PRIMARY KEY (projet), projet VARCHAR(42) NOT NULL, libelle_projet VARCHAR(42) ); CREATE TABLE RESPONSABLE ( PRIMARY KEY (responsable), responsable VARCHAR(42) NOT NULL, nom_responsable VARCHAR(255) ); ALTER TABLE GERER ADD FOREIGN KEY (responsable) REFERENCES RESPONSABLE (responsable); ALTER TABLE GERER ADD FOREIGN KEY (projet) REFERENCES PROJET (projet); ALTER TABLE GERER ADD FOREIGN KEY (ingenieur) REFERENCES INGENIEUR (ingenieur); ================================================ FILE: test/zoo/triple_NN1/exported/triple_NN1_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Ingénieur"] 5 [label="Projet"] 4 [label="Responsable"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] // Weak and strong entity attributes 2 [label=<ingénieur>] 6 [label=<projet>] 7 [label=<responsable>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="Gérer"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 4 -- 7 5 -- 6 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 4 -- 3 edge [headlabel=N] 1 -- 3 5 -- 3 } ================================================ FILE: test/zoo/triple_NN1/exported/triple_NN1_0_erd_chen.txt ================================================ [Ingénieur] ==N== [Projet] ==N== [Responsable] ==1== ================================================ FILE: test/zoo/triple_NN1/exported/triple_NN1_0_erd_crow.mmd ================================================ Not implemented for n-ary associations. ================================================ FILE: test/zoo/triple_NN1/exported/triple_NN1_0_uml.puml ================================================ @startuml "triple_NN1" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Ingénieur") { {field} + pk(ingénieur) } diamond N_ARY_0 N_ARY_0 -- "1" "Responsable" N_ARY_0 -- "1..*" "Ingénieur" N_ARY_0 -- "1..*" "Projet" Table("Projet") { {field} + pk(projet) } Table("Responsable") { {field} + pk(responsable) } @enduml ================================================ FILE: test/zoo/triple_NN1/exported/triple_NN1_1_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Ingénieur"] 6 [label="Projet"] 5 [label="Responsable"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="nom\ningénieur"] 8 [label="libellé\nprojet"] 10 [label="nom\nresponsable"] // Weak and strong entity attributes 2 [label=<ingénieur>] 7 [label=<projet>] 9 [label=<responsable>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="Gérer"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 9 5 -- 10 6 -- 7 6 -- 8 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 5 -- 4 edge [headlabel=N] 1 -- 4 6 -- 4 } ================================================ FILE: test/zoo/triple_NN1/exported/triple_NN1_1_erd_chen.txt ================================================ [Ingénieur] ==N== [Projet] ==N== [Responsable] ==1== ================================================ FILE: test/zoo/triple_NN1/exported/triple_NN1_1_erd_crow.mmd ================================================ Not implemented for n-ary associations. ================================================ FILE: test/zoo/triple_NN1/exported/triple_NN1_1_uml.puml ================================================ @startuml "triple_NN1" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Ingénieur") { {field} + pk(ingénieur) {field} + nom ingénieur } diamond N_ARY_0 N_ARY_0 -- "1" "Responsable" N_ARY_0 -- "1..*" "Ingénieur" N_ARY_0 -- "1..*" "Projet" Table("Projet") { {field} + pk(projet) {field} + libellé projet } Table("Responsable") { {field} + pk(responsable) {field} + nom responsable } @enduml ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Gérer ingénieur ! primary_ex_foreign_key True Ingénieur Ingénieur Gérer Gérer projet ! primary_ex_foreign_key True Projet Projet Gérer Gérer responsable ! stopped_ex_foreign_key False Responsable Responsable Gérer ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_0_dependencies.gv ================================================ digraph { node [shape=box] } ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD triple_NN1
    Gérer ( ingénieur, projet, responsable! )
    • Le champ ingénieur fait partie de la clé primaire de la table. Sa table d'origine (Ingénieur) ayant été supprimée, il n'est pas considéré comme clé étrangère.
    • Le champ projet fait partie de la clé primaire de la table. Sa table d'origine (Projet) ayant été supprimée, il n'est pas considéré comme clé étrangère.
    • Le champ à saisie obligatoire responsable est un simple attribut. Il a migré directement à partir de l'entité Responsable en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère.


    NB. Les tables Ingénieur, Projet et Responsable ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_0_mld.mcd ================================================ %%mocodo : Gérer: ingénieur, _projet, responsable : ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_0_mld.md ================================================ - **Gérer** (ingénieur, projet, responsable!) - Le champ _ingénieur_ fait partie de la clé primaire de la table. Sa table d'origine (_Ingénieur_) ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ _projet_ fait partie de la clé primaire de la table. Sa table d'origine (_Projet_) ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ à saisie obligatoire _responsable_ est un simple attribut. Il a migré directement à partir de l'entité _Responsable_ en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère.
    ---- **NB.** Les tables _Ingénieur_, _Projet_ et _Responsable_ ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{triple\_NN1}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Gérer} (\prim{ingénieur}, \prim{projet}, \attr{responsable!}) \begin{itemize} \item Le champ \emph{ingénieur} fait partie de la clé primaire de la table. Sa table d'origine (\emph{Ingénieur}) ayant été supprimée, il n'est pas considéré comme clé étrangère. \item Le champ \emph{projet} fait partie de la clé primaire de la table. Sa table d'origine (\emph{Projet}) ayant été supprimée, il n'est pas considéré comme clé étrangère. \item Le champ à saisie obligatoire \emph{responsable} est un simple attribut. Il a migré directement à partir de l'entité \emph{Responsable} en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_0_mld.txt ================================================ - Gérer (_ingénieur_, _projet_, responsable!) - Le champ « ingénieur » fait partie de la clé primaire de la table. Sa table d'origine (« Ingénieur ») ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ « projet » fait partie de la clé primaire de la table. Sa table d'origine (« Projet ») ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ à saisie obligatoire « responsable » est un simple attribut. Il a migré directement à partir de l'entité « Responsable » en perdant son caractère identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère.
    -------------------------------------------------------------------------------- NB. Les tables « Ingénieur », « Projet » et « Responsable » ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_1_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Gérer ingénieur ! primary_foreign_key True Ingénieur Ingénieur Gérer Gérer projet ! primary_foreign_key True Projet Projet Gérer Gérer responsable ! stopped_foreign_key False Responsable Responsable Gérer Ingénieur ingénieur ! primary_key True Ingénieur nom ingénieur normal_attribute False Projet projet ! primary_key True Projet libellé projet normal_attribute False Responsable responsable ! primary_key True Responsable nom responsable normal_attribute False ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_1_dependencies.gv ================================================ digraph { node [shape=box] "Responsable" -> "Gérer" "Projet" -> "Gérer" "Ingénieur" -> "Gérer" } ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_1_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD triple_NN1
    Gérer ( #ingénieur, #projet, #responsable! )
    • Le champ ingénieur fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Ingénieur.
    • Le champ projet fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Projet.
    • Le champ à saisie obligatoire responsable est une clé étrangère. Il a migré directement à partir de l'entité Responsable en perdant son caractère identifiant.
    Ingénieur ( ingénieur, nom ingénieur )
    • Le champ ingénieur constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Ingénieur.
    • Le champ nom ingénieur était déjà un simple attribut de l'entité Ingénieur.
    Projet ( projet, libellé projet )
    • Le champ projet constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Projet.
    • Le champ libellé projet était déjà un simple attribut de l'entité Projet.
    Responsable ( responsable, nom responsable )
    • Le champ responsable constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Responsable.
    • Le champ nom responsable était déjà un simple attribut de l'entité Responsable.
    ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_1_mld.mcd ================================================ %%mocodo : Ingénieur: ingénieur, nom ingénieur : Gérer: #ingénieur > Ingénieur > ingénieur, _#projet > Projet > projet, #responsable > Responsable > responsable : Projet: projet, libellé projet : ::: Responsable: responsable, nom responsable ::: ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_1_mld.md ================================================ - **Gérer** (_#ingénieur_, _#projet_, _#responsable!_) - Le champ _ingénieur_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Ingénieur_. - Le champ _projet_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Projet_. - Le champ à saisie obligatoire _responsable_ est une clé étrangère. Il a migré directement à partir de l'entité _Responsable_ en perdant son caractère identifiant. - **Ingénieur** (ingénieur, nom ingénieur) - Le champ _ingénieur_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Ingénieur_. - Le champ _nom ingénieur_ était déjà un simple attribut de l'entité _Ingénieur_. - **Projet** (projet, libellé projet) - Le champ _projet_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Projet_. - Le champ _libellé projet_ était déjà un simple attribut de l'entité _Projet_. - **Responsable** (responsable, nom responsable) - Le champ _responsable_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Responsable_. - Le champ _nom responsable_ était déjà un simple attribut de l'entité _Responsable_. ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_1_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{triple\_NN1}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Gérer} (\foreign{\prim{ingénieur}}, \foreign{\prim{projet}}, \foreign{responsable!}) \begin{itemize} \item Le champ \emph{ingénieur} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{Ingénieur}. \item Le champ \emph{projet} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{Projet}. \item Le champ à saisie obligatoire \emph{responsable} est une clé étrangère. Il a migré directement à partir de l'entité \emph{Responsable} en perdant son caractère identifiant. \end{itemize} \item \relat{Ingénieur} (\prim{ingénieur}, \attr{nom ingénieur}) \begin{itemize} \item Le champ \emph{ingénieur} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Ingénieur}. \item Le champ \emph{nom ingénieur} était déjà un simple attribut de l'entité \emph{Ingénieur}. \end{itemize} \item \relat{Projet} (\prim{projet}, \attr{libellé projet}) \begin{itemize} \item Le champ \emph{projet} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Projet}. \item Le champ \emph{libellé projet} était déjà un simple attribut de l'entité \emph{Projet}. \end{itemize} \item \relat{Responsable} (\prim{responsable}, \attr{nom responsable}) \begin{itemize} \item Le champ \emph{responsable} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Responsable}. \item Le champ \emph{nom responsable} était déjà un simple attribut de l'entité \emph{Responsable}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/triple_NN1/mld/triple_NN1_1_mld.txt ================================================ - Gérer (_#ingénieur_, _#projet_, #responsable!) - Le champ « ingénieur » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « Ingénieur ». - Le champ « projet » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « Projet ». - Le champ à saisie obligatoire « responsable » est une clé étrangère. Il a migré directement à partir de l'entité « Responsable » en perdant son caractère identifiant. - Ingénieur (_ingénieur_, nom ingénieur) - Le champ « ingénieur » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Ingénieur ». - Le champ « nom ingénieur » était déjà un simple attribut de l'entité « Ingénieur ». - Projet (_projet_, libellé projet) - Le champ « projet » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Projet ». - Le champ « libellé projet » était déjà un simple attribut de l'entité « Projet ». - Responsable (_responsable_, nom responsable) - Le champ « responsable » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Responsable ». - Le champ « nom responsable » était déjà un simple attribut de l'entité « Responsable ». ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_0_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1 ASSOC 4_, /1N ENTITÉ 3_, 1N ENTITÉ 1_, 1N ENTITÉ 2_ ENTITÉ 2_: at 2 1 ENTITÉ 3_: at 3 1 ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_0_rw_explode_arity=2,weak.mcd ================================================ Projet: projet DF, _11 Gérer, 1N Projet Gérer: DF, 11 Gérer, 1N Responsable : Ingénieur: ingénieur DF, _11 Gérer, 1N Ingénieur Responsable: responsable ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_0_rw_explode_arity=3,weak.mcd ================================================ Projet: projet DF, _11 Gérer, 1N Projet Gérer: DF, 11 Gérer, 1N Responsable : Ingénieur: ingénieur DF, _11 Gérer, 1N Ingénieur Responsable: responsable ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_1_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_1_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_1_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2 ASSOC 4_, /1N ENTITÉ 3_, 1N ENTITÉ 1_, 1N ENTITÉ 2_ ENTITÉ 2_: at 2 1, at 2 2 ENTITÉ 3_: at 3 1, at 3 2 ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_1_rw_explode_arity=2,weak.mcd ================================================ Projet: projet, libellé projet DF, _11 Gérer, 1N Projet Gérer: DF, 11 Gérer, 1N Responsable : Ingénieur: ingénieur, nom ingénieur DF, _11 Gérer, 1N Ingénieur Responsable: responsable, nom responsable ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_1_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_1_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_1_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_1_rw_explode_arity=3,weak.mcd ================================================ Projet: projet, libellé projet DF, _11 Gérer, 1N Projet Gérer: DF, 11 Gérer, 1N Responsable : Ingénieur: ingénieur, nom ingénieur DF, _11 Gérer, 1N Ingénieur Responsable: responsable, nom responsable ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_1_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NN1/rewritten/triple_NN1_1_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NNN/_triple_NNN_0.mcd ================================================ Employé: employé Appliquer, 0N Employé, 1N Projet, 1N Compétence Projet: projet Compétence: compétence ================================================ FILE: test/zoo/triple_NNN/_triple_NNN_1.mcd ================================================ Employé: employé, nom Appliquer, 0N Employé, 1N Projet, 1N Compétence Projet: projet, date début, date fin Compétence: compétence, libellé ================================================ FILE: test/zoo/triple_NNN/ddl/triple_NNN_0_ddl.d2 ================================================ "Appliquer": { shape: sql_table "employé": VARCHAR(42) {constraint: PK} "projet": VARCHAR(42) {constraint: PK} "compétence": VARCHAR(42) {constraint: PK} } ================================================ FILE: test/zoo/triple_NNN/ddl/triple_NNN_0_ddl.dbml ================================================ Table "Appliquer" { "employé" VARCHAR(42) [NOT NULL] "projet" VARCHAR(42) [NOT NULL] "compétence" VARCHAR(42) [NOT NULL] Indexes { ("employé", "projet", "compétence") [pk] } } ================================================ FILE: test/zoo/triple_NNN/ddl/triple_NNN_0_ddl.sql ================================================ CREATE TABLE APPLIQUER ( PRIMARY KEY (employe, projet, competence), employe VARCHAR(42) NOT NULL, projet VARCHAR(42) NOT NULL, competence VARCHAR(42) NOT NULL ); ================================================ FILE: test/zoo/triple_NNN/ddl/triple_NNN_1_ddl.d2 ================================================ "Appliquer": { shape: sql_table "employé": VARCHAR(42) {constraint: [PK; FK]} "projet": VARCHAR(42) {constraint: [PK; FK]} "compétence": VARCHAR(42) {constraint: [PK; FK]} } "Compétence": { shape: sql_table "compétence": VARCHAR(42) {constraint: PK} "libellé": VARCHAR(42) } "Employé": { shape: sql_table "employé": VARCHAR(42) {constraint: PK} "nom": VARCHAR(42) } "Projet": { shape: sql_table "projet": VARCHAR(42) {constraint: PK} "date début": VARCHAR(42) "date fin": VARCHAR(42) } "Appliquer"."employé" -> "Employé"."employé" "Appliquer"."projet" -> "Projet"."projet" "Appliquer"."compétence" -> "Compétence"."compétence" ================================================ FILE: test/zoo/triple_NNN/ddl/triple_NNN_1_ddl.dbml ================================================ Table "Appliquer" { "employé" VARCHAR(42) [NOT NULL] "projet" VARCHAR(42) [NOT NULL] "compétence" VARCHAR(42) [NOT NULL] Indexes { ("employé", "projet", "compétence") [pk] } } Table "Compétence" { "compétence" VARCHAR(42) [pk, NOT NULL] "libellé" VARCHAR(42) } Table "Employé" { "employé" VARCHAR(42) [pk, NOT NULL] "nom" VARCHAR(42) } Table "Projet" { "projet" VARCHAR(42) [pk, NOT NULL] "date début" VARCHAR(42) "date fin" VARCHAR(42) } Ref:"Appliquer"."employé" > "Employé"."employé" Ref:"Appliquer"."projet" > "Projet"."projet" Ref:"Appliquer"."compétence" > "Compétence"."compétence" ================================================ FILE: test/zoo/triple_NNN/ddl/triple_NNN_1_ddl.sql ================================================ CREATE TABLE APPLIQUER ( PRIMARY KEY (employe, projet, competence), employe VARCHAR(42) NOT NULL, projet VARCHAR(42) NOT NULL, competence VARCHAR(42) NOT NULL ); CREATE TABLE COMPETENCE ( PRIMARY KEY (competence), competence VARCHAR(42) NOT NULL, libelle VARCHAR(50) ); CREATE TABLE EMPLOYE ( PRIMARY KEY (employe), employe VARCHAR(42) NOT NULL, nom VARCHAR(255) ); CREATE TABLE PROJET ( PRIMARY KEY (projet), projet VARCHAR(42) NOT NULL, date_debut DATE, date_fin DATE ); ALTER TABLE APPLIQUER ADD FOREIGN KEY (competence) REFERENCES COMPETENCE (competence); ALTER TABLE APPLIQUER ADD FOREIGN KEY (projet) REFERENCES PROJET (projet); ALTER TABLE APPLIQUER ADD FOREIGN KEY (employe) REFERENCES EMPLOYE (employe); ================================================ FILE: test/zoo/triple_NNN/exported/triple_NNN_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Employé"] 4 [label="Projet"] 5 [label="Compétence"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] // Weak and strong entity attributes 2 [label=<employé>] 6 [label=<projet>] 7 [label=<compétence>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="Appliquer"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 4 -- 6 5 -- 7 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] edge [headlabel=N] 4 -- 3 5 -- 3 1 -- 3 [color="#000000"] } ================================================ FILE: test/zoo/triple_NNN/exported/triple_NNN_0_erd_chen.txt ================================================ [Compétence] ==N== [Employé] --N-- [Projet] ==N== ================================================ FILE: test/zoo/triple_NNN/exported/triple_NNN_0_erd_crow.mmd ================================================ Not implemented for n-ary associations. ================================================ FILE: test/zoo/triple_NNN/exported/triple_NNN_0_uml.puml ================================================ @startuml "triple_NNN" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Employé") { {field} + pk(employé) } diamond N_ARY_0 N_ARY_0 -- "*" "Employé" N_ARY_0 -- "1..*" "Projet" N_ARY_0 -- "1..*" "Compétence" Table("Projet") { {field} + pk(projet) } Table("Compétence") { {field} + pk(compétence) } @enduml ================================================ FILE: test/zoo/triple_NNN/exported/triple_NNN_1_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Employé"] 5 [label="Projet"] 6 [label="Compétence"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="nom"] 8 [label="date\ndébut"] 9 [label="date fin"] 11 [label="libellé"] // Weak and strong entity attributes 2 [label=<employé>] 7 [label=<projet>] 10 [label=<compétence>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="Appliquer"] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 7 5 -- 8 5 -- 9 6 -- 10 6 -- 11 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] edge [headlabel=N] 5 -- 4 6 -- 4 1 -- 4 [color="#000000"] } ================================================ FILE: test/zoo/triple_NNN/exported/triple_NNN_1_erd_chen.txt ================================================ [Compétence] ==N== [Employé] --N-- [Projet] ==N== ================================================ FILE: test/zoo/triple_NNN/exported/triple_NNN_1_erd_crow.mmd ================================================ Not implemented for n-ary associations. ================================================ FILE: test/zoo/triple_NNN/exported/triple_NNN_1_uml.puml ================================================ @startuml "triple_NNN" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Employé") { {field} + pk(employé) {field} + nom } diamond N_ARY_0 N_ARY_0 -- "*" "Employé" N_ARY_0 -- "1..*" "Projet" N_ARY_0 -- "1..*" "Compétence" Table("Projet") { {field} + pk(projet) {field} + date début {field} + date fin } Table("Compétence") { {field} + pk(compétence) {field} + libellé } @enduml ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Appliquer employé ! primary_ex_foreign_key True Employé Employé Appliquer Appliquer projet ! primary_ex_foreign_key True Projet Projet Appliquer Appliquer compétence ! primary_ex_foreign_key True Compétence Compétence Appliquer ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_0_dependencies.gv ================================================ digraph { node [shape=box] } ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD triple_NNN
    Appliquer ( employé, projet, compétence )
    • Le champ employé fait partie de la clé primaire de la table. Sa table d'origine (Employé) ayant été supprimée, il n'est pas considéré comme clé étrangère.
    • Le champ projet fait partie de la clé primaire de la table. Sa table d'origine (Projet) ayant été supprimée, il n'est pas considéré comme clé étrangère.
    • Le champ compétence fait partie de la clé primaire de la table. Sa table d'origine (Compétence) ayant été supprimée, il n'est pas considéré comme clé étrangère.


    NB. Les tables Compétence, Employé et Projet ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_0_mld.mcd ================================================ %%mocodo : Appliquer: employé, _projet, _compétence : ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_0_mld.md ================================================ - **Appliquer** (employé, projet, compétence) - Le champ _employé_ fait partie de la clé primaire de la table. Sa table d'origine (_Employé_) ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ _projet_ fait partie de la clé primaire de la table. Sa table d'origine (_Projet_) ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ _compétence_ fait partie de la clé primaire de la table. Sa table d'origine (_Compétence_) ayant été supprimée, il n'est pas considéré comme clé étrangère.
    ---- **NB.** Les tables _Compétence_, _Employé_ et _Projet_ ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{triple\_NNN}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Appliquer} (\prim{employé}, \prim{projet}, \prim{compétence}) \begin{itemize} \item Le champ \emph{employé} fait partie de la clé primaire de la table. Sa table d'origine (\emph{Employé}) ayant été supprimée, il n'est pas considéré comme clé étrangère. \item Le champ \emph{projet} fait partie de la clé primaire de la table. Sa table d'origine (\emph{Projet}) ayant été supprimée, il n'est pas considéré comme clé étrangère. \item Le champ \emph{compétence} fait partie de la clé primaire de la table. Sa table d'origine (\emph{Compétence}) ayant été supprimée, il n'est pas considéré comme clé étrangère. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_0_mld.txt ================================================ - Appliquer (_employé_, _projet_, _compétence_) - Le champ « employé » fait partie de la clé primaire de la table. Sa table d'origine (« Employé ») ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ « projet » fait partie de la clé primaire de la table. Sa table d'origine (« Projet ») ayant été supprimée, il n'est pas considéré comme clé étrangère. - Le champ « compétence » fait partie de la clé primaire de la table. Sa table d'origine (« Compétence ») ayant été supprimée, il n'est pas considéré comme clé étrangère.
    -------------------------------------------------------------------------------- NB. Les tables « Compétence », « Employé » et « Projet » ont été supprimées car elles étaient réduites à la clé primaire de leur entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_1_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Appliquer employé ! primary_foreign_key True Employé Employé Appliquer Appliquer projet ! primary_foreign_key True Projet Projet Appliquer Appliquer compétence ! primary_foreign_key True Compétence Compétence Appliquer Compétence compétence ! primary_key True Compétence libellé normal_attribute False Employé employé ! primary_key True Employé nom normal_attribute False Projet projet ! primary_key True Projet date début normal_attribute False Projet date fin normal_attribute False ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_1_dependencies.gv ================================================ digraph { node [shape=box] "Compétence" -> "Appliquer" "Projet" -> "Appliquer" "Employé" -> "Appliquer" } ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_1_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD triple_NNN
    Appliquer ( #employé, #projet, #compétence )
    • Le champ employé fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Employé.
    • Le champ projet fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Projet.
    • Le champ compétence fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Compétence.
    Compétence ( compétence, libellé )
    • Le champ compétence constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Compétence.
    • Le champ libellé était déjà un simple attribut de l'entité Compétence.
    Employé ( employé, nom )
    • Le champ employé constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Employé.
    • Le champ nom était déjà un simple attribut de l'entité Employé.
    Projet ( projet, date début, date fin )
    • Le champ projet constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Projet.
    • Les champs date début et date fin étaient déjà de simples attributs de l'entité Projet.
    ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_1_mld.mcd ================================================ %%mocodo : Employé: employé, nom : Appliquer: #employé > Employé > employé, _#projet > Projet > projet, _#compétence > Compétence > compétence : Projet: projet, date début, date fin : ::: Compétence: compétence, libellé ::: ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_1_mld.md ================================================ - **Appliquer** (_#employé_, _#projet_, _#compétence_) - Le champ _employé_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Employé_. - Le champ _projet_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Projet_. - Le champ _compétence_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Compétence_. - **Compétence** (compétence, libellé) - Le champ _compétence_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Compétence_. - Le champ _libellé_ était déjà un simple attribut de l'entité _Compétence_. - **Employé** (employé, nom) - Le champ _employé_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Employé_. - Le champ _nom_ était déjà un simple attribut de l'entité _Employé_. - **Projet** (projet, date début, date fin) - Le champ _projet_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Projet_. - Les champs _date début_ et _date fin_ étaient déjà de simples attributs de l'entité _Projet_. ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_1_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{triple\_NNN}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Appliquer} (\foreign{\prim{employé}}, \foreign{\prim{projet}}, \foreign{\prim{compétence}}) \begin{itemize} \item Le champ \emph{employé} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{Employé}. \item Le champ \emph{projet} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{Projet}. \item Le champ \emph{compétence} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité \emph{Compétence}. \end{itemize} \item \relat{Compétence} (\prim{compétence}, \attr{libellé}) \begin{itemize} \item Le champ \emph{compétence} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Compétence}. \item Le champ \emph{libellé} était déjà un simple attribut de l'entité \emph{Compétence}. \end{itemize} \item \relat{Employé} (\prim{employé}, \attr{nom}) \begin{itemize} \item Le champ \emph{employé} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Employé}. \item Le champ \emph{nom} était déjà un simple attribut de l'entité \emph{Employé}. \end{itemize} \item \relat{Projet} (\prim{projet}, \attr{date début}, \attr{date fin}) \begin{itemize} \item Le champ \emph{projet} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Projet}. \item Les champs \emph{date début} et \emph{date fin} étaient déjà de simples attributs de l'entité \emph{Projet}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/triple_NNN/mld/triple_NNN_1_mld.txt ================================================ - Appliquer (_#employé_, _#projet_, _#compétence_) - Le champ « employé » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « Employé ». - Le champ « projet » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « Projet ». - Le champ « compétence » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité « Compétence ». - Compétence (_compétence_, libellé) - Le champ « compétence » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Compétence ». - Le champ « libellé » était déjà un simple attribut de l'entité « Compétence ». - Employé (_employé_, nom) - Le champ « employé » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Employé ». - Le champ « nom » était déjà un simple attribut de l'entité « Employé ». - Projet (_projet_, date début, date fin) - Le champ « projet » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Projet ». - Les champs « date début » et « date fin » étaient déjà de simples attributs de l'entité « Projet ». ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_0_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_0_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1 ASSOC 4_, 0N ENTITÉ 1_, 1N ENTITÉ 2_, 1N ENTITÉ 3_ ENTITÉ 2_: at 2 1 ENTITÉ 3_: at 3 1 ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_0_rw_explode_arity=2,weak.mcd ================================================ Projet: projet DF, _11 Appliquer, 1N Projet Appliquer: DF, _11 Appliquer, 0N Employé : Compétence: compétence DF, _11 Appliquer, 1N Compétence Employé: employé ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_0_rw_explode_arity=2.mcd ================================================ Projet: projet DF, 11 Appliquer, 1N Projet Appliquer: id. appliquer DF, 11 Appliquer, 0N Employé : Compétence: compétence DF, 11 Appliquer, 1N Compétence Employé: employé ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_0_rw_explode_arity=3,weak.mcd ================================================ Projet: projet DF, _11 Appliquer, 1N Projet Appliquer: DF, _11 Appliquer, 0N Employé : Compétence: compétence DF, _11 Appliquer, 1N Compétence Employé: employé ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_0_rw_explode_arity=3.mcd ================================================ Projet: projet DF, 11 Appliquer, 1N Projet Appliquer: id. appliquer DF, 11 Appliquer, 0N Employé : Compétence: compétence DF, 11 Appliquer, 1N Compétence Employé: employé ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_1_rw_create_df_arrows=across.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_1_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_1_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2 ASSOC 4_, 0N ENTITÉ 1_, 1N ENTITÉ 2_, 1N ENTITÉ 3_ ENTITÉ 2_: at 2 1, at 2 2, at 2 3 ENTITÉ 3_: at 3 1, at 3 2 ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_1_rw_explode_arity=2,weak.mcd ================================================ Projet: projet, date début, date fin DF, _11 Appliquer, 1N Projet Appliquer: DF, _11 Appliquer, 0N Employé : Compétence: compétence, libellé DF, _11 Appliquer, 1N Compétence Employé: employé, nom ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_1_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_1_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_1_rw_explode_arity=2.mcd ================================================ Projet: projet, date début, date fin DF, 11 Appliquer, 1N Projet Appliquer: id. appliquer DF, 11 Appliquer, 0N Employé : Compétence: compétence, libellé DF, 11 Appliquer, 1N Compétence Employé: employé, nom ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_1_rw_explode_arity=3,weak.mcd ================================================ Projet: projet, date début, date fin DF, _11 Appliquer, 1N Projet Appliquer: DF, _11 Appliquer, 0N Employé : Compétence: compétence, libellé DF, _11 Appliquer, 1N Compétence Employé: employé, nom ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_1_rw_explode_arity=3.mcd ================================================ Projet: projet, date début, date fin DF, 11 Appliquer, 1N Projet Appliquer: id. appliquer DF, 11 Appliquer, 0N Employé : Compétence: compétence, libellé DF, 11 Appliquer, 1N Compétence Employé: employé, nom ================================================ FILE: test/zoo/triple_NNN/rewritten/triple_NNN_1_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/_weak_0.mcd ================================================ Œuvre: œuvre DF, 0N Œuvre, _11 Exemplaire: foobar Exemplaire: exemplaire ================================================ FILE: test/zoo/weak/_weak_1.mcd ================================================ Œuvre: œuvre, auteur DF, 0N Œuvre, _11 Exemplaire: foobar Exemplaire: exemplaire, nb pages, date achat ================================================ FILE: test/zoo/weak/_weak_2.mcd ================================================ Appartement: num appart., nb pièces Composer, 0N Étage, _11 Appartement Étage: num étage, nb appartements Appartenir, 1N Immeuble, _11 Étage Immeuble: num immeuble, nb étages Se situer, 0N Rue, _11 Immeuble Rue: code rue, nom rue ================================================ FILE: test/zoo/weak/ddl/weak_0_ddl.d2 ================================================ "Exemplaire": { shape: sql_table "œuvre": VARCHAR(42) {constraint: PK} "exemplaire": VARCHAR(42) {constraint: PK} "foobar": VARCHAR(42) } ================================================ FILE: test/zoo/weak/ddl/weak_0_ddl.dbml ================================================ Table "Exemplaire" { "œuvre" VARCHAR(42) [NOT NULL] "exemplaire" VARCHAR(42) [NOT NULL] "foobar" VARCHAR(42) Indexes { ("œuvre", "exemplaire") [pk] } } ================================================ FILE: test/zoo/weak/ddl/weak_0_ddl.sql ================================================ CREATE TABLE EXEMPLAIRE ( PRIMARY KEY (oeuvre, exemplaire), oeuvre VARCHAR(42) NOT NULL, exemplaire VARCHAR(42) NOT NULL, foobar VARCHAR(42) ); ================================================ FILE: test/zoo/weak/ddl/weak_1_ddl.d2 ================================================ "Exemplaire": { shape: sql_table "œuvre": VARCHAR(42) {constraint: [PK; FK]} "exemplaire": VARCHAR(42) {constraint: PK} "nb pages": VARCHAR(42) "date achat": VARCHAR(42) "foobar": VARCHAR(42) } "Œuvre": { shape: sql_table "œuvre": VARCHAR(42) {constraint: PK} "auteur": VARCHAR(42) } "Exemplaire"."œuvre" -> "Œuvre"."œuvre" ================================================ FILE: test/zoo/weak/ddl/weak_1_ddl.dbml ================================================ Table "Exemplaire" { "œuvre" VARCHAR(42) [NOT NULL] "exemplaire" VARCHAR(42) [NOT NULL] "nb pages" VARCHAR(42) "date achat" VARCHAR(42) "foobar" VARCHAR(42) Indexes { ("œuvre", "exemplaire") [pk] } } Table "Œuvre" { "œuvre" VARCHAR(42) [pk, NOT NULL] "auteur" VARCHAR(42) } Ref:"Exemplaire"."œuvre" > "Œuvre"."œuvre" ================================================ FILE: test/zoo/weak/ddl/weak_1_ddl.sql ================================================ CREATE TABLE EXEMPLAIRE ( PRIMARY KEY (oeuvre, exemplaire), oeuvre VARCHAR(42) NOT NULL, exemplaire VARCHAR(42) NOT NULL, nb_pages INTEGER, date_achat DATE, foobar VARCHAR(42) ); CREATE TABLE OEUVRE ( PRIMARY KEY (oeuvre), oeuvre VARCHAR(42) NOT NULL, auteur VARCHAR(42) ); ALTER TABLE EXEMPLAIRE ADD FOREIGN KEY (oeuvre) REFERENCES OEUVRE (oeuvre); ================================================ FILE: test/zoo/weak/ddl/weak_2_ddl.d2 ================================================ "Appartement": { shape: sql_table "code rue": VARCHAR(42) {constraint: [PK; FK]} "num immeuble": VARCHAR(42) {constraint: [PK; FK]} "num étage": VARCHAR(42) {constraint: [PK; FK]} "num appart.": VARCHAR(42) {constraint: PK} "nb pièces": VARCHAR(42) } "Étage": { shape: sql_table "code rue": VARCHAR(42) {constraint: [PK; FK]} "num immeuble": VARCHAR(42) {constraint: [PK; FK]} "num étage": VARCHAR(42) {constraint: PK} "nb appartements": VARCHAR(42) } "Immeuble": { shape: sql_table "code rue": VARCHAR(42) {constraint: [PK; FK]} "num immeuble": VARCHAR(42) {constraint: PK} "nb étages": VARCHAR(42) } "Rue": { shape: sql_table "code rue": VARCHAR(42) {constraint: PK} "nom rue": VARCHAR(42) } "Appartement"."code rue" -> "Étage"."code rue" "Appartement"."num immeuble" -> "Étage"."num immeuble" "Appartement"."num étage" -> "Étage"."num étage" "Étage"."code rue" -> "Immeuble"."code rue" "Étage"."num immeuble" -> "Immeuble"."num immeuble" "Immeuble"."code rue" -> "Rue"."code rue" ================================================ FILE: test/zoo/weak/ddl/weak_2_ddl.dbml ================================================ Table "Appartement" { "code rue" VARCHAR(42) [NOT NULL] "num immeuble" VARCHAR(42) [NOT NULL] "num étage" VARCHAR(42) [NOT NULL] "num appart." VARCHAR(42) [NOT NULL] "nb pièces" VARCHAR(42) Indexes { ("code rue", "num immeuble", "num étage", "num appart.") [pk] } } Table "Étage" { "code rue" VARCHAR(42) [NOT NULL] "num immeuble" VARCHAR(42) [NOT NULL] "num étage" VARCHAR(42) [NOT NULL] "nb appartements" VARCHAR(42) Indexes { ("code rue", "num immeuble", "num étage") [pk] } } Table "Immeuble" { "code rue" VARCHAR(42) [NOT NULL] "num immeuble" VARCHAR(42) [NOT NULL] "nb étages" VARCHAR(42) Indexes { ("code rue", "num immeuble") [pk] } } Table "Rue" { "code rue" VARCHAR(42) [pk, NOT NULL] "nom rue" VARCHAR(42) } Ref:"Appartement".("code rue", "num étage", "num immeuble") > "Étage".("code rue", "num étage", "num immeuble") Ref:"Étage".("code rue", "num immeuble") > "Immeuble".("code rue", "num immeuble") Ref:"Immeuble"."code rue" > "Rue"."code rue" ================================================ FILE: test/zoo/weak/ddl/weak_2_ddl.sql ================================================ CREATE TABLE APPARTEMENT ( PRIMARY KEY (code_rue, num_immeuble, num_etage, num_appart), code_rue VARCHAR(8) NOT NULL, num_immeuble VARCHAR(8) NOT NULL, num_etage VARCHAR(8) NOT NULL, num_appart VARCHAR(8) NOT NULL, nb_pieces INTEGER ); CREATE TABLE ETAGE ( PRIMARY KEY (code_rue, num_immeuble, num_etage), code_rue VARCHAR(8) NOT NULL, num_immeuble VARCHAR(8) NOT NULL, num_etage VARCHAR(8) NOT NULL, nb_appartements INTEGER ); CREATE TABLE IMMEUBLE ( PRIMARY KEY (code_rue, num_immeuble), code_rue VARCHAR(8) NOT NULL, num_immeuble VARCHAR(8) NOT NULL, nb_etages INTEGER ); CREATE TABLE RUE ( PRIMARY KEY (code_rue), code_rue VARCHAR(8) NOT NULL, nom_rue VARCHAR(255) ); ALTER TABLE APPARTEMENT ADD FOREIGN KEY (code_rue, num_immeuble, num_etage) REFERENCES ETAGE (code_rue, num_immeuble, num_etage); ALTER TABLE ETAGE ADD FOREIGN KEY (code_rue, num_immeuble) REFERENCES IMMEUBLE (code_rue, num_immeuble); ALTER TABLE IMMEUBLE ADD FOREIGN KEY (code_rue) REFERENCES RUE (code_rue); ================================================ FILE: test/zoo/weak/exported/weak_0_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Œuvre"] 4 [label="Exemplaire",peripheries=2] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] // Weak and strong entity attributes 2 [label=<œuvre>] 6 [label=<exemplaire> style="dashed,filled"] // Relationship attributes node [ fillcolor="#FFFFFF" ] 5 [label="foobar"] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="DF",peripheries=2] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 4 -- 6 // Edges between relationships and attributes edge [color="#000000"] 3 -- 5 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 3 [color="#000000"] edge [headlabel=N] 4 -- 3 } ================================================ FILE: test/zoo/weak/exported/weak_0_erd_chen.txt ================================================ [[Exemplaire]] ==N== <> [Œuvre] --1-- <> ================================================ FILE: test/zoo/weak/exported/weak_0_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    Œuvre
    PKœuvre
    >] 2 [label=<
    Exemplaire
    PKexemplaire
    foobar
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 1 -> 2 [arrowhead="crowodot" arrowtail="teetee" label="DF" style=dotted] } ================================================ FILE: test/zoo/weak/exported/weak_0_erd_crow.mmd ================================================ erDiagram OEuvre { TYPE oeuvre PK } Exemplaire { TYPE exemplaire PK TYPE foobar } OEuvre ||..o{ Exemplaire: DF ================================================ FILE: test/zoo/weak/exported/weak_0_uml.puml ================================================ @startuml "weak" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Œuvre") { {field} + pk(œuvre) } "Œuvre" "1" *-- "*" "Exemplaire" Table("Exemplaire") { {field} + pk(exemplaire) {field} + foobar } @enduml ================================================ FILE: test/zoo/weak/exported/weak_1_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Œuvre"] 5 [label="Exemplaire",peripheries=2] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="auteur"] 8 [label="nb\npages"] 9 [label="date\nachat"] // Weak and strong entity attributes 2 [label=<œuvre>] 7 [label=<exemplaire> style="dashed,filled"] // Relationship attributes node [ fillcolor="#FFFFFF" ] 6 [label="foobar"] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="DF",peripheries=2] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 7 5 -- 8 5 -- 9 // Edges between relationships and attributes edge [color="#000000"] 4 -- 6 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 1 -- 4 [color="#000000"] edge [headlabel=N] 5 -- 4 } ================================================ FILE: test/zoo/weak/exported/weak_1_erd_chen.txt ================================================ [[Exemplaire]] ==N== <> [Œuvre] --1-- <> ================================================ FILE: test/zoo/weak/exported/weak_1_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    Œuvre
    PKœuvre
    auteur
    >] 2 [label=<
    Exemplaire
    PKexemplaire
    nb pages
    date achat
    foobar
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 1 -> 2 [arrowhead="crowodot" arrowtail="teetee" label="DF" style=dotted] } ================================================ FILE: test/zoo/weak/exported/weak_1_erd_crow.mmd ================================================ erDiagram OEuvre { TYPE oeuvre PK TYPE auteur } Exemplaire { TYPE exemplaire PK TYPE nb_pages TYPE date_achat TYPE foobar } OEuvre ||..o{ Exemplaire: DF ================================================ FILE: test/zoo/weak/exported/weak_1_uml.puml ================================================ @startuml "weak" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Œuvre") { {field} + pk(œuvre) {field} + auteur } "Œuvre" "1" *-- "*" "Exemplaire" Table("Exemplaire") { {field} + pk(exemplaire) {field} + nb pages {field} + date achat {field} + foobar } @enduml ================================================ FILE: test/zoo/weak/exported/weak_2_erd_chen.gv ================================================ graph{ start=42 // Entities node [ shape=box style=filled penwidth=1.5 fillcolor="#FFFFFF" ] 1 [label="Appartement",peripheries=2] 5 [label="Étage",peripheries=2] 9 [label="Immeuble",peripheries=2] 13 [label="Rue"] // Normal entity attributes node [ shape=oval penwidth=1.5 fillcolor="#FFFFFF" ] 3 [label="nb\npièces"] 7 [label="nb\nappartements"] 11 [label="nb\nétages"] 15 [label="nom rue"] // Weak and strong entity attributes 2 [label=<num
    appart.
    > style="dashed,filled"] 6 [label=<num
    étage
    > style="dashed,filled"] 10 [label=<num
    immeuble
    > style="dashed,filled"] 14 [label=<code rue>] // Relationships node [ shape=diamond height=0.7 penwidth=1.5 fillcolor="#FFFFFF" ] 4 [label="Composer",peripheries=2] 8 [label="Appartenir",peripheries=2] 12 [label="Se\nsituer",peripheries=2] // Edges between entities and attributes edge [ penwidth=1.5 ] 1 -- 2 1 -- 3 5 -- 6 5 -- 7 9 -- 10 9 -- 11 13 -- 14 13 -- 15 // Edges between entities and relationships edge [ penwidth=1 color="#000000:#000000" labeldistance=2 headlabel=1 ] 9 -- 8 5 -- 4 [color="#000000"] 13 -- 12 [color="#000000"] edge [headlabel=N] 1 -- 4 5 -- 8 9 -- 12 } ================================================ FILE: test/zoo/weak/exported/weak_2_erd_chen.txt ================================================ [Rue] --1-- <> [[Appartement]] ==N== <> [[Immeuble]] ==1== <> [[Immeuble]] ==N== <> [[Étage]] --1-- <> [[Étage]] ==N== <> ================================================ FILE: test/zoo/weak/exported/weak_2_erd_crow.gv ================================================ digraph{ layout=dot bgcolor="#FFFFFF" nodesep=0.5 // Nodes node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] node [ shape=none fontcolor="#000000" fontsize=15 fontname="Helvetica" ] 1 [label=<
    Appartement
    PKnum appart.
    nb pièces
    >] 2 [label=<
    Étage
    PKnum étage
    nb appartements
    >] 3 [label=<
    Immeuble
    PKnum immeuble
    nb étages
    >] 4 [label=<
    Rue
    PKcode rue
    nom rue
    >] // Edges edge [ penwidth=1 color="#000000" fontcolor="#000000" fontname="Futura" fontsize=11 dir=both ] 2 -> 1 [arrowhead="crowodot" arrowtail="teetee" label="Composer" style=dotted] 3 -> 2 [arrowhead="crowtee" arrowtail="teetee" label="Appartenir" style=dotted] 4 -> 3 [arrowhead="crowodot" arrowtail="teetee" label="Se situer" style=dotted] } ================================================ FILE: test/zoo/weak/exported/weak_2_erd_crow.mmd ================================================ erDiagram Appartement { TYPE num_appart PK TYPE nb_pieces } Etage { TYPE num_etage PK TYPE nb_appartements } Immeuble { TYPE num_immeuble PK TYPE nb_etages } Rue { TYPE code_rue PK TYPE nom_rue } Etage ||..o{ Appartement: Composer Immeuble ||..|{ Etage: Appartenir Rue ||..o{ Immeuble: Se_situer ================================================ FILE: test/zoo/weak/exported/weak_2_uml.puml ================================================ @startuml "weak" !define Table(x) class "x" << (T,#FFFFFF) >> !define pk(x) x Table("Appartement") { {field} + pk(num appart.) {field} + nb pièces } "Étage" "1" *-- "*" "Appartement": "Composer" Table("Étage") { {field} + pk(num étage) {field} + nb appartements } "Immeuble" "1" *-- "1..*" "Étage": "Appartenir" Table("Immeuble") { {field} + pk(num immeuble) {field} + nb étages } "Rue" "1" *-- "*" "Immeuble": "Se situer" Table("Rue") { {field} + pk(code rue) {field} + nom rue } @enduml ================================================ FILE: test/zoo/weak/mld/weak_0_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Exemplaire œuvre ! strengthening_primary_ex_foreign_key True Œuvre Œuvre DF Exemplaire exemplaire ! primary_key True Exemplaire foobar outer_attribute False DF ================================================ FILE: test/zoo/weak/mld/weak_0_dependencies.gv ================================================ digraph { node [shape=box] } ================================================ FILE: test/zoo/weak/mld/weak_0_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD weak
    Exemplaire ( œuvre, exemplaire, foobar )
    • Le champ œuvre fait partie de la clé primaire de la table. Il a migré à partir de l'entité Œuvre pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère.
    • Le champ exemplaire fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité Exemplaire.
    • Le champ foobar a migré à partir de l'association de dépendance fonctionnelle DF.


    NB. La table Œuvre a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
    ================================================ FILE: test/zoo/weak/mld/weak_0_mld.mcd ================================================ %%mocodo : Exemplaire: œuvre, _exemplaire, foobar : ================================================ FILE: test/zoo/weak/mld/weak_0_mld.md ================================================ - **Exemplaire** (œuvre, exemplaire, foobar) - Le champ _œuvre_ fait partie de la clé primaire de la table. Il a migré à partir de l'entité _Œuvre_ pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. - Le champ _exemplaire_ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _Exemplaire_. - Le champ _foobar_ a migré à partir de l'association de dépendance fonctionnelle _DF_.
    ---- **NB.** La table _Œuvre_ a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/weak/mld/weak_0_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{weak}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Exemplaire} (\prim{œuvre}, \prim{exemplaire}, \attr{foobar}) \begin{itemize} \item Le champ \emph{œuvre} fait partie de la clé primaire de la table. Il a migré à partir de l'entité \emph{Œuvre} pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. \item Le champ \emph{exemplaire} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Exemplaire}. \item Le champ \emph{foobar} a migré à partir de l'association de dépendance fonctionnelle \emph{DF}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/weak/mld/weak_0_mld.txt ================================================ - Exemplaire (_œuvre_, _exemplaire_, foobar) - Le champ « œuvre » fait partie de la clé primaire de la table. Il a migré à partir de l'entité « Œuvre » pour renforcer l'identifiant. Cependant, comme la table créée à partir de cette entité a été supprimée, il n'est pas considéré comme clé étrangère. - Le champ « exemplaire » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « Exemplaire ». - Le champ « foobar » a migré à partir de l'association de dépendance fonctionnelle « DF ».
    -------------------------------------------------------------------------------- NB. La table « Œuvre » a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine. ================================================ FILE: test/zoo/weak/mld/weak_1_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Exemplaire œuvre ! strengthening_primary_foreign_key True Œuvre Œuvre DF Exemplaire exemplaire ! primary_key True Exemplaire nb pages normal_attribute False Exemplaire date achat normal_attribute False Exemplaire foobar outer_attribute False DF Œuvre œuvre ! primary_key True Œuvre auteur normal_attribute False ================================================ FILE: test/zoo/weak/mld/weak_1_dependencies.gv ================================================ digraph { node [shape=box] "Œuvre" -> "Exemplaire" } ================================================ FILE: test/zoo/weak/mld/weak_1_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD weak
    Exemplaire ( #œuvre, exemplaire, nb pages, date achat, foobar )
    • Le champ œuvre fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité Œuvre pour renforcer l'identifiant.
    • Le champ exemplaire fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité Exemplaire.
    • Les champs nb pages et date achat étaient déjà de simples attributs de l'entité Exemplaire.
    • Le champ foobar a migré à partir de l'association de dépendance fonctionnelle DF.
    Œuvre ( œuvre, auteur )
    • Le champ œuvre constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Œuvre.
    • Le champ auteur était déjà un simple attribut de l'entité Œuvre.
    ================================================ FILE: test/zoo/weak/mld/weak_1_mld.mcd ================================================ %%mocodo : Œuvre: œuvre, auteur : Exemplaire: #œuvre > Œuvre > œuvre, _exemplaire, nb pages, date achat, foobar : ================================================ FILE: test/zoo/weak/mld/weak_1_mld.md ================================================ - **Exemplaire** (_#œuvre_, exemplaire, nb pages, date achat, foobar) - Le champ _œuvre_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _Œuvre_ pour renforcer l'identifiant. - Le champ _exemplaire_ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _Exemplaire_. - Les champs _nb pages_ et _date achat_ étaient déjà de simples attributs de l'entité _Exemplaire_. - Le champ _foobar_ a migré à partir de l'association de dépendance fonctionnelle _DF_. - **Œuvre** (œuvre, auteur) - Le champ _œuvre_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Œuvre_. - Le champ _auteur_ était déjà un simple attribut de l'entité _Œuvre_. ================================================ FILE: test/zoo/weak/mld/weak_1_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{weak}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Exemplaire} (\foreign{\prim{œuvre}}, \prim{exemplaire}, \attr{nb pages}, \attr{date achat}, \attr{foobar}) \begin{itemize} \item Le champ \emph{œuvre} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{Œuvre} pour renforcer l'identifiant. \item Le champ \emph{exemplaire} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Exemplaire}. \item Les champs \emph{nb pages} et \emph{date achat} étaient déjà de simples attributs de l'entité \emph{Exemplaire}. \item Le champ \emph{foobar} a migré à partir de l'association de dépendance fonctionnelle \emph{DF}. \end{itemize} \item \relat{Œuvre} (\prim{œuvre}, \attr{auteur}) \begin{itemize} \item Le champ \emph{œuvre} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Œuvre}. \item Le champ \emph{auteur} était déjà un simple attribut de l'entité \emph{Œuvre}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/weak/mld/weak_1_mld.txt ================================================ - Exemplaire (_#œuvre_, _exemplaire_, nb pages, date achat, foobar) - Le champ « œuvre » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « Œuvre » pour renforcer l'identifiant. - Le champ « exemplaire » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « Exemplaire ». - Les champs « nb pages » et « date achat » étaient déjà de simples attributs de l'entité « Exemplaire ». - Le champ « foobar » a migré à partir de l'association de dépendance fonctionnelle « DF ». - Œuvre (_œuvre_, auteur) - Le champ « œuvre » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Œuvre ». - Le champ « auteur » était déjà un simple attribut de l'entité « Œuvre ». ================================================ FILE: test/zoo/weak/mld/weak_2_debug.tsv ================================================ this relation name attribute optionality unicities nature is primary adjacent source outer source association name datatype leg note Appartement code rue ! strengthening_primary_foreign_key True Étage Étage Composer Appartement num immeuble ! strengthening_primary_foreign_key True Étage Étage Composer Appartement num étage ! strengthening_primary_foreign_key True Étage Étage Composer Appartement num appart. ! primary_key True Appartement nb pièces normal_attribute False Étage code rue ! strengthening_primary_foreign_key True Immeuble Immeuble Appartenir Étage num immeuble ! strengthening_primary_foreign_key True Immeuble Immeuble Appartenir Étage num étage ! primary_key True Étage nb appartements normal_attribute False Immeuble code rue ! strengthening_primary_foreign_key True Rue Rue Se situer Immeuble num immeuble ! primary_key True Immeuble nb étages normal_attribute False Rue code rue ! primary_key True Rue nom rue normal_attribute False ================================================ FILE: test/zoo/weak/mld/weak_2_dependencies.gv ================================================ digraph { node [shape=box] "Étage" -> "Appartement" "Immeuble" -> "Étage" "Rue" -> "Immeuble" } ================================================ FILE: test/zoo/weak/mld/weak_2_mld.html ================================================

    Conversion en relationnel

    Générée par Mocodo

    MCD weak
    Appartement ( #code rue, #num immeuble, #num étage, num appart., nb pièces )
    • Le champ code rue, num immeuble, num étage fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité Étage pour renforcer l'identifiant.
    • Le champ num appart. fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité Appartement.
    • Le champ nb pièces était déjà un simple attribut de l'entité Appartement.
    Étage ( #code rue, #num immeuble, num étage, nb appartements )
    • Le champ code rue, num immeuble fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité Immeuble pour renforcer l'identifiant.
    • Le champ num étage fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité Étage.
    • Le champ nb appartements était déjà un simple attribut de l'entité Étage.
    Immeuble ( #code rue, num immeuble, nb étages )
    • Le champ code rue fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité Rue pour renforcer l'identifiant.
    • Le champ num immeuble fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité Immeuble.
    • Le champ nb étages était déjà un simple attribut de l'entité Immeuble.
    Rue ( code rue, nom rue )
    • Le champ code rue constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Rue.
    • Le champ nom rue était déjà un simple attribut de l'entité Rue.
    ================================================ FILE: test/zoo/weak/mld/weak_2_mld.mcd ================================================ %%mocodo : Appartement: #code rue > Étage > code rue, _#num immeuble > Étage > num immeuble, _#num étage > Étage > num étage, _num appart., nb pièces : Étage: #code rue > Immeuble > code rue, _#num immeuble > Immeuble > num immeuble, _num étage, nb appartements : Immeuble: #code rue > Rue > code rue, _num immeuble, nb étages : Rue: code rue, nom rue : ================================================ FILE: test/zoo/weak/mld/weak_2_mld.md ================================================ - **Appartement** (_#code rue_, _#num immeuble_, _#num étage_, num appart., nb pièces) - Le champ _code rue_, _num immeuble_, _num étage_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _Étage_ pour renforcer l'identifiant. - Le champ _num appart._ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _Appartement_. - Le champ _nb pièces_ était déjà un simple attribut de l'entité _Appartement_. - **Étage** (_#code rue_, _#num immeuble_, num étage, nb appartements) - Le champ _code rue_, _num immeuble_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _Immeuble_ pour renforcer l'identifiant. - Le champ _num étage_ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _Étage_. - Le champ _nb appartements_ était déjà un simple attribut de l'entité _Étage_. - **Immeuble** (_#code rue_, num immeuble, nb étages) - Le champ _code rue_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité _Rue_ pour renforcer l'identifiant. - Le champ _num immeuble_ fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité _Immeuble_. - Le champ _nb étages_ était déjà un simple attribut de l'entité _Immeuble_. - **Rue** (code rue, nom rue) - Le champ _code rue_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Rue_. - Le champ _nom rue_ était déjà un simple attribut de l'entité _Rue_. ================================================ FILE: test/zoo/weak/mld/weak_2_mld.tex ================================================ \documentclass[a4paper]{article} \usepackage[normalem]{ulem} \usepackage[T1]{fontenc} \usepackage[french]{babel} \frenchsetup{StandardLayout=true} \newcommand{\relat}[1]{\textsc{#1}} \newcommand{\attr}[1]{#1} \newcommand{\prim}[1]{\uline{#1}} \newcommand{\foreign}[1]{\#\textsl{#1}} \title{Conversion en relationnel\\du MCD \emph{weak}} \author{\emph{Généré par Mocodo}} \begin{document} \maketitle \begin{itemize} \item \relat{Appartement} (\foreign{\prim{code rue}}, \foreign{\prim{num immeuble}}, \foreign{\prim{num étage}}, \prim{num appart.}, \attr{nb pièces}) \begin{itemize} \item Le champ \emph{code rue}, \emph{num immeuble}, \emph{num étage} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{Étage} pour renforcer l'identifiant. \item Le champ \emph{num appart.} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Appartement}. \item Le champ \emph{nb pièces} était déjà un simple attribut de l'entité \emph{Appartement}. \end{itemize} \item \relat{Étage} (\foreign{\prim{code rue}}, \foreign{\prim{num immeuble}}, \prim{num étage}, \attr{nb appartements}) \begin{itemize} \item Le champ \emph{code rue}, \emph{num immeuble} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{Immeuble} pour renforcer l'identifiant. \item Le champ \emph{num étage} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Étage}. \item Le champ \emph{nb appartements} était déjà un simple attribut de l'entité \emph{Étage}. \end{itemize} \item \relat{Immeuble} (\foreign{\prim{code rue}}, \prim{num immeuble}, \attr{nb étages}) \begin{itemize} \item Le champ \emph{code rue} fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité \emph{Rue} pour renforcer l'identifiant. \item Le champ \emph{num immeuble} fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Immeuble}. \item Le champ \emph{nb étages} était déjà un simple attribut de l'entité \emph{Immeuble}. \end{itemize} \item \relat{Rue} (\prim{code rue}, \attr{nom rue}) \begin{itemize} \item Le champ \emph{code rue} constitue la clé primaire de la table. C'était déjà un identifiant de l'entité \emph{Rue}. \item Le champ \emph{nom rue} était déjà un simple attribut de l'entité \emph{Rue}. \end{itemize} \end{itemize} \end{document} ================================================ FILE: test/zoo/weak/mld/weak_2_mld.txt ================================================ - Appartement (_#code rue_, _#num immeuble_, _#num étage_, _num appart._, nb pièces) - Le champ « code rue », « num immeuble », « num étage » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « Étage » pour renforcer l'identifiant. - Le champ « num appart. » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « Appartement ». - Le champ « nb pièces » était déjà un simple attribut de l'entité « Appartement ». - Étage (_#code rue_, _#num immeuble_, _num étage_, nb appartements) - Le champ « code rue », « num immeuble » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « Immeuble » pour renforcer l'identifiant. - Le champ « num étage » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « Étage ». - Le champ « nb appartements » était déjà un simple attribut de l'entité « Étage ». - Immeuble (_#code rue_, _num immeuble_, nb étages) - Le champ « code rue » fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré à partir de l'entité « Rue » pour renforcer l'identifiant. - Le champ « num immeuble » fait partie de la clé primaire de la table. C'était déjà un identifiant de l'entité « Immeuble ». - Le champ « nb étages » était déjà un simple attribut de l'entité « Immeuble ». - Rue (_code rue_, nom rue) - Le champ « code rue » constitue la clé primaire de la table. C'était déjà un identifiant de l'entité « Rue ». - Le champ « nom rue » était déjà un simple attribut de l'entité « Rue ». ================================================ FILE: test/zoo/weak/rewritten/weak_0_rw_create_df_arrows=across.mcd ================================================ Œuvre: œuvre DF, 0N> Œuvre, _11 Exemplaire: foobar Exemplaire: exemplaire ================================================ FILE: test/zoo/weak/rewritten/weak_0_rw_drain.mcd ================================================ Œuvre: œuvre DF, 0N Œuvre, _11 Exemplaire Exemplaire: exemplaire, foobar ================================================ FILE: test/zoo/weak/rewritten/weak_0_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1 DF, 0N ENTITÉ 1_, _11 ENTITÉ 2_: at 3 1 ENTITÉ 2_: at 2 1 ================================================ FILE: test/zoo/weak/rewritten/weak_0_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_0_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_0_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_0_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_0_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_0_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_0_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_1_rw_create_df_arrows=across.mcd ================================================ Œuvre: œuvre, auteur DF, 0N> Œuvre, _11 Exemplaire: foobar Exemplaire: exemplaire, nb pages, date achat ================================================ FILE: test/zoo/weak/rewritten/weak_1_rw_drain.mcd ================================================ Œuvre: œuvre, auteur DF, 0N Œuvre, _11 Exemplaire Exemplaire: exemplaire, nb pages, date achat, foobar ================================================ FILE: test/zoo/weak/rewritten/weak_1_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2 DF, 0N ENTITÉ 1_, _11 ENTITÉ 2_: at 3 1 ENTITÉ 2_: at 2 1, at 2 2, at 2 3 ================================================ FILE: test/zoo/weak/rewritten/weak_1_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_1_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_1_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_1_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_1_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_1_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_1_rw_split.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_2_rw_create_df_arrows=across.mcd ================================================ Appartement: num appart., nb pièces Composer, 0N> Étage, _11 Appartement Étage: num étage, nb appartements Appartenir, 1N> Immeuble, _11 Étage Immeuble: num immeuble, nb étages Se situer, 0N> Rue, _11 Immeuble Rue: code rue, nom rue ================================================ FILE: test/zoo/weak/rewritten/weak_2_rw_drain.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_2_rw_drown.mcd ================================================ ENTITÉ 1_: at 1 1, at 1 2 ASSOC 5_, 0N ENTITÉ 2_, _11 ENTITÉ 1_ ENTITÉ 2_: at 2 1, at 2 2 ASSOC 6_, 1N ENTITÉ 3_, _11 ENTITÉ 2_ ENTITÉ 3_: at 3 1, at 3 2 ASSOC 7_, 0N ENTITÉ 4_, _11 ENTITÉ 3_ ENTITÉ 4_: at 4 1, at 4 2 ================================================ FILE: test/zoo/weak/rewritten/weak_2_rw_explode_arity=2,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_2_rw_explode_arity=2.5,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_2_rw_explode_arity=2.5.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_2_rw_explode_arity=2.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_2_rw_explode_arity=3,weak.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_2_rw_explode_arity=3.mcd ================================================ % same as the source file ================================================ FILE: test/zoo/weak/rewritten/weak_2_rw_split.mcd ================================================ % same as the source file ================================================ FILE: web/ace-builds/ace.js ================================================ (function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE="",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;un.length)t=n.length;t-=e.length;var r=n.indexOf(e,t);return r!==-1&&r===t}),String.prototype.repeat||r(String.prototype,"repeat",function(e){var t="",n=this;while(e>0){e&1&&(t+=n);if(e>>=1)n+=n}return t}),String.prototype.includes||r(String.prototype,"includes",function(e,t){return this.indexOf(e,t)!=-1}),Object.assign||(Object.assign=function(e){if(e===undefined||e===null)throw new TypeError("Cannot convert undefined or null to object");var t=Object(e);for(var n=1;n>>0,r=arguments[1],i=r>>0,s=i<0?Math.max(n+i,0):Math.min(i,n),o=arguments[2],u=o===undefined?n:o>>0,a=u<0?Math.max(n+u,0):Math.min(u,n);while(s0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n=0?parseFloat((s.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((s.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=s.match(/ Gecko\/\d+/),t.isOpera=typeof opera=="object"&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(s.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(s.split(" Chrome/")[1])||undefined,t.isEdge=parseFloat(s.split(" Edge/")[1])||undefined,t.isAIR=s.indexOf("AdobeAIR")>=0,t.isAndroid=s.indexOf("Android")>=0,t.isChromeOS=s.indexOf(" CrOS ")>=0,t.isIOS=/iPad|iPhone|iPod/.test(s)&&!window.MSStream,t.isIOS&&(t.isMac=!0),t.isMobile=t.isIOS||t.isAndroid}),define("ace/lib/dom",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";function u(){var e=o;o=null,e&&e.forEach(function(e){a(e[0],e[1])})}function a(e,n,r){if(typeof document=="undefined")return;if(o)if(r)u();else if(r===!1)return o.push([e,n]);if(s)return;var i=r;if(!r||!r.getRootNode)i=document;else{i=r.getRootNode();if(!i||i==r)i=document}var a=i.ownerDocument||i;if(n&&t.hasCssString(n,i))return null;n&&(e+="\n/*# sourceURL=ace/css/"+n+" */");var f=t.createElement("style");f.appendChild(a.createTextNode(e)),n&&(f.id=n),i==a&&(i=t.getDocumentHead(a)),i.insertBefore(f,i.firstChild)}var r=e("./useragent"),i="http://www.w3.org/1999/xhtml";t.buildDom=function l(e,t,n){if(typeof e=="string"&&e){var r=document.createTextNode(e);return t&&t.appendChild(r),r}if(!Array.isArray(e))return e&&e.appendChild&&t&&t.appendChild(e),e;if(typeof e[0]!="string"||!e[0]){var i=[];for(var s=0;s=1.5:!0,r.isChromeOS&&(t.HI_DPI=!1);if(typeof document!="undefined"){var f=document.createElement("div");t.HI_DPI&&f.style.transform!==undefined&&(t.HAS_CSS_TRANSFORMS=!0),!r.isEdge&&typeof f.style.animationName!="undefined"&&(t.HAS_CSS_ANIMATION=!0),f=null}t.HAS_CSS_TRANSFORMS?t.translate=function(e,t,n){e.style.transform="translate("+Math.round(t)+"px, "+Math.round(n)+"px)"}:t.translate=function(e,t,n){e.style.top=Math.round(n)+"px",e.style.left=Math.round(t)+"px"}}),define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o1&&(i=n[n.length-2]);var o=u[t+"Path"];return o==null?o=u.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return u.$moduleUrls[e]=t};var a=function(t,n){return t==="ace/theme/textmate"||t==="./theme/textmate"?n(null,e("./theme/textmate")):console.error("loader is not configured")};t.setLoader=function(e){a=e},t.dynamicModules=Object.create(null),t.$loading={},t.loadModule=function(n,r){var s,o;Array.isArray(n)&&(o=n[0],n=n[1]);var u=function(e){if(e&&!t.$loading[n])return r&&r(e);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var s=function(){a(n,function(e,r){t._emit("load.module",{name:n,module:r});var i=t.$loading[n];t.$loading[n]=null,i.forEach(function(e){e&&e(r)})})};if(!t.get("packaged"))return s();i.loadScript(t.moduleUrl(n,o),s),f()};if(t.dynamicModules[n])t.dynamicModules[n]().then(function(e){e.default?u(e.default):u(e)});else{try{s=e(n)}catch(l){}u(s)}},t.setModuleLoader=function(e,n){t.dynamicModules[e]=n};var f=function(){!u.basePath&&!u.workerPath&&!u.modePath&&!u.themePath&&!Object.keys(u.$moduleUrls).length&&(console.error("Unable to infer path to ace from script src,","use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes","or with webpack use ace/webpack-resolver"),f=function(){})};t.version="1.22.1"}),define("ace/loader_build",["require","exports","module","ace/lib/fixoldbrowsers","ace/config"],function(e,t,n){"use strict";function s(t){if(!i||!i.document)return;r.set("packaged",t||e.packaged||n.packaged||i.define&&define.packaged);var s={},u="",a=document.currentScript||document._currentScript,f=a&&a.ownerDocument||document;a&&a.src&&(u=a.src.split(/[?#]/)[0].split("/").slice(0,-1).join("/")||"");var l=f.getElementsByTagName("script");for(var c=0;c ["+this.end.row+"/"+this.end.column+"]"},e.prototype.contains=function(e,t){return this.compare(e,t)==0},e.prototype.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},e.prototype.comparePoint=function(e){return this.compare(e.row,e.column)},e.prototype.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},e.prototype.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},e.prototype.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},e.prototype.isStart=function(e,t){return this.start.row==e&&this.start.column==t},e.prototype.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},e.prototype.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},e.prototype.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},e.prototype.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},e.prototype.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},e.prototype.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},e.prototype.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},e.prototype.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},e.prototype.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},e.prototype.clipRows=function(t,n){if(this.end.row>n)var r={row:n+1,column:0};else if(this.end.rown)var i={row:n+1,column:0};else if(this.start.row1?(u++,u>4&&(u=1)):u=1;if(i.isIE){var o=Math.abs(e.clientX-a)>5||Math.abs(e.clientY-f)>5;if(!l||o)u=1;l&&clearTimeout(l),l=setTimeout(function(){l=null},n[u-1]||600),u==1&&(a=e.clientX,f=e.clientY)}e._clicks=u,r[s]("mousedown",e);if(u>4)u=0;else if(u>1)return r[s](h[u],e)}var u=0,a,f,l,h={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){c(e,"mousedown",p,o)})};var p=function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[p(e)]},t.addCommandKeyListener=function(e,n,r){if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;c(e,"keydown",function(e){o=e.keyCode},r),c(e,"keypress",function(e){return d(n,e,o)},r)}else{var u=null;c(e,"keydown",function(e){s[e.keyCode]=(s[e.keyCode]||0)+1;var t=d(n,e,e.keyCode);return u=e.defaultPrevented,t},r),c(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)},r),c(e,"keyup",function(e){s[e.keyCode]=null},r),s||(v(),c(window,"focus",v))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var m=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+m++,i=function(s){s.data==r&&(t.stopPropagation(s),h(n,"message",i),e())};c(n,"message",i),n.postMessage(r,"*")}}t.$idleBlocked=!1,t.onIdle=function(e,n){return setTimeout(function r(){t.$idleBlocked?setTimeout(r,100):e()},n)},t.$idleBlockId=null,t.blockIdle=function(e){t.$idleBlockId&&clearTimeout(t.$idleBlockId),t.$idleBlocked=!0,t.$idleBlockId=setTimeout(function(){t.$idleBlocked=!1},e||100)},t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),define("ace/clipboard",["require","exports","module"],function(e,t,n){"use strict";var r;n.exports={lineMode:!1,pasteCancelled:function(){return r&&r>Date.now()-50?!0:r=!1},cancel:function(){r=Date.now()}}}),define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/config","ace/lib/useragent","ace/lib/dom","ace/lib/lang","ace/clipboard","ace/lib/keys"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../config").nls,s=e("../lib/useragent"),o=e("../lib/dom"),u=e("../lib/lang"),a=e("../clipboard"),f=s.isChrome<18,l=s.isIE,c=s.isChrome>63,h=400,p=e("../lib/keys"),d=p.KEY_MODS,v=s.isIOS,m=v?/\s/:/\n/,g=s.isMobile,y=function(e,t){function P(e){(!n||!n.parentNode)&&document.removeEventListener("selectionchange",P);if(w)return;if(n.selectionStart!==n.selectionEnd)return;var r=n.selectionStart-C,i=k-C;r>0?r=Math.max(r-i,1):r===0&&i&&(r=-1);var s=Math.abs(r),o=r>0?p.right:p.left;for(var u=0;uk&&N[s]=="\n")o=p.end;else if(rk&&N.slice(0,s).split("\n").length>2)o=p.down;else if(s>k&&N[s-1]==" ")o=p.right,u=d.option;else if(s>k||s==k&&k!=C&&r==s)o=p.right;r!==s&&(u|=d.shift);if(o){var a=t.onCommandKey({},u,o);if(!a&&t.commands){o=p.keyCodeToString(o);var f=t.commands.findKeyCommand(u,o);f&&t.execCommand(f)}C=r,k=s,M("")}};document.addEventListener("selectionchange",s),t.on("destroy",function(){document.removeEventListener("selectionchange",s)})}var n=o.createElement("textarea");n.className="ace_text-input",n.setAttribute("wrap","off"),n.setAttribute("autocorrect","off"),n.setAttribute("autocapitalize","off"),n.setAttribute("spellcheck",!1),n.style.opacity="0",e.insertBefore(n,e.firstChild);var y=!1,b=!1,w=!1,E=!1,S="";g||(n.style.fontSize="1px");var x=!1,T=!1,N="",C=0,k=0,L=0;try{var A=document.activeElement===n}catch(O){}this.setAriaOptions=function(e){e.activeDescendant?(n.setAttribute("aria-haspopup","true"),n.setAttribute("aria-autocomplete",e.inline?"both":"list"),n.setAttribute("aria-activedescendant",e.activeDescendant)):(n.setAttribute("aria-haspopup","false"),n.setAttribute("aria-autocomplete","both"),n.removeAttribute("aria-activedescendant")),e.role&&n.setAttribute("role",e.role)},this.setAriaLabel=function(){if(t.session&&t.renderer.enableKeyboardAccessibility){var e=t.session.selection.cursor.row;n.setAttribute("aria-roledescription",i("editor")),n.setAttribute("aria-label",i("Cursor at row $0",[e+1]))}else n.removeAttribute("aria-roledescription"),n.removeAttribute("aria-label")},this.setAriaOptions({role:"textbox"}),this.setAriaLabel(),r.addListener(n,"blur",function(e){if(T)return;t.onBlur(e),A=!1,g&&!v&&document.removeEventListener("selectionchange",P)},t),r.addListener(n,"focus",function(e){if(T)return;A=!0;if(s.isEdge)try{if(!document.hasFocus())return}catch(e){}t.onFocus(e),s.isEdge?setTimeout(M):M(),g&&!v&&document.addEventListener("selectionchange",P)},t),this.$focusScroll=!1,this.focus=function(){this.setAriaLabel();if(S||c||this.$focusScroll=="browser")return n.focus({preventScroll:!0});var e=n.style.top;n.style.position="fixed",n.style.top="0px";try{var t=n.getBoundingClientRect().top!=0}catch(r){return}var i=[];if(t){var s=n.parentElement;while(s&&s.nodeType==1)i.push(s),s.setAttribute("ace_nocontext",!0),!s.parentElement&&s.getRootNode?s=s.getRootNode().host:s=s.parentElement}n.focus({preventScroll:!0}),t&&i.forEach(function(e){e.removeAttribute("ace_nocontext")}),setTimeout(function(){n.style.position="",n.style.top=="0px"&&(n.style.top=e)},0)},this.blur=function(){n.blur()},this.isFocused=function(){return A},t.on("beforeEndOperation",function(){var e=t.curOp,r=e&&e.command&&e.command.name;if(r=="insertstring")return;var i=r&&(e.docChanged||e.selectionChanged);w&&i&&(N=n.value="",V()),M()});var M=v?function(e){if(!A||y&&!e||E)return;e||(e="");var r="\n ab"+e+"cde fg\n";r!=n.value&&(n.value=N=r);var i=4,s=4+(e.length||(t.selection.isEmpty()?0:1));(C!=i||k!=s)&&n.setSelectionRange(i,s),C=i,k=s}:function(){if(w||E)return;if(!A&&!B)return;w=!0;var e=0,r=0,i="";if(t.session){var s=t.selection,o=s.getRange(),u=s.cursor.row;e=o.start.column,r=o.end.column,i=t.session.getLine(u);if(o.start.row!=u){var a=t.session.getLine(u-1);e=o.start.rowu+1?f.length:r,r+=i.length+1,i=i+"\n"+f}else g&&u>0&&(i="\n"+i,r+=1,e+=1);i.length>h&&(e=N.length&&e.value===N&&N&&e.selectionEnd!==k},D=function(e){if(w)return;y?y=!1:_(n)?(t.selectAll(),M()):g&&n.selectionStart!=C&&M()},H=null;this.setInputHandler=function(e){H=e},this.getInputHandler=function(){return H};var B=!1,j=function(e,r){B&&(B=!1);if(b)return M(),e&&t.onPaste(e),b=!1,"";var i=n.selectionStart,o=n.selectionEnd,u=C,a=N.length-k,f=e,l=e.length-i,c=e.length-o,h=0;while(u>0&&N[h]==e[h])h++,u--;f=f.slice(h),h=1;while(a>0&&N.length-h>C-1&&N[N.length-h]==e[e.length-h])h++,a--;l-=h-1,c-=h-1;var p=f.length-h+1;p<0&&(u=-p,p=0),f=f.slice(0,p);if(!r&&!f&&!l&&!u&&!a&&!c)return"";E=!0;var d=!1;return s.isAndroid&&f==". "&&(f=" ",d=!0),f&&!u&&!a&&!l&&!c||x?t.onTextInput(f):t.onTextInput(f,{extendLeft:u,extendRight:a,restoreStart:l,restoreEnd:c}),E=!1,N=e,C=i,k=o,L=c,d?"\n":f},F=function(e){if(w)return X();if(e&&e.inputType){if(e.inputType=="historyUndo")return t.execCommand("undo");if(e.inputType=="historyRedo")return t.execCommand("redo")}var r=n.value,i=j(r,!0);(r.length>h+100||m.test(i)||g&&C<1&&C==k)&&M()},I=function(e,t,n){var r=e.clipboardData||window.clipboardData;if(!r||f)return;var i=l||n?"Text":"text/plain";try{return t?r.setData(i,t)!==!1:r.getData(i)}catch(e){if(!n)return I(e,t,!0)}},q=function(e,i){var s=t.getCopyText();if(!s)return r.preventDefault(e);I(e,s)?(v&&(M(s),y=s,setTimeout(function(){y=!1},10)),i?t.onCut():t.onCopy(),r.preventDefault(e)):(y=!0,n.value=s,n.select(),setTimeout(function(){y=!1,M(),i?t.onCut():t.onCopy()}))},R=function(e){q(e,!0)},U=function(e){q(e,!1)},z=function(e){var i=I(e);if(a.pasteCancelled())return;typeof i=="string"?(i&&t.onPaste(i,e),s.isIE&&setTimeout(M),r.preventDefault(e)):(n.value="",b=!0)};r.addCommandKeyListener(n,t.onCommandKey.bind(t),t),r.addListener(n,"select",D,t),r.addListener(n,"input",F,t),r.addListener(n,"cut",R,t),r.addListener(n,"copy",U,t),r.addListener(n,"paste",z,t),(!("oncut"in n)||!("oncopy"in n)||!("onpaste"in n))&&r.addListener(e,"keydown",function(e){if(s.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:U(e);break;case 86:z(e);break;case 88:R(e)}},t);var W=function(e){if(w||!t.onCompositionStart||t.$readOnly)return;w={};if(x)return;e.data&&(w.useTextareaForIME=!1),setTimeout(X,0),t._signal("compositionStart"),t.on("mousedown",$);var r=t.getSelectionRange();r.end.row=r.start.row,r.end.column=r.start.column,w.markerRange=r,w.selectionStart=C,t.onCompositionStart(w),w.useTextareaForIME?(N=n.value="",C=0,k=0):(n.msGetInputContext&&(w.context=n.msGetInputContext()),n.getInputContext&&(w.context=n.getInputContext()))},X=function(){if(!w||!t.onCompositionUpdate||t.$readOnly)return;if(x)return $();if(w.useTextareaForIME)t.onCompositionUpdate(n.value);else{var e=n.value;j(e),w.markerRange&&(w.context&&(w.markerRange.start.column=w.selectionStart=w.context.compositionStartOffset),w.markerRange.end.column=w.markerRange.start.column+k-w.selectionStart+L)}},V=function(e){if(!t.onCompositionEnd||t.$readOnly)return;w=!1,t.onCompositionEnd(),t.off("mousedown",$),e&&F()},J=u.delayedCall(X,50).schedule.bind(null,null);r.addListener(n,"compositionstart",W,t),r.addListener(n,"compositionupdate",X,t),r.addListener(n,"keyup",K,t),r.addListener(n,"keydown",J,t),r.addListener(n,"compositionend",V,t),this.getElement=function(){return n},this.setCommandMode=function(e){x=e,n.readOnly=!1},this.setReadOnly=function(e){x||(n.readOnly=e)},this.setCopyWithEmptySelection=function(e){},this.onContextMenu=function(e){B=!0,M(),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,i){S||(S=n.style.cssText),n.style.cssText=(i?"z-index:100000;":"")+(s.isIE?"opacity:0.1;":"")+"text-indent: -"+(C+k)*t.renderer.characterWidth*.5+"px;";var u=t.container.getBoundingClientRect(),a=o.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(u.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){o.translate(n,e.clientX-l-2,Math.min(e.clientY-f-2,c))};h(e);if(e.type!="mousedown")return;t.renderer.$isMousePressed=!0,clearTimeout(Q),s.isWin&&r.capture(t.container,h,G)},this.onContextMenuClose=G;var Q,Y=function(e){t.textInput.onContextMenu(e),G()};r.addListener(n,"mouseup",Y,t),r.addListener(n,"mousedown",function(e){e.preventDefault(),G()},t),r.addListener(t.renderer.scroller,"contextmenu",Y,t),r.addListener(n,"contextmenu",Y,t),v&&Z(e,t,n),this.destroy=function(){n.parentElement&&n.parentElement.removeChild(n)}};t.TextInput=y,t.$setUserAgentForTests=function(e,t){g=e,v=t}}),define("ace/mouse/default_handlers",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/useragent"),i=0,s=550,o=function(){function e(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e));var n=["select","startSelect","selectEnd","selectAllEnd","selectByWordsEnd","selectByLinesEnd","dragWait","dragWaitEnd","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange")}return e.prototype.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var i=this.editor,s=e.getButton();if(s!==0){var o=i.getSelectionRange(),u=o.isEmpty();(u||s==1)&&i.selection.moveToPosition(n),s==2&&(i.textInput.onContextMenu(e.domEvent),r.isMozilla||e.preventDefault());return}this.mousedownEvent.time=Date.now();if(t&&!i.isFocused()){i.focus();if(this.$focusTimeout&&!this.$clickSelection&&!i.inMultiSelectMode){this.setState("focusWait"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},e.prototype.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;if(!this.mousedownEvent)return;this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.setStyle("ace_selecting"),this.setState("select")},e.prototype.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=a(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.renderer.scrollCursorIntoView()},e.prototype.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=a(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},e.prototype.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting")},e.prototype.focusWait=function(){var e=u(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>i||t-this.mousedownEvent.time>this.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},e.prototype.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},e.prototype.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},e.prototype.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},e.prototype.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=this.editor;this.$lastScroll||(this.$lastScroll={t:0,vx:0,vy:0,allowed:0});var n=this.$lastScroll,r=e.domEvent.timeStamp,i=r-n.t,o=i?e.wheelX/i:n.vx,u=i?e.wheelY/i:n.vy;i=1&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(f=!0),a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed)&&(f=!0);if(f)n.allowed=r;else if(r-n.allowed=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},s=e("./lib/dom"),o=e("./range").Range,u="ace_tooltip",a=function(){function e(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}return e.prototype.$init=function(){return this.$element=s.createElement("div"),this.$element.className=u,this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},e.prototype.getElement=function(){return this.$element||this.$init()},e.prototype.setText=function(e){this.getElement().textContent=e},e.prototype.setHtml=function(e){this.getElement().innerHTML=e},e.prototype.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},e.prototype.setClassName=function(e){s.addCssClass(this.getElement(),e)},e.prototype.setTheme=function(e){this.$element.className=u+" "+(e.isDark?"ace_dark ":"")+(e.cssClass||"")},e.prototype.show=function(e,t,n){e!=null&&this.setText(e),t!=null&&n!=null&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},e.prototype.hide=function(){this.isOpen&&(this.getElement().style.display="none",this.getElement().className=u,this.isOpen=!1)},e.prototype.getHeight=function(){return this.getElement().offsetHeight},e.prototype.getWidth=function(){return this.getElement().offsetWidth},e.prototype.destroy=function(){this.isOpen=!1,this.$element&&this.$element.parentNode&&this.$element.parentNode.removeChild(this.$element)},e}(),f=function(){function e(){this.popups=[]}return e.prototype.addPopup=function(e){this.popups.push(e),this.updatePopups()},e.prototype.removePopup=function(e){var t=this.popups.indexOf(e);t!==-1&&(this.popups.splice(t,1),this.updatePopups())},e.prototype.updatePopups=function(){var e,t,n,r;this.popups.sort(function(e,t){return t.priority-e.priority});var s=[];try{for(var o=i(this.popups),u=o.next();!u.done;u=o.next()){var a=u.value,f=!0;try{for(var l=(n=void 0,i(s)),c=l.next();!c.done;c=l.next()){var h=c.value;if(this.doPopupsOverlap(h,a)){f=!1;break}}}catch(p){n={error:p}}finally{try{c&&!c.done&&(r=l.return)&&r.call(l)}finally{if(n)throw n.error}}f?s.push(a):a.hide()}}catch(d){e={error:d}}finally{try{u&&!u.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}},e.prototype.doPopupsOverlap=function(e,t){var n=e.getElement().getBoundingClientRect(),r=t.getElement().getBoundingClientRect();return n.leftr.left&&n.topr.top},e}(),l=new f;t.popupManager=l,t.Tooltip=a;var c=function(e){function t(t){t===void 0&&(t=document.body);var n=e.call(this,t)||this;n.timeout=undefined,n.lastT=0,n.idleTime=350,n.lastEvent=undefined,n.onMouseOut=n.onMouseOut.bind(n),n.onMouseMove=n.onMouseMove.bind(n),n.waitForHover=n.waitForHover.bind(n),n.hide=n.hide.bind(n);var r=n.getElement();return r.style.whiteSpace="pre-wrap",r.style.pointerEvents="auto",r.addEventListener("mouseout",n.onMouseOut),r.tabIndex=-1,r.addEventListener("blur",function(){r.contains(document.activeElement)||this.hide()}.bind(n)),n}return r(t,e),t.prototype.addToEditor=function(e){e.on("mousemove",this.onMouseMove),e.on("mousedown",this.hide),e.renderer.getMouseEventTarget().addEventListener("mouseout",this.onMouseOut,!0)},t.prototype.removeFromEditor=function(e){e.off("mousemove",this.onMouseMove),e.off("mousedown",this.hide),e.renderer.getMouseEventTarget().removeEventListener("mouseout",this.onMouseOut,!0),this.timeout&&(clearTimeout(this.timeout),this.timeout=null)},t.prototype.onMouseMove=function(e,t){this.lastEvent=e,this.lastT=Date.now();var n=t.$mouseHandler.isMousePressed;if(this.isOpen){var r=this.lastEvent&&this.lastEvent.getDocumentPosition();(!this.range||!this.range.contains(r.row,r.column)||n||this.isOutsideOfText(this.lastEvent))&&this.hide()}if(this.timeout||n)return;this.lastEvent=e,this.timeout=setTimeout(this.waitForHover,this.idleTime)},t.prototype.waitForHover=function(){this.timeout&&clearTimeout(this.timeout);var e=Date.now()-this.lastT;if(this.idleTime-e>10){this.timeout=setTimeout(this.waitForHover,this.idleTime-e);return}this.timeout=null,this.lastEvent&&!this.isOutsideOfText(this.lastEvent)&&this.$gatherData(this.lastEvent,this.lastEvent.editor)},t.prototype.isOutsideOfText=function(e){var t=e.editor,n=e.getDocumentPosition(),r=t.session.getLine(n.row);if(n.column==r.length){var i=t.renderer.pixelToScreenCoordinates(e.clientX,e.clientY),s=t.session.documentToScreenPosition(n.row,n.column);if(s.column!=i.column||s.row!=i.row)return!0}return!1},t.prototype.setDataProvider=function(e){this.$gatherData=e},t.prototype.showForRange=function(e,t,n,r){if(r&&r!=this.lastEvent)return;if(this.isOpen&&document.activeElement==this.getElement())return;var i=e.renderer;this.isOpen||(l.addPopup(this),this.$registerCloseEvents(),this.setTheme(i.theme)),this.isOpen=!0,this.addMarker(t,e.session),this.range=o.fromPoints(t.start,t.end);var s=this.getElement();s.innerHTML="",s.appendChild(n),s.style.display="block";var u=i.textToScreenCoordinates(t.start.row,t.start.column),a=e.getCursorPosition(),f=s.clientHeight,c=i.scroller.getBoundingClientRect(),h=!0;this.row>a.row?h=!0:this.rowc.bottom&&(h=!1),h?u.pageY+=i.lineHeight:u.pageY-=f,s.style.maxWidth=c.width-(u.pageX-c.left)+"px",this.setPosition(u.pageX,u.pageY)},t.prototype.addMarker=function(e,t){this.marker&&this.$markerSession.removeMarker(this.marker),this.$markerSession=t,this.marker=t&&t.addMarker(e,"ace_highlight-marker","text")},t.prototype.hide=function(e){if(!e&&document.activeElement==this.getElement())return;if(e&&e.target&&(e.type!="keydown"||e.ctrlKey||e.metaKey)&&this.$element.contains(e.target))return;this.lastEvent=null,this.timeout&&clearTimeout(this.timeout),this.timeout=null,this.addMarker(null),this.isOpen&&(this.$removeCloseEvents(),this.getElement().style.display="none",this.isOpen=!1,l.removePopup(this))},t.prototype.$registerCloseEvents=function(){window.addEventListener("keydown",this.hide,!0),window.addEventListener("mousewheel",this.hide,!0),window.addEventListener("mousedown",this.hide,!0)},t.prototype.$removeCloseEvents=function(){window.removeEventListener("keydown",this.hide,!0),window.removeEventListener("mousewheel",this.hide,!0),window.removeEventListener("mousedown",this.hide,!0)},t.prototype.onMouseOut=function(e){this.timeout&&(clearTimeout(this.timeout),this.timeout=null),this.lastEvent=null;if(!this.isOpen)return;if(!e.relatedTarget||e.relatedTarget==this.getElement())return;if(e&&e.currentTarget.contains(e.relatedTarget))return;e.relatedTarget.classList.contains("ace_content")||this.hide()},t}(a);t.HoverTooltip=c}),define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/tooltip","ace/config"],function(e,t,n){"use strict";function f(e){function a(){var i=u.getDocumentPosition().row,s=t.session.getLength();if(i==s){var o=t.renderer.pixelToScreenCoordinates(0,u.y).row,a=u.$pos;if(o>t.session.documentToScreenRow(a.row,a.column))return f()}r.showTooltip(i);if(!r.isOpen)return;t.on("mousewheel",f);if(e.$tooltipFollowsMouse)c(u);else{var l=n.$lines.cells[i].element.querySelector("[class*=ace_icon]"),h=l.getBoundingClientRect(),p=r.getElement().style;p.left=h.right+"px",p.top=h.bottom+"px"}}function f(){i&&(i=clearTimeout(i)),r.isOpen&&(r.hide(),t._signal("hideGutterTooltip",r),t.off("mousewheel",f))}function c(e){r.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,r=new l(t);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var i,u;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(s.hasCssClass(n,"ace_fold-widget"))return f();r.isOpen&&e.$tooltipFollowsMouse&&c(t),u=t;if(i)return;i=setTimeout(function(){i=null,u&&!e.isMousePressed?a():f()},50)}),o.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!r.isOpen||i)return;i=setTimeout(function(){i=null,f()},50)},t),t.on("changeSession",f),t.on("input",f)}var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=this&&this.__values||function(e){var t=typeof Symbol=="function"&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&typeof e.length=="number")return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},s=e("../lib/dom"),o=e("../lib/event"),u=e("../tooltip").Tooltip,a=e("../config").nls;t.GutterHandler=f;var l=function(e){function t(t){var n=e.call(this,t.container)||this;return n.editor=t,n}return r(t,e),t.prototype.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),u.prototype.setPosition.call(this,e,t)},Object.defineProperty(t,"annotationLabels",{get:function(){return{error:{singular:a("error"),plural:a("errors")},warning:{singular:a("warning"),plural:a("warnings")},info:{singular:a("information message"),plural:a("information messages")}}},enumerable:!1,configurable:!0}),t.prototype.showTooltip=function(e){var n=this.editor.renderer.$gutterLayer,r=n.$annotations[e],i;r?i={text:Array.from(r.text),type:Array.from(r.type)}:i={text:[],type:[]};var s=n.session.getFoldLine(e);if(s&&n.$showFoldedAnnotations){var o={error:[],warning:[],info:[]},u;for(var a=e+1;a<=s.end.row;a++){if(!n.$annotations[a])continue;for(var f=0;f ").concat(i.text[a]);h[i.type[a].replace("_fold","")].push(d)}var v=[].concat(h.error,h.warning,h.info).join("
    ");this.setHtml(v),this.setClassName("ace_gutter-tooltip"),this.$element.setAttribute("aria-live","polite"),this.isOpen||this.setTheme(this.editor.renderer.theme),this.editor._signal("showGutterTooltip",this),this.show()},t.annotationsToSummaryString=function(e){var n,r,s=[],o=["error","warning","info"];try{for(var u=i(o),a=u.next();!a.done;a=u.next()){var f=a.value;if(!e[f].length)continue;var l=e[f].length===1?t.annotationLabels[f].singular:t.annotationLabels[f].plural;s.push("".concat(e[f].length," ").concat(l))}}catch(c){n={error:c}}finally{try{a&&!a.done&&(r=u.return)&&r.call(u)}finally{if(n)throw n.error}}return s.join(", ")},t}(u);t.GutterTooltip=l}),define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=function(){function e(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1}return e.prototype.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},e.prototype.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},e.prototype.stop=function(){this.stopPropagation(),this.preventDefault()},e.prototype.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},e.prototype.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},e.prototype.getButton=function(){return r.getButton(this.domEvent)},e.prototype.getShiftKey=function(){return this.domEvent.shiftKey},e.prototype.getAccelKey=function(){return i.isMac?this.domEvent.metaKey:this.domEvent.ctrlKey},e}();t.MouseEvent=s}),define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.moveCursorToPosition(e),S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.selection.fromOrientedRange(m),t.isFocused()&&!w&&t.$resetCursorStyle(),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("div");n.style.cssText="top:-100px;position:absolute;z-index:2147483647;opacity:0.5",n.textContent="\u00a0";var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.on("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",t.container.appendChild(n),i.setDragImage&&i.setDragImage(n,0,0),setTimeout(function(){t.container.removeChild(n)}),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.$resetCursorStyle()}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e),t),i.addListener(c,"dragend",this.onDragEnd.bind(e),t),i.addListener(c,"dragenter",this.onDragEnter.bind(e),t),i.addListener(c,"dragover",this.onDragOver.bind(e),t),i.addListener(c,"dragleave",this.onDragLeave.bind(e),t),i.addListener(c,"drop",this.onDrop.bind(e),t);var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.$resetCursorStyle(),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),define("ace/mouse/touch_handler",["require","exports","module","ace/mouse/mouse_event","ace/lib/event","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./mouse_event").MouseEvent,i=e("../lib/event"),s=e("../lib/dom");t.addTouchListeners=function(e,t){function b(){var e=window.navigator&&window.navigator.clipboard,r=!1,i=function(){var n=t.getCopyText(),i=t.session.getUndoManager().hasUndo();y.replaceChild(s.buildDom(r?["span",!n&&["span",{"class":"ace_mobile-button",action:"selectall"},"Select All"],n&&["span",{"class":"ace_mobile-button",action:"copy"},"Copy"],n&&["span",{"class":"ace_mobile-button",action:"cut"},"Cut"],e&&["span",{"class":"ace_mobile-button",action:"paste"},"Paste"],i&&["span",{"class":"ace_mobile-button",action:"undo"},"Undo"],["span",{"class":"ace_mobile-button",action:"find"},"Find"],["span",{"class":"ace_mobile-button",action:"openCommandPallete"},"Palette"]]:["span"]),y.firstChild)},o=function(n){var s=n.target.getAttribute("action");if(s=="more"||!r)return r=!r,i();if(s=="paste")e.readText().then(function(e){t.execCommand(s,e)});else if(s){if(s=="cut"||s=="copy")e?e.writeText(t.getCopyText()):document.execCommand("copy");t.execCommand(s)}y.firstChild.style.display="none",r=!1,s!="openCommandPallete"&&t.focus()};y=s.buildDom(["div",{"class":"ace_mobile-menu",ontouchstart:function(e){n="menu",e.stopPropagation(),e.preventDefault(),t.textInput.focus()},ontouchend:function(e){e.stopPropagation(),e.preventDefault(),o(e)},onclick:o},["span"],["span",{"class":"ace_mobile-button",action:"more"},"..."]],t.container)}function w(){y||b();var e=t.selection.cursor,n=t.renderer.textToScreenCoordinates(e.row,e.column),r=t.renderer.textToScreenCoordinates(0,0).pageX,i=t.renderer.scrollLeft,s=t.container.getBoundingClientRect();y.style.top=n.pageY-s.top-3+"px",n.pageX-s.left=2?t.selection.getLineRange(p.row):t.session.getBracketRange(p);e&&!e.isEmpty()?t.selection.setRange(e):t.selection.selectWord(),n="wait"}function T(){h+=60,c=setInterval(function(){h--<=0&&(clearInterval(c),c=null),Math.abs(v)<.01&&(v=0),Math.abs(m)<.01&&(m=0),h<20&&(v=.9*v),h<20&&(m=.9*m);var e=t.session.getScrollTop();t.renderer.scrollBy(10*v,10*m),e==t.session.getScrollTop()&&(h=0)},10)}var n="scroll",o,u,a,f,l,c,h=0,p,d=0,v=0,m=0,g,y;i.addListener(e,"contextmenu",function(e){if(!g)return;var n=t.textInput.getElement();n.focus()},t),i.addListener(e,"touchstart",function(e){var i=e.touches;if(l||i.length>1){clearTimeout(l),l=null,a=-1,n="zoom";return}g=t.$mouseHandler.isMousePressed=!0;var s=t.renderer.layerConfig.lineHeight,c=t.renderer.layerConfig.lineHeight,y=e.timeStamp;f=y;var b=i[0],w=b.clientX,E=b.clientY;Math.abs(o-w)+Math.abs(u-E)>s&&(a=-1),o=e.clientX=w,u=e.clientY=E,v=m=0;var T=new r(e,t);p=T.getDocumentPosition();if(y-a<500&&i.length==1&&!h)d++,e.preventDefault(),e.button=0,x();else{d=0;var N=t.selection.cursor,C=t.selection.isEmpty()?N:t.selection.anchor,k=t.renderer.$cursorLayer.getPixelPosition(N,!0),L=t.renderer.$cursorLayer.getPixelPosition(C,!0),A=t.renderer.scroller.getBoundingClientRect(),O=t.renderer.layerConfig.offset,M=t.renderer.scrollLeft,_=function(e,t){return e/=c,t=t/s-.75,e*e+t*t};if(e.clientXP?"cursor":"anchor"),P<3.5?n="anchor":D<3.5?n="cursor":n="scroll",l=setTimeout(S,450)}a=y},t),i.addListener(e,"touchend",function(e){g=t.$mouseHandler.isMousePressed=!1,c&&clearInterval(c),n=="zoom"?(n="",h=0):l?(t.selection.moveToPosition(p),h=0,w()):n=="scroll"?(T(),E()):w(),clearTimeout(l),l=null},t),i.addListener(e,"touchmove",function(e){l&&(clearTimeout(l),l=null);var i=e.touches;if(i.length>1||n=="zoom")return;var s=i[0],a=o-s.clientX,c=u-s.clientY;if(n=="wait"){if(!(a*a+c*c>4))return e.preventDefault();n="cursor"}o=s.clientX,u=s.clientY,e.clientX=s.clientX,e.clientY=s.clientY;var h=e.timeStamp,p=h-f;f=h;if(n=="scroll"){var d=new r(e,t);d.speed=1,d.wheelX=a,d.wheelY=c,10*Math.abs(a)0)if(g==16){for(w=b;w-1){for(w=b;w=0;C--){if(r[C]!=N)break;t[C]=s}}}function I(e,t,n){if(o=e){u=i+1;while(u=e)u++;for(a=i,l=u-1;a=t.length||(o=n[r-1])!=b&&o!=w||(c=t[r+1])!=b&&c!=w)return E;return u&&(c=w),c==o?c:E;case k:o=r>0?n[r-1]:S;if(o==b&&r+10&&n[r-1]==b)return b;if(u)return E;p=r+1,h=t.length;while(p=1425&&d<=2303||d==64286;o=t[p];if(v&&(o==y||o==T))return y}if(r<1||(o=t[r-1])==S)return E;return n[r-1];case S:return u=!1,f=!0,s;case x:return l=!0,E;case O:case M:case D:case P:case _:u=!1;case H:return E}}function R(e){var t=e.charCodeAt(0),n=t>>8;return n==0?t>191?g:B[t]:n==5?/[\u0591-\u05f4]/.test(e)?y:g:n==6?/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(e)?A:/[\u0660-\u0669\u066b-\u066c]/.test(e)?w:t==1642?L:/[\u06f0-\u06f9]/.test(e)?b:T:n==32&&t<=8287?j[t&255]:n==254?t>=65136?T:E:E}function U(e){return e>="\u064b"&&e<="\u0655"}var r=["\u0621","\u0641"],i=["\u063a","\u064a"],s=0,o=0,u=!1,a=!1,f=!1,l=!1,c=!1,h=!1,p=[[0,3,0,1,0,0,0],[0,3,0,1,2,2,0],[0,3,0,17,2,0,1],[0,3,5,5,4,1,0],[0,3,21,21,4,0,1],[0,3,5,5,4,2,0]],d=[[2,0,1,1,0,1,0],[2,0,1,1,0,2,0],[2,0,2,1,3,2,0],[2,0,2,33,3,1,1]],v=0,m=1,g=0,y=1,b=2,w=3,E=4,S=5,x=6,T=7,N=8,C=9,k=10,L=11,A=12,O=13,M=14,_=15,D=16,P=17,H=18,B=[H,H,H,H,H,H,H,H,H,x,S,x,N,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,S,S,S,x,N,E,E,L,L,L,E,E,E,E,E,k,C,k,C,C,b,b,b,b,b,b,b,b,b,b,C,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,H,H,H,H,H,H,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,C,E,L,L,L,L,E,E,E,E,g,E,E,H,E,E,L,L,b,b,E,g,E,E,E,b,g,E,E,E,E,E],j=[N,N,N,N,N,N,N,N,N,N,N,H,H,H,g,y,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N,S,O,M,_,D,P,C,L,L,L,L,L,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,C,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N];t.L=g,t.R=y,t.EN=b,t.ON_R=3,t.AN=4,t.R_H=5,t.B=6,t.RLE=7,t.DOT="\u00b7",t.doBidiReorder=function(e,n,r){if(e.length<2)return{};var i=e.split(""),o=new Array(i.length),u=new Array(i.length),a=[];s=r?m:v,F(i,a,i.length,n);for(var f=0;fT&&n[f]0&&i[f-1]==="\u0644"&&/\u0622|\u0623|\u0625|\u0627/.test(i[f])&&(a[f-1]=a[f]=t.R_H,f++);i[i.length-1]===t.DOT&&(a[i.length-1]=t.B),i[0]==="\u202b"&&(a[0]=t.RLE);for(var f=0;f=0&&(e=this.session.$docRowCache[n])}return e},e.prototype.getSplitIndex=function(){var e=0,t=this.session.$screenRowCache;if(t.length){var n,r=this.session.$getRowCacheIndex(t,this.currentRow);while(this.currentRow-e>0){n=this.session.$getRowCacheIndex(t,this.currentRow-e-1);if(n!==r)break;r=n,e++}}else e=this.currentRow;return e},e.prototype.updateRowLine=function(e,t){e===undefined&&(e=this.getDocumentRow());var n=e===this.session.getLength()-1,s=n?this.EOF:this.EOL;this.wrapIndent=0,this.line=this.session.getLine(e),this.isRtlDir=this.$isRtl||this.line.charAt(0)===this.RLE;if(this.session.$useWrapMode){var o=this.session.$wrapData[e];o&&(t===undefined&&(t=this.getSplitIndex()),t>0&&o.length?(this.wrapIndent=o.indent,this.wrapOffset=this.wrapIndent*this.charWidths[r.L],this.line=tt?this.session.getOverwrite()?e:e-1:t,i=r.getVisualFromLogicalIdx(n,this.bidiMap),s=this.bidiMap.bidiLevels,o=0;!this.session.getOverwrite()&&e<=t&&s[i]%2!==0&&i++;for(var u=0;ut&&s[i]%2===0&&(o+=this.charWidths[s[i]]),this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset),this.isRtlDir&&(o+=this.rtlLineOffset),o},e.prototype.getSelections=function(e,t){var n=this.bidiMap,r=n.bidiLevels,i,s=[],o=0,u=Math.min(e,t)-this.wrapIndent,a=Math.max(e,t)-this.wrapIndent,f=!1,l=!1,c=0;this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset);for(var h,p=0;p=u&&hn+s/2){n+=s;if(r===i.length-1){s=0;break}s=this.charWidths[i[++r]]}return r>0&&i[r-1]%2!==0&&i[r]%2===0?(e0&&i[r-1]%2===0&&i[r]%2!==0?t=1+(e>n?this.bidiMap.logicalFromVisual[r]:this.bidiMap.logicalFromVisual[r-1]):this.isRtlDir&&r===i.length-1&&s===0&&i[r-1]%2===0||!this.isRtlDir&&r===0&&i[r]%2!==0?t=1+this.bidiMap.logicalFromVisual[r]:(r>0&&i[r-1]%2!==0&&s!==0&&r--,t=this.bidiMap.logicalFromVisual[r]),t===0&&this.isRtlDir&&t++,t+this.wrapIndent},e}();t.BidiHandler=o}),define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.cursor=this.lead=this.doc.createAnchor(0,0),this.anchor=this.doc.createAnchor(0,0),this.$silent=!1;var t=this;this.cursor.on("change",function(e){t.$cursorChanged=!0,t.$silent||t._emit("changeCursor"),!t.$isEmpty&&!t.$silent&&t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.anchor.on("change",function(){t.$anchorChanged=!0,!t.$isEmpty&&!t.$silent&&t._emit("changeSelection")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return!this.$isEmpty&&this.anchor.row!=this.cursor.row},this.getCursor=function(){return this.lead.getPosition()},this.setAnchor=function(e,t){this.$isEmpty=!1,this.anchor.setPosition(e,t)},this.setSelectionAnchor=this.setAnchor,this.getAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionAnchor=this.getAnchor,this.getSelectionLead=function(){return this.lead.getPosition()},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.$isEmpty?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){this.$setSelection(0,0,Number.MAX_VALUE,Number.MAX_VALUE)},this.setRange=this.setSelectionRange=function(e,t){var n=t?e.end:e.start,r=t?e.start:e.end;this.$setSelection(n.row,n.column,r.row,r.column)},this.$setSelection=function(e,t,n,r){if(this.$silent)return;var i=this.$isEmpty,s=this.inMultiSelectMode;this.$silent=!0,this.$cursorChanged=this.$anchorChanged=!1,this.anchor.setPosition(e,t),this.cursor.setPosition(n,r),this.$isEmpty=!o.comparePoints(this.anchor,this.cursor),this.$silent=!1,this.$cursorChanged&&this._emit("changeCursor"),(this.$cursorChanged||this.$anchorChanged||i!=this.$isEmpty||s)&&this._emit("changeSelection")},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.wouldMoveIntoSoftTab=function(e,t,n){var r=e.column,i=e.column+t;return n<0&&(r=e.column-t,i=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(r,i).split(" ").length-1==t},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.wouldMoveIntoSoftTab(e,n,-1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t);this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var i=this.session.getFoldAt(e,t,1);if(i){this.moveCursorTo(i.end.row,i.end.column);return}this.session.nonTokenRe.exec(r)&&(t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t));if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}this.session.tokenRe.exec(s)&&(t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0),this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t=0,n,r=/\s/,i=this.session.tokenRe;i.lastIndex=0;if(this.session.tokenRe.exec(e))t=this.session.tokenRe.lastIndex;else{while((n=e[t])&&r.test(n))t++;if(t<1){i.lastIndex=0;while((n=e[t])&&!i.test(n)){i.lastIndex=0,t++;if(r.test(n)){if(t>2){t--;break}while((n=e[t])&&r.test(n))t++;if(t>2)break}}}}return i.lastIndex=0,t},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column),r;t===0&&(e!==0&&(this.session.$bidiHandler.isBidiRow(n.row,this.lead.row)?(r=this.session.$bidiHandler.getPosLeft(n.column),n.column=Math.round(r/this.session.$bidiHandler.charWidths[0])):r=n.column*this.session.$bidiHandler.charWidths[0]),this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);if(e!=0&&this.session.lineWidgets&&this.session.lineWidgets[this.lead.row]){var i=this.session.lineWidgets[this.lead.row];e<0?e-=i.rowsAbove||0:e>0&&(e+=i.rowCount-(i.rowsAbove||0))}var s=this.session.screenToDocumentPosition(n.row+e,n.column,r);e!==0&&t===0&&s.row===this.lead.row&&s.column===this.lead.column,this.moveCursorTo(s.row,s.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0;var i=this.session.getLine(e);/[\uDC00-\uDFFF]/.test(i.charAt(t))&&i.charAt(t-1)&&(this.lead.row==e&&this.lead.column==t+1?t-=1:t+=1),this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach()},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList&&e.length>1){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(){function e(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}}return e.prototype.$setMaxTokenCount=function(e){i=e|0},e.prototype.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yi){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},e}();s.prototype.reportError=r.reportError,t.Tokenizer=s}),define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},e.prototype.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},e.prototype.getCurrentTokenRow=function(){return this.$row},e.prototype.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},e.prototype.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}},e.prototype.getCurrentTokenRange=function(){var e=this.$rowTokens[this.$tokenIndex],t=this.getCurrentTokenColumn();return new r(this.$row,t,this.$row,t+e.value.length)},e}();t.TokenIterator=i}),define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","rparen","paren","punctuation.operator"],a=["text","paren.rparen","rparen","paren","punctuation.operator","comment"],f,l={},c={'"':'"',"'":"'"},h=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},p=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},d=function(e){e=e||{},this.add("braces","insertion",function(t,n,r,i,s){var u=r.getCursorPosition(),a=i.doc.getLine(u.row);if(s=="{"){h(r);var l=r.getSelectionRange(),c=i.doc.getTextRange(l);if(c!==""&&c!=="{"&&r.getWrapBehavioursEnabled())return p(l,c,"{","}");if(d.isSaneInsertion(r,i))return/[\]\}\)]/.test(a[u.column])||r.inMultiSelectMode||e.braces?(d.recordAutoInsert(r,i,"}"),{text:"{}",selection:[1,1]}):(d.recordMaybeInsert(r,i,"{"),{text:"{",selection:[1,1]})}else if(s=="}"){h(r);var v=a.substring(u.column,u.column+1);if(v=="}"){var m=i.$findOpeningBracket("}",{column:u.column+1,row:u.row});if(m!==null&&d.isAutoInsertedClosing(u,a,s))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(s=="\n"||s=="\r\n"){h(r);var g="";d.isMaybeInsertedClosing(u,a)&&(g=o.stringRepeat("}",f.maybeInsertedBrackets),d.clearMaybeInsertedClosing());var v=a.substring(u.column,u.column+1);if(v==="}"){var y=i.findMatchingBracket({row:u.row,column:u.column+1},"}");if(!y)return null;var b=this.$getIndent(i.getLine(y.row))}else{if(!g){d.clearMaybeInsertedClosing();return}var b=this.$getIndent(a)}var w=b+i.getTabString();return{text:"\n"+w+"\n"+b+g,selection:[1,w.length,1,w.length]}}d.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"(",")");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"[","]");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){var s=r.$mode.$quotes||c;if(i.length==1&&s[i]){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;h(n);var o=i,u=n.getSelectionRange(),a=r.doc.getTextRange(u);if(a!==""&&(a.length!=1||!s[a])&&n.getWrapBehavioursEnabled())return p(u,a,o,o);if(!a){var f=n.getCursorPosition(),l=r.doc.getLine(f.row),d=l.substring(f.column-1,f.column),v=l.substring(f.column,f.column+1),m=r.getTokenAt(f.row,f.column),g=r.getTokenAt(f.row,f.column+1);if(d=="\\"&&m&&/escape/.test(m.type))return null;var y=m&&/string|escape/.test(m.type),b=!g||/string|escape/.test(g.type),w;if(v==o)w=y!==b,w&&/string\.end/.test(g.type)&&(w=!1);else{if(y&&!b)return null;if(y&&b)return null;var E=r.$mode.tokenRe;E.lastIndex=0;var S=E.test(d);E.lastIndex=0;var x=E.test(v),T=r.$mode.$pairQuotesAfter,N=T&&T[o]&&T[o].test(d);if(!N&&S||x)return null;if(v&&!/[\s;,.})\]\\]/.test(v))return null;var C=l[f.column-2];if(!(d!=o||C!=o&&!E.test(C)))return null;w=!0}return{text:w?o+o:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.$mode.$quotes||c,o=r.doc.getTextRange(i);if(!i.isMultiLine()&&s.hasOwnProperty(o)){h(n);var u=r.doc.getLine(i.start.row),a=u.substring(i.start.column+1,i.start.column+2);if(a==o)return i.end.column++,i}}),e.closeDocComment!==!1&&this.add("doc comment end","insertion",function(e,t,n,r,i){if(e==="doc-start"&&(i==="\n"||i==="\r\n")&&n.selection.isEmpty()){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=r.doc.getLine(s.row+1),a=this.$getIndent(o);if(/\s*\*/.test(u))return/^\s*\*/.test(o)?{text:i+a+"* ",selection:[1,3+a.length,1,3+a.length]}:{text:i+a+" * ",selection:[1,3+a.length,1,3+a.length]};if(/\/\*\*/.test(o.substring(0,s.column)))return{text:i+a+" * "+i+" "+a+"*/",selection:[1,4+a.length,1,4+a.length]}}})};d.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){if(/[)}\]]/.test(e.session.getLine(n.row)[n.column]))return!0;var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},d.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},d.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},d.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},d.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},d.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},d.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},d.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(d,i),t.CstyleBehaviour=d}),define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";var r=[48,9,8,25,5,0,2,25,48,0,11,0,5,0,6,22,2,30,2,457,5,11,15,4,8,0,2,0,18,116,2,1,3,3,9,0,2,2,2,0,2,19,2,82,2,138,2,4,3,155,12,37,3,0,8,38,10,44,2,0,2,1,2,1,2,0,9,26,6,2,30,10,7,61,2,9,5,101,2,7,3,9,2,18,3,0,17,58,3,100,15,53,5,0,6,45,211,57,3,18,2,5,3,11,3,9,2,1,7,6,2,2,2,7,3,1,3,21,2,6,2,0,4,3,3,8,3,1,3,3,9,0,5,1,2,4,3,11,16,2,2,5,5,1,3,21,2,6,2,1,2,1,2,1,3,0,2,4,5,1,3,2,4,0,8,3,2,0,8,15,12,2,2,8,2,2,2,21,2,6,2,1,2,4,3,9,2,2,2,2,3,0,16,3,3,9,18,2,2,7,3,1,3,21,2,6,2,1,2,4,3,8,3,1,3,2,9,1,5,1,2,4,3,9,2,0,17,1,2,5,4,2,2,3,4,1,2,0,2,1,4,1,4,2,4,11,5,4,4,2,2,3,3,0,7,0,15,9,18,2,2,7,2,2,2,22,2,9,2,4,4,7,2,2,2,3,8,1,2,1,7,3,3,9,19,1,2,7,2,2,2,22,2,9,2,4,3,8,2,2,2,3,8,1,8,0,2,3,3,9,19,1,2,7,2,2,2,22,2,15,4,7,2,2,2,3,10,0,9,3,3,9,11,5,3,1,2,17,4,23,2,8,2,0,3,6,4,0,5,5,2,0,2,7,19,1,14,57,6,14,2,9,40,1,2,0,3,1,2,0,3,0,7,3,2,6,2,2,2,0,2,0,3,1,2,12,2,2,3,4,2,0,2,5,3,9,3,1,35,0,24,1,7,9,12,0,2,0,2,0,5,9,2,35,5,19,2,5,5,7,2,35,10,0,58,73,7,77,3,37,11,42,2,0,4,328,2,3,3,6,2,0,2,3,3,40,2,3,3,32,2,3,3,6,2,0,2,3,3,14,2,56,2,3,3,66,5,0,33,15,17,84,13,619,3,16,2,25,6,74,22,12,2,6,12,20,12,19,13,12,2,2,2,1,13,51,3,29,4,0,5,1,3,9,34,2,3,9,7,87,9,42,6,69,11,28,4,11,5,11,11,39,3,4,12,43,5,25,7,10,38,27,5,62,2,28,3,10,7,9,14,0,89,75,5,9,18,8,13,42,4,11,71,55,9,9,4,48,83,2,2,30,14,230,23,280,3,5,3,37,3,5,3,7,2,0,2,0,2,0,2,30,3,52,2,6,2,0,4,2,2,6,4,3,3,5,5,12,6,2,2,6,67,1,20,0,29,0,14,0,17,4,60,12,5,0,4,11,18,0,5,0,3,9,2,0,4,4,7,0,2,0,2,0,2,3,2,10,3,3,6,4,5,0,53,1,2684,46,2,46,2,132,7,6,15,37,11,53,10,0,17,22,10,6,2,6,2,6,2,6,2,6,2,6,2,6,2,6,2,31,48,0,470,1,36,5,2,4,6,1,5,85,3,1,3,2,2,89,2,3,6,40,4,93,18,23,57,15,513,6581,75,20939,53,1164,68,45,3,268,4,27,21,31,3,13,13,1,2,24,9,69,11,1,38,8,3,102,3,1,111,44,25,51,13,68,12,9,7,23,4,0,5,45,3,35,13,28,4,64,15,10,39,54,10,13,3,9,7,22,4,1,5,66,25,2,227,42,2,1,3,9,7,11171,13,22,5,48,8453,301,3,61,3,105,39,6,13,4,6,11,2,12,2,4,2,0,2,1,2,1,2,107,34,362,19,63,3,53,41,11,5,15,17,6,13,1,25,2,33,4,2,134,20,9,8,25,5,0,2,25,12,88,4,5,3,5,3,5,3,2],i=0,s=[];for(var o=0;o2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),u==Infinity&&(u=E,s=!1,o=!1),l&&u%f!=0&&(u=Math.floor(u/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new f(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,a=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new l(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new f(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new l(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);a.start.row==c&&(a.start.column+=h),a.end.row==c&&(a.end.column+=h),t.selection.fromOrientedRange(a)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)if(e[t]){var n=e[t],i=n.prototype.$id,s=r.$modes[i];s||(r.$modes[i]=s=new n),r.$modes[t]||(r.$modes[t]=s),this.$embeds.push(t),this.$modes[t]=s}var o=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";function o(e,t,n){var r=n?e.column<=t.column:e.columnthis.row)return;var t=u(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(t.row,t.column,!0)},e.prototype.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},e.prototype.detach=function(){this.document.off("change",this.$onChange)},e.prototype.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},e.prototype.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n},e}();s.prototype.$insertRight=!1,r.implement(s.prototype,i),t.Anchor=s}),define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(){function e(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)}return e.prototype.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e||"")},e.prototype.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},e.prototype.createAnchor=function(e,t){return new u(this,e,t)},e.prototype.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},e.prototype.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},e.prototype.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},e.prototype.getNewLineMode=function(){return this.$newLineMode},e.prototype.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},e.prototype.getLine=function(e){return this.$lines[e]||""},e.prototype.getLines=function(e,t){return this.$lines.slice(e,t+1)},e.prototype.getAllLines=function(){return this.getLines(0,this.getLength())},e.prototype.getLength=function(){return this.$lines.length},e.prototype.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},e.prototype.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},e.prototype.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},e.prototype.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},e.prototype.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},e.prototype.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},e.prototype.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},e.prototype.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},e.prototype.clonePos=function(e){return{row:e.row,column:e.column}},e.prototype.pos=function(e,t){return{row:e,column:t}},e.prototype.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},e.prototype.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},e.prototype.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},e.prototype.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},e.prototype.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4?this.$splitAndapplyLargeDelta(e,2e4):(i(this.$lines,e,t),this._signal("change",e))},e.prototype.$safeApplyDelta=function(e){var t=this.$lines.length;(e.action=="remove"&&e.start.row20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,r==-1&&(r=t),s<=r&&n.fireUpdateEvent(s,r)}}return e.prototype.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},e.prototype.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},e.prototype.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},e.prototype.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},e.prototype.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},e.prototype.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},e.prototype.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},e.prototype.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},e.prototype.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},e.prototype.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens},e.prototype.cleanup=function(){this.running=!1,this.lines=[],this.states=[],this.currentLine=0,this.removeAllListeners()},e}();r.implement(s.prototype,i),t.BackgroundTokenizer=s}),define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./range").Range,s=function(){function e(e,t,n){n===void 0&&(n="text"),this.setRegexp(e),this.clazz=t,this.type=n}return e.prototype.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},e.prototype.update=function(e,t,n,s){if(!this.regExp)return;var o=s.firstRow,u=s.lastRow,a={};for(var f=o;f<=u;f++){var l=this.cache[f];l==null&&(l=r.getMatchOffsets(n.getLine(f),this.regExp),l.length>this.MAX_RANGES&&(l=l.slice(0,this.MAX_RANGES)),l=l.map(function(e){return new i(f,e.offset,f,e.offset+e.length)}),this.cache[f]=l.length?l:"");for(var c=l.length;c--;){var h=l[c].toScreenRange(n),p=h.toString();if(a[p])continue;a[p]=!0,t.drawSingleLineMarker(e,h,this.clazz,s)}}},e}();s.prototype.MAX_RANGES=500,t.SearchHighlight=s}),define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){function e(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}return e.prototype.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},e.prototype.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},e.prototype.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},e.prototype.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},e.prototype.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},e.prototype.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},e.prototype.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},e.prototype.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},e.prototype.containsPoint=function(e){return this.pointIndex(e)>=0},e.prototype.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},e.prototype.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row=r)break}if(e.action=="insert"){var f=i-r,l=-t.column+n.column;for(;or)break;a.start.row==r&&a.start.column>=t.column&&(a.start.column==t.column&&this.$bias<=0||(a.start.column+=l,a.start.row+=f));if(a.end.row==r&&a.end.column>=t.column){if(a.end.column==t.column&&this.$bias<0)continue;a.end.column==t.column&&l>0&&oa.start.column&&a.end.column==s[o+1].start.column&&(a.end.column-=l),a.end.column+=l,a.end.row+=f}}}else{var f=r-i,l=t.column-n.column;for(;oi)break;if(a.end.rowt.column)a.end.column=t.column,a.end.row=t.row}else a.end.column+=l,a.end.row+=f;else a.end.row>i&&(a.end.row+=f);if(a.start.rowt.column)a.start.column=t.column,a.start.row=t.row}else a.start.column+=l,a.start.row+=f;else a.start.row>i&&(a.start.row+=f)}}if(f!=0&&o=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column,c=this.getFoldAt(u,a,1),h=this.getFoldAt(f,l,-1);if(c&&h==c)return c.addSubFold(o);c&&!c.range.isStart(u,a)&&this.removeFold(c),h&&!h.range.isEnd(f,l)&&this.removeFold(h);var p=this.getFoldsInRange(o.range);p.length>0&&(this.removeFolds(p),o.collapseChildren||p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;if(e==null)n=new r(0,0,this.getLength(),0),t==null&&(t=!0);else if(typeof e=="number")n=new r(e,0,e,this.getLine(e).length);else if("row"in e)n=r.fromPoints(e,e);else{if(Array.isArray(e))return i=[],e.forEach(function(e){i=i.concat(this.unfold(e))},this),i;n=e}i=this.getFoldsInRangeList(n);var s=i;while(i.length==1&&r.comparePoints(i[0].start,n.start)<0&&r.comparePoints(i[0].end,n.end)>0)this.expandFolds(i),i=this.getFoldsInRangeList(n);t!=0?this.removeFolds(i):this.expandFolds(i);if(s.length)return s},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(tl)break}while(s&&a.test(s.type)&&!/^comment.start/.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return f.end.row=i.getCurrentTokenRow(),f.end.column=i.getCurrentTokenColumn(),/^comment.end/.test(s.type)||(f.end.column+=s.value.length-2),f}},this.foldAll=function(e,t,n,r){n==undefined&&(n=1e5);var i=this.foldWidgets;if(!i)return;t=t||this.getLength(),e=e||0;for(var s=e;s=e&&(s=o.end.row,o.collapseChildren=n,this.addFold("...",o))}},this.foldToLevel=function(e){this.foldAll();while(e-->0)this.unfold(null,!1)},this.foldAllComments=function(){var e=this;this.foldAll(null,null,null,function(t){var n=e.getTokens(t);for(var r=0;r=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t instanceof u&&(t=t.domEvent);var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s)return t.children||t.all?this.removeFold(s):this.expandFold(s),s;var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range))return this.removeFold(s),s}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator,u=e("../mouse/mouse_event").MouseEvent;t.Folding=a}),define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.getMatchingBracketRanges=function(e,t){var n=this.getLine(e.row),r=/([\(\[\{])|([\)\]\}])/,s=!t&&n.charAt(e.column-1),o=s&&s.match(r);o||(s=(t===undefined||t)&&n.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(r));if(!o)return null;var u=new i(e.row,e.column-1,e.row,e.column),a=o[1]?this.$findClosingBracket(o[1],e):this.$findOpeningBracket(o[2],e);if(!a)return[u];var f=new i(a.row,a.column,a.row,a.column+1);return[u,f]},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{","<":">",">":"<"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)").replace(/-close\b/,"-(close|open)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)").replace(/-open\b/,"-(close|open)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a"?r=!0:t.type.indexOf("tag-name")!==-1&&(n=!0));while(t&&!n);return t},this.$findClosingTag=function(e,t){var n,r=t.value,s=t.value,o=0,u=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);t=e.stepForward();var a=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+t.value.length),f=!1;do{n=t,t=e.stepForward();if(t){if(t.value===">"&&!f){var l=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);f=!0}if(t.type.indexOf("tag-name")!==-1){r=t.value;if(s===r)if(n.value==="<")o++;else if(n.value==="")return;var p=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1)}}}else if(s===r&&t.value==="/>"){o--;if(o<0)var c=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+2),h=c,p=h,l=new i(a.end.row,a.end.column,a.end.row,a.end.column+1)}}}while(t&&o>=0);if(u&&l&&c&&p&&a&&h)return{openTag:new i(u.start.row,u.start.column,l.end.row,l.end.column),closeTag:new i(c.start.row,c.start.column,p.end.row,p.end.column),openTagName:a,closeTagName:h}},this.$findOpeningTag=function(e,t){var n=e.getCurrentToken(),r=t.value,s=0,o=e.getCurrentTokenRow(),u=e.getCurrentTokenColumn(),a=u+2,f=new i(o,u,o,a);e.stepForward();var l=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+t.value.length);t=e.stepForward();if(!t||t.value!==">")return;var c=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);e.stepBackward(),e.stepBackward();do{t=n,o=e.getCurrentTokenRow(),u=e.getCurrentTokenColumn(),a=u+t.value.length,n=e.stepBackward();if(t)if(t.type.indexOf("tag-name")!==-1){if(r===t.value)if(n.value==="<"){s++;if(s>0){var h=new i(o,u,o,a),p=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);do t=e.stepForward();while(t&&t.value!==">");var d=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1)}}else n.value===""){var v=0,m=n;while(m){if(m.type.indexOf("tag-name")!==-1&&m.value===r){s--;break}if(m.value==="<")break;m=e.stepBackward(),v++}for(var g=0;g=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./bidihandler").BidiHandler,o=e("./config"),u=e("./lib/event_emitter").EventEmitter,a=e("./selection").Selection,f=e("./mode/text").Mode,l=e("./range").Range,c=e("./document").Document,h=e("./background_tokenizer").BackgroundTokenizer,p=e("./search_highlight").SearchHighlight,d=function(){function e(t,n){this.$breakpoints=[],this.$decorations=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$undoSelect=!0,this.$foldData=[],this.id="session"+ ++e.$uid,this.$foldData.toString=function(){return this.join("\n")},this.bgTokenizer=new h((new f).getTokenizer(),this);var r=this;this.bgTokenizer.on("update",function(e){r._signal("tokenizerUpdate",e)}),this.on("changeFold",this.onChangeFold.bind(this)),this.$onChange=this.onChange.bind(this);if(typeof t!="object"||!t.getLine)t=new c(t);this.setDocument(t),this.selection=new a(this),this.$bidiHandler=new s(this),o.resetOptions(this),this.setMode(n),o._signal("session",this),this.destroyed=!1}return e.prototype.setDocument=function(e){this.doc&&this.doc.off("change",this.$onChange),this.doc=e,e.on("change",this.$onChange,!0),this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},e.prototype.getDocument=function(){return this.doc},e.prototype.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},e.prototype.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},e.prototype.setUndoManager=function(e){this.$undoManager=e,this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;e.addSession(this),this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.mergeUndoDeltas=!1},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}else this.$syncInformUndoManager=function(){}},e.prototype.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},e.prototype.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},e.prototype.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},e.prototype.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},e.prototype.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},e.prototype.setTabSize=function(e){this.setOption("tabSize",e)},e.prototype.getTabSize=function(){return this.$tabSize},e.prototype.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},e.prototype.setNavigateWithinSoftTabs=function(e){this.setOption("navigateWithinSoftTabs",e)},e.prototype.getNavigateWithinSoftTabs=function(){return this.$navigateWithinSoftTabs},e.prototype.setOverwrite=function(e){this.setOption("overwrite",e)},e.prototype.getOverwrite=function(){return this.$overwrite},e.prototype.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},e.prototype.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},e.prototype.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},e.prototype.getBreakpoints=function(){return this.$breakpoints},e.prototype.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},e.prototype.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},e.prototype.getLine=function(e){return this.doc.getLine(e)},e.prototype.getLines=function(e,t){return this.doc.getLines(e,t)},e.prototype.getLength=function(){return this.doc.getLength()},e.prototype.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},e.prototype.insert=function(e,t){return this.doc.insert(e,t)},e.prototype.remove=function(e){return this.doc.remove(e)},e.prototype.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},e.prototype.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=e.length-1;n!=-1;n--){var r=e[n];r.action=="insert"||r.action=="remove"?this.doc.revertDelta(r):r.folds&&this.addFolds(r.folds)}!t&&this.$undoSelect&&(e.selectionBefore?this.selection.fromJSON(e.selectionBefore):this.selection.setRange(this.$getUndoSelection(e,!0))),this.$fromUndo=!1},e.prototype.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=0;ne.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,f=s.start,o=f.row-a.row,u=f.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},e.prototype.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},e.prototype.outdentRows=function(e){var t=e.collapseRows(),n=new l(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new l(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},e.prototype.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},e.prototype.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},e.prototype.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},e.prototype.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},e.prototype.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},e.prototype.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},e.prototype.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},e.prototype.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},e.prototype.getUseWrapMode=function(){return this.$useWrapMode},e.prototype.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$bidiHandler.markAsDirty(),this.$useWrapMode&&this._signal("changeWrapMode")},e.prototype.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},e.prototype.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},e.prototype.getWrapLimit=function(){return this.$wrapLimit},e.prototype.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},e.prototype.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},e.prototype.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},e.prototype.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},e.prototype.$updateWrapData=function(e,t){var n=this.doc.getAllLines(),r=this.getTabSize(),i=this.$wrapData,s=this.$wrapLimit,o,u,a=e;t=Math.min(t,n.length-1);while(a<=t)u=this.getFoldLine(a,u),u?(o=[],u.walk(function(e,t,r,i){var s;if(e!=null){s=this.$getDisplayTokens(e,o.length),s[0]=g;for(var u=1;ut-h){var p=s+t-h;if(e[p-1]>=w&&e[p]>=w){c(p);continue}if(e[p]==g||e[p]==y){for(p;p!=s-1;p--)if(e[p]==g)break;if(p>s){c(p);continue}p=s+t;for(p;p>2)),s-1);while(p>d&&e[p]d&&e[p]d&&e[p]==b)p--}else while(p>d&&e[p]d){c(++p);continue}p=s+t,e[p]==m&&p--,c(p-h)}return r},e.prototype.$getDisplayTokens=function(e,t){var n=[],r;t=t||0;for(var i=0;i39&&s<48||s>57&&s<64?n.push(b):s>=4352&&x(s)?n.push(v,m):n.push(v)}return n},e.prototype.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&x(r)?n+=2:n+=1;if(n>t)break}return[n,i]},e.prototype.getRowLength=function(e){var t=1;return this.lineWidgets&&(t+=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0),!this.$useWrapMode||!this.$wrapData[e]?t:this.$wrapData[e].length+t},e.prototype.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},e.prototype.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]=0)var u=f[l],i=this.$docRowCache[l],h=e>f[c-1];else var h=!c;var p=this.getLength()-1,d=this.getNextFoldLine(i),v=d?d.start.row:Infinity;while(u<=e){a=this.getRowLength(i);if(u+a>e||i>=p)break;u+=a,i++,i>v&&(i=d.end.row+1,d=this.getNextFoldLine(i,d),v=d?d.start.row:Infinity),h&&(this.$docRowCache.push(i),this.$screenRowCache.push(u))}if(d&&d.start.row<=i)r=this.getFoldDisplayLine(d),i=d.start.row;else{if(u+a<=e||i>p)return{row:p,column:this.getLine(p).length};r=this.getLine(i),d=null}var m=0,g=Math.floor(e-u);if(this.$useWrapMode){var y=this.$wrapData[i];y&&(o=y[g],g>0&&y.length&&(m=y.indent,s=y[g-1]||y[y.length-1],r=r.substring(s)))}return n!==undefined&&this.$bidiHandler.isBidiRow(u+g,i,g)&&(t=this.$bidiHandler.offsetToCol(n)),s+=this.$getStringScreenWidth(r,t-m)[1],this.$useWrapMode&&s>=o&&(s=o-1),d?d.idxToPosition(s):{row:i,column:s}},e.prototype.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return this.lineWidgets&&this.lineWidgets[u]&&this.lineWidgets[u].rowsAbove&&(r+=this.lineWidgets[u].rowsAbove),{row:r,column:v+this.$getStringScreenWidth(d)[0]}},e.prototype.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},e.prototype.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},e.prototype.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},e.prototype.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;sn)break}return[r,s]}},e.prototype.destroy=function(){this.destroyed||(this.bgTokenizer.setDocument(null),this.bgTokenizer.cleanup(),this.destroyed=!0),this.$stopWorker(),this.removeAllListeners(),this.doc&&this.doc.off("change",this.$onChange),this.selection.detach()},e}();d.$uid=0,d.prototype.$modes=o.$modes,d.prototype.getValue=d.prototype.toString,d.prototype.$defaultUndoManager={undo:function(){},redo:function(){},hasUndo:function(){},hasRedo:function(){},reset:function(){},add:function(){},addSelection:function(){},startNewGroup:function(){},addSession:function(){}},d.prototype.$overwrite=!1,d.prototype.$mode=null,d.prototype.$modeId=null,d.prototype.$scrollTop=0,d.prototype.$scrollLeft=0,d.prototype.$wrapLimit=80,d.prototype.$useWrapMode=!1,d.prototype.$wrapLimitRange={min:null,max:null},d.prototype.lineWidgets=null,d.prototype.isFullWidth=x,r.implement(d.prototype,u);var v=1,m=2,g=3,y=4,b=9,w=10,E=11,S=12;e("./edit_session/folding").Folding.call(d.prototype),e("./edit_session/bracket_match").BracketMatch.call(d.prototype),o.defineOptions(d.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0)))},initialValue:"auto"},indentedSoftWrap:{set:function(){this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0))},initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){e=parseInt(e),e>0&&this.$tabSize!==e&&(this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize"))},initialValue:4,handlesSet:!0},navigateWithinSoftTabs:{initialValue:!1},foldStyle:{set:function(e){this.setFoldStyle(e)},handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId},handlesSet:!0}}),t.EditSession=d}),define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";function u(e,t){function n(e){return/\w/.test(e)||t.regExp?"\\b":""}return n(e[0])+e+n(e[e.length-1])}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){function e(){this.$options={}}return e.prototype.set=function(e){return i.mixin(this.$options,e),this},e.prototype.getOptions=function(){return r.copyObject(this.$options)},e.prototype.setOptions=function(e){this.$options=e},e.prototype.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i,o){return r=new s(e,n,i,o),n==o&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start)?(r=null,!1):!0}),r},e.prototype.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;gE&&o[h].end.row==S)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g=u;n--)if(c(n,Number.MAX_VALUE,e))return;if(t.wrap==0)return;for(n=a,u=o.row;n>=u;n--)if(c(n,Number.MAX_VALUE,e))return};else var f=function(e){var n=o.row;if(c(n,o.column,e))return;for(n+=1;n<=a;n++)if(c(n,0,e))return;if(t.wrap==0)return;for(n=u,a=o.row;n<=a;n++)if(c(n,0,e))return};if(t.$isMultiLine)var l=n.length,c=function(t,i,s){var o=r?t-l+1:t;if(o<0||o+l>e.getLength())return;var u=e.getLine(o),a=u.search(n[0]);if(!r&&ai)return;if(s(o,a,o+l-1,c))return!0};else if(r)var c=function(t,r,i){var s=e.getLine(t),o=[],u,a=0;n.lastIndex=0;while(u=n.exec(s)){var f=u[0].length;a=u.index;if(!f){if(a>=s.length)break;n.lastIndex=a+=1}if(u.index+f>r)break;o.push(u.index,f)}for(var l=o.length-1;l>=0;l-=2){var c=o[l-1],f=o[l];if(i(t,c,t,c+f))return!0}};else var c=function(t,r,i){var s=e.getLine(t),o,u;n.lastIndex=r;while(u=n.exec(s)){var a=u[0].length;o=u.index;if(i(t,o,t,o+a))return!0;if(!a){n.lastIndex=o+=1;if(o>=s.length)return!1}}};return{forEach:f}},e}();t.Search=o}),define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||(e.isDefault?-100:0)}var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=e("../lib/keys"),s=e("../lib/useragent"),o=i.KEY_MODS,u=function(){function e(e,t){this.$init(e,t,!1)}return e.prototype.$init=function(e,t,n){this.platform=t||(s.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=n},e.prototype.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},e.prototype.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},e.prototype.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=o[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var s=this.parseKeys(e),u=o[s.hashId]+s.key;this._addCommandToBinding(r+u,t,n)},this)},e.prototype._addCommandToBinding=function(e,t,n){var r=this.commandKeyBinding,i;if(!t)delete r[e];else if(!r[e]||this.$singleCommand)r[e]=t;else{Array.isArray(r[e])?(i=r[e].indexOf(t))!=-1&&r[e].splice(i,1):r[e]=[r[e]],typeof n!="number"&&(n=a(t));var s=r[e];for(i=0;in)break}s.splice(i,0,t)}},e.prototype.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},e.prototype.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},e.prototype.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},e.prototype._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},e.prototype.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),r=i[n];if(i.FUNCTION_KEYS[r])n=i.FUNCTION_KEYS[r].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=i.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},e.prototype.findKeyCommand=function(e,t){var n=o[e]+t;return this.commandKeyBinding[n]},e.prototype.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=o[t]+n,s=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,s=this.commandKeyBinding[e.$keyChain]||s);if(s)if(s=="chainKeys"||s[s.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:s}},e.prototype.getStatusText=function(e,t){return t.$keyChain||""},e}(),f=function(e){function t(t,n){var r=e.call(this,t,n)||this;return r.$singleCommand=!0,r}return r(t,e),t}(u);f.call=function(e,t,n){u.prototype.$init.call(e,t,n,!0)},u.call=function(e,t,n){u.prototype.$init.call(e,t,n,!1)},t.HashHandler=f,t.MultiHashHandler=u}),define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=e("../lib/oop"),s=e("../keyboard/hash_handler").MultiHashHandler,o=e("../lib/event_emitter").EventEmitter,u=function(e){function t(t,n){var r=e.call(this,n,t)||this;return r.byName=r.commands,r.setDefaultHandler("exec",function(e){return e.args?e.command.exec(e.editor,e.args,e.event,!1):e.command.exec(e.editor,{},e.event,!0)}),r}return r(t,e),t.prototype.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;if(this.$checkCommandState!=0&&e.isAvailable&&!e.isAvailable(t))return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},t.prototype.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.off("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},t.prototype.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},t.prototype.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})},t}(s);i.implement(u.prototype,o),t.CommandManager=u}),define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",description:"Show settings menu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",description:"Go to next error",bindKey:o("Alt-E","F4"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",description:"Go to previous error",bindKey:o("Alt-Shift-E","Shift-F4"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",description:"Select all",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",description:"Center selection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",description:"Go to line...",bindKey:o("Ctrl-L","Command-L"),exec:function(e,t){typeof t=="number"&&!isNaN(t)&&e.gotoLine(t),e.prompt({$type:"gotoLine"})},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",description:"Toggle fold widget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",description:"Toggle parent fold widget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",description:"Fold all",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldAllComments",description:"Fold all comments",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAllComments()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",description:"Fold other",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",description:"Unfold all",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",description:"Find next",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",description:"Find previous",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",description:"Select or find next",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",description:"Select or find previous",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",description:"Find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",description:"Overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",description:"Select to start",bindKey:o("Ctrl-Shift-Home","Command-Shift-Home|Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",description:"Go to start",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",description:"Select up",bindKey:o("Shift-Up","Shift-Up|Ctrl-Shift-P"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",description:"Go line up",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",description:"Select to end",bindKey:o("Ctrl-Shift-End","Command-Shift-End|Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",description:"Go to end",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",description:"Select down",bindKey:o("Shift-Down","Shift-Down|Ctrl-Shift-N"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",description:"Go line down",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",description:"Select word left",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",description:"Go to word left",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",description:"Select to line start",bindKey:o("Alt-Shift-Left","Command-Shift-Left|Ctrl-Shift-A"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",description:"Go to line start",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",description:"Select left",bindKey:o("Shift-Left","Shift-Left|Ctrl-Shift-B"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",description:"Go to left",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",description:"Select word right",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",description:"Go to word right",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",description:"Select to line end",bindKey:o("Alt-Shift-Right","Command-Shift-Right|Shift-End|Ctrl-Shift-E"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",description:"Go to line end",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",description:"Select right",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",description:"Go to right",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",description:"Select page down",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",description:"Page down",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",description:"Go to page down",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",description:"Select page up",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",description:"Page up",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",description:"Go to page up",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",description:"Scroll up",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",description:"Scroll down",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",description:"Select line start",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",description:"Select line end",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",description:"Toggle recording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",description:"Replay macro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",description:"Jump to matching",bindKey:o("Ctrl-\\|Ctrl-P","Command-\\"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",description:"Select to matching",bindKey:o("Ctrl-Shift-\\|Ctrl-Shift-P","Command-Shift-\\"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",description:"Expand to matching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",description:"Pass keys to browser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",description:"Copy",exec:function(e){},readOnly:!0},{name:"cut",description:"Cut",exec:function(e){var t=e.$copyWithEmptySelection&&e.selection.isEmpty(),n=t?e.selection.getLineRange():e.selection.getRange();e._emit("cut",n),n.isEmpty()||e.session.remove(n),e.clearSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",description:"Paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",description:"Remove line",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",description:"Duplicate selection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",description:"Sort lines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",description:"Toggle comment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",description:"Toggle block comment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",description:"Modify number up",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",description:"Modify number down",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",description:"Replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",description:"Undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",description:"Redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",description:"Copy lines up",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",description:"Move lines up",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",description:"Copy lines down",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",description:"Move lines down",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",description:"Delete",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",description:"Backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",description:"Cut or delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",description:"Remove to line start",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",description:"Remove to line end",bindKey:o("Alt-Delete","Ctrl-K|Command-Delete"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestarthard",description:"Remove to line start hard",bindKey:o("Ctrl-Shift-Backspace",null),exec:function(e){var t=e.selection.getRange();t.start.column=0,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineendhard",description:"Remove to line end hard",bindKey:o("Ctrl-Shift-Delete",null),exec:function(e){var t=e.selection.getRange();t.end.column=Number.MAX_VALUE,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",description:"Remove word left",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",description:"Remove word right",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",description:"Outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",description:"Indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",description:"Block outdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",description:"Block indent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",description:"Insert string",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",description:"Insert text",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",description:"Split line",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",description:"Transpose letters",bindKey:o("Alt-Shift-X","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",description:"To uppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",description:"To lowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"autoindent",description:"Auto Indent",bindKey:o(null,null),exec:function(e){e.autoIndent()},multiSelectAction:"forEachLine",scrollIntoView:"animate"},{name:"expandtoline",description:"Expand to line",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"openlink",bindKey:o("Ctrl+F3","F3"),exec:function(e){e.openLink()}},{name:"joinlines",description:"Join lines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",description:"Invert selection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;ot[n].column&&n++,s.unshift(n,0),t.splice.apply(t,s),this.$updateRows()}},e.prototype.$updateRows=function(){var e=this.session.lineWidgets;if(!e)return;var t=!0;e.forEach(function(e,n){if(e){t=!1,e.row=n;while(e.$oldWidget)e.$oldWidget.row=n,e=e.$oldWidget}}),t&&(this.session.lineWidgets=null)},e.prototype.$registerLineWidget=function(e){this.session.lineWidgets||(this.session.lineWidgets=new Array(this.session.getLength()));var t=this.session.lineWidgets[e.row];return t&&(e.$oldWidget=t,t.el&&t.el.parentNode&&(t.el.parentNode.removeChild(t.el),t._inDocument=!1)),this.session.lineWidgets[e.row]=e,e},e.prototype.addLineWidget=function(e){this.$registerLineWidget(e),e.session=this.session;if(!this.editor)return e;var t=this.editor.renderer;e.html&&!e.el&&(e.el=r.createElement("div"),e.el.innerHTML=e.html),e.text&&!e.el&&(e.el=r.createElement("div"),e.el.textContent=e.text),e.el&&(r.addCssClass(e.el,"ace_lineWidgetContainer"),e.className&&r.addCssClass(e.el,e.className),e.el.style.position="absolute",e.el.style.zIndex=5,t.container.appendChild(e.el),e._inDocument=!0,e.coverGutter||(e.el.style.zIndex=3),e.pixelHeight==null&&(e.pixelHeight=e.el.offsetHeight)),e.rowCount==null&&(e.rowCount=e.pixelHeight/t.layerConfig.lineHeight);var n=this.session.getFoldAt(e.row,0);e.$fold=n;if(n){var i=this.session.lineWidgets;e.row==n.end.row&&!i[n.start.row]?i[n.start.row]=e:e.hidden=!0}return this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows(),this.renderWidgets(null,t),this.onWidgetChanged(e),e},e.prototype.removeLineWidget=function(e){e._inDocument=!1,e.session=null,e.el&&e.el.parentNode&&e.el.parentNode.removeChild(e.el);if(e.editor&&e.editor.destroy)try{e.editor.destroy()}catch(t){}if(this.session.lineWidgets){var n=this.session.lineWidgets[e.row];if(n==e)this.session.lineWidgets[e.row]=e.$oldWidget,e.$oldWidget&&this.onWidgetChanged(e.$oldWidget);else while(n){if(n.$oldWidget==e){n.$oldWidget=e.$oldWidget;break}n=n.$oldWidget}}this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows()},e.prototype.getWidgetsAtRow=function(e){var t=this.session.lineWidgets,n=t&&t[e],r=[];while(n)r.push(n),n=n.$oldWidget;return r},e.prototype.onWidgetChanged=function(e){this.session._changedWidgets.push(e),this.editor&&this.editor.renderer.updateFull()},e.prototype.measureWidgets=function(e,t){var n=this.session._changedWidgets,r=t.layerConfig;if(!n||!n.length)return;var i=Infinity;for(var s=0;s0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}},e}();t.LineWidgets=i}),define("ace/keyboard/gutter_handler",["require","exports","module","ace/lib/keys","ace/mouse/default_gutter_handler"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../mouse/default_gutter_handler").GutterTooltip,s=function(){function e(e){this.editor=e,this.gutterLayer=e.renderer.$gutterLayer,this.element=e.renderer.$gutter,this.lines=e.renderer.$gutterLayer.$lines,this.activeRowIndex=null,this.activeLane=null,this.annotationTooltip=new i(this.editor)}return e.prototype.addListener=function(){this.element.addEventListener("keydown",this.$onGutterKeyDown.bind(this)),this.element.addEventListener("focusout",this.$blurGutter.bind(this)),this.editor.on("mousewheel",this.$blurGutter.bind(this))},e.prototype.removeListener=function(){this.element.removeEventListener("keydown",this.$onGutterKeyDown.bind(this)),this.element.removeEventListener("focusout",this.$blurGutter.bind(this)),this.editor.off("mousewheel",this.$blurGutter.bind(this))},e.prototype.$onGutterKeyDown=function(e){if(this.annotationTooltip.isOpen){e.preventDefault(),e.keyCode===r.escape&&this.annotationTooltip.hide();return}if(e.target===this.element){if(e.keyCode!=r["enter"])return;e.preventDefault();var t=this.editor.getCursorPosition().row;this.editor.isRowVisible(t)||this.editor.scrollToLine(t,!0,!0),setTimeout(function(){var e=this.$rowToRowIndex(this.gutterLayer.$cursorCell.row),t=this.$findNearestFoldWidget(e),n=this.$findNearestAnnotation(e);if(t===null&&n===null)return;if(t===null&&n!==null){this.activeRowIndex=n,this.activeLane="annotation",this.$focusAnnotation(this.activeRowIndex);return}if(t!==null&&n===null){this.activeRowIndex=t,this.activeLane="fold",this.$focusFoldWidget(this.activeRowIndex);return}if(Math.abs(n-e)0||e+t=0&&this.$isFoldWidgetVisible(e-t))return e-t;if(e+t<=this.lines.getLength()-1&&this.$isFoldWidgetVisible(e+t))return e+t}return null},e.prototype.$findNearestAnnotation=function(e){if(this.$isAnnotationVisible(e))return e;var t=0;while(e-t>0||e+t=0&&this.$isAnnotationVisible(e-t))return e-t;if(e+t<=this.lines.getLength()-1&&this.$isAnnotationVisible(e+t))return e+t}return null},e.prototype.$focusFoldWidget=function(e){if(e==null)return;var t=this.$getFoldWidget(e);t.classList.add(this.editor.renderer.keyboardFocusClassName),t.focus()},e.prototype.$focusAnnotation=function(e){if(e==null)return;var t=this.$getAnnotation(e);t.classList.add(this.editor.renderer.keyboardFocusClassName),t.setAttribute("role","button"),t.focus()},e.prototype.$blurFoldWidget=function(e){var t=this.$getFoldWidget(e);t.classList.remove(this.editor.renderer.keyboardFocusClassName),t.blur()},e.prototype.$blurAnnotation=function(e){var t=this.$getAnnotation(e);t.classList.remove(this.editor.renderer.keyboardFocusClassName),t.removeAttribute("role"),t.blur()},e.prototype.$moveFoldWidgetUp=function(){var e=this.activeRowIndex;while(e>0){e--;if(this.$isFoldWidgetVisible(e)){this.$blurFoldWidget(this.activeRowIndex),this.activeRowIndex=e,this.$focusFoldWidget(this.activeRowIndex);return}}return},e.prototype.$moveFoldWidgetDown=function(){var e=this.activeRowIndex;while(e0){e--;if(this.$isAnnotationVisible(e)){this.$blurAnnotation(this.activeRowIndex),this.activeRowIndex=e,this.$focusAnnotation(this.activeRowIndex);return}}return},e.prototype.$moveAnnotationDown=function(){var e=this.activeRowIndex;while(e=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=e("./lib/oop"),s=e("./lib/dom"),o=e("./lib/lang"),u=e("./lib/useragent"),a=e("./keyboard/textinput").TextInput,f=e("./mouse/mouse_handler").MouseHandler,l=e("./mouse/fold_handler").FoldHandler,c=e("./keyboard/keybinding").KeyBinding,h=e("./edit_session").EditSession,p=e("./search").Search,d=e("./range").Range,v=e("./lib/event_emitter").EventEmitter,m=e("./commands/command_manager").CommandManager,g=e("./commands/default_commands").commands,y=e("./config"),b=e("./token_iterator").TokenIterator,w=e("./line_widgets").LineWidgets,E=e("./keyboard/gutter_handler").GutterKeyboardHandler,S=e("./config").nls,x=e("./clipboard"),T=e("./lib/keys"),N=function(){function e(t,n,r){this.$toDestroy=[];var i=t.getContainerElement();this.container=i,this.renderer=t,this.id="editor"+ ++e.$uid,this.commands=new m(u.isMac?"mac":"win",g),typeof document=="object"&&(this.textInput=new a(t.getTextAreaContainer(),this),this.renderer.textarea=this.textInput.getElement(),this.$mouseHandler=new f(this),new l(this)),this.keyBinding=new c(this),this.$search=(new p).set({wrap:!0}),this.$historyTracker=this.$historyTracker.bind(this),this.commands.on("exec",this.$historyTracker),this.$initOperationListeners(),this._$emitInputEvent=o.delayedCall(function(){this._signal("input",{}),this.session&&!this.session.destroyed&&this.session.bgTokenizer.scheduleStart()}.bind(this)),this.on("change",function(e,t){t._$emitInputEvent.schedule(31)}),this.setSession(n||r&&r.session||new h("")),y.resetOptions(this),r&&this.setOptions(r),y._signal("editor",this)}return e.prototype.$initOperationListeners=function(){this.commands.on("exec",this.startOperation.bind(this),!0),this.commands.on("afterExec",this.endOperation.bind(this),!0),this.$opResetTimer=o.delayedCall(this.endOperation.bind(this,!0)),this.on("change",function(){this.curOp||(this.startOperation(),this.curOp.selectionBefore=this.$lastSel),this.curOp.docChanged=!0}.bind(this),!0),this.on("changeSelection",function(){this.curOp||(this.startOperation(),this.curOp.selectionBefore=this.$lastSel),this.curOp.selectionChanged=!0}.bind(this),!0)},e.prototype.startOperation=function(e){if(this.curOp){if(!e||this.curOp.command)return;this.prevOp=this.curOp}e||(this.previousCommand=null,e={}),this.$opResetTimer.schedule(),this.curOp=this.session.curOp={command:e.command||{},args:e.args,scrollTop:this.renderer.scrollTop},this.curOp.selectionBefore=this.selection.toJSON()},e.prototype.endOperation=function(e){if(this.curOp&&this.session){if(e&&e.returnValue===!1||!this.session)return this.curOp=null;if(e==1&&this.curOp.command&&this.curOp.command.name=="mouse")return;this._signal("beforeEndOperation");if(!this.curOp)return;var t=this.curOp.command,n=t&&t.scrollIntoView;if(n){switch(n){case"center-animate":n="animate";case"center":this.renderer.scrollCursorIntoView(null,.5);break;case"animate":case"cursor":this.renderer.scrollCursorIntoView();break;case"selectionPart":var r=this.selection.getRange(),i=this.renderer.layerConfig;(r.start.row>=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}var s=this.selection.toJSON();this.curOp.selectionAfter=s,this.$lastSel=this.selection.toJSON(),this.session.getUndoManager().addSelection(s),this.prevOp=this.curOp,this.curOp=null}},e.prototype.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},e.prototype.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"&&e!="ace"){this.$keybindingId=e;var n=this;y.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},e.prototype.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},e.prototype.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.onCursorChange(),this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this}),e&&!e.destroyed&&e.bgTokenizer.scheduleStart()},e.prototype.getSession=function(){return this.session},e.prototype.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},e.prototype.getValue=function(){return this.session.getValue()},e.prototype.getSelection=function(){return this.selection},e.prototype.resize=function(e){this.renderer.onResize(e)},e.prototype.setTheme=function(e,t){this.renderer.setTheme(e,t)},e.prototype.getTheme=function(){return this.renderer.getTheme()},e.prototype.setStyle=function(e){this.renderer.setStyle(e)},e.prototype.unsetStyle=function(e){this.renderer.unsetStyle(e)},e.prototype.getFontSize=function(){return this.getOption("fontSize")||s.computedStyle(this.container).fontSize},e.prototype.setFontSize=function(e){this.setOption("fontSize",e)},e.prototype.$highlightBrackets=function(){if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||t.destroyed)return;t.$bracketHighlight&&(t.$bracketHighlight.markerIds.forEach(function(e){t.removeMarker(e)}),t.$bracketHighlight=null);var n=e.getCursorPosition(),r=e.getKeyboardHandler(),i=r&&r.$getDirectionForHighlight&&r.$getDirectionForHighlight(e),s=t.getMatchingBracketRanges(n,i);if(!s){var o=new b(t,n.row,n.column),u=o.getCurrentToken();if(u&&/\b(?:tag-open|tag-name)/.test(u.type)){var a=t.getMatchingTags(n);a&&(s=[a.openTagName,a.closeTagName])}}!s&&t.$mode.getMatching&&(s=t.$mode.getMatching(e.session));if(!s){e.getHighlightIndentGuides()&&e.renderer.$textLayer.$highlightIndentGuide();return}var f="ace_bracket";Array.isArray(s)?s.length==1&&(f="ace_error_bracket"):s=[s],s.length==2&&(d.comparePoints(s[0].end,s[1].start)==0?s=[d.fromPoints(s[0].start,s[1].end)]:d.comparePoints(s[0].start,s[1].end)==0&&(s=[d.fromPoints(s[1].start,s[0].end)])),t.$bracketHighlight={ranges:s,markerIds:s.map(function(e){return t.addMarker(e,f,"text")})},e.getHighlightIndentGuides()&&e.renderer.$textLayer.$highlightIndentGuide()},50)},e.prototype.focus=function(){this.textInput.focus()},e.prototype.isFocused=function(){return this.textInput.isFocused()},e.prototype.blur=function(){this.textInput.blur()},e.prototype.onFocus=function(e){if(this.$isFocused)return;this.$isFocused=!0,this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit("focus",e)},e.prototype.onBlur=function(e){if(!this.$isFocused)return;this.$isFocused=!1,this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit("blur",e)},e.prototype.$cursorChange=function(){this.renderer.updateCursor(),this.$highlightBrackets(),this.$updateHighlightActiveLine()},e.prototype.onDocumentChange=function(e){var t=this.session.$useWrapMode,n=e.start.row==e.end.row?e.end.row:Infinity;this.renderer.updateLines(e.start.row,n,t),this._signal("change",e),this.$cursorChange()},e.prototype.onTokenizerUpdate=function(e){var t=e.data;this.renderer.updateLines(t.first,t.last)},e.prototype.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},e.prototype.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},e.prototype.onCursorChange=function(){this.$cursorChange(),this._signal("changeSelection")},e.prototype.$updateHighlightActiveLine=function(){var e=this.getSession(),t;if(this.$highlightActiveLine){if(this.$selectionStyle!="line"||!this.selection.isMultiLine())t=this.getCursorPosition();this.renderer.theme&&this.renderer.theme.$selectionColorConflict&&!this.selection.isEmpty()&&(t=!1),this.renderer.$maxLines&&this.session.getLength()===1&&!(this.renderer.$minLines>1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new d(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},e.prototype.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},e.prototype.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column,r=t.end.column,i=e.getLine(t.start.row),s=i.substring(n,r);if(s.length>5e3||!/[\w\d]/.test(s))return;var o=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:s}),u=i.substring(n-1,r+1);if(!o.test(u))return;return o},e.prototype.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},e.prototype.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},e.prototype.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},e.prototype.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},e.prototype.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},e.prototype.onChangeWrapLimit=function(){this.renderer.updateFull()},e.prototype.onChangeWrapMode=function(){this.renderer.onResize(!0)},e.prototype.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},e.prototype.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},e.prototype.getCopyText=function(){var e=this.getSelectedText(),t=this.session.doc.getNewLineCharacter(),n=!1;if(!e&&this.$copyWithEmptySelection){n=!0;var r=this.selection.getAllRanges();for(var i=0;iu.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e);n.insert(i,e),s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new d(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new d(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(this.$enableAutoIndent){if(n.getDocument().isNewLine(e)){var h=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},h)}c&&r.autoOutdent(l,n,i.row)}},e.prototype.autoIndent=function(){var e=this.session,t=e.getMode(),n,r;if(this.selection.isEmpty())n=0,r=e.doc.getLength()-1;else{var i=this.getSelectionRange();n=i.start.row,r=i.end.row}var s="",o="",u="",a,f,l,c=e.getTabString();for(var h=n;h<=r;h++)h>0&&(s=e.getState(h-1),o=e.getLine(h-1),u=t.getNextLineIndent(s,o,c)),a=e.getLine(h),f=t.$getIndent(a),u!==f&&(f.length>0&&(l=new d(h,0,h,f.length),e.remove(l)),u.length>0&&e.insert({row:h,column:0},u)),t.autoOutdent(s,e,h)},e.prototype.onTextInput=function(e,t){if(!t)return this.keyBinding.onTextInput(e);this.startOperation({command:{name:"insertstring"}});var n=this.applyComposition.bind(this,e,t);this.selection.rangeCount?this.forEachSelection(n):n(),this.endOperation()},e.prototype.applyComposition=function(e,t){if(t.extendLeft||t.extendRight){var n=this.selection.getRange();n.start.column-=t.extendLeft,n.end.column+=t.extendRight,n.start.column<0&&(n.start.row--,n.start.column+=this.session.getLine(n.start.row).length+1),this.selection.setRange(n),!e&&!n.isEmpty()&&this.remove()}(e||!this.selection.isEmpty())&&this.insert(e,!0);if(t.restoreStart||t.restoreEnd){var n=this.selection.getRange();n.start.column-=t.restoreStart,n.end.column-=t.restoreEnd,this.selection.setRange(n)}},e.prototype.onCommandKey=function(e,t,n){return this.keyBinding.onCommandKey(e,t,n)},e.prototype.setOverwrite=function(e){this.session.setOverwrite(e)},e.prototype.getOverwrite=function(){return this.session.getOverwrite()},e.prototype.toggleOverwrite=function(){this.session.toggleOverwrite()},e.prototype.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},e.prototype.getScrollSpeed=function(){return this.getOption("scrollSpeed")},e.prototype.setDragDelay=function(e){this.setOption("dragDelay",e)},e.prototype.getDragDelay=function(){return this.getOption("dragDelay")},e.prototype.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},e.prototype.getSelectionStyle=function(){return this.getOption("selectionStyle")},e.prototype.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},e.prototype.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},e.prototype.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},e.prototype.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},e.prototype.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},e.prototype.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},e.prototype.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},e.prototype.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},e.prototype.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},e.prototype.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},e.prototype.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},e.prototype.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},e.prototype.setHighlightIndentGuides=function(e){this.renderer.setHighlightIndentGuides(e)},e.prototype.getHighlightIndentGuides=function(){return this.renderer.getHighlightIndentGuides()},e.prototype.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},e.prototype.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},e.prototype.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},e.prototype.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},e.prototype.setReadOnly=function(e){this.setOption("readOnly",e)},e.prototype.getReadOnly=function(){return this.getOption("readOnly")},e.prototype.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},e.prototype.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},e.prototype.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},e.prototype.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},e.prototype.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},e.prototype.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},e.prototype.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},e.prototype.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},e.prototype.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},e.prototype.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},e.prototype.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},e.prototype.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.selection.isEmpty()&&this.selection.selectLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},e.prototype.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},e.prototype.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},e.prototype.setGhostText=function(e,t){this.session.widgetManager||(this.session.widgetManager=new w(this.session),this.session.widgetManager.attach(this)),this.renderer.setGhostText(e,t)},e.prototype.removeGhostText=function(){if(!this.session.widgetManager)return;this.renderer.removeGhostText()},e.prototype.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var i=new d(0,0,0,0);for(var r=e.first;r<=e.last;r++){var s=t.getLine(r);i.start.row=r,i.end.row=r,i.end.column=s.length,t.replace(i,n[r-e.first])}},e.prototype.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},e.prototype.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},e.prototype.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},e.prototype.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new d(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n=u&&s<=a&&(n=t,f.selection.clearSelection(),f.moveCursorTo(e,u+r),f.selection.selectTo(e,a+r)),u=a});var l=this.$toggleWordPairs,c;for(var h=0;h=a&&u<=f&&p.match(/((?:https?|ftp):\/\/[\S]+)/)){l=p.replace(/[\s:.,'";}\]]+$/,"");break}a=f}}catch(d){n={error:d}}finally{try{h&&!h.done&&(i=c.return)&&i.call(c)}finally{if(n)throw n.error}}return l},e.prototype.openLink=function(){var e=this.selection.getCursor(),t=this.findLinkAt(e.row,e.column);return t&&window.open(t,"_blank"),t!=null},e.prototype.removeLines=function(){var e=this.$getSelectedRows();this.session.removeFullLines(e.first,e.last),this.clearSelection()},e.prototype.duplicateSelection=function(){var e=this.selection,t=this.session,n=e.getRange(),r=e.isBackwards();if(n.isEmpty()){var i=n.start.row;t.duplicateLines(i,i)}else{var s=r?n.start:n.end,o=t.insert(s,t.getTextRange(n),!1);n.start=s,n.end=o,e.setSelectionRange(n,r)}},e.prototype.moveLinesDown=function(){this.$moveLines(1,!1)},e.prototype.moveLinesUp=function(){this.$moveLines(-1,!1)},e.prototype.moveText=function(e,t,n){return this.session.moveText(e,t,n)},e.prototype.copyLinesUp=function(){this.$moveLines(-1,!0)},e.prototype.copyLinesDown=function(){this.$moveLines(1,!0)},e.prototype.$moveLines=function(e,t){var n,r,i=this.selection;if(!i.inMultiSelectMode||this.inVirtualSelectionMode){var s=i.toOrientedRange();n=this.$getSelectedRows(s),r=this.session.$moveLines(n.first,n.last,t?0:e),t&&e==-1&&(r=0),s.moveBy(r,0),i.fromOrientedRange(s)}else{var o=i.rangeList.ranges;i.rangeList.detach(this.session),this.inVirtualSelectionMode=!0;var u=0,a=0,f=o.length;for(var l=0;lp+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},e.prototype.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},e.prototype.onCompositionStart=function(e){this.renderer.showComposition(e)},e.prototype.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},e.prototype.onCompositionEnd=function(){this.renderer.hideComposition()},e.prototype.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},e.prototype.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},e.prototype.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},e.prototype.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},e.prototype.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},e.prototype.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection());var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},e.prototype.selectPageDown=function(){this.$moveByPage(1,!0)},e.prototype.selectPageUp=function(){this.$moveByPage(-1,!0)},e.prototype.gotoPageDown=function(){this.$moveByPage(1,!1)},e.prototype.gotoPageUp=function(){this.$moveByPage(-1,!1)},e.prototype.scrollPageDown=function(){this.$moveByPage(1)},e.prototype.scrollPageUp=function(){this.$moveByPage(-1)},e.prototype.scrollToRow=function(e){this.renderer.scrollToRow(e)},e.prototype.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},e.prototype.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},e.prototype.getCursorPosition=function(){return this.selection.getCursor()},e.prototype.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},e.prototype.getSelectionRange=function(){return this.selection.getRange()},e.prototype.selectAll=function(){this.selection.selectAll()},e.prototype.clearSelection=function(){this.selection.clearSelection()},e.prototype.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},e.prototype.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},e.prototype.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new b(this.session,n.row,n.column),i=r.getCurrentToken(),s=0;i&&i.type.indexOf("tag-name")!==-1&&(i=r.stepBackward());var o=i||r.stepForward();if(!o)return;var u,a=!1,f={},l=n.column-o.start,c,h={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(o.value.match(/[{}()\[\]]/g))for(;l1?f[o.value]++:i.value==="=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),r},e.prototype.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},e.prototype.getLastSearchOptions=function(){return this.$search.getOptions()},e.prototype.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&i.mixin(t,e);var r=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(r)||this.$search.$options.needle,e||(r=this.session.getWordRange(r.start.row,r.start.column),e=this.session.getTextRange(r)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:r});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?r.start=r.end:r.end=r.start,this.selection.setRange(r)},e.prototype.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},e.prototype.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},e.prototype.revealRange=function(e,t){this.session.unfold(e),this.selection.setSelectionRange(e);var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},e.prototype.undo=function(){this.session.getUndoManager().undo(this.session),this.renderer.scrollCursorIntoView(null,.5)},e.prototype.redo=function(){this.session.getUndoManager().redo(this.session),this.renderer.scrollCursorIntoView(null,.5)},e.prototype.destroy=function(){this.$toDestroy&&(this.$toDestroy.forEach(function(e){e.destroy()}),this.$toDestroy=null),this.$mouseHandler&&this.$mouseHandler.destroy(),this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy(),this._$emitInputEvent&&this._$emitInputEvent.cancel(),this.removeAllListeners()},e.prototype.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},e.prototype.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",s.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))},e.prototype.prompt=function(e,t,n){var r=this;y.loadModule("ace/ext/prompt",function(i){i.prompt(r,e,t,n)})},e}();N.$uid=0,N.prototype.curOp=null,N.prototype.prevOp={},N.prototype.$mergeableCommands=["backspace","del","insertstring"],N.prototype.$toggleWordPairs=[["first","last"],["true","false"],["yes","no"],["width","height"],["top","bottom"],["right","left"],["on","off"],["x","y"],["get","set"],["max","min"],["horizontal","vertical"],["show","hide"],["add","remove"],["up","down"],["before","after"],["even","odd"],["in","out"],["inside","outside"],["next","previous"],["increase","decrease"],["attach","detach"],["&&","||"],["==","!="]],i.implement(N.prototype,v),y.defineOptions(N.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.textInput.setReadOnly(e),this.$resetCursorStyle()},initialValue:!1},copyWithEmptySelection:{set:function(e){this.textInput.setCopyWithEmptySelection(e)},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},enableAutoIndent:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.$keybindingId},handlesSet:!0},value:{set:function(e){this.session.setValue(e)},get:function(){return this.getValue()},handlesSet:!0,hidden:!0},session:{set:function(e){this.setSession(e)},get:function(){return this.session},handlesSet:!0,hidden:!0},showLineNumbers:{set:function(e){this.renderer.$gutterLayer.setShowLineNumbers(e),this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER),e&&this.$relativeLineNumbers?C.attach(this):C.detach(this)},initialValue:!0},relativeLineNumbers:{set:function(e){this.$showLineNumbers&&e?C.attach(this):C.detach(this)}},placeholder:{set:function(e){this.$updatePlaceholder||(this.$updatePlaceholder=function(){var e=this.session&&(this.renderer.$composition||this.session.getLength()>1||this.session.getLine(0).length>0);if(e&&this.renderer.placeholderNode)this.renderer.off("afterRender",this.$updatePlaceholder),s.removeCssClass(this.container,"ace_hasPlaceholder"),this.renderer.placeholderNode.remove(),this.renderer.placeholderNode=null;else if(!e&&!this.renderer.placeholderNode){this.renderer.on("afterRender",this.$updatePlaceholder),s.addCssClass(this.container,"ace_hasPlaceholder");var t=s.createElement("div");t.className="ace_placeholder",t.textContent=this.$placeholder||"",this.renderer.placeholderNode=t,this.renderer.content.appendChild(this.renderer.placeholderNode)}else!e&&this.renderer.placeholderNode&&(this.renderer.placeholderNode.textContent=this.$placeholder||"")}.bind(this),this.on("input",this.$updatePlaceholder)),this.$updatePlaceholder()}},enableKeyboardAccessibility:{set:function(e){var t={name:"blurTextInput",description:"Set focus to the editor content div to allow tabbing through the page",bindKey:"Esc",exec:function(e){e.blur(),e.renderer.scroller.focus()},readOnly:!0},n=function(e){if(e.target==this.renderer.scroller&&e.keyCode===T.enter){e.preventDefault();var t=this.getCursorPosition().row;this.isRowVisible(t)||this.scrollToLine(t,!0,!0),this.focus()}},r;e?(this.renderer.enableKeyboardAccessibility=!0,this.renderer.keyboardFocusClassName="ace_keyboard-focus",this.textInput.getElement().setAttribute("tabindex",-1),this.renderer.scroller.setAttribute("tabindex",0),this.renderer.scroller.setAttribute("role","group"),this.renderer.scroller.setAttribute("aria-roledescription",S("editor")),this.renderer.scroller.classList.add(this.renderer.keyboardFocusClassName),this.renderer.scroller.setAttribute("aria-label",S("Editor content, press Enter to start editing, press Escape to exit")),this.renderer.scroller.addEventListener("keyup",n.bind(this)),this.commands.addCommand(t),this.renderer.$gutter.setAttribute("tabindex",0),this.renderer.$gutter.setAttribute("aria-hidden",!1),this.renderer.$gutter.setAttribute("role","group"),this.renderer.$gutter.setAttribute("aria-roledescription",S("editor")),this.renderer.$gutter.setAttribute("aria-label",S("Editor gutter, press Enter to interact with controls using arrow keys, press Escape to exit")),this.renderer.$gutter.classList.add(this.renderer.keyboardFocusClassName),this.renderer.content.setAttribute("aria-hidden",!0),r||(r=new E(this)),r.addListener()):(this.renderer.enableKeyboardAccessibility=!1,this.textInput.getElement().setAttribute("tabindex",0),this.renderer.scroller.setAttribute("tabindex",-1),this.renderer.scroller.removeAttribute("role"),this.renderer.scroller.removeAttribute("aria-roledescription"),this.renderer.scroller.classList.remove(this.renderer.keyboardFocusClassName),this.renderer.scroller.removeAttribute("aria-label"),this.renderer.scroller.removeEventListener("keyup",n.bind(this)),this.commands.removeCommand(t),this.renderer.content.removeAttribute("aria-hidden"),this.renderer.$gutter.setAttribute("tabindex",-1),this.renderer.$gutter.setAttribute("aria-hidden",!0),this.renderer.$gutter.removeAttribute("role"),this.renderer.$gutter.removeAttribute("aria-roledescription"),this.renderer.$gutter.removeAttribute("aria-label"),this.renderer.$gutter.classList.remove(this.renderer.keyboardFocusClassName),r&&r.removeListener())},initialValue:!1},customScrollbar:"renderer",hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",displayIndentGuides:"renderer",highlightIndentGuides:"renderer",showGutter:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",hasCssTransforms:"renderer",maxPixelHeight:"renderer",useTextareaForIME:"renderer",useResizeObserver:"renderer",useSvgGutterIcons:"renderer",showFoldedAnnotations:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimeout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",navigateWithinSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"});var C={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?"\u00b7":""))+""},getWidth:function(e,t,n){return Math.max(t.toString().length,(n.lastRow+1).toString().length,2)*n.characterWidth},update:function(e,t){t.renderer.$loop.schedule(t.renderer.CHANGE_GUTTER)},attach:function(e){e.renderer.$gutterLayer.$renderer=this,e.on("changeSelection",this.update),this.update(null,e)},detach:function(e){e.renderer.$gutterLayer.$renderer==this&&(e.renderer.$gutterLayer.$renderer=null),e.off("changeSelection",this.update),this.update(null,e)}};t.Editor=N}),define("ace/undomanager",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){for(var n=t;n--;){var r=e[n];if(r&&!r[0].ignore){while(n0){a.row+=i,a.column+=a.row==r.row?s:0;continue}!t&&l<=0&&(a.row=n.row,a.column=n.column,l===0&&(a.bias=1))}}function f(e){return{row:e.row,column:e.column}}function l(e){return{start:f(e.start),end:f(e.end),action:e.action,lines:e.lines.slice()}}function c(e){e=e||this;if(Array.isArray(e))return e.map(c).join("\n");var t="";e.action?(t=e.action=="insert"?"+":"-",t+="["+e.lines+"]"):e.value&&(Array.isArray(e.value)?t=e.value.map(h).join("\n"):t=h(e.value)),e.start&&(t+=h(e));if(e.id||e.rev)t+=" ("+(e.id||e.rev)+")";return t}function h(e){return e.start.row+":"+e.start.column+"=>"+e.end.row+":"+e.end.column}function p(e,t){var n=e.action=="insert",r=t.action=="insert";if(n&&r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}else if(!n&&r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(!n&&!r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}return[t,e]}function d(e,t){for(var n=e.length;n--;)for(var r=0;r=0?m(e,t,-1):o(e.start,t.start)<=0?m(t,e,1):(m(e,s.fromPoints(t.start,e.start),-1),m(t,e,1));else if(!n&&r)o(t.start,e.end)>=0?m(t,e,-1):o(t.start,e.start)<=0?m(e,t,1):(m(t,s.fromPoints(e.start,t.start),-1),m(e,t,1));else if(!n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0)){var i,u;return o(e.start,t.start)<0&&(i=e,e=y(e,t.start)),o(e.end,t.end)>0&&(u=y(e,t.end)),g(t.end,e.start,e.end,-1),u&&!i&&(e.lines=u.lines,e.start=u.start,e.end=u.end,u=e),[t,i,u].filter(Boolean)}m(e,t,-1)}return[t,e]}function m(e,t,n){g(e.start,t.start,t.end,n),g(e.end,t.start,t.end,n)}function g(e,t,n,r){e.row==(r==1?t:n).row&&(e.column+=r*(n.column-t.column)),e.row+=r*(n.row-t.row)}function y(e,t){var n=e.lines,r=e.end;e.end=f(t);var i=e.end.row-e.start.row,s=n.splice(i,n.length),o=i?t.column:t.column-e.start.column;n.push(s[0].substring(0,o)),s[0]=s[0].substr(o);var u={start:f(t),end:r,lines:s,action:e.action};return u}function b(e,t){t=l(t);for(var n=e.length;n--;){var r=e[n];for(var i=0;ithis.$undoDepth-1&&this.$undoStack.splice(0,r-this.$undoDepth+1),this.$undoStack.push(this.lastDeltas),e.id=this.$rev=++this.$maxRev}if(e.action=="remove"||e.action=="insert")this.$lastDelta=e;this.lastDeltas.push(e)},e.prototype.addSelection=function(e,t){this.selections.push({value:e,rev:t||this.$rev})},e.prototype.startNewGroup=function(){return this.lastDeltas=null,this.$rev},e.prototype.markIgnored=function(e,t){t==null&&(t=this.$rev+1);var n=this.$undoStack;for(var r=n.length;r--;){var i=n[r][0];if(i.id<=e)break;i.id0},e.prototype.canRedo=function(){return this.$redoStack.length>0},e.prototype.bookmark=function(e){e==undefined&&(e=this.$rev),this.mark=e},e.prototype.isAtBookmark=function(){return this.$rev===this.mark},e.prototype.toJSON=function(){},e.prototype.fromJSON=function(){},e.prototype.$prettyPrint=function(e){return e?c(e):c(this.$undoStack)+"\n---\n"+c(this.$redoStack)},e}();r.prototype.hasUndo=r.prototype.canUndo,r.prototype.hasRedo=r.prototype.canRedo,r.prototype.isClean=r.prototype.isAtBookmark,r.prototype.markClean=r.prototype.bookmark;var s=e("./range").Range,o=s.comparePoints,u=s.comparePoints;t.UndoManager=r}),define("ace/layer/lines",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(){function e(e,t){this.element=e,this.canvasHeight=t||5e5,this.element.style.height=this.canvasHeight*2+"px",this.cells=[],this.cellCache=[],this.$offsetCoefficient=0}return e.prototype.moveContainer=function(e){r.translate(this.element,0,-(e.firstRowScreen*e.lineHeight%this.canvasHeight)-e.offset*this.$offsetCoefficient)},e.prototype.pageChanged=function(e,t){return Math.floor(e.firstRowScreen*e.lineHeight/this.canvasHeight)!==Math.floor(t.firstRowScreen*t.lineHeight/this.canvasHeight)},e.prototype.computeLineTop=function(e,t,n){var r=t.firstRowScreen*t.lineHeight,i=Math.floor(r/this.canvasHeight),s=n.documentToScreenRow(e,0)*t.lineHeight;return s-i*this.canvasHeight},e.prototype.computeLineHeight=function(e,t,n){return t.lineHeight*n.getRowLineCount(e)},e.prototype.getLength=function(){return this.cells.length},e.prototype.get=function(e){return this.cells[e]},e.prototype.shift=function(){this.$cacheCell(this.cells.shift())},e.prototype.pop=function(){this.$cacheCell(this.cells.pop())},e.prototype.push=function(e){if(Array.isArray(e)){this.cells.push.apply(this.cells,e);var t=r.createFragment(this.element);for(var n=0;ns&&(a=i.end.row+1,i=t.getNextFoldLine(a,i),s=i?i.start.row:Infinity);if(a>r){while(this.$lines.getLength()>u+1)this.$lines.pop();break}o=this.$lines.get(++u),o?o.row=a:(o=this.$lines.createCell(a,e,this.session,l),this.$lines.push(o)),this.$renderCell(o,e,i,a),a++}this._signal("afterRender"),this.$updateGutterWidth(e)},e.prototype.$updateGutterWidth=function(e){var t=this.session,n=t.gutterRenderer||this.$renderer,r=t.$firstLineNumber,i=this.$lines.last()?this.$lines.last().text:"";if(this.$fixedWidth||t.$useWrapMode)i=t.getLength()+r-1;var s=n?n.getWidth(t,i,e):i.toString().length*e.characterWidth,o=this.$padding||this.$computePadding();s+=o.left+o.right,s!==this.gutterWidth&&!isNaN(s)&&(this.gutterWidth=s,this.element.parentNode.style.width=this.element.style.width=Math.ceil(this.gutterWidth)+"px",this._signal("changeGutterWidth",s))},e.prototype.$updateCursorRow=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.getCursor();if(this.$cursorRow===e.row)return;this.$cursorRow=e.row},e.prototype.updateLineHighlight=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.cursor.row;this.$cursorRow=e;if(this.$cursorCell&&this.$cursorCell.row==e)return;this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ",""));var t=this.$lines.cells;this.$cursorCell=null;for(var n=0;n=this.$cursorRow){if(r.row>this.$cursorRow){var i=this.session.getFoldLine(this.$cursorRow);if(!(n>0&&i&&i.start.row==t[n-1].row))break;r=t[n-1]}r.element.className="ace_gutter-active-line "+r.element.className,this.$cursorCell=r;break}}},e.prototype.scrollLines=function(e){var t=this.config;this.config=e,this.$updateCursorRow();if(this.$lines.pageChanged(t,e))return this.update(e);this.$lines.moveContainer(e);var n=Math.min(e.lastRow+e.gutterOffset,this.session.getLength()-1),r=this.oldLastRow;this.oldLastRow=n;if(!t||r0;i--)this.$lines.shift();if(r>n)for(var i=this.session.getFoldedRowCount(n+1,r);i>0;i--)this.$lines.pop();e.firstRowr&&this.$lines.push(this.$renderLines(e,r+1,n)),this.updateLineHighlight(),this._signal("afterRender"),this.$updateGutterWidth(e)},e.prototype.$renderLines=function(e,t,n){var r=[],i=t,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>n)break;var u=this.$lines.createCell(i,e,this.session,l);this.$renderCell(u,e,s,i),r.push(u),i++}return r},e.prototype.$renderCell=function(e,t,n,i){var s=e.element,o=this.session,u=s.childNodes[0],f=s.childNodes[1],l=s.childNodes[2],c=l.firstChild,h=o.$firstLineNumber,p=o.$breakpoints,d=o.$decorations,v=o.gutterRenderer||this.$renderer,m=this.$showFoldWidgets&&o.foldWidgets,g=n?n.start.row:Number.MAX_VALUE,y=t.lineHeight+"px",b=this.$useSvgGutterIcons?"ace_gutter-cell_svg-icons ":"ace_gutter-cell ",w=this.$useSvgGutterIcons?"ace_icon_svg":"ace_icon",E=(v?v.getText(o,i):i+h).toString();this.$highlightGutterLine&&(i==this.$cursorRow||n&&i=g&&this.$cursorRow<=n.end.row)&&(b+="ace_gutter-active-line ",this.$cursorCell!=e&&(this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ","")),this.$cursorCell=e)),p[i]&&(b+=p[i]),d[i]&&(b+=d[i]),this.$annotations[i]&&i!==g&&(b+=this.$annotations[i].className),s.className!=b&&(s.className=b);if(m){var S=m[i];S==null&&(S=m[i]=o.getFoldWidget(i))}if(S){var b="ace_fold-widget ace_"+S;if(S=="start"&&i==g&&in.right-t.right)return"foldWidgets"},e}();f.prototype.$fixedWidth=!1,f.prototype.$highlightGutterLine=!0,f.prototype.$renderer="",f.prototype.$showLineNumbers=!0,f.prototype.$showFoldWidgets=!0,i.implement(f.prototype,o),t.Gutter=f}),define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";function o(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}var r=e("../range").Range,i=e("../lib/dom"),s=function(){function e(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)}return e.prototype.setPadding=function(e){this.$padding=e},e.prototype.setSession=function(e){this.session=e},e.prototype.setMarkers=function(e){this.markers=e},e.prototype.elt=function(e,t){var n=this.i!=-1&&this.element.childNodes[this.i];n?this.i++:(n=document.createElement("div"),this.element.appendChild(n),this.i=-1),n.style.cssText=t,n.className=e},e.prototype.update=function(e){if(!e)return;this.config=e,this.i=0;var t;for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}if(this.i!=-1)while(this.ip,l==f),i,l==f?0:1,s)},e.prototype.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"";if(this.session.$bidiHandler.isBidiRow(t.start.row)){var f=t.clone();f.end.row=f.start.row,f.end.column=this.session.getLine(f.start.row).length,this.drawBidiSingleLineMarker(e,f,n+" ace_br1 ace_start",r,null,i)}else this.elt(n+" ace_br1 ace_start","height:"+o+"px;"+"right:0;"+"top:"+u+"px;left:"+a+"px;"+(i||""));if(this.session.$bidiHandler.isBidiRow(t.end.row)){var f=t.clone();f.start.row=f.end.row,f.start.column=0,this.drawBidiSingleLineMarker(e,f,n+" ace_br12",r,null,i)}else{u=this.$getTop(t.end.row,r);var l=t.end.column*r.characterWidth;this.elt(n+" ace_br12","height:"+o+"px;"+"width:"+l+"px;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))}o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var c=(t.start.column?1:0)|(t.end.column?0:8);this.elt(n+(c?" ace_br"+c:""),"height:"+o+"px;"+"right:0;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))},e.prototype.drawSingleLineMarker=function(e,t,n,r,i,s){if(this.session.$bidiHandler.isBidiRow(t.start.row))return this.drawBidiSingleLineMarker(e,t,n,r,i,s);var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;this.elt(n,"height:"+o+"px;"+"width:"+u+"px;"+"top:"+a+"px;"+"left:"+f+"px;"+(s||""))},e.prototype.drawBidiSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=this.$getTop(t.start.row,r),a=this.$padding,f=this.session.$bidiHandler.getSelections(t.start.column,t.end.column);f.forEach(function(e){this.elt(n,"height:"+o+"px;"+"width:"+(e.width+(i||0))+"px;"+"top:"+u+"px;"+"left:"+(a+e.left)+"px;"+(s||""))},this)},e.prototype.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))},e.prototype.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))},e}();s.prototype.$padding=0,t.Marker=s}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/layer/lines","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("./lines").Lines,u=e("../lib/event_emitter").EventEmitter,a=e("../config").nls,f=function(){function e(e){this.dom=i,this.element=this.dom.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this),this.$lines=new o(this.element)}return e.prototype.$updateEolChar=function(){var e=this.session.doc,t=e.getNewLineCharacter()=="\n"&&e.getNewLineMode()!="windows",n=t?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=n)return this.EOL_CHAR=n,!0},e.prototype.setPadding=function(e){this.$padding=e,this.element.style.margin="0 "+e+"px"},e.prototype.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},e.prototype.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},e.prototype.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},e.prototype.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},e.prototype.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},e.prototype.setSession=function(e){this.session=e,e&&this.$computeTabString()},e.prototype.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,typeof e=="string"?(this.showSpaces=/tab/i.test(e),this.showTabs=/space/i.test(e),this.showEOL=/eol/i.test(e)):this.showSpaces=this.showTabs=this.showEOL=e,this.$computeTabString(),!0)},e.prototype.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},e.prototype.setHighlightIndentGuides=function(e){return this.$highlightIndentGuides===e?!1:(this.$highlightIndentGuides=e,e)},e.prototype.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;nl&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),l=a?a.start.row:Infinity);if(u>i)break;var c=s[o++];if(c){this.dom.removeChildren(c),this.$renderLine(c,u,u==l?a:!1),f&&(c.style.top=this.$lines.computeLineTop(u,e,this.session)+"px");var h=e.lineHeight*this.session.getRowLength(u)+"px";c.style.height!=h&&(f=!0,c.style.height=h)}u++}if(f)while(o0;i--)this.$lines.shift();if(t.lastRow>e.lastRow)for(var i=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);i>0;i--)this.$lines.pop();e.firstRowt.lastRow&&this.$lines.push(this.$renderLinesFragment(e,t.lastRow+1,e.lastRow)),this.$highlightIndentGuide()},e.prototype.$renderLinesFragment=function(e,t,n){var r=[],s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=this.$lines.createCell(s,e,this.session),f=a.element;this.dom.removeChildren(f),i.setStyle(f.style,"height",this.$lines.computeLineHeight(s,e,this.session)+"px"),i.setStyle(f.style,"top",this.$lines.computeLineTop(s,e,this.session)+"px"),this.$renderLine(f,s,s==u?o:!1),this.$useLineGroups()?f.className="ace_line_group":f.className="ace_line",r.push(a),s++}return r},e.prototype.update=function(e){this.$lines.moveContainer(e),this.config=e;var t=e.firstRow,n=e.lastRow,r=this.$lines;while(r.getLength())r.pop();r.push(this.$renderLinesFragment(e,t,n))},e.prototype.$renderToken=function(e,t,n,r){var i=this,o=/(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC\u2066\u2067\u2068\u202A\u202B\u202D\u202E\u202C\u2069]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g,u=this.dom.createFragment(this.element),f,l=0;while(f=o.exec(r)){var c=f[1],h=f[2],p=f[3],d=f[4],v=f[5];if(!i.showSpaces&&h)continue;var m=l!=f.index?r.slice(l,f.index):"";l=f.index+f[0].length,m&&u.appendChild(this.dom.createTextNode(m,this.element));if(c){var g=i.session.getScreenTabSize(t+f.index);u.appendChild(i.$tabStrings[g].cloneNode(!0)),t+=g-1}else if(h)if(i.showSpaces){var y=this.dom.createElement("span");y.className="ace_invisible ace_invisible_space",y.textContent=s.stringRepeat(i.SPACE_CHAR,h.length),u.appendChild(y)}else u.appendChild(this.com.createTextNode(h,this.element));else if(p){var y=this.dom.createElement("span");y.className="ace_invisible ace_invisible_space ace_invalid",y.textContent=s.stringRepeat(i.SPACE_CHAR,p.length),u.appendChild(y)}else if(d){t+=1;var y=this.dom.createElement("span");y.style.width=i.config.characterWidth*2+"px",y.className=i.showSpaces?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",y.textContent=i.showSpaces?i.SPACE_CHAR:d,u.appendChild(y)}else if(v){t+=1;var y=this.dom.createElement("span");y.style.width=i.config.characterWidth*2+"px",y.className="ace_cjk",y.textContent=v,u.appendChild(y)}}u.appendChild(this.dom.createTextNode(l?r.slice(l):r,this.element));if(!this.$textToken[n.type]){var b="ace_"+n.type.replace(/\./g," ace_"),y=this.dom.createElement("span");n.type=="fold"&&(y.style.width=n.value.length*this.config.characterWidth+"px",y.setAttribute("title",a("Unfold code"))),y.className=b,y.appendChild(u),e.appendChild(y)}else e.appendChild(u);return t+r.length},e.prototype.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);if(r<=0||r>=n)return t;if(t[0]==" "){r-=r%this.tabSize;var i=r/this.tabSize;for(var s=0;ss[o].start.row?this.$highlightIndentGuideMarker.dir=-1:this.$highlightIndentGuideMarker.dir=1;break}}if(!this.$highlightIndentGuideMarker.end&&e[t.row]!==""&&t.column===e[t.row].length){this.$highlightIndentGuideMarker.dir=1;for(var o=t.row+1;o0)for(var i=0;i=this.$highlightIndentGuideMarker.start+1){if(r.row>=this.$highlightIndentGuideMarker.end)break;this.$setIndentGuideActive(r,t)}}else for(var n=e.length-1;n>=0;n--){var r=e[n];if(this.$highlightIndentGuideMarker.end&&r.row=o)u=this.$renderToken(a,u,l,c.substring(0,o-r)),c=c.substring(o-r),r=o,a=this.$createLineElement(),e.appendChild(a),a.appendChild(this.dom.createTextNode(s.stringRepeat("\u00a0",n.indent),this.element)),i++,u=0,o=n[i]||Number.MAX_VALUE;c.length!=0&&(r+=c.length,u=this.$renderToken(a,u,l,c))}}n[n.length-1]>this.MAX_LINE_LENGTH&&this.$renderOverflowMessage(a,u,null,"",!0)},e.prototype.$renderSimpleLine=function(e,t){var n=0;for(var r=0;rthis.MAX_LINE_LENGTH)return this.$renderOverflowMessage(e,n,i,s);n=this.$renderToken(e,n,i,s)}},e.prototype.$renderOverflowMessage=function(e,t,n,r,i){n&&this.$renderToken(e,t,n,r.slice(0,this.MAX_LINE_LENGTH-t));var s=this.dom.createElement("span");s.className="ace_inline_button ace_keyword ace_toggle_wrap",s.textContent=i?"":"",e.appendChild(s)},e.prototype.$renderLine=function(e,t,n){!n&&n!=0&&(n=this.session.getFoldLine(t));if(n)var r=this.$getFoldLineTokens(t,n);else var r=this.session.getTokens(t);var i=e;if(r.length){var s=this.session.getRowSplitData(t);if(s&&s.length){this.$renderWrappedLine(e,r,s);var i=e.lastChild}else{var i=e;this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i)),this.$renderSimpleLine(i,r)}}else this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i));if(this.showEOL&&i){n&&(t=n.end.row);var o=this.dom.createElement("span");o.className="ace_invisible ace_invisible_eol",o.textContent=t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,i.appendChild(o)}},e.prototype.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},e.prototype.$useLineGroups=function(){return this.session.getUseWrapMode()},e}();f.prototype.$textToken={text:!0,rparen:!0,lparen:!0},f.prototype.EOF_CHAR="\u00b6",f.prototype.EOL_CHAR_LF="\u00ac",f.prototype.EOL_CHAR_CRLF="\u00a4",f.prototype.EOL_CHAR=f.prototype.EOL_CHAR_LF,f.prototype.TAB_CHAR="\u2014",f.prototype.SPACE_CHAR="\u00b7",f.prototype.$padding=0,f.prototype.MAX_LINE_LENGTH=1e4,f.prototype.showInvisibles=!1,f.prototype.showSpaces=!1,f.prototype.showTabs=!1,f.prototype.showEOL=!1,f.prototype.displayIndentGuides=!0,f.prototype.$highlightIndentGuides=!0,f.prototype.$tabStrings=[],f.prototype.destroy={},f.prototype.onChangeTabSize=f.prototype.$computeTabString,r.implement(f.prototype,u),t.Text=f}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(){function e(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=this.$updateOpacity.bind(this)}return e.prototype.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)r.setStyle(t[n].style,"opacity",e?"":"0")},e.prototype.$startCssAnimation=function(){var e=this.cursors;for(var t=e.length;t--;)e[t].style.animationDuration=this.blinkInterval+"ms";this.$isAnimating=!0,setTimeout(function(){this.$isAnimating&&r.addCssClass(this.element,"ace_animate-blinking")}.bind(this))},e.prototype.$stopCssAnimation=function(){this.$isAnimating=!1,r.removeCssClass(this.element,"ace_animate-blinking")},e.prototype.setPadding=function(e){this.$padding=e},e.prototype.setSession=function(e){this.session=e},e.prototype.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},e.prototype.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},e.prototype.setSmoothBlinking=function(e){e!=this.smoothBlinking&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.restartTimer())},e.prototype.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},e.prototype.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},e.prototype.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},e.prototype.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},e.prototype.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.$stopCssAnimation(),this.smoothBlinking&&(this.$isSmoothBlinking=!1,r.removeCssClass(this.element,"ace_smooth-blinking")),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible){this.$stopCssAnimation();return}this.smoothBlinking&&(this.$isSmoothBlinking=!0,setTimeout(function(){this.$isSmoothBlinking&&r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this)));if(r.HAS_CSS_ANIMATION)this.$startCssAnimation();else{var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()}},e.prototype.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+(this.session.$bidiHandler.isBidiRow(n.row,e.row)?this.session.$bidiHandler.getPosLeft(n.column):n.column*this.config.characterWidth),i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},e.prototype.isCursorInView=function(e,t){return e.top>=0&&e.tope.height+e.offset||o.top<0)&&n>1)continue;var u=this.cursors[i++]||this.addCursor(),a=u.style;this.drawCursor?this.drawCursor(u,o,e,t[n],this.session):this.isCursorInView(o,e)?(r.setStyle(a,"display","block"),r.translate(u,o.left,o.top),r.setStyle(a,"width",Math.round(e.characterWidth)+"px"),r.setStyle(a,"height",e.lineHeight+"px")):r.setStyle(a,"display","none")}while(this.cursors.length>i)this.removeCursor();var f=this.session.getOverwrite();this.$setOverwrite(f),this.$pixelPos=o,this.restartTimer()},e.prototype.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},e.prototype.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)},e}();i.prototype.$padding=0,i.prototype.drawCursor=null,t.Cursor=i}),define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=e("./lib/oop"),s=e("./lib/dom"),o=e("./lib/event"),u=e("./lib/event_emitter").EventEmitter,a=32768,f=function(){function e(e,t){this.element=s.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+t,this.inner=s.createElement("div"),this.inner.className="ace_scrollbar-inner",this.inner.textContent="\u00a0",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,o.addListener(this.element,"scroll",this.onScroll.bind(this)),o.addListener(this.element,"mousedown",o.preventDefault)}return e.prototype.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1},e}();i.implement(f.prototype,u);var l=function(e){function t(t,n){var r=e.call(this,t,"-v")||this;return r.scrollTop=0,r.scrollHeight=0,n.$scrollbarWidth=r.width=s.scrollbarWidth(t.ownerDocument),r.inner.style.width=r.element.style.width=(r.width||15)+5+"px",r.$minWidth=0,r}return r(t,e),t.prototype.onScroll=function(){if(!this.skipEvent){this.scrollTop=this.element.scrollTop;if(this.coeff!=1){var e=this.element.clientHeight/this.scrollHeight;this.scrollTop=this.scrollTop*(1-e)/(this.coeff-e)}this._emit("scroll",{data:this.scrollTop})}this.skipEvent=!1},t.prototype.getWidth=function(){return Math.max(this.isVisible?this.width:0,this.$minWidth||0)},t.prototype.setHeight=function(e){this.element.style.height=e+"px"},t.prototype.setScrollHeight=function(e){this.scrollHeight=e,e>a?(this.coeff=a/e,e=a):this.coeff!=1&&(this.coeff=1),this.inner.style.height=e+"px"},t.prototype.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=e,this.element.scrollTop=e*this.coeff)},t}(f);l.prototype.setInnerHeight=l.prototype.setScrollHeight;var c=function(e){function t(t,n){var r=e.call(this,t,"-h")||this;return r.scrollLeft=0,r.height=n.$scrollbarWidth,r.inner.style.height=r.element.style.height=(r.height||15)+5+"px",r}return r(t,e),t.prototype.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},t.prototype.getHeight=function(){return this.isVisible?this.height:0},t.prototype.setWidth=function(e){this.element.style.width=e+"px"},t.prototype.setInnerWidth=function(e){this.inner.style.width=e+"px"},t.prototype.setScrollWidth=function(e){this.inner.style.width=e+"px"},t.prototype.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)},t}(f);t.ScrollBar=l,t.ScrollBarV=l,t.ScrollBarH=c,t.VScrollBar=l,t.HScrollBar=c}),define("ace/scrollbar_custom",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=e("./lib/oop"),s=e("./lib/dom"),o=e("./lib/event"),u=e("./lib/event_emitter").EventEmitter;s.importCssString(".ace_editor>.ace_sb-v div, .ace_editor>.ace_sb-h div{\n position: absolute;\n background: rgba(128, 128, 128, 0.6);\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n border: 1px solid #bbb;\n border-radius: 2px;\n z-index: 8;\n}\n.ace_editor>.ace_sb-v, .ace_editor>.ace_sb-h {\n position: absolute;\n z-index: 6;\n background: none;\n overflow: hidden!important;\n}\n.ace_editor>.ace_sb-v {\n z-index: 6;\n right: 0;\n top: 0;\n width: 12px;\n}\n.ace_editor>.ace_sb-v div {\n z-index: 8;\n right: 0;\n width: 100%;\n}\n.ace_editor>.ace_sb-h {\n bottom: 0;\n left: 0;\n height: 12px;\n}\n.ace_editor>.ace_sb-h div {\n bottom: 0;\n height: 100%;\n}\n.ace_editor>.ace_sb_grabbed {\n z-index: 8;\n background: #000;\n}","ace_scrollbar.css",!1);var a=function(){function e(e,t){this.element=s.createElement("div"),this.element.className="ace_sb"+t,this.inner=s.createElement("div"),this.inner.className="",this.element.appendChild(this.inner),this.VScrollWidth=12,this.HScrollHeight=12,e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,o.addMultiMouseDownListener(this.element,[500,300,300],this,"onMouseDown")}return e.prototype.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1},e}();i.implement(a.prototype,u);var f=function(e){function t(t,n){var r=e.call(this,t,"-v")||this;return r.scrollTop=0,r.scrollHeight=0,r.parent=t,r.width=r.VScrollWidth,r.renderer=n,r.inner.style.width=r.element.style.width=(r.width||15)+"px",r.$minWidth=0,r}return r(t,e),t.prototype.onMouseDown=function(e,t){if(e!=="mousedown")return;if(o.getButton(t)!==0||t.detail===2)return;if(t.target===this.inner){var n=this,r=t.clientY,i=function(e){r=e.clientY},s=function(){clearInterval(l)},u=t.clientY,a=this.thumbTop,f=function(){if(r===undefined)return;var e=n.scrollTopFromThumbTop(a+r-u);if(e===n.scrollTop)return;n._emit("scroll",{data:e})};o.capture(this.inner,i,s);var l=setInterval(f,20);return o.preventDefault(t)}var c=t.clientY-this.element.getBoundingClientRect().top-this.thumbHeight/2;return this._emit("scroll",{data:this.scrollTopFromThumbTop(c)}),o.preventDefault(t)},t.prototype.getHeight=function(){return this.height},t.prototype.scrollTopFromThumbTop=function(e){var t=e*(this.pageHeight-this.viewHeight)/(this.slideHeight-this.thumbHeight);return t>>=0,t<0?t=0:t>this.pageHeight-this.viewHeight&&(t=this.pageHeight-this.viewHeight),t},t.prototype.getWidth=function(){return Math.max(this.isVisible?this.width:0,this.$minWidth||0)},t.prototype.setHeight=function(e){this.height=Math.max(0,e),this.slideHeight=this.height,this.viewHeight=this.height,this.setScrollHeight(this.pageHeight,!0)},t.prototype.setScrollHeight=function(e,t){if(this.pageHeight===e&&!t)return;this.pageHeight=e,this.thumbHeight=this.slideHeight*this.viewHeight/this.pageHeight,this.thumbHeight>this.slideHeight&&(this.thumbHeight=this.slideHeight),this.thumbHeight<15&&(this.thumbHeight=15),this.inner.style.height=this.thumbHeight+"px",this.scrollTop>this.pageHeight-this.viewHeight&&(this.scrollTop=this.pageHeight-this.viewHeight,this.scrollTop<0&&(this.scrollTop=0),this._emit("scroll",{data:this.scrollTop}))},t.prototype.setScrollTop=function(e){this.scrollTop=e,e<0&&(e=0),this.thumbTop=e*(this.slideHeight-this.thumbHeight)/(this.pageHeight-this.viewHeight),this.inner.style.top=this.thumbTop+"px"},t}(a);f.prototype.setInnerHeight=f.prototype.setScrollHeight;var l=function(e){function t(t,n){var r=e.call(this,t,"-h")||this;return r.scrollLeft=0,r.scrollWidth=0,r.height=r.HScrollHeight,r.inner.style.height=r.element.style.height=(r.height||12)+"px",r.renderer=n,r}return r(t,e),t.prototype.onMouseDown=function(e,t){if(e!=="mousedown")return;if(o.getButton(t)!==0||t.detail===2)return;if(t.target===this.inner){var n=this,r=t.clientX,i=function(e){r=e.clientX},s=function(){clearInterval(l)},u=t.clientX,a=this.thumbLeft,f=function(){if(r===undefined)return;var e=n.scrollLeftFromThumbLeft(a+r-u);if(e===n.scrollLeft)return;n._emit("scroll",{data:e})};o.capture(this.inner,i,s);var l=setInterval(f,20);return o.preventDefault(t)}var c=t.clientX-this.element.getBoundingClientRect().left-this.thumbWidth/2;return this._emit("scroll",{data:this.scrollLeftFromThumbLeft(c)}),o.preventDefault(t)},t.prototype.getHeight=function(){return this.isVisible?this.height:0},t.prototype.scrollLeftFromThumbLeft=function(e){var t=e*(this.pageWidth-this.viewWidth)/(this.slideWidth-this.thumbWidth);return t>>=0,t<0?t=0:t>this.pageWidth-this.viewWidth&&(t=this.pageWidth-this.viewWidth),t},t.prototype.setWidth=function(e){this.width=Math.max(0,e),this.element.style.width=this.width+"px",this.slideWidth=this.width,this.viewWidth=this.width,this.setScrollWidth(this.pageWidth,!0)},t.prototype.setScrollWidth=function(e,t){if(this.pageWidth===e&&!t)return;this.pageWidth=e,this.thumbWidth=this.slideWidth*this.viewWidth/this.pageWidth,this.thumbWidth>this.slideWidth&&(this.thumbWidth=this.slideWidth),this.thumbWidth<15&&(this.thumbWidth=15),this.inner.style.width=this.thumbWidth+"px",this.scrollLeft>this.pageWidth-this.viewWidth&&(this.scrollLeft=this.pageWidth-this.viewWidth,this.scrollLeft<0&&(this.scrollLeft=0),this._emit("scroll",{data:this.scrollLeft}))},t.prototype.setScrollLeft=function(e){this.scrollLeft=e,e<0&&(e=0),this.thumbLeft=e*(this.slideWidth-this.thumbWidth)/(this.pageWidth-this.viewWidth),this.inner.style.left=this.thumbLeft+"px"},t}(a);l.prototype.setInnerWidth=l.prototype.setScrollWidth,t.ScrollBar=f,t.ScrollBarV=f,t.ScrollBarH=l,t.VScrollBar=f,t.HScrollBar=l}),define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(){function e(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.$recursionLimit=2,this.window=t||window;var n=this;this._flush=function(e){n.pending=!1;var t=n.changes;t&&(r.blockIdle(100),n.changes=0,n.onRender(t));if(n.changes){if(n.$recursionLimit--<0)return;n.schedule()}else n.$recursionLimit=2}}return e.prototype.schedule=function(e){this.changes=this.changes|e,this.changes&&!this.pending&&(r.nextFrame(this._flush),this.pending=!0)},e.prototype.clear=function(e){var t=this.changes;return this.changes=0,t},e}();t.RenderLoop=i}),define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/event"),u=e("../lib/useragent"),a=e("../lib/event_emitter").EventEmitter,f=512,l=typeof ResizeObserver=="function",c=200,h=function(){function e(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),this.$measureNode.textContent=s.stringRepeat("X",f),this.$characterSize={width:0,height:0},l?this.$addObserver():this.checkForSizeChanges()}return e.prototype.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",u.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},e.prototype.checkForSizeChanges=function(e){e===undefined&&(e=this.$measureSizes());if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},e.prototype.$addObserver=function(){var e=this;this.$observer=new window.ResizeObserver(function(t){e.checkForSizeChanges()}),this.$observer.observe(this.$measureNode)},e.prototype.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer||this.$observer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=o.onIdle(function t(){e.checkForSizeChanges(),o.onIdle(t,500)},500)},e.prototype.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},e.prototype.$measureSizes=function(e){var t={height:(e||this.$measureNode).clientHeight,width:(e||this.$measureNode).clientWidth/f};return t.width===0||t.height===0?null:t},e.prototype.$measureCharWidth=function(e){this.$main.textContent=s.stringRepeat(e,f);var t=this.$main.getBoundingClientRect();return t.width/f},e.prototype.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},e.prototype.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$observer&&this.$observer.disconnect(),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)},e.prototype.$getZoom=function(e){return!e||!e.parentElement?1:(window.getComputedStyle(e).zoom||1)*this.$getZoom(e.parentElement)},e.prototype.$initTransformMeasureNodes=function(){var e=function(e,t){return["div",{style:"position: absolute;top:"+e+"px;left:"+t+"px;"}]};this.els=i.buildDom([e(0,0),e(c,0),e(0,c),e(c,c)],this.el)},e.prototype.transformCoordinates=function(e,t){function r(e,t,n){var r=e[1]*t[0]-e[0]*t[1];return[(-t[1]*n[0]+t[0]*n[1])/r,(+e[1]*n[0]-e[0]*n[1])/r]}function i(e,t){return[e[0]-t[0],e[1]-t[1]]}function s(e,t){return[e[0]+t[0],e[1]+t[1]]}function o(e,t){return[e*t[0],e*t[1]]}function u(e){var t=e.getBoundingClientRect();return[t.left,t.top]}if(e){var n=this.$getZoom(this.el);e=o(1/n,e)}this.els||this.$initTransformMeasureNodes();var a=u(this.els[0]),f=u(this.els[1]),l=u(this.els[2]),h=u(this.els[3]),p=r(i(h,f),i(h,l),i(s(f,l),s(h,a))),d=o(1+p[0],i(f,a)),v=o(1+p[1],i(l,a));if(t){var m=t,g=p[0]*m[0]/c+p[1]*m[1]/c+1,y=s(o(m[0],d),o(m[1],v));return s(o(1/g/c,y),a)}var b=i(e,a),w=r(i(d,o(p[0],b)),i(v,o(p[1],b)),b);return o(c,w)},e}();h.prototype.$characterSize={width:0,height:0},r.implement(h.prototype,a),t.FontMetrics=h}),define("ace/css/editor.css",["require","exports","module"],function(e,t,n){n.exports='\n.ace_br1 {border-top-left-radius : 3px;}\n.ace_br2 {border-top-right-radius : 3px;}\n.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}\n.ace_br4 {border-bottom-right-radius: 3px;}\n.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}\n.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}\n.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}\n.ace_br8 {border-bottom-left-radius : 3px;}\n.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}\n.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}\n.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}\n.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\n.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\n.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\n.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\n\n\n.ace_editor {\n position: relative;\n overflow: hidden;\n padding: 0;\n font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'Source Code Pro\', \'source-code-pro\', monospace;\n direction: ltr;\n text-align: left;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\n.ace_scroller {\n position: absolute;\n overflow: hidden;\n top: 0;\n bottom: 0;\n background-color: inherit;\n -ms-user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n user-select: none;\n cursor: text;\n}\n\n.ace_content {\n position: absolute;\n box-sizing: border-box;\n min-width: 100%;\n contain: style size layout;\n font-variant-ligatures: no-common-ligatures;\n}\n\n.ace_keyboard-focus:focus {\n box-shadow: inset 0 0 0 2px #5E9ED6;\n outline: none;\n}\n\n.ace_dragging .ace_scroller:before{\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n content: \'\';\n background: rgba(250, 250, 250, 0.01);\n z-index: 1000;\n}\n.ace_dragging.ace_dark .ace_scroller:before{\n background: rgba(0, 0, 0, 0.01);\n}\n\n.ace_gutter {\n position: absolute;\n overflow : hidden;\n width: auto;\n top: 0;\n bottom: 0;\n left: 0;\n cursor: default;\n z-index: 4;\n -ms-user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n user-select: none;\n contain: style size layout;\n}\n\n.ace_gutter-active-line {\n position: absolute;\n left: 0;\n right: 0;\n}\n\n.ace_scroller.ace_scroll-left:after {\n content: "";\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;\n pointer-events: none;\n}\n\n.ace_gutter-cell, .ace_gutter-cell_svg-icons {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding-left: 19px;\n padding-right: 6px;\n background-repeat: no-repeat;\n}\n\n.ace_gutter-cell_svg-icons .ace_gutter_annotation {\n margin-left: -14px;\n float: left;\n}\n\n.ace_gutter-cell .ace_gutter_annotation {\n margin-left: -19px;\n float: left;\n}\n\n.ace_gutter-cell.ace_error, .ace_icon.ace_error, .ace_icon.ace_error_fold {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_warning, .ace_icon.ace_warning, .ace_icon.ace_warning_fold {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_info, .ace_icon.ace_info {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n.ace_dark .ace_gutter-cell.ace_info, .ace_dark .ace_icon.ace_info {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");\n}\n\n.ace_icon_svg.ace_error {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJyZWQiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIj4KPGNpcmNsZSBmaWxsPSJub25lIiBjeD0iOCIgY3k9IjgiIHI9IjciIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPGxpbmUgeDE9IjExIiB5MT0iNSIgeDI9IjUiIHkyPSIxMSIvPgo8bGluZSB4MT0iMTEiIHkxPSIxMSIgeDI9IjUiIHkyPSI1Ii8+CjwvZz4KPC9zdmc+");\n background-color: crimson;\n}\n.ace_icon_svg.ace_warning {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJkYXJrb3JhbmdlIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+Cjxwb2x5Z29uIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiIHBvaW50cz0iOCAxIDE1IDE1IDEgMTUgOCAxIi8+CjxyZWN0IHg9IjgiIHk9IjEyIiB3aWR0aD0iMC4wMSIgaGVpZ2h0PSIwLjAxIi8+CjxsaW5lIHgxPSI4IiB5MT0iNiIgeDI9IjgiIHkyPSIxMCIvPgo8L2c+Cjwvc3ZnPg==");\n background-color: darkorange;\n}\n.ace_icon_svg.ace_info {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJibHVlIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+CjxjaXJjbGUgZmlsbD0ibm9uZSIgY3g9IjgiIGN5PSI4IiByPSI3IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjggMTEgOCA4Ii8+Cjxwb2x5bGluZSBwb2ludHM9IjkgOCA2IDgiLz4KPGxpbmUgeDE9IjEwIiB5MT0iMTEiIHgyPSI2IiB5Mj0iMTEiLz4KPHJlY3QgeD0iOCIgeT0iNSIgd2lkdGg9IjAuMDEiIGhlaWdodD0iMC4wMSIvPgo8L2c+Cjwvc3ZnPg==");\n background-color: royalblue;\n}\n\n.ace_icon_svg.ace_error_fold {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiIgZmlsbD0ibm9uZSI+CiAgPHBhdGggZD0ibSAxOC45Mjk4NTEsNy44Mjk4MDc2IGMgMC4xNDYzNTMsNi4zMzc0NjA0IC02LjMyMzE0Nyw3Ljc3Nzg0NDQgLTcuNDc3OTEyLDcuNzc3ODQ0NCAtMi4xMDcyNzI2LC0wLjEyODc1IDUuMTE3Njc4LDAuMzU2MjQ5IDUuMDUxNjk4LC03Ljg3MDA2MTggLTAuNjA0NjcyLC04LjAwMzk3MzQ5IC03LjA3NzI3MDYsLTcuNTYzMTE4OSAtNC44NTczLC03LjQzMDM5NTU2IDEuNjA2LC0wLjExNTE0MjI1IDYuODk3NDg1LDEuMjYyNTQ1OTYgNy4yODM1MTQsNy41MjI2MTI5NiB6IiBmaWxsPSJjcmltc29uIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0ibSA4LjExNDc1NjIsMi4wNTI5ODI4IGMgMy4zNDkxNjk4LDAgNi4wNjQxMzI4LDIuNjc2ODYyNyA2LjA2NDEzMjgsNS45Nzg5NTMgMCwzLjMwMjExMjIgLTIuNzE0OTYzLDUuOTc4OTIwMiAtNi4wNjQxMzI4LDUuOTc4OTIwMiAtMy4zNDkxNDczLDAgLTYuMDY0MTc3MiwtMi42NzY4MDggLTYuMDY0MTc3MiwtNS45Nzg5MjAyIDAuMDA1MzksLTMuMjk5ODg2MSAyLjcxNzI2NTYsLTUuOTczNjQwOCA2LjA2NDE3NzIsLTUuOTc4OTUzIHogbSAwLC0xLjczNTgyNzE5IGMgLTQuMzIxNDgzNiwwIC03LjgyNDc0MDM4LDMuNDU0MDE4NDkgLTcuODI0NzQwMzgsNy43MTQ3ODAxOSAwLDQuMjYwNzI4MiAzLjUwMzI1Njc4LDcuNzE0NzQ1MiA3LjgyNDc0MDM4LDcuNzE0NzQ1MiA0LjMyMTQ0OTgsMCA3LjgyNDY5OTgsLTMuNDU0MDE3IDcuODI0Njk5OCwtNy43MTQ3NDUyIDAsLTIuMDQ2MDkxNCAtMC44MjQzOTIsLTQuMDA4MzY3MiAtMi4yOTE3NTYsLTUuNDU1MTc0NiBDIDEyLjE4MDIyNSwxLjEyOTk2NDggMTAuMTkwMDEzLDAuMzE3MTU1NjEgOC4xMTQ3NTYyLDAuMzE3MTU1NjEgWiBNIDYuOTM3NDU2Myw4LjI0MDU5ODUgNC42NzE4Njg1LDEwLjQ4NTg1MiA2LjAwODY4MTQsMTEuODc2NzI4IDguMzE3MDAzNSw5LjYwMDc5MTEgMTAuNjI1MzM3LDExLjg3NjcyOCAxMS45NjIxMzgsMTAuNDg1ODUyIDkuNjk2NTUwOCw4LjI0MDU5ODUgMTEuOTYyMTM4LDYuMDA2ODA2NiAxMC41NzMyNDYsNC42Mzc0MzM1IDguMzE3MDAzNSw2Ljg3MzQyOTcgNi4wNjA3NjA3LDQuNjM3NDMzNSA0LjY3MTg2ODUsNi4wMDY4MDY2IFoiIGZpbGw9ImNyaW1zb24iIHN0cm9rZS13aWR0aD0iMiIvPgo8L3N2Zz4=");\n background-color: crimson;\n}\n.ace_icon_svg.ace_warning_fold {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAyMCAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNC43NzY5IDE0LjczMzdMOC42NTE5MiAyLjQ4MzY5QzguMzI5NDYgMS44Mzg3NyA3LjQwOTEzIDEuODM4NzcgNy4wODY2NyAyLjQ4MzY5TDAuOTYxNjY5IDE0LjczMzdDMC42NzA3NzUgMTUuMzE1NSAxLjA5MzgzIDE2IDEuNzQ0MjkgMTZIMTMuOTk0M0MxNC42NDQ4IDE2IDE1LjA2NzggMTUuMzE1NSAxNC43NzY5IDE0LjczMzdaTTMuMTYwMDcgMTQuMjVMNy44NjkyOSA0LjgzMTU2TDEyLjU3ODUgMTQuMjVIMy4xNjAwN1pNOC43NDQyOSAxMS42MjVWMTMuMzc1SDYuOTk0MjlWMTEuNjI1SDguNzQ0MjlaTTYuOTk0MjkgMTAuNzVWNy4yNUg4Ljc0NDI5VjEwLjc1SDYuOTk0MjlaIiBmaWxsPSIjRUM3MjExIi8+CjxwYXRoIGQ9Ik0xMS4xOTkxIDIuOTUyMzhDMTAuODgwOSAyLjMxNDY3IDEwLjM1MzcgMS44MDUyNiA5LjcwNTUgMS41MDlMMTEuMDQxIDEuMDY5NzhDMTEuNjg4MyAwLjk0OTgxNCAxMi4zMzcgMS4yNzI2MyAxMi42MzE3IDEuODYxNDFMMTcuNjEzNiAxMS44MTYxQzE4LjM1MjcgMTMuMjkyOSAxNy41OTM4IDE1LjA4MDQgMTYuMDE4IDE1LjU3NDVDMTYuNDA0NCAxNC40NTA3IDE2LjMyMzEgMTMuMjE4OCAxNS43OTI0IDEyLjE1NTVMMTEuMTk5MSAyLjk1MjM4WiIgZmlsbD0iI0VDNzIxMSIvPgo8L3N2Zz4=");\n background-color: darkorange;\n}\n\n.ace_scrollbar {\n contain: strict;\n position: absolute;\n right: 0;\n bottom: 0;\n z-index: 6;\n}\n\n.ace_scrollbar-inner {\n position: absolute;\n cursor: text;\n left: 0;\n top: 0;\n}\n\n.ace_scrollbar-v{\n overflow-x: hidden;\n overflow-y: scroll;\n top: 0;\n}\n\n.ace_scrollbar-h {\n overflow-x: scroll;\n overflow-y: hidden;\n left: 0;\n}\n\n.ace_print-margin {\n position: absolute;\n height: 100%;\n}\n\n.ace_text-input {\n position: absolute;\n z-index: 0;\n width: 0.5em;\n height: 1em;\n opacity: 0;\n background: transparent;\n -moz-appearance: none;\n appearance: none;\n border: none;\n resize: none;\n outline: none;\n overflow: hidden;\n font: inherit;\n padding: 0 1px;\n margin: 0 -1px;\n contain: strict;\n -ms-user-select: text;\n -moz-user-select: text;\n -webkit-user-select: text;\n user-select: text;\n /*with `pre-line` chrome inserts   instead of space*/\n white-space: pre!important;\n}\n.ace_text-input.ace_composition {\n background: transparent;\n color: inherit;\n z-index: 1000;\n opacity: 1;\n}\n.ace_composition_placeholder { color: transparent }\n.ace_composition_marker { \n border-bottom: 1px solid;\n position: absolute;\n border-radius: 0;\n margin-top: 1px;\n}\n\n[ace_nocontext=true] {\n transform: none!important;\n filter: none!important;\n clip-path: none!important;\n mask : none!important;\n contain: none!important;\n perspective: none!important;\n mix-blend-mode: initial!important;\n z-index: auto;\n}\n\n.ace_layer {\n z-index: 1;\n position: absolute;\n overflow: hidden;\n /* workaround for chrome bug https://github.com/ajaxorg/ace/issues/2312*/\n word-wrap: normal;\n white-space: pre;\n height: 100%;\n width: 100%;\n box-sizing: border-box;\n /* setting pointer-events: auto; on node under the mouse, which changes\n during scroll, will break mouse wheel scrolling in Safari */\n pointer-events: none;\n}\n\n.ace_gutter-layer {\n position: relative;\n width: auto;\n text-align: right;\n pointer-events: auto;\n height: 1000000px;\n contain: style size layout;\n}\n\n.ace_text-layer {\n font: inherit !important;\n position: absolute;\n height: 1000000px;\n width: 1000000px;\n contain: style size layout;\n}\n\n.ace_text-layer > .ace_line, .ace_text-layer > .ace_line_group {\n contain: style size layout;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n}\n\n.ace_hidpi .ace_text-layer,\n.ace_hidpi .ace_gutter-layer,\n.ace_hidpi .ace_content,\n.ace_hidpi .ace_gutter {\n contain: strict;\n}\n.ace_hidpi .ace_text-layer > .ace_line, \n.ace_hidpi .ace_text-layer > .ace_line_group {\n contain: strict;\n}\n\n.ace_cjk {\n display: inline-block;\n text-align: center;\n}\n\n.ace_cursor-layer {\n z-index: 4;\n}\n\n.ace_cursor {\n z-index: 4;\n position: absolute;\n box-sizing: border-box;\n border-left: 2px solid;\n /* workaround for smooth cursor repaintng whole screen in chrome */\n transform: translatez(0);\n}\n\n.ace_multiselect .ace_cursor {\n border-left-width: 1px;\n}\n\n.ace_slim-cursors .ace_cursor {\n border-left-width: 1px;\n}\n\n.ace_overwrite-cursors .ace_cursor {\n border-left-width: 0;\n border-bottom: 1px solid;\n}\n\n.ace_hidden-cursors .ace_cursor {\n opacity: 0.2;\n}\n\n.ace_hasPlaceholder .ace_hidden-cursors .ace_cursor {\n opacity: 0;\n}\n\n.ace_smooth-blinking .ace_cursor {\n transition: opacity 0.18s;\n}\n\n.ace_animate-blinking .ace_cursor {\n animation-duration: 1000ms;\n animation-timing-function: step-end;\n animation-name: blink-ace-animate;\n animation-iteration-count: infinite;\n}\n\n.ace_animate-blinking.ace_smooth-blinking .ace_cursor {\n animation-duration: 1000ms;\n animation-timing-function: ease-in-out;\n animation-name: blink-ace-animate-smooth;\n}\n \n@keyframes blink-ace-animate {\n from, to { opacity: 1; }\n 60% { opacity: 0; }\n}\n\n@keyframes blink-ace-animate-smooth {\n from, to { opacity: 1; }\n 45% { opacity: 1; }\n 60% { opacity: 0; }\n 85% { opacity: 0; }\n}\n\n.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {\n position: absolute;\n z-index: 3;\n}\n\n.ace_marker-layer .ace_selection {\n position: absolute;\n z-index: 5;\n}\n\n.ace_marker-layer .ace_bracket {\n position: absolute;\n z-index: 6;\n}\n\n.ace_marker-layer .ace_error_bracket {\n position: absolute;\n border-bottom: 1px solid #DE5555;\n border-radius: 0;\n}\n\n.ace_marker-layer .ace_active-line {\n position: absolute;\n z-index: 2;\n}\n\n.ace_marker-layer .ace_selected-word {\n position: absolute;\n z-index: 4;\n box-sizing: border-box;\n}\n\n.ace_line .ace_fold {\n box-sizing: border-box;\n\n display: inline-block;\n height: 11px;\n margin-top: -2px;\n vertical-align: middle;\n\n background-image:\n url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),\n url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n color: transparent;\n\n border: 1px solid black;\n border-radius: 2px;\n\n cursor: pointer;\n pointer-events: auto;\n}\n\n.ace_dark .ace_fold {\n}\n\n.ace_fold:hover{\n background-image:\n url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),\n url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");\n}\n\n.ace_tooltip {\n background-color: #f5f5f5;\n border: 1px solid gray;\n border-radius: 1px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n color: black;\n max-width: 100%;\n padding: 3px 4px;\n position: fixed;\n z-index: 999999;\n box-sizing: border-box;\n cursor: default;\n white-space: pre;\n word-wrap: break-word;\n line-height: normal;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n pointer-events: none;\n}\n\n.ace_tooltip.ace_dark {\n background-color: #636363;\n color: #fff;\n}\n\n.ace_tooltip:focus {\n outline: 1px solid #5E9ED6;\n}\n\n.ace_icon {\n display: inline-block;\n width: 18px;\n vertical-align: top;\n}\n\n.ace_icon_svg {\n display: inline-block;\n width: 12px;\n vertical-align: top;\n -webkit-mask-repeat: no-repeat;\n -webkit-mask-size: 12px;\n -webkit-mask-position: center;\n}\n\n.ace_folding-enabled > .ace_gutter-cell, .ace_folding-enabled > .ace_gutter-cell_svg-icons {\n padding-right: 13px;\n}\n\n.ace_fold-widget {\n box-sizing: border-box;\n\n margin: 0 -12px 0 1px;\n display: none;\n width: 11px;\n vertical-align: top;\n\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");\n background-repeat: no-repeat;\n background-position: center;\n\n border-radius: 3px;\n \n border: 1px solid transparent;\n cursor: pointer;\n}\n\n.ace_folding-enabled .ace_fold-widget {\n display: inline-block; \n}\n\n.ace_fold-widget.ace_end {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");\n}\n\n.ace_fold-widget.ace_closed {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");\n}\n\n.ace_fold-widget:hover {\n border: 1px solid rgba(0, 0, 0, 0.3);\n background-color: rgba(255, 255, 255, 0.2);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n}\n\n.ace_fold-widget:active {\n border: 1px solid rgba(0, 0, 0, 0.4);\n background-color: rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n}\n/**\n * Dark version for fold widgets\n */\n.ace_dark .ace_fold-widget {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");\n}\n.ace_dark .ace_fold-widget.ace_end {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");\n}\n.ace_dark .ace_fold-widget.ace_closed {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");\n}\n.ace_dark .ace_fold-widget:hover {\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\n background-color: rgba(255, 255, 255, 0.1);\n}\n.ace_dark .ace_fold-widget:active {\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\n}\n\n.ace_inline_button {\n border: 1px solid lightgray;\n display: inline-block;\n margin: -1px 8px;\n padding: 0 5px;\n pointer-events: auto;\n cursor: pointer;\n}\n.ace_inline_button:hover {\n border-color: gray;\n background: rgba(200,200,200,0.2);\n display: inline-block;\n pointer-events: auto;\n}\n\n.ace_fold-widget.ace_invalid {\n background-color: #FFB4B4;\n border-color: #DE5555;\n}\n\n.ace_fade-fold-widgets .ace_fold-widget {\n transition: opacity 0.4s ease 0.05s;\n opacity: 0;\n}\n\n.ace_fade-fold-widgets:hover .ace_fold-widget {\n transition: opacity 0.05s ease 0.05s;\n opacity:1;\n}\n\n.ace_underline {\n text-decoration: underline;\n}\n\n.ace_bold {\n font-weight: bold;\n}\n\n.ace_nobold .ace_bold {\n font-weight: normal;\n}\n\n.ace_italic {\n font-style: italic;\n}\n\n\n.ace_error-marker {\n background-color: rgba(255, 0, 0,0.2);\n position: absolute;\n z-index: 9;\n}\n\n.ace_highlight-marker {\n background-color: rgba(255, 255, 0,0.2);\n position: absolute;\n z-index: 8;\n}\n\n.ace_mobile-menu {\n position: absolute;\n line-height: 1.5;\n border-radius: 4px;\n -ms-user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n user-select: none;\n background: white;\n box-shadow: 1px 3px 2px grey;\n border: 1px solid #dcdcdc;\n color: black;\n}\n.ace_dark > .ace_mobile-menu {\n background: #333;\n color: #ccc;\n box-shadow: 1px 3px 2px grey;\n border: 1px solid #444;\n\n}\n.ace_mobile-button {\n padding: 2px;\n cursor: pointer;\n overflow: hidden;\n}\n.ace_mobile-button:hover {\n background-color: #eee;\n opacity:1;\n}\n.ace_mobile-button:active {\n background-color: #ddd;\n}\n\n.ace_placeholder {\n font-family: arial;\n transform: scale(0.9);\n transform-origin: left;\n white-space: pre;\n opacity: 0.7;\n margin: 0 10px;\n}\n\n.ace_ghost_text {\n opacity: 0.5;\n font-style: italic;\n white-space: pre;\n}'}),define("ace/layer/decorators",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event_emitter").EventEmitter,o=function(){function e(e,t){this.canvas=r.createElement("canvas"),this.renderer=t,this.pixelRatio=1,this.maxHeight=t.layerConfig.maxHeight,this.lineHeight=t.layerConfig.lineHeight,this.canvasHeight=e.parent.scrollHeight,this.heightRatio=this.canvasHeight/this.maxHeight,this.canvasWidth=e.width,this.minDecorationHeight=2*this.pixelRatio|0,this.halfMinDecorationHeight=this.minDecorationHeight/2|0,this.canvas.width=this.canvasWidth,this.canvas.height=this.canvasHeight,this.canvas.style.top="0px",this.canvas.style.right="0px",this.canvas.style.zIndex="7px",this.canvas.style.position="absolute",this.colors={},this.colors.dark={error:"rgba(255, 18, 18, 1)",warning:"rgba(18, 136, 18, 1)",info:"rgba(18, 18, 136, 1)"},this.colors.light={error:"rgb(255,51,51)",warning:"rgb(32,133,72)",info:"rgb(35,68,138)"},e.element.appendChild(this.canvas)}return e.prototype.$updateDecorators=function(e){function i(e,t){return e.priorityt.priority?1:0}var t=this.renderer.theme.isDark===!0?this.colors.dark:this.colors.light;if(e){this.maxHeight=e.maxHeight,this.lineHeight=e.lineHeight,this.canvasHeight=e.height;var n=(e.lastRow+1)*this.lineHeight;nthis.canvasHeight&&(v=this.canvasHeight-this.halfMinDecorationHeight),h=Math.round(v-this.halfMinDecorationHeight),p=Math.round(v+this.halfMinDecorationHeight)}r.fillStyle=t[s[a].type]||null,r.fillRect(0,c,this.canvasWidth,p-h)}}var m=this.renderer.session.selection.getCursor();if(m){var l=this.compensateFoldRows(m.row,u),c=Math.round((m.row-l)*this.lineHeight*this.heightRatio);r.fillStyle="rgba(0, 0, 0, 0.5)",r.fillRect(0,c,this.canvasWidth,2)}},e.prototype.compensateFoldRows=function(e,t){var n=0;if(t&&t.length>0)for(var r=0;rt[r].start.row&&e=t[r].end.row&&(n+=t[r].end.row-t[r].start.row);return n},e}();i.implement(o.prototype,s),t.Decorator=o}),define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/config","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/scrollbar_custom","ace/scrollbar_custom","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter","ace/css/editor.css","ace/layer/decorators","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/lang"),o=e("./config"),u=e("./layer/gutter").Gutter,a=e("./layer/marker").Marker,f=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").HScrollBar,h=e("./scrollbar").VScrollBar,p=e("./scrollbar_custom").HScrollBar,d=e("./scrollbar_custom").VScrollBar,v=e("./renderloop").RenderLoop,m=e("./layer/font_metrics").FontMetrics,g=e("./lib/event_emitter").EventEmitter,y=e("./css/editor.css"),b=e("./layer/decorators").Decorator,w=e("./lib/useragent");i.importCssString(y,"ace_editor.css",!1);var E=function(){function e(e,t){var n=this;this.container=e||i.createElement("div"),i.addCssClass(this.container,"ace_editor"),i.HI_DPI&&i.addCssClass(this.container,"ace_hidpi"),this.setTheme(t),o.get("useStrictCSP")==null&&o.set("useStrictCSP",!1),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.$gutter.setAttribute("aria-hidden",!0),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new u(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new a(this.content);var r=this.$textLayer=new f(this.content);this.canvas=r.element,this.$markerFront=new a(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new h(this.container,this),this.scrollBarH=new c(this.container,this),this.scrollBarV.on("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.on("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new m(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.on("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.margin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$keepTextAreaAtCursor=!w.isIOS,this.$loop=new v(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),this.$addResizeObserver(),o.resetOptions(this),o._signal("renderer",this)}return e.prototype.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin(),i.setStyle(this.scroller.style,"line-height",this.lineHeight+"px")},e.prototype.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.scrollBarH.scrollLeft=this.scrollBarV.scrollTop=null,this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},e.prototype.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},e.prototype.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar(),this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR)},e.prototype.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},e.prototype.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},e.prototype.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},e.prototype.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},e.prototype.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},e.prototype.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);this.$resizeTimer&&this.$resizeTimer.cancel();if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarH.scrollLeft=this.scrollBarV.scrollTop=null,this.$customScrollbar&&this.$updateCustomScrollbar(!0)},e.prototype.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var s=0,o=this.$size,u={width:o.width,height:o.height,scrollerHeight:o.scrollerHeight,scrollerWidth:o.scrollerWidth};r&&(e||o.height!=r)&&(o.height=r,s|=this.CHANGE_SIZE,o.scrollerHeight=o.height,this.$horizScroll&&(o.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.setHeight(o.scrollerHeight),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",s|=this.CHANGE_SCROLL);if(n&&(e||o.width!=n)){s|=this.CHANGE_SIZE,o.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,i.setStyle(this.scrollBarH.element.style,"left",t+"px"),i.setStyle(this.scroller.style,"left",t+this.margin.left+"px"),o.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()-this.margin.h),i.setStyle(this.$gutter.style,"left",this.margin.left+"px");var a=this.scrollBarV.getWidth()+"px";i.setStyle(this.scrollBarH.element.style,"right",a),i.setStyle(this.scroller.style,"right",a),i.setStyle(this.scroller.style,"bottom",this.scrollBarH.getHeight()),this.scrollBarH.setWidth(o.scrollerWidth);if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)s|=this.CHANGE_FULL}return o.$dirty=!n||!r,s&&this._signal("resize",u),s},e.prototype.onGutterResize=function(e){var t=this.$showGutter?e:0;t!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,t,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):this.$computeLayerConfig()},e.prototype.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},e.prototype.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},e.prototype.getAnimatedScroll=function(){return this.$animatedScroll},e.prototype.setShowInvisibles=function(e){this.setOption("showInvisibles",e),this.session.$bidiHandler.setShowInvisibles(e)},e.prototype.getShowInvisibles=function(){return this.getOption("showInvisibles")},e.prototype.getDisplayIndentGuide=function(){return this.getOption("displayIndentGuides")},e.prototype.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},e.prototype.getHighlightIndentGuides=function(){return this.getOption("highlightIndentGuides")},e.prototype.setHighlightIndentGuides=function(e){this.setOption("highlightIndentGuides",e)},e.prototype.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},e.prototype.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},e.prototype.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},e.prototype.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},e.prototype.getShowGutter=function(){return this.getOption("showGutter")},e.prototype.setShowGutter=function(e){return this.setOption("showGutter",e)},e.prototype.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},e.prototype.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},e.prototype.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},e.prototype.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},e.prototype.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=Math.round(this.characterWidth*this.$printMarginColumn+this.$padding)+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},e.prototype.getContainerElement=function(){return this.container},e.prototype.getMouseEventTarget=function(){return this.scroller},e.prototype.getTextAreaContainer=function(){return this.container},e.prototype.$moveTextAreaToCursor=function(){if(this.$isMousePressed)return;var e=this.textarea.style,t=this.$composition;if(!this.$keepTextAreaAtCursor&&!t){i.translate(this.textarea,-100,0);return}var n=this.$cursorLayer.$pixelPos;if(!n)return;t&&t.markerRange&&(n=this.$cursorLayer.getPixelPosition(t.markerRange.start,!0));var r=this.layerConfig,s=n.top,o=n.left;s-=r.offset;var u=t&&t.useTextareaForIME||w.isMobile?this.lineHeight:1;if(s<0||s>r.height-u){i.translate(this.textarea,0,0);return}var a=1,f=this.$size.height-u;if(!t)s+=this.lineHeight;else if(t.useTextareaForIME){var l=this.textarea.value;a=this.characterWidth*this.session.$getStringScreenWidth(l)[0]}else s+=this.lineHeight+2;o-=this.scrollLeft,o>this.$size.scrollerWidth-a&&(o=this.$size.scrollerWidth-a),o+=this.gutterWidth+this.margin.left,i.setStyle(e,"height",u+"px"),i.setStyle(e,"width",a+"px"),i.translate(this.textarea,Math.min(o,this.$size.scrollerWidth-a),Math.min(s,f))},e.prototype.getFirstVisibleRow=function(){return this.layerConfig.firstRow},e.prototype.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},e.prototype.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},e.prototype.getLastVisibleRow=function(){return this.layerConfig.lastRow},e.prototype.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},e.prototype.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},e.prototype.setMargin=function(e,t,n,r){var i=this.margin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,this.$updateCachedSize(!0,this.gutterWidth,this.$size.width,this.$size.height),this.updateFull()},e.prototype.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},e.prototype.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},e.prototype.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},e.prototype.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},e.prototype.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},e.prototype.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},e.prototype.freeze=function(){this.$frozen=!0},e.prototype.unfreeze=function(){this.$frozen=!1},e.prototype.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender",e),this.session&&this.session.$bidiHandler&&this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics);var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig()|this.$loop.clear();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-Math.max(this.layerConfig.firstRow,0))*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig()|this.$loop.clear())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),i.translate(this.content,-this.scrollLeft,-n.offset);var s=n.width+2*this.$padding+"px",o=n.minHeight+"px";i.setStyle(this.content.style,"width",s),i.setStyle(this.content.style,"height",o)}e&this.CHANGE_H_SCROLL&&(i.translate(this.content,-this.scrollLeft,-n.offset),this.scroller.className=this.scrollLeft<=0?"ace_scroller ":"ace_scroller ace_scroll-left ",this.enableKeyboardAccessibility&&(this.scroller.className+=this.keyboardFocusClassName));if(e&this.CHANGE_FULL){this.$changedLines=null,this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender",e);return}if(e&this.CHANGE_SCROLL){this.$changedLines=null,e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&(e&this.CHANGE_GUTTER||e&this.CHANGE_LINES?this.$gutterLayer.update(n):this.$gutterLayer.scrollLines(n)),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender",e);return}e&this.CHANGE_TEXT?(this.$changedLines=null,this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n)):e&this.CHANGE_LINES?((this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n)):e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER?(this.$showGutter&&this.$gutterLayer.update(n),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n)):e&this.CHANGE_CURSOR&&(this.$highlightGutterLine&&this.$gutterLayer.updateLineHighlight(n),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n)),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender",e)},e.prototype.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=n<=2*this.lineHeight,i=!r&&e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||i!=this.$vScroll){i!=this.$vScroll&&(this.$vScroll=i,this.scrollBarV.setVisible(i));var s=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,s,n),this.desiredHeight=n,this._signal("autosize")}},e.prototype.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=t.scrollerHeight+this.lineHeight,l=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=l;var c=this.scrollMargin;this.session.setScrollTop(Math.max(-c.top,Math.min(this.scrollTop,i-t.scrollerHeight+c.bottom))),this.session.setScrollLeft(Math.max(-c.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+c.right)));var h=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+l<0||this.scrollTop>c.top),p=a!==h;p&&(this.$vScroll=h,this.scrollBarV.setVisible(h));var d=this.scrollTop%this.lineHeight,v=Math.ceil(f/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-d)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),f=t.scrollerHeight+e.getRowLength(g)*w+b,d=this.scrollTop-y*w;var S=0;if(this.layerConfig.width!=s||u)S=this.CHANGE_H_SCROLL;if(u||p)S|=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),p&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:f,maxHeight:i,offset:d,gutterOffset:w?Math.max(0,Math.ceil((d+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},this.session.$bidiHandler&&this.session.$bidiHandler.setContentWidth(s-this.$padding),S},e.prototype.$updateLines=function(){if(!this.$changedLines)return;var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(tthis.$textLayer.MAX_LINE_LENGTH&&(e=this.$textLayer.MAX_LINE_LENGTH+30),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},e.prototype.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},e.prototype.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},e.prototype.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},e.prototype.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},e.prototype.updateBreakpoints=function(e){this.$loop.schedule(this.CHANGE_GUTTER)},e.prototype.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},e.prototype.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},e.prototype.hideCursor=function(){this.$cursorLayer.hideCursor()},e.prototype.showCursor=function(){this.$cursorLayer.showCursor()},e.prototype.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},e.prototype.scrollCursorIntoView=function(e,t,n){if(this.$size.scrollerHeight===0)return;var r=this.$cursorLayer.getPixelPosition(e),i=r.left,s=r.top,o=n&&n.top||0,u=n&&n.bottom||0;this.$scrollAnimation&&(this.$stopAnimation=!0);var a=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;a+o>s?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-u=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},e.prototype.pixelToScreenCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),u=this.$blockCursor?Math.floor(s):Math.round(s);return{row:o,column:u,side:s-u>0?1:-1,offsetX:i}},e.prototype.screenToTextCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=this.$blockCursor?Math.floor(s):Math.round(s),u=Math.floor((t+this.scrollTop-n.top)/this.lineHeight);return this.session.screenToDocumentPosition(u,Math.max(o,0),i)},e.prototype.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+(this.session.$bidiHandler.isBidiRow(r.row,e)?this.session.$bidiHandler.getPosLeft(r.column):Math.round(r.column*this.characterWidth)),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},e.prototype.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},e.prototype.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},e.prototype.showComposition=function(e){this.$composition=e,e.cssText||(e.cssText=this.textarea.style.cssText),e.useTextareaForIME==undefined&&(e.useTextareaForIME=this.$useTextareaForIME),this.$useTextareaForIME?(i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor(),this.$cursorLayer.element.style.display="none"):e.markerId=this.session.addMarker(e.markerRange,"ace_composition_marker","text")},e.prototype.setCompositionText=function(e){var t=this.session.selection.cursor;this.addToken(e,"composition_placeholder",t.row,t.column),this.$moveTextAreaToCursor()},e.prototype.hideComposition=function(){if(!this.$composition)return;this.$composition.markerId&&this.session.removeMarker(this.$composition.markerId),i.removeCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText=this.$composition.cssText;var e=this.session.selection.cursor;this.removeExtraToken(e.row,e.column),this.$composition=null,this.$cursorLayer.element.style.display=""},e.prototype.setGhostText=function(e,t){var n=this.session.selection.cursor,r=t||{row:n.row,column:n.column};this.removeGhostText();var i=e.split("\n");this.addToken(i[0],"ghost_text",r.row,r.column),this.$ghostText={text:e,position:{row:r.row,column:r.column}},i.length>1&&(this.$ghostTextWidget={text:i.slice(1).join("\n"),row:r.row,column:r.column,className:"ace_ghost_text"},this.session.widgetManager.addLineWidget(this.$ghostTextWidget))},e.prototype.removeGhostText=function(){if(!this.$ghostText)return;var e=this.$ghostText.position;this.removeExtraToken(e.row,e.column),this.$ghostTextWidget&&(this.session.widgetManager.removeLineWidget(this.$ghostTextWidget),this.$ghostTextWidget=null),this.$ghostText=null},e.prototype.addToken=function(e,t,n,r){var i=this.session;i.bgTokenizer.lines[n]=null;var s={type:t,value:e},o=i.getTokens(n);if(r==null||!o.length)o.push(s);else{var u=0;for(var a=0;a1||Math.abs(e.$size.height-r)>1?e.$resizeTimer.delay():e.$resizeTimer.cancel()}),this.$resizeObserver.observe(this.container)},e}();E.prototype.CHANGE_CURSOR=1,E.prototype.CHANGE_MARKER=2,E.prototype.CHANGE_GUTTER=4,E.prototype.CHANGE_SCROLL=8,E.prototype.CHANGE_LINES=16,E.prototype.CHANGE_TEXT=32,E.prototype.CHANGE_SIZE=64,E.prototype.CHANGE_MARKER_BACK=128,E.prototype.CHANGE_MARKER_FRONT=256,E.prototype.CHANGE_FULL=512,E.prototype.CHANGE_H_SCROLL=1024,E.prototype.$changes=0,E.prototype.$padding=null,E.prototype.$frozen=!1,E.prototype.STEPS=8,r.implement(E.prototype,g),o.defineOptions(E.prototype,"renderer",{useResizeObserver:{set:function(e){!e&&this.$resizeObserver?(this.$resizeObserver.disconnect(),this.$resizeTimer.cancel(),this.$resizeTimer=this.$resizeObserver=null):e&&!this.$resizeObserver&&this.$addResizeObserver()}},animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e=="number"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},useSvgGutterIcons:{set:function(e){this.$gutterLayer.$useSvgGutterIcons=e},initialValue:!1},showFoldedAnnotations:{set:function(e){this.$gutterLayer.$showFoldedAnnotations=e},initialValue:!1},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightIndentGuides:{set:function(e){this.$textLayer.setHighlightIndentGuides(e)==1?this.$textLayer.$highlightIndentGuide():this.$textLayer.$clearActiveIndentGuide(this.$textLayer.$lines.cells)},initialValue:!0},highlightGutterLine:{set:function(e){this.$gutterLayer.setHighlightGutterLine(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e=="number"&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.$minLines<562949953421311||(this.$minLines=0),this.updateFull()}},maxPixelHeight:{set:function(e){this.updateFull()},initialValue:0},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},customScrollbar:{set:function(e){this.$updateCustomScrollbar(e)},initialValue:!1},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:"./theme/textmate",handlesSet:!0},hasCssTransforms:{},useTextareaForIME:{initialValue:!w.isMobile&&!w.isIE}}),t.VirtualRenderer=E}),define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";function u(e){var t="importScripts('"+i.qualifyURL(e)+"');";try{return new Blob([t],{type:"application/javascript"})}catch(n){var r=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,s=new r;return s.append(t),s.getBlob("application/javascript")}}function a(e){if(typeof Worker=="undefined")return{postMessage:function(){},terminate:function(){}};if(o.get("loadWorkerFromBlob")){var t=u(e),n=window.URL||window.webkitURL,r=n.createObjectURL(t);return new Worker(r)}return new Worker(e)}var r=e("../lib/oop"),i=e("../lib/net"),s=e("../lib/event_emitter").EventEmitter,o=e("../config"),f=function(e){e.postMessage||(e=this.$createWorkerFromOldConfig.apply(this,arguments)),this.$worker=e,this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.$createWorkerFromOldConfig=function(t,n,r,i,s){e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl);if(o.get("packaged")||!e.toUrl)i=i||o.moduleUrl(n,"worker");else{var u=this.$normalizePath;i=i||u(e.toUrl("ace/worker/worker.js",null,"_"));var f={};t.forEach(function(t){f[t]=u(e.toUrl(t,null,"_").replace(/(\.js)?(\?.*)?$/,""))})}return this.$worker=a(i),s&&this.send("importScripts",s),this.$worker.postMessage({init:!0,tlns:f,module:n,classname:r}),this.$worker},this.onMessage=function(e){var t=e.data;switch(t.type){case"event":this._signal(t.name,{data:t.data});break;case"call":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id]);break;case"error":this.reportError(t.data);break;case"log":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return i.qualifyURL(e)},this.terminate=function(){this._signal("terminate",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker.onerror=function(e){e.preventDefault()},this.$worker=null,this.$doc&&this.$doc.off("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{t.data&&t.data.err&&(t.data.err={message:t.data.err.message,stack:t.data.err.stack,code:t.data.err.code}),this.$worker&&this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){console.error(n.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener,!0)},this.changeListener=function(e){this.deltaQueue||(this.deltaQueue=[],setTimeout(this.$sendDeltaQueue,0)),e.action=="insert"?this.deltaQueue.push(e.start,e.lines):this.deltaQueue.push(e.start,e.end)},this.$sendDeltaQueue=function(){var e=this.deltaQueue;if(!e)return;this.deltaQueue=null,e.length>50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})}}).call(f.prototype);var l=function(e,t,n){var r=null,i=!1,u=Object.create(s),a=[],l=new f({messageBuffer:a,terminate:function(){},postMessage:function(e){a.push(e);if(!r)return;i?setTimeout(c):c()}});l.setEmitSync=function(e){i=e};var c=function(){var e=a.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};return u.postMessage=function(e){l.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.length)c()}),l};t.UIWorkerClient=l,t.WorkerClient=f,t.createWorker=a}),define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(){function e(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate,!0),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)}return e.prototype.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},e.prototype.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},e.prototype.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},e.prototype.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},e.prototype.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},e.prototype.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},e.prototype.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.off("change",this.$onUpdate),this.session.selection.off("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},e.prototype.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n1?e.multiSelect.joinSelections():e.multiSelect.splitIntoLines()},bindKey:{win:"Ctrl-Alt-L",mac:"Ctrl-Alt-L"},readOnly:!0},{name:"splitSelectionIntoLines",description:"Split into lines",exec:function(e){e.multiSelect.splitIntoLines()},readOnly:!0},{name:"alignCursors",description:"Align cursors",exec:function(e){e.alignCursors()},bindKey:{win:"Ctrl-Alt-A",mac:"Ctrl-Alt-A"},scrollIntoView:"cursor"},{name:"findAll",description:"Find all",exec:function(e){e.findAll()},bindKey:{win:"Ctrl-Alt-K",mac:"Ctrl-Alt-G"},scrollIntoView:"cursor",readOnly:!0}],t.multiSelectCommands=[{name:"singleSelection",description:"Single selection",bindKey:"esc",exec:function(e){e.exitMultiSelectMode()},scrollIntoView:"cursor",readOnly:!0,isAvailable:function(e){return e&&e.inMultiSelectMode}}];var r=e("../keyboard/hash_handler").HashHandler;t.keyboardHandler=new r(t.multiSelectCommands)}),define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/lib/event","ace/lib/lang","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor","ace/config"],function(e,t,n){function h(e,t,n){return c.$options.wrap=!0,c.$options.needle=t,c.$options.backwards=n==-1,c.find(e)}function v(e,t){return e.row==t.row&&e.column==t.column}function m(e){if(e.$multiselectOnSessionChange)return;e.$onAddRange=e.$onAddRange.bind(e),e.$onRemoveRange=e.$onRemoveRange.bind(e),e.$onMultiSelect=e.$onMultiSelect.bind(e),e.$onSingleSelect=e.$onSingleSelect.bind(e),e.$multiselectOnSessionChange=t.onSessionChange.bind(e),e.$checkMultiselectChange=e.$checkMultiselectChange.bind(e),e.$multiselectOnSessionChange(e),e.on("changeSession",e.$multiselectOnSessionChange),e.on("mousedown",o),e.commands.addCommands(f.defaultCommands),g(e)}function g(e){function r(t){n&&(e.renderer.setMouseCursor(""),n=!1)}if(!e.textInput)return;var t=e.textInput.getElement(),n=!1;u.addListener(t,"keydown",function(t){var i=t.keyCode==18&&!(t.ctrlKey||t.shiftKey||t.metaKey);e.$blockSelectEnabled&&i?n||(e.renderer.setMouseCursor("crosshair"),n=!0):n&&r()},e),u.addListener(t,"keyup",r,e),u.addListener(t,"blur",r,e)}var r=e("./range_list").RangeList,i=e("./range").Range,s=e("./selection").Selection,o=e("./mouse/multi_select_handler").onMouseDown,u=e("./lib/event"),a=e("./lib/lang"),f=e("./commands/multi_select_commands");t.commands=f.defaultCommands.concat(f.multiSelectCommands);var l=e("./search").Search,c=new l,p=e("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(p.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(e,t){if(!e)return;if(!this.inMultiSelectMode&&this.rangeCount===0){var n=this.toOrientedRange();this.rangeList.add(n),this.rangeList.add(e);if(this.rangeList.ranges.length!=2)return this.rangeList.removeAll(),t||this.fromOrientedRange(e);this.rangeList.removeAll(),this.rangeList.add(n),this.$onAddRange(n)}e.cursor||(e.cursor=e.end);var r=this.rangeList.add(e);return this.$onAddRange(e),r.length&&this.$onRemoveRange(r),this.rangeCount>1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length&&this.$onRemoveRange(e)},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){var e=this.ranges.length?this.ranges:[this.getRange()],t=[];for(var n=0;n1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.cursor),s=this.session.documentToScreenPosition(this.anchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)g--;if(g>0){var y=0;while(r[y].isEmpty())y++}for(var b=g;b>=y;b--)r[b].isEmpty()&&r.splice(b,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges(),u.ranges[0]&&u.fromOrientedRange(u.ranges[0]);var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),st[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?this.on("mousedown",o):this.off("mousedown",o)},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++tf){var p=e.getLine(l).length;return new r(f,u,l,p)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range","ace/config"],function(e,t,n){"use strict";function u(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function a(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=u(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var o=r[i];if(!o||!n)return;if(o.row===t){do o=r[i+=n];while(o&&o.row===t);if(!o)return r.slice()}var a=[];t=o.row;do a[n<0?"unshift":"push"](o),o=r[i+=n];while(o&&o.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range,o=e("../config").nls;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),u=s.row,f=n.widgetManager.getWidgetsAtRow(u).filter(function(e){return e.type=="errorMarker"})[0];f?f.destroy():u-=t;var l=a(n,u,t),c;if(l){var h=l[0];s.column=(h.pos&&typeof h.column!="number"?h.pos.sc:h.column)||0,s.row=h.row,c=e.renderer.$gutterLayer.$annotations[s.row]}else{if(f)return;c={text:[o("Looks good!")],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var p={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},d=p.el.appendChild(i.createElement("div")),v=p.el.appendChild(i.createElement("div"));v.className="error_widget_arrow "+c.className;var m=e.renderer.$cursorLayer.getPixelPosition(s).left;v.style.left=m+e.renderer.gutterWidth-5+"px",p.el.className="error_widget_wrapper",d.className="error_widget "+c.className,d.innerHTML=c.text.join("
    "),d.appendChild(i.createElement("div"));var g=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return p.destroy(),{command:"null"}};p.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(g),n.widgetManager.removeLineWidget(p),e.off("changeSelection",p.destroy),e.off("changeSession",p.destroy),e.off("mouseup",p.destroy),e.off("change",p.destroy)},e.keyBinding.addKeyboardHandler(g),e.on("changeSelection",p.destroy),e.on("changeSession",p.destroy),e.on("mouseup",p.destroy),e.on("change",p.destroy),e.session.widgetManager.addLineWidget(p),p.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:p.el.offsetHeight})},i.importCssString("\n .error_widget_wrapper {\n background: inherit;\n color: inherit;\n border:none\n }\n .error_widget {\n border-top: solid 2px;\n border-bottom: solid 2px;\n margin: 5px 0;\n padding: 10px 40px;\n white-space: pre-wrap;\n }\n .error_widget.ace_error, .error_widget_arrow.ace_error{\n border-color: #ff5a5a\n }\n .error_widget.ace_warning, .error_widget_arrow.ace_warning{\n border-color: #F1D817\n }\n .error_widget.ace_info, .error_widget_arrow.ace_info{\n border-color: #5a5a5a\n }\n .error_widget.ace_ok, .error_widget_arrow.ace_ok{\n border-color: #5aaa5a\n }\n .error_widget_arrow {\n position: absolute;\n border: solid 5px;\n border-top-color: transparent!important;\n border-right-color: transparent!important;\n border-left-color: transparent!important;\n top: -5px;\n }\n","error_marker.css",!1)}),define("ace/ace",["require","exports","module","ace/lib/dom","ace/range","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config","ace/loader_build"],function(e,t,n){"use strict";e("./loader_build")(t);var r=e("./lib/dom"),i=e("./range").Range,s=e("./editor").Editor,o=e("./edit_session").EditSession,u=e("./undomanager").UndoManager,a=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.edit=function(e,n){if(typeof e=="string"){var i=e;e=document.getElementById(i);if(!e)throw new Error("ace.edit can't find div #"+i)}if(e&&e.env&&e.env.editor instanceof s)return e.env.editor;var o="";if(e&&/input|textarea/i.test(e.tagName)){var u=e;o=u.value,e=r.createElement("pre"),u.parentNode.replaceChild(e,u)}else e&&(o=e.textContent,e.innerHTML="");var f=t.createEditSession(o),l=new s(new a(e),f,n),c={document:f,editor:l,onResize:l.resize.bind(l,null)};return u&&(c.textarea=u),l.on("destroy",function(){c.editor.container.env=null}),l.container.env=l.env=c,l},t.createEditSession=function(e,t){var n=new o(e,t);return n.setUndoManager(new u),n},t.Range=i,t.Editor=s,t.EditSession=o,t.UndoManager=u,t.VirtualRenderer=a,t.version=t.config.version}); (function() { window.require(["ace/ace"], function(a) { if (a) { a.config.init(true); a.define = window.define; } if (!window.ace) window.ace = a; for (var key in a) if (a.hasOwnProperty(key)) window.ace[key] = a[key]; window.ace["default"] = window.ace; if (typeof module == "object" && typeof exports == "object" && module) { module.exports = window.ace; } }); })(); ================================================ FILE: web/ace-builds/ext-language_tools.js ================================================ define("ace/snippets",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event_emitter","ace/lib/lang","ace/range","ace/range_list","ace/keyboard/hash_handler","ace/tokenizer","ace/clipboard","ace/editor"],function(e,t,n){"use strict";function p(e){var t=(new Date).toLocaleString("en-us",e);return t.length==1?"0"+t:t}var r=e("./lib/dom"),i=e("./lib/oop"),s=e("./lib/event_emitter").EventEmitter,o=e("./lib/lang"),u=e("./range").Range,a=e("./range_list").RangeList,f=e("./keyboard/hash_handler").HashHandler,l=e("./tokenizer").Tokenizer,c=e("./clipboard"),h={CURRENT_WORD:function(e){return e.session.getTextRange(e.session.getWordRange())},SELECTION:function(e,t,n){var r=e.session.getTextRange();return n?r.replace(/\n\r?([ \t]*\S)/g,"\n"+n+"$1"):r},CURRENT_LINE:function(e){return e.session.getLine(e.getCursorPosition().row)},PREV_LINE:function(e){return e.session.getLine(e.getCursorPosition().row-1)},LINE_INDEX:function(e){return e.getCursorPosition().row},LINE_NUMBER:function(e){return e.getCursorPosition().row+1},SOFT_TABS:function(e){return e.session.getUseSoftTabs()?"YES":"NO"},TAB_SIZE:function(e){return e.session.getTabSize()},CLIPBOARD:function(e){return c.getText&&c.getText()},FILENAME:function(e){return/[^/\\]*$/.exec(this.FILEPATH(e))[0]},FILENAME_BASE:function(e){return/[^/\\]*$/.exec(this.FILEPATH(e))[0].replace(/\.[^.]*$/,"")},DIRECTORY:function(e){return this.FILEPATH(e).replace(/[^/\\]*$/,"")},FILEPATH:function(e){return"/not implemented.txt"},WORKSPACE_NAME:function(){return"Unknown"},FULLNAME:function(){return"Unknown"},BLOCK_COMMENT_START:function(e){var t=e.session.$mode||{};return t.blockComment&&t.blockComment.start||""},BLOCK_COMMENT_END:function(e){var t=e.session.$mode||{};return t.blockComment&&t.blockComment.end||""},LINE_COMMENT:function(e){var t=e.session.$mode||{};return t.lineCommentStart||""},CURRENT_YEAR:p.bind(null,{year:"numeric"}),CURRENT_YEAR_SHORT:p.bind(null,{year:"2-digit"}),CURRENT_MONTH:p.bind(null,{month:"numeric"}),CURRENT_MONTH_NAME:p.bind(null,{month:"long"}),CURRENT_MONTH_NAME_SHORT:p.bind(null,{month:"short"}),CURRENT_DATE:p.bind(null,{day:"2-digit"}),CURRENT_DAY_NAME:p.bind(null,{weekday:"long"}),CURRENT_DAY_NAME_SHORT:p.bind(null,{weekday:"short"}),CURRENT_HOUR:p.bind(null,{hour:"2-digit",hour12:!1}),CURRENT_MINUTE:p.bind(null,{minute:"2-digit"}),CURRENT_SECOND:p.bind(null,{second:"2-digit"})};h.SELECTED_TEXT=h.SELECTION;var d=function(){function e(){this.snippetMap={},this.snippetNameMap={},this.variables=h}return e.prototype.getTokenizer=function(){return e.$tokenizer||this.createTokenizer()},e.prototype.createTokenizer=function(){function t(e){return e=e.substr(1),/^\d+$/.test(e)?[{tabstopId:parseInt(e,10)}]:[{text:e}]}function n(e){return"(?:[^\\\\"+e+"]|\\\\.)"}var r={regex:"/("+n("/")+"+)/",onMatch:function(e,t,n){var r=n[0];return r.fmtString=!0,r.guard=e.slice(1,-1),r.flag="",""},next:"formatString"};return e.$tokenizer=new l({start:[{regex:/\\./,onMatch:function(e,t,n){var r=e[1];return r=="}"&&n.length?e=r:"`$\\".indexOf(r)!=-1&&(e=r),[e]}},{regex:/}/,onMatch:function(e,t,n){return[n.length?n.shift():e]}},{regex:/\$(?:\d+|\w+)/,onMatch:t},{regex:/\$\{[\dA-Z_a-z]+/,onMatch:function(e,n,r){var i=t(e.substr(1));return r.unshift(i[0]),i},next:"snippetVar"},{regex:/\n/,token:"newline",merge:!1}],snippetVar:[{regex:"\\|"+n("\\|")+"*\\|",onMatch:function(e,t,n){var r=e.slice(1,-1).replace(/\\[,|\\]|,/g,function(e){return e.length==2?e[1]:"\0"}).split("\0").map(function(e){return{value:e}});return n[0].choices=r,[r[0]]},next:"start"},r,{regex:"([^:}\\\\]|\\\\.)*:?",token:"",next:"start"}],formatString:[{regex:/:/,onMatch:function(e,t,n){return n.length&&n[0].expectElse?(n[0].expectElse=!1,n[0].ifEnd={elseEnd:n[0]},[n[0].ifEnd]):":"}},{regex:/\\./,onMatch:function(e,t,n){var r=e[1];return r=="}"&&n.length?e=r:"`$\\".indexOf(r)!=-1?e=r:r=="n"?e="\n":r=="t"?e=" ":"ulULE".indexOf(r)!=-1&&(e={changeCase:r,local:r>"a"}),[e]}},{regex:"/\\w*}",onMatch:function(e,t,n){var r=n.shift();return r&&(r.flag=e.slice(1,-1)),this.next=r&&r.tabstopId?"start":"",[r||e]},next:"start"},{regex:/\$(?:\d+|\w+)/,onMatch:function(e,t,n){return[{text:e.slice(1)}]}},{regex:/\${\w+/,onMatch:function(e,t,n){var r={text:e.slice(2)};return n.unshift(r),[r]},next:"formatStringVar"},{regex:/\n/,token:"newline",merge:!1},{regex:/}/,onMatch:function(e,t,n){var r=n.shift();return this.next=r&&r.tabstopId?"start":"",[r||e]},next:"start"}],formatStringVar:[{regex:/:\/\w+}/,onMatch:function(e,t,n){var r=n[0];return r.formatFunction=e.slice(2,-1),[n.shift()]},next:"formatString"},r,{regex:/:[\?\-+]?/,onMatch:function(e,t,n){e[1]=="+"&&(n[0].ifEnd=n[0]),e[1]=="?"&&(n[0].expectElse=!0)},next:"formatString"},{regex:"([^:}\\\\]|\\\\.)*:?",token:"",next:"formatString"}]}),e.$tokenizer},e.prototype.tokenizeTmSnippet=function(e,t){return this.getTokenizer().getLineTokens(e,t).tokens.map(function(e){return e.value||e})},e.prototype.getVariableValue=function(e,t,n){if(/^\d+$/.test(t))return(this.variables.__||{})[t]||"";if(/^[A-Z]\d+$/.test(t))return(this.variables[t[0]+"__"]||{})[t.substr(1)]||"";t=t.replace(/^TM_/,"");if(!this.variables.hasOwnProperty(t))return"";var r=this.variables[t];return typeof r=="function"&&(r=this.variables[t](e,t,n)),r==null?"":r},e.prototype.tmStrFormat=function(e,t,n){if(!t.fmt)return e;var r=t.flag||"",i=t.guard;i=new RegExp(i,r.replace(/[^gim]/g,""));var s=typeof t.fmt=="string"?this.tokenizeTmSnippet(t.fmt,"formatString"):t.fmt,o=this,u=e.replace(i,function(){var e=o.variables.__;o.variables.__=[].slice.call(arguments);var t=o.resolveVariables(s,n),r="E";for(var i=0;i=0&&s.splice(o,1)}}var n=this.snippetMap,r=this.snippetNameMap;e.content?i(e):Array.isArray(e)&&e.forEach(i)},e.prototype.parseSnippetFile=function(e){e=e.replace(/\r/g,"");var t=[],n={},r=/^#.*|^({[\s\S]*})\s*$|^(\S+) (.*)$|^((?:\n*\t.*)+)/gm,i;while(i=r.exec(e)){if(i[1])try{n=JSON.parse(i[1]),t.push(n)}catch(s){}if(i[4])n.content=i[4].replace(/^\t/gm,""),t.push(n),n={};else{var o=i[2],u=i[3];if(o=="regex"){var a=/\/((?:[^\/\\]|\\.)*)|$/g;n.guard=a.exec(u)[1],n.trigger=a.exec(u)[1],n.endTrigger=a.exec(u)[1],n.endGuard=a.exec(u)[1]}else o=="snippet"?(n.tabTrigger=u.match(/^\S*/)[0],n.name||(n.name=u)):o&&(n[o]=u)}}return t},e.prototype.getSnippetByName=function(e,t){var n=this.snippetNameMap,r;return this.getActiveScopes(t).some(function(t){var i=n[t];return i&&(r=i[e]),!!r},this),r},e}();i.implement(d.prototype,s);var v=function(e,t,n){function l(e){var t=[];for(var n=0;n1?(y=t[t.length-1].length,g+=t.length-1):y+=e.length,b+=e}else e&&(e.start?e.end={row:g,column:y}:e.start={row:g,column:y})}),{text:b,tabstops:a,tokens:u}},m=function(){function e(e){this.index=0,this.ranges=[],this.tabstops=[];if(e.tabstopManager)return e.tabstopManager;e.tabstopManager=this,this.$onChange=this.onChange.bind(this),this.$onChangeSelection=o.delayedCall(this.onChangeSelection.bind(this)).schedule,this.$onChangeSession=this.onChangeSession.bind(this),this.$onAfterExec=this.onAfterExec.bind(this),this.attach(e)}return e.prototype.attach=function(e){this.$openTabstops=null,this.selectedTabstop=null,this.editor=e,this.session=e.session,this.editor.on("change",this.$onChange),this.editor.on("changeSelection",this.$onChangeSelection),this.editor.on("changeSession",this.$onChangeSession),this.editor.commands.on("afterExec",this.$onAfterExec),this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler)},e.prototype.detach=function(){this.tabstops.forEach(this.removeTabstopMarkers,this),this.ranges.length=0,this.tabstops.length=0,this.selectedTabstop=null,this.editor.off("change",this.$onChange),this.editor.off("changeSelection",this.$onChangeSelection),this.editor.off("changeSession",this.$onChangeSession),this.editor.commands.off("afterExec",this.$onAfterExec),this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler),this.editor.tabstopManager=null,this.session=null,this.editor=null},e.prototype.onChange=function(e){var t=e.action[0]=="r",n=this.selectedTabstop||{},r=n.parents||{},i=this.tabstops.slice();for(var s=0;s2&&(this.tabstops.length&&o.push(o.splice(2,1)[0]),this.tabstops.splice.apply(this.tabstops,o))},e.prototype.addTabstopMarkers=function(e){var t=this.session;e.forEach(function(e){e.markerId||(e.markerId=t.addMarker(e,"ace_snippet-marker","text"))})},e.prototype.removeTabstopMarkers=function(e){var t=this.session;e.forEach(function(e){t.removeMarker(e.markerId),e.markerId=null})},e.prototype.removeRange=function(e){var t=e.tabstop.indexOf(e);t!=-1&&e.tabstop.splice(t,1),t=this.ranges.indexOf(e),t!=-1&&this.ranges.splice(t,1),t=e.tabstop.rangeList.ranges.indexOf(e),t!=-1&&e.tabstop.splice(t,1),this.session.removeMarker(e.markerId),e.tabstop.length||(t=this.tabstops.indexOf(e.tabstop),t!=-1&&this.tabstops.splice(t,1),this.tabstops.length||this.detach())},e}();m.prototype.keyboardHandler=new f,m.prototype.keyboardHandler.bindKeys({Tab:function(e){if(t.snippetManager&&t.snippetManager.expandWithTab(e))return;e.tabstopManager.tabNext(1),e.renderer.scrollCursorIntoView()},"Shift-Tab":function(e){e.tabstopManager.tabNext(-1),e.renderer.scrollCursorIntoView()},Esc:function(e){e.tabstopManager.detach()}});var g=function(e,t){e.row==0&&(e.column+=t.column),e.row+=t.row},y=function(e,t){e.row==t.row&&(e.column-=t.column),e.row-=t.row};r.importCssString("\n.ace_snippet-marker {\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n background: rgba(194, 193, 208, 0.09);\n border: 1px dotted rgba(211, 208, 235, 0.62);\n position: absolute;\n}","snippets.css",!1),t.snippetManager=new d;var b=e("./editor").Editor;(function(){this.insertSnippet=function(e,n){return t.snippetManager.insertSnippet(this,e,n)},this.expandSnippet=function(e){return t.snippetManager.expandWithTab(this,e)}}).call(b.prototype)}),define("ace/autocomplete/popup",["require","exports","module","ace/virtual_renderer","ace/editor","ace/range","ace/lib/event","ace/lib/lang","ace/lib/dom","ace/config"],function(e,t,n){"use strict";var r=e("../virtual_renderer").VirtualRenderer,i=e("../editor").Editor,s=e("../range").Range,o=e("../lib/event"),u=e("../lib/lang"),a=e("../lib/dom"),f=e("../config").nls,l=function(e){return"suggest-aria-id:".concat(e)},c=function(e){var t=new r(e);t.$maxLines=4;var n=new i(t);return n.setHighlightActiveLine(!1),n.setShowPrintMargin(!1),n.renderer.setShowGutter(!1),n.renderer.setHighlightGutterLine(!1),n.$mouseHandler.$focusTimeout=0,n.$highlightTagPending=!0,n},h=function(){function e(e){var t=a.createElement("div"),n=new c(t);e&&e.appendChild(t),t.style.display="none",n.renderer.content.style.cursor="default",n.renderer.setStyle("ace_autocomplete"),n.renderer.container.setAttribute("role","listbox"),n.renderer.container.setAttribute("aria-label",f("Autocomplete suggestions")),n.setOption("displayIndentGuides",!1),n.setOption("dragDelay",150);var r=function(){};n.focus=r,n.$isFocused=!0,n.renderer.$cursorLayer.restartTimer=r,n.renderer.$cursorLayer.element.style.opacity=0,n.renderer.$maxLines=8,n.renderer.$keepTextAreaAtCursor=!1,n.setHighlightActiveLine(!1),n.session.highlight(""),n.session.$searchHighlight.clazz="ace_highlight-marker",n.on("mousedown",function(e){var t=e.getDocumentPosition();n.selection.moveToPosition(t),p.start.row=p.end.row=t.row,e.stop()});var i,h=new s(-1,0,-1,Infinity),p=new s(-1,0,-1,Infinity);p.id=n.session.addMarker(p,"ace_active-line","fullLine"),n.setSelectOnHover=function(e){e?h.id&&(n.session.removeMarker(h.id),h.id=null):h.id=n.session.addMarker(h,"ace_line-hover","fullLine")},n.setSelectOnHover(!1),n.on("mousemove",function(e){if(!i){i=e;return}if(i.x==e.x&&i.y==e.y)return;i=e,i.scrollTop=n.renderer.scrollTop;var t=i.getDocumentPosition().row;h.start.row!=t&&(h.id||n.setRow(t),v(t))}),n.renderer.on("beforeRender",function(){if(i&&h.start.row!=-1){i.$pos=null;var e=i.getDocumentPosition().row;h.id||n.setRow(e),v(e,!0)}}),n.renderer.on("afterRender",function(){var e=n.getRow(),t=n.renderer.$textLayer,r=t.element.childNodes[e-t.config.firstRow],i=document.activeElement;r!==t.selectedNode&&t.selectedNode&&(a.removeCssClass(t.selectedNode,"ace_selected"),i.removeAttribute("aria-activedescendant"),t.selectedNode.removeAttribute("id")),t.selectedNode=r;if(r){a.addCssClass(r,"ace_selected");var s=l(e);r.id=s,n.renderer.container.setAttribute("aria-activedescendant",s),i.setAttribute("aria-activedescendant",s),r.setAttribute("role","option"),r.setAttribute("aria-label",n.getData(e).value),r.setAttribute("aria-setsize",n.data.length),r.setAttribute("aria-posinset",e),r.setAttribute("aria-describedby","doc-tooltip")}});var d=function(){v(-1)},v=function(e,t){e!==h.start.row&&(h.start.row=h.end.row=e,t||n.session._emit("changeBackMarker"),n._emit("changeHoverMarker"))};n.getHoveredRow=function(){return h.start.row},o.addListener(n.container,"mouseout",d),n.on("hide",d),n.on("changeSelection",d),n.session.doc.getLength=function(){return n.data.length},n.session.doc.getLine=function(e){var t=n.data[e];return typeof t=="string"?t:t&&t.value||""};var m=n.session.bgTokenizer;return m.$tokenizeRow=function(e){function s(e,n){e&&r.push({type:(t.className||"")+(n||""),value:e})}var t=n.data[e],r=[];if(!t)return r;typeof t=="string"&&(t={value:t});var i=t.caption||t.value||t.name,o=i.toLowerCase(),u=(n.filterText||"").toLowerCase(),a=0,f=0;for(var l=0;l<=u.length;l++)if(l!=f&&(t.matchMask&1<=l?r="bottom":r="top"),r==="top"?(c.bottom=e.top-this.$borderSize,c.top=c.bottom-l):r==="bottom"&&(c.top=e.top+t+this.$borderSize,c.bottom=c.top+l);var d=c.top>=0&&c.bottom<=u;if(!s&&!d)return!1;d?f.$maxPixelHeight=null:r==="top"?f.$maxPixelHeight=p:f.$maxPixelHeight=h,r==="top"?(o.style.top="",o.style.bottom=u-c.bottom+"px",n.isTopdown=!1):(o.style.top=c.top+"px",o.style.bottom="",n.isTopdown=!0),o.style.display="";var v=e.left;return v+o.offsetWidth>a&&(v=a-o.offsetWidth),o.style.left=v+"px",o.style.right="",n.isOpen||(n.isOpen=!0,this._signal("show"),i=null),n.anchorPos=e,n.anchor=r,!0},n.show=function(e,t,n){this.tryShow(e,t,n?"bottom":undefined,!0)},n.goTo=function(e){var t=this.getRow(),n=this.session.getLength()-1;switch(e){case"up":t=t<=0?n:t-1;break;case"down":t=t>=n?-1:t+1;break;case"start":t=0;break;case"end":t=n}this.setRow(t)},n.getTextLeftOffset=function(){return this.$borderSize+this.renderer.$padding+this.$imageSize},n.$imageSize=0,n.$borderSize=1,n}return e}();a.importCssString("\n.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\n background-color: #CAD6FA;\n z-index: 1;\n}\n.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\n background-color: #3a674e;\n}\n.ace_editor.ace_autocomplete .ace_line-hover {\n border: 1px solid #abbffe;\n margin-top: -1px;\n background: rgba(233,233,253,0.4);\n position: absolute;\n z-index: 2;\n}\n.ace_dark.ace_editor.ace_autocomplete .ace_line-hover {\n border: 1px solid rgba(109, 150, 13, 0.8);\n background: rgba(58, 103, 78, 0.62);\n}\n.ace_completion-meta {\n opacity: 0.5;\n margin: 0 0.9em;\n}\n.ace_completion-message {\n color: blue;\n}\n.ace_editor.ace_autocomplete .ace_completion-highlight{\n color: #2d69c7;\n}\n.ace_dark.ace_editor.ace_autocomplete .ace_completion-highlight{\n color: #93ca12;\n}\n.ace_editor.ace_autocomplete {\n width: 300px;\n z-index: 200000;\n border: 1px lightgray solid;\n position: fixed;\n box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n line-height: 1.4;\n background: #fefefe;\n color: #111;\n}\n.ace_dark.ace_editor.ace_autocomplete {\n border: 1px #484747 solid;\n box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.51);\n line-height: 1.4;\n background: #25282c;\n color: #c1c1c1;\n}\n.ace_autocomplete_right .ace_text-layer {\n width: calc(100% - 8px);\n}\n.ace_autocomplete_right .ace_line {\n display: flex;\n}\n.ace_autocomplete_right .ace_completion-spacer {\n flex: 1;\n}\n","autocompletion.css",!1),t.AcePopup=h,t.$singleLineEditor=c,t.getAriaId=l}),define("ace/autocomplete/inline",["require","exports","module","ace/snippets"],function(e,t,n){"use strict";var r=e("../snippets").snippetManager,i=function(){function e(){this.editor=null}return e.prototype.show=function(e,t,n){n=n||"",e&&this.editor&&this.editor!==e&&(this.hide(),this.editor=null);if(!e||!t)return!1;var i=t.snippet?r.getDisplayTextForSnippet(e,t.snippet):t.value;return!i||!i.startsWith(n)?!1:(this.editor=e,i=i.slice(n.length),i===""?e.removeGhostText():e.setGhostText(i),!0)},e.prototype.isOpen=function(){return this.editor?!!this.editor.renderer.$ghostText:!1},e.prototype.hide=function(){return this.editor?(this.editor.removeGhostText(),!0):!1},e.prototype.destroy=function(){this.hide(),this.editor=null},e}();t.AceInline=i}),define("ace/autocomplete/util",["require","exports","module"],function(e,t,n){"use strict";t.parForEach=function(e,t,n){var r=0,i=e.length;i===0&&n();for(var s=0;s=0;s--){if(!n.test(e[s]))break;i.push(e[s])}return i.reverse().join("")},t.retrieveFollowingIdentifier=function(e,t,n){n=n||r;var i=[];for(var s=t;sthis.filterText&&e.lastIndexOf(this.filterText,0)===0)var t=this.filtered;else var t=this.all;this.filterText=e,t=this.filterCompletions(t,this.filterText),t=t.sort(function(e,t){return t.exactMatch-e.exactMatch||t.$score-e.$score||(e.caption||e.value).localeCompare(t.caption||t.value)});var n=null;t=t.filter(function(e){var t=e.snippet||e.caption||e.value;return t===n?!1:(n=t,!0)}),this.filtered=t},e.prototype.filterCompletions=function(e,t){var n=[],r=t.toUpperCase(),i=t.toLowerCase();e:for(var s=0,o;o=e[s];s++){var u=!this.ignoreCaption&&o.caption||o.value||o.snippet;if(!u)continue;var a=-1,f=0,l=0,c,h;if(this.exactMatch){if(t!==u.substr(0,t.length))continue e}else{var p=u.toLowerCase().indexOf(i);if(p>-1)l=p;else for(var d=0;d=0?m<0||v0&&(a===-1&&(l+=10),l+=h,f|=1<",o.escapeHTML(e.caption),"","
    ",o.escapeHTML(l(e.snippet))].join(""))},id:"snippetCompleter"},h=[c,a,f];t.setCompleters=function(e){h.length=0,e&&h.push.apply(h,e)},t.addCompleter=function(e){h.push(e)},t.textCompleter=a,t.keyWordCompleter=f,t.snippetCompleter=c;var p={name:"expandSnippet",exec:function(e){return r.expandWithTab(e)},bindKey:"Tab"},d=function(e,t){v(t.session.$mode)},v=function(e){typeof e=="string"&&(e=s.$modes[e]);if(!e)return;r.files||(r.files={}),m(e.$id,e.snippetFileId),e.modes&&e.modes.forEach(v)},m=function(e,t){if(!t||!e||r.files[e])return;r.files[e]={},s.loadModule(t,function(t){if(!t)return;r.files[e]=t,!t.snippets&&t.snippetText&&(t.snippets=r.parseSnippetFile(t.snippetText)),r.register(t.snippets||[],t.scope),t.includeScopes&&(r.snippetMap[t.scope].includeScopes=t.includeScopes,t.includeScopes.forEach(function(e){v("ace/mode/"+e)}))})},g=function(e){var t=e.editor,n=t.completer&&t.completer.activated;if(e.command.name==="backspace")n&&!u.getCompletionPrefix(t)&&t.completer.detach();else if(e.command.name==="insertstring"){var r=u.getCompletionPrefix(t),s=u.triggerAutocomplete(t);if((r||s)&&!n){var o=i.for(t);o.autoShown=!0,o.showPopup(t)}}},y=e("../editor").Editor;e("../config").defineOptions(y.prototype,"editor",{enableBasicAutocompletion:{set:function(e){e?(this.completers||(this.completers=Array.isArray(e)?e:h),this.commands.addCommand(i.startCommand)):this.commands.removeCommand(i.startCommand)},value:!1},enableLiveAutocompletion:{set:function(e){e?(this.completers||(this.completers=Array.isArray(e)?e:h),this.commands.on("afterExec",g)):this.commands.removeListener("afterExec",g)},value:!1},enableSnippets:{set:function(e){e?(this.commands.addCommand(p),this.on("changeMode",d),d(null,this)):(this.commands.removeCommand(p),this.off("changeMode",d))},value:!1}})}); (function() { window.require(["ace/ext/language_tools"], function(m) { if (typeof module == "object" && typeof exports == "object" && module) { module.exports = m; } }); })(); ================================================ FILE: web/ace-builds/mode-mocodo.js ================================================ define("ace/mode/mocodo_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module){"use strict"; var oop = require("../lib/oop"); var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; var MocodoHighlightRules = function() { // regexp must not have capturing parentheses. Use (?:) instead. // regexps are ordered -> the first match is used this.$rules = { start: [{ include: "#indent" }, { include: "#comment" }, { include: "#phantoms" }, { include: "#constraint_clause" }, { include: "#inheritance_clause" }, { include: "#association_clause" }, { include: "#entity_clause" }, { include: "#invalid" }], "#indent": [{ token: "text", regex: /^\s+/ }], "#comment": [{ token: "comment.line.magic.mocodo markup.bold", regex: /%%mocodo\b.*$/ }, { token: "comment.line.normal.mocodo", regex: /%.*$/ }], "#phantoms": [{ token: "punctuation.separator.phantom.mocodo markup.italic", regex: /:[:\s]*$/ }], "#association_clause": [{ token: [ "keyword.control.mocodo", "entity.other.attribute-name.association.mocodo markup.bold" ], regex: /((?:\+|-)?)((?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF])[^=<>,:\\]*[^-\[\]\\=<>,:\s\\])(?=\s*,\s*)/, push: [{ token: "association.mocodo", regex: /$|^/, next: "pop" }, { include: "#association_legs" }, { defaultToken: "association.mocodo" }] }], "#association_legs": [{ token: [ "text", "keyword.control.card_hidden.mocodo", "keyword.control.card_prefix.mocodo", "entity.other.attribute-name.cardinality.mocodo", "keyword.control.arrow.mocodo", "text", "invisible.note.mocodo", "text", "heading.entity.mocodo", "text" ], regex: /(\s*,\s*)(?:((?:-)?)((?:[_\/])?)((?![-_\/])[\w?]{2}(?=[\s]*[^\w,\r$:])))?((?:[<>])?)(\s*)((?:\[.*?\])?)(\s*)((?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF])[^=<>,:\\]*[^-\[\]\\=<>,:\s\\])(\s*)/ }, { include: "#association_attrs" }, { include: "#invalid" }], "#association_attrs": [{ token: "attrs.mocodo", regex: /\s*:\s*/, push: [{ token: "attrs.mocodo", regex: /$|^/, next: "pop" }, { include: "#typed_attr" }, { token: "punctuation.separator", regex: /\s*,\s*/ }, { defaultToken: "attrs.mocodo" }] }], "#typed_attr": [{ token: [ "variable.attribute.mocodo", "typed_attribute.mocodo", "variable.parameter.datatype.mocodo markup.italic", "typed_attribute.mocodo" ], regex: /((?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s])(\s*)((?:\[.*?\])?)(\s*)/ }], "#entity_clause": [{ token: [ "keyword.control.mocodo", "heading.entity.mocodo markup.bold", "entity.mocodo" ], regex: /((?:\+|-)?)((?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF])[^=<>,:\\]*[^-\[\]\\=<>,:\s\\])(\s*:\s*)/, push: [{ token: "entity.mocodo", regex: /$|^/, next: "pop" }, { include: "#entity_first_attr" }, { defaultToken: "entity.mocodo" }] }], "#entity_first_attr": [{ token: [ "invisible.id_symbols.mocodo", "variable.attribute.mocodo", "invisible.alt_id_symbols.mocodo", "variable.attribute.mocodo markup.underline", "text", "variable.parameter.datatype.mocodo markup.italic" ], regex: /(?:((?:\d*0\d*_|_))((?:(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]|#(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]\s*>\s*(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]\s*>\s*(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]))|((?:[1-9]+_)?)((?:(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]|#(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]\s*>\s*(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]\s*>\s*(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]))|)(\s*)((?:\[.*?\])?)/, push: [{ token: "text", regex: /$|^/, next: "pop" }, { include: "#entity_next_attrs" }, { include: "#invalid" }] }], "#entity_next_attrs": [{ token: [ "text", "invisible.id_symbols.mocodo", "variable.attribute.mocodo markup.underline", "invisible.alt_id_symbols.mocodo", "variable.attribute.mocodo", "text", "variable.parameter.datatype.mocodo markup.italic" ], regex: /(\s*,\s*)(?:((?:\d*0\d*_|_))((?:(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]|#(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]\s*>\s*(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]\s*>\s*(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]))|((?:[1-9]+_)?)((?:(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]|#(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]\s*>\s*(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]\s*>\s*(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]))|)(\s*)((?:\[.*?\])?)/ }, { include: "#invalid" }], "#constraint_clause": [{ token: [ "support.function.constraint_name.mocodo markup.bold", "constraint.mocodo", "invisible.note.mocodo", "constraint.mocodo" ], regex: /(\([a-zA-Z\u00C0-\u024F\u1E00-\u1EFF\d_\s]{0,3}\))(\s*)((?:\[.*?\])?)(\s*)/, push: [{ token: "constraint.mocodo", regex: /$|^/, next: "pop" }, { include: "#constraint_targets" }, { defaultToken: "constraint.mocodo" }] }], "#constraint_targets": [{ token: [ "string.regexp.constraint_leg.mocodo", "text", "support.type.box.mocodo" ], regex: /((?:?)?)(\s*)((?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF])[^=<>,:\\]*[^-\[\]\\=<>,:\s\\])/ }, { token: "text", regex: /\s*,\s*/ }, { include: "#constraint_coords" }, { include: "#invalid" }], "#constraint_coords": [{ token: [ "coords.mocodo", "invisible.constraint_coords.mocodo markup.italic", "coords.mocodo", "invisible.constraint_coords.mocodo markup.italic" ], regex: /(\s*:\s*)((?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF])[^=<>,:\\]*[^-\[\]\\=<>,:\s\\]))(\s*,\s*)((?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF])[^=<>,:\\]*[^-\[\]\\=<>,:\s\\]))$/ }], "#inheritance_clause": [{ token: [ "entity.other.attribute-name.inheritance.mocodo markup.underline markup.bold", "inheritance.mocodo", "heading.parent.mocodo", "inheritance.mocodo", "keyword.control.arrow.mocodo", "inheritance.mocodo" ], regex: /(\/(?:XT\d?|TX\d?|X\d?|T\d?|\d?)\\{1,2})(\s*)((?:(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF])[^=<>,:\\]*[^-\[\]\\=<>,:\s\\])?)(\s*)((?:<==?|<--?|--?>|==?>))(\s*)/, push: [{ token: "inheritance.mocodo", regex: /$|^/, next: "pop" }, { include: "#inheritance_children" }, { defaultToken: "inheritance.mocodo" }] }], "#inheritance_children": [{ token: "heading.child.mocodo", regex: /(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF])[^=<>,:\\]*[^-\[\]\\=<>,:\s\\]/ }, { token: "text", regex: /\s*,\s*/ }, { include: "#inheritance_attrs" }, { include: "#invalid" }], "#inheritance_attrs": [{ token: "attrs.mocodo", regex: /\s*:\s*/, push: [{ token: "attrs.mocodo", regex: /$|^/, next: "pop" }, { token: "invisible.inheritance_attribute.mocodo markup.italic", regex: /(?=[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF0-9])[^\[\]>,]*[^\[\]>,\s]/ }, { token: "text", regex: /\s*,\s*/ }, { include: "#invalid" }, { defaultToken: "attrs.mocodo" }] }], "#invalid": [{ token: "invalid.illegal.mocodo", regex: /.+/ }] } this.normalizeRules(); }; MocodoHighlightRules.metaData = { name: "Mocodo", scopeName: "source.mocodo" } oop.inherits(MocodoHighlightRules, TextHighlightRules); exports.MocodoHighlightRules = MocodoHighlightRules; }); define("ace/mode/mocodo",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/mocodo_highlight_rules"], function(require, exports, module){"use strict"; var oop = require("../lib/oop"); var TextMode = require("./text").Mode; var MocodoHighlightRules = require("./mocodo_highlight_rules").MocodoHighlightRules; var Mode = function () { this.HighlightRules = MocodoHighlightRules; this.$behaviour = this.$defaultBehaviour; }; oop.inherits(Mode, TextMode); (function () { this.lineCommentStart = "%"; this.$id = "ace/mode/mocodo"; }).call(Mode.prototype); exports.Mode = Mode; }); (function() { window.require(["ace/mode/mocodo"], function(m) { if (typeof module == "object" && typeof exports == "object" && module) { module.exports = m; } }); })(); ================================================ FILE: web/ace-builds/mode-text.js ================================================ ; (function() { window.require(["ace/mode/text"], function(m) { if (typeof module == "object" && typeof exports == "object" && module) { module.exports = m; } }); })(); ================================================ FILE: web/ace-builds/text_highlight_rules.js ================================================ "use strict"; var lang = require("../lib/lang"); var TextHighlightRules = function() { // regexp must not have capturing parentheses // regexps are ordered -> the first match is used this.$rules = { "start" : [{ token : "empty_line", regex : '^$' }, { defaultToken : "text" }] }; }; (function() { this.addRules = function(rules, prefix) { if (!prefix) { for (var key in rules) this.$rules[key] = rules[key]; return; } for (var key in rules) { var state = rules[key]; for (var i = 0; i < state.length; i++) { var rule = state[i]; if (rule.next || rule.onMatch) { if (typeof rule.next == "string") { if (rule.next.indexOf(prefix) !== 0) rule.next = prefix + rule.next; } if (rule.nextState && rule.nextState.indexOf(prefix) !== 0) rule.nextState = prefix + rule.nextState; } } this.$rules[prefix + key] = state; } }; this.getRules = function() { return this.$rules; }; this.embedRules = function (HighlightRules, prefix, escapeRules, states, append) { var embedRules = typeof HighlightRules == "function" ? new HighlightRules().getRules() : HighlightRules; if (states) { for (var i = 0; i < states.length; i++) states[i] = prefix + states[i]; } else { states = []; for (var key in embedRules) states.push(prefix + key); } this.addRules(embedRules, prefix); if (escapeRules) { var addRules = Array.prototype[append ? "push" : "unshift"]; for (var i = 0; i < states.length; i++) addRules.apply(this.$rules[states[i]], lang.deepCopy(escapeRules)); } if (!this.$embeds) this.$embeds = []; this.$embeds.push(prefix); }; this.getEmbeds = function() { return this.$embeds; }; var pushState = function(currentState, stack) { if (currentState != "start" || stack.length) stack.unshift(this.nextState, currentState); return this.nextState; }; var popState = function(currentState, stack) { // if (stack[0] === currentState) stack.shift(); return stack.shift() || "start"; }; this.normalizeRules = function() { var id = 0; var rules = this.$rules; function processState(key) { var state = rules[key]; state.processed = true; for (var i = 0; i < state.length; i++) { var rule = state[i]; var toInsert = null; if (Array.isArray(rule)) { toInsert = rule; rule = {}; } if (!rule.regex && rule.start) { rule.regex = rule.start; if (!rule.next) rule.next = []; rule.next.push({ defaultToken: rule.token }, { token: rule.token + ".end", regex: rule.end || rule.start, next: "pop" }); rule.token = rule.token + ".start"; rule.push = true; } var next = rule.next || rule.push; if (next && Array.isArray(next)) { var stateName = rule.stateName; if (!stateName) { stateName = rule.token; if (typeof stateName != "string") stateName = stateName[0] || ""; if (rules[stateName]) stateName += id++; } rules[stateName] = next; rule.next = stateName; processState(stateName); } else if (next == "pop") { rule.next = popState; } if (rule.push) { rule.nextState = rule.next || rule.push; rule.next = pushState; delete rule.push; } if (rule.rules) { for (var r in rule.rules) { if (rules[r]) { if (rules[r].push) rules[r].push.apply(rules[r], rule.rules[r]); } else { rules[r] = rule.rules[r]; } } } var includeName = typeof rule == "string" ? rule : rule.include; if (includeName) { if (includeName === "$self") includeName = "start"; if (Array.isArray(includeName)) toInsert = includeName.map(function(x) { return rules[x]; }); else toInsert = rules[includeName]; } if (toInsert) { var args = [i, 1].concat(toInsert); if (rule.noEscape) args = args.filter(function(x) {return !x.next;}); state.splice.apply(state, args); // skip included rules since they are already processed //i += args.length - 3; i--; } if (rule.keywordMap) { rule.token = this.createKeywordMapper( rule.keywordMap, rule.defaultToken || "text", rule.caseInsensitive ); delete rule.defaultToken; } } } Object.keys(rules).forEach(processState, this); }; this.createKeywordMapper = function(map, defaultToken, ignoreCase, splitChar) { var keywords = Object.create(null); this.$keywordList = []; Object.keys(map).forEach(function(className) { var a = map[className]; var list = a.split(splitChar || "|"); for (var i = list.length; i--; ) { var word = list[i]; this.$keywordList.push(word); if (ignoreCase) word = word.toLowerCase(); keywords[word] = className; } }, this); map = null; return ignoreCase ? function(value) {return keywords[value.toLowerCase()] || defaultToken; } : function(value) {return keywords[value] || defaultToken; }; }; this.getKeywords = function() { return this.$keywords; }; }).call(TextHighlightRules.prototype); exports.TextHighlightRules = TextHighlightRules; ================================================ FILE: web/ace-builds/theme-chrome.js ================================================ define("ace/theme/chrome.css",["require","exports","module"], function(require, exports, module){module.exports = ".ace-chrome .ace_gutter {\n background: #ebebeb;\n color: #333;\n overflow : hidden;\n}\n\n.ace-chrome .ace_print-margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-chrome {\n background-color: #FFFFFF;\n color: black;\n}\n\n.ace-chrome .ace_cursor {\n color: black;\n}\n\n.ace-chrome .ace_invisible {\n color: rgb(191, 191, 191);\n}\n\n.ace-chrome .ace_constant.ace_buildin {\n color: rgb(88, 72, 246);\n}\n\n.ace-chrome .ace_constant.ace_language {\n color: rgb(88, 92, 246);\n}\n\n.ace-chrome .ace_constant.ace_library {\n color: rgb(6, 150, 14);\n}\n\n.ace-chrome .ace_invalid {\n background-color: rgb(153, 0, 0);\n color: white;\n}\n\n.ace-chrome .ace_fold {\n}\n\n.ace-chrome .ace_support.ace_function {\n color: rgb(60, 76, 114);\n}\n\n.ace-chrome .ace_support.ace_constant {\n color: rgb(6, 150, 14);\n}\n\n.ace-chrome .ace_support.ace_type,\n.ace-chrome .ace_support.ace_class\n.ace-chrome .ace_support.ace_other {\n color: rgb(109, 121, 222);\n}\n\n.ace-chrome .ace_variable.ace_parameter {\n font-style:italic;\n color:#FD971F;\n}\n.ace-chrome .ace_keyword.ace_operator {\n color: rgb(104, 118, 135);\n}\n\n.ace-chrome .ace_comment {\n color: #236e24;\n}\n\n.ace-chrome .ace_comment.ace_doc {\n color: #236e24;\n}\n\n.ace-chrome .ace_comment.ace_doc.ace_tag {\n color: #236e24;\n}\n\n.ace-chrome .ace_constant.ace_numeric {\n color: rgb(0, 0, 205);\n}\n\n.ace-chrome .ace_variable {\n color: rgb(49, 132, 149);\n}\n\n.ace-chrome .ace_xml-pe {\n color: rgb(104, 104, 91);\n}\n\n.ace-chrome .ace_entity.ace_name.ace_function {\n color: #0000A2;\n}\n\n\n.ace-chrome .ace_heading {\n color: rgb(12, 7, 255);\n}\n\n.ace-chrome .ace_list {\n color:rgb(185, 6, 144);\n}\n\n.ace-chrome .ace_marker-layer .ace_selection {\n background: rgb(181, 213, 255);\n}\n\n.ace-chrome .ace_marker-layer .ace_step {\n background: rgb(252, 255, 0);\n}\n\n.ace-chrome .ace_marker-layer .ace_stack {\n background: rgb(164, 229, 101);\n}\n\n.ace-chrome .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgb(192, 192, 192);\n}\n\n.ace-chrome .ace_marker-layer .ace_active-line {\n background: rgba(0, 0, 0, 0.07);\n}\n\n.ace-chrome .ace_gutter-active-line {\n background-color : #dcdcdc;\n}\n\n.ace-chrome .ace_marker-layer .ace_selected-word {\n background: rgb(250, 250, 255);\n border: 1px solid rgb(200, 200, 250);\n}\n\n.ace-chrome .ace_storage,\n.ace-chrome .ace_keyword,\n.ace-chrome .ace_meta.ace_tag {\n color: rgb(147, 15, 128);\n}\n\n.ace-chrome .ace_string.ace_regex {\n color: rgb(255, 0, 0)\n}\n\n.ace-chrome .ace_string {\n color: #1A1AA6;\n}\n\n.ace-chrome .ace_entity.ace_other.ace_attribute-name {\n color: #994409;\n}\n\n.ace-chrome .ace_indent-guide {\n background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\n}\n \n.ace-chrome .ace_indent-guide-active {\n background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAZSURBVHjaYvj///9/hivKyv8BAAAA//8DACLqBhbvk+/eAAAAAElFTkSuQmCC\") right repeat-y;\n}\n"; }); define("ace/theme/chrome",["require","exports","module","ace/theme/chrome.css","ace/lib/dom"], function(require, exports, module){exports.isDark = false; exports.cssClass = "ace-chrome"; exports.cssText = require("./chrome.css"); var dom = require("../lib/dom"); dom.importCssString(exports.cssText, exports.cssClass, false); }); (function() { window.require(["ace/theme/chrome"], function(m) { if (typeof module == "object" && typeof exports == "object" && module) { module.exports = m; } }); })(); ================================================ FILE: web/favicons/browserconfig.xml ================================================ #ffc40d ================================================ FILE: web/favicons/manifest.json ================================================ { "name": "Mocodo", "icons": [ { "src": "\/android-chrome-192x192.png", "sizes": "192x192", "type": "image\/png" }, { "src": "\/android-chrome-512x512.png", "sizes": "512x512", "type": "image\/png" } ], "theme_color": "#fff", "display": "standalone" } ================================================ FILE: web/flashlight.js ================================================ function switchOnFlashlight(event) { document.documentElement.classList.toggle('flashlight'); document.addEventListener('mousemove', trackMouse); document.addEventListener('mousedown', switchOffFlashlight); trackMouse(event); } function switchOffFlashlight() { document.documentElement.classList.toggle('flashlight'); document.removeEventListener('mousemove', trackMouse); document.removeEventListener('mousedown', switchOffFlashlight); document.getElementById('flashlight').checked = false; } function trackMouse(event) { document.documentElement.style.setProperty('--cursorXPos', event.clientX + 'px'); document.documentElement.style.setProperty('--cursorYPos', event.clientY + 'px'); } ================================================ FILE: web/generate.php ================================================ "url", "_data_dict_2.md" => "data_dict:label,type=_Description_", "_data_dict_3.md" => "data_dict", "_mld.html" => "html:e", "_mld.md" => "markdown", "_mld.txt" => "text", "_mld.tex" => "latex:b", "_mld.mcd" => "diagram", "_mld_with_constraints.html" => "html:ce", "_mld_with_constraints.md" => "markdown:c", "_mld_with_constraints.txt" => "text:c", "_mld_with_constraints.tex" => "latex:bc", "_mld_with_constraints.mcd" => "diagram:c", "_dependencies.gv" => "dependencies", "_ddl.sql" => "sql", "_uml.puml" => "uml", "_ddl.dbml" => "dbml", "_prompt_for_types.md" => "prompt:types", "_prompt_for_cards.md" => "prompt:cards", ); // Prevent the automatic substitution of : by / on Mac OS X (IPV6 syntax) $user_path = "/sessions/" . str_replace(":", "_", $_SERVER['REMOTE_ADDR'] . "-" . session_id()); $local_path = __DIR__ . $user_path; // Make a folder for this user file_exists($local_path) or mkdir($local_path) or die('{"err": "PHP: Failed to create user folder."}'); chdir($local_path); $postId = md5(serialize($_POST)); $title = $_POST['title']; // Sanitize the title for filesystem safety $sanitizedTitle = preg_replace('/[\/\\:*?"<>|]/', '-', $title); // Replace invalid characters with '-' $sanitizedTitle = trim($sanitizedTitle); // Remove leading/trailing spaces if (empty($sanitizedTitle) || $sanitizedTitle === '.' || $sanitizedTitle === '..') { $sanitizedTitle = "MCD"; // Default title if invalid $title = "MCD"; }; $_POST['input'] = "{$sanitizedTitle}.mcd"; if ($_POST['png']) { $mocodo .= " --svg_to png"; }; if ($_POST['pdf']) { $mocodo .= " --svg_to pdf"; }; $basthon_source = preg_replace("/\n+$/", "", $_POST['text']); if ($_POST['state']=="moved") { $geo = json_decode(file_get_contents("{$title}_geo.json"),true); $geo['width'] = intval($_POST['width']); $geo['height'] = intval($_POST['height']); foreach ($geo["cx"] as $i => $value) { $geo["cx"][$i] = array($value[0],intval($_POST["cx".$i])); $geo["cy"][$i] = array($value[0],intval($_POST["cy".$i])); }; foreach ($geo["shift"] as $i => $value) { $geo["shift"][$i] = array($value[0],floatval($_POST["shift".$i])); }; foreach ($geo["ratio"] as $i => $value) { $geo["ratio"][$i] = array($value[0],floatval($_POST["ratio".$i])); }; $chan = fopen("{$title}_geo.json", 'w') or die('{"err": "PHP: Can\'t open geo file."}'); fwrite($chan, json_encode($geo, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); fclose($chan); $mocodo .= " --reuse_geo"; } else { // Clean the directory up foreach (glob("*.*") as $filename) { unlink($filename); }; // Create the MCD text file $chan = fopen($_POST['input'], 'w') or die('{"err": "PHP: Can\'t open MCD file."}'); $_POST['text'] = str_replace('"','',$_POST['text']); # double-check js validation $_POST['text'] = str_replace('`','',$_POST['text']); # double-check js validation $_POST['text'] = str_replace("\\'","'",$_POST['text']); # disable http://en.wikipedia.org/wiki/Magic_quotes fwrite($chan, $_POST['text']); fclose($chan); // Prepare the contents of the options file unset($_POST['text']); unset($_POST['state']); $_POST['language'] = 'fr'; $_POST['encodings'] = array("utf8"); // Write it $chan = fopen("params.json", 'w') or die('{"err": "PHP: Can\'t open \'params.json\' file."}'); fwrite($chan, json_encode($_POST)); fclose($chan); }; $basthon_options .= ""; if ($_POST['conversions']) { $transformation_options = ""; $conversions = array(); foreach ($_POST['conversions'] as $ext) { if ($ext == "_ddl.sql") { $transformation_options .= " " . escapeshellarg($_POST["sql_case"]) . ":labels"; }; if ($_POST['with_constraints']) { $option = $transformations[str_replace("_mld", "_mld_with_constraints", $ext)]; } else { $option = $transformations[$ext]; }; $transformation_options .= " " . $option; $conversions[] = $ext; }; $mocodo .= " -t{$transformation_options}"; $basthon_options .= " --select all -t{$transformation_options}"; }; if (!isset($_POST["knowledge"]) || !is_array($_POST["knowledge"]) || !in_array("assoc_ids", $_POST["knowledge"])) { $mocodo .= " --no_assoc_ids"; }; if (isset($_POST["basthon"])) { $basthon_options = str_replace(" html:", " md:", $basthon_options); // patch: since the CSS style is not generated along with the HTML, important features are lost, such as the underlining of primary keys. We use markdown instead. $default_option_values = array( "shapes" => "copperplate", "colors" => "bw", "adjust_width" => "1.00", "fk_format" => "#{label}", "strengthen_card" => "_1,1_", "lib" => null, "seed" => null, ); foreach ($default_option_values as $option => $default_value) { if (isset($_POST[$option]) && ($_POST[$option] != $default_value)) { $basthon_options .= " --{$option}=" . escapeshellarg($_POST[$option]); }; }; $basthon_options = substr($basthon_options, 1); // strip the first space if ($_POST["detect_overlaps"] == "on") { $basthon_options .= " --detect_overlaps"; }; $result = array( "options" => $basthon_options, "source" => $basthon_source, ); echo json_encode($result); exit(); }; // Launch the script $out = array(); $command_line = "{$mocodo} 2>&1 >/dev/null"; exec($command_line, $out); if (!empty($out)) { echo json_encode(array("err" => implode("\n",$out))); exit(); }; // Use the sanitized title for the ZIP file on the server $zipName = $sanitizedTitle . ".zip"; // Create the ZIP archive $zip = new ZipArchive(); if ($zip->open($zipName, ZIPARCHIVE::CREATE) !== TRUE) { die('{"err": "PHP: Can\'t open <{$zipName}>\n"}'); } // Add files to the ZIP archive with error handling $filesToAdd = [ "{$sanitizedTitle}_geo.json", "{$sanitizedTitle}.mcd", "{$sanitizedTitle}.svg", "{$sanitizedTitle}_static.svg", // Optional "{$sanitizedTitle}.png", // Optional "{$sanitizedTitle}.pdf", // Optional ]; foreach ($filesToAdd as $file) { if (file_exists($file)) { $zip->addFile($file); } } foreach ($conversions as $ext) { $file = "{$sanitizedTitle}{$ext}"; if (file_exists($file)) { $zip->addFile($file); } } $zip->close(); // return the response $svg = file_get_contents("{$sanitizedTitle}.svg"); $count = 1; $result = array( "svg" => str_replace('stroke="none" stroke-width="0"', 'stroke="#808080" stroke-width="1" stroke-dasharray="2,2"', $svg, $count), "geo" => file_get_contents("{$sanitizedTitle}_geo.json"), "zipName" => $zipName, "zipURL" => $web_url . $user_path . "/" . $zipName, "conversions" => array(), "title" => $title, ); foreach ($conversions as $ext) { $str = file_get_contents("{$sanitizedTitle}{$ext}"); $result["conversions"][] = array($ext, $str); }; // fwrite($php_log, json_encode($result) . "\n"); echo json_encode($result); ?> ================================================ FILE: web/get_from_lib.php ================================================ [ 'verify_peer' => false, 'verify_peer_name' => false, ], ]); // Try to get the file from the lib url provided by the user $mcd_url = $_POST['lib'] . "/{$title}.mcd"; $contents = file_get_contents($mcd_url) or ""; // If the file is not found, try to get it from mocodo.net if ($contents == "") { $mcd_url = "https://mocodo.net/web/lib/{$title}.mcd"; $contents = file_get_contents($mcd_url) or ""; }; echo $contents; ?> ================================================ FILE: web/js.cookie.js ================================================ /*! * JavaScript Cookie v2.0.0-pre * https://github.com/js-cookie/js-cookie * * Copyright 2006, 2015 Klaus Hartl * Released under the MIT license */ (function (factory) { if (typeof define === 'function' && define.amd) { define(factory); } else if (typeof exports === 'object') { module.exports = factory(); } else { var _OldCookies = window.Cookies; var api = window.Cookies = factory(window.jQuery); api.noConflict = function () { window.Cookies = _OldCookies; return api; }; } }(function () { function extend () { var i = 0; var result = {}; for (; i < arguments.length; i++) { var attributes = arguments[ i ]; for (var key in attributes) { result[key] = attributes[key]; } } return result; } function init (converter) { function api (key, value, attributes) { var result; // Write if (arguments.length > 1) { attributes = extend({ path: '/' }, api.defaults, attributes); if (typeof attributes.expires === 'number') { var expires = new Date(); expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); attributes.expires = expires; } try { result = JSON.stringify(value); if (/^[\{\[]/.test(result)) { value = result; } } catch (e) {} value = encodeURIComponent(String(value)); value = value.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); key = encodeURIComponent(String(key)); key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); key = key.replace(/[\(\)]/g, escape); return (document.cookie = [ key, '=', value, attributes.expires && '; expires=' + attributes.expires.toUTCString(), // use expires attribute, max-age is not supported by IE attributes.path && '; path=' + attributes.path, attributes.domain && '; domain=' + attributes.domain, attributes.secure && '; secure' ].join('')); } // Read if (!key) { result = {}; } // To prevent the for loop in the first place assign an empty array // in case there are no cookies at all. Also prevents odd result when // calling "get()" var cookies = document.cookie ? document.cookie.split('; ') : []; var rdecode = /(%[0-9A-Z]{2})+/g; var i = 0; for (; i < cookies.length; i++) { var parts = cookies[i].split('='); var name = parts[0].replace(rdecode, decodeURIComponent); var cookie = parts.slice(1).join('='); if (cookie.charAt(0) === '"') { cookie = cookie.slice(1, -1); } cookie = converter && converter(cookie, name) || cookie.replace(rdecode, decodeURIComponent); if (this.json) { try { cookie = JSON.parse(cookie); } catch (e) {} } if (key === name) { result = cookie; break; } if (!key) { result[name] = cookie; } } return result; } api.get = api.set = api; api.getJSON = function () { return api.apply({ json: true }, [].slice.call(arguments)); }; api.defaults = {}; api.remove = function (key, attributes) { api(key, '', extend(attributes, { expires: -1 })); }; api.withConverter = init; return api; } return init(); })); ================================================ FILE: web/mocodo.js ================================================ /*global $, jQuery*/ 'use strict'; const openInNewTab = (href) => { const a = document.createElement("a"); a.target = "_blank"; a.rel = "noopener noreferrer"; a.href = href; a.style.display = "none"; document.body.appendChild(a); // Append the link to the document a.click(); // Programmatically click the link document.body.removeChild(a); // Remove the link after clicking }; const basthon_template = JSON.stringify({ cells: [ { metadata: { trusted: true }, cell_type: "code", source: "from mocodo.magic import mocodo", execution_count: null, outputs: [], }, { metadata: { trusted: true }, cell_type: "code", source: 'mocodo("""\n%%mocodo {options}\n{source}\n""")', execution_count: null, outputs: [], }, ], metadata: { kernelspec: { name: "python3", display_name: "Python 3", language: "python", }, }, nbformat: 4, nbformat_minor: 2, }); var request_lock = false; var conversions = { "_url.url": { "default": true, "highlighting": "none", "title": "URL d'une session Mocodo online pré-remplie avec le texte-source de votre MCD.", "name": "Lien de partage du MCD", }, "_mld.mcd": { "default": false, "highlighting": "none", "title": "Résultat à réinjecter sous l'onglet Entrée pour tracer un diagramme sagittal des relations.", "name": "Diagramme relationnel en Mocodo, clés étrangères  ", }, "_mld.html": { "default": false, "highlighting": "markup", "title": "Affiché également au-dessous du diagramme conceptuel. Cliquez sur un schéma de relation pour faire apparaître une explication du passage du MCD au MLD.", "name": "Schéma relationnel expliqué", }, "_prompt_for_types.md": { "default": false, "highlighting": "markdown", "title": "À copier-coller sous l'IA conversationnelle de votre choix pour remplir les types des attributs.", "name": "Prompt de remplissage des types par IA", "advanced": false, }, "_ddl.sql": { "default": false, "highlighting": "sql", "title": "DDL œcuménique, pour peu que vous utilisiez les types requis par le dialecte-cible (MySQL, SQLite, PostgreSQL, Oracle, SQL Server, etc.). Les libellés sont automatiquement privés de leurs accents et espaces pour éviter de polluer le code SQL avec des délimiteurs de chaînes, qui plus est non portables.", "name": "Script SQL de création des tables, libellés en  ", }, "_prompt_for_cards.md": { "default": false, "highlighting": "markdown", "title": "À copier-coller sous l'IA conversationnelle de votre choix pour ajouter les explications qui apparaîtront au survol des cardinalités.", "name": "Prompt d'explication des cardinalités par IA", "advanced": true, }, "_data_dict_2.md": { "default": false, "highlighting": "markdown", "title": "Colonnes : attribut / descriptif.", "name": "Dictionnaire des données sur deux colonnes", "advanced": true, }, "_data_dict_3.md": { "default": false, "highlighting": "markdown", "title": "Colonnes : entité ou association / attribut / type.", "name": "Dictionnaire des données sur trois colonnes", "advanced": true, }, "_dependencies.gv": { "default": false, "highlighting": "graphviz", "title": "Vue simplifiée des contraintes de clés étrangères. Copiez-collez le résultat sur le site donné en lien pour visualiser le diagramme.", "name": "Graphe des dépendances pour Graphviz", "advanced": true, }, "_mld.txt": { "default": false, "highlighting": "txt", "name": "Schéma relationnel en texte brut", "advanced": true, }, "_mld.tex": { "default": false, "highlighting": "tex", "name": "Schéma relationnel en LaTeX", "advanced": true, }, "_mld.md": { "default": false, "highlighting": "markdown", "name": "Schéma relationnel en Markdown", "advanced": true, }, "_uml.puml": { "default": false, "highlighting": "none", "title": "Copiez-collez le résultat sur le site donné en lien pour visualiser le diagramme.", "name": "Diagramme de classes UML pour PlantUML", "advanced": true, }, "_ddl.dbml": { "default": false, "highlighting": "dbml", "title": "DBML (database markup language) est un langage dédié simple et lisible conçu pour définir des structures de base de données.", "name": "Définition de la base en DBML", "advanced": true, }, } var knowledge = { "advanced_tutorial": { "name": "Tutoriel (2/2)", "title": "Cochez pour remplacer la première partie du tutoriel par la seconde.", "default": false, "onchange": "setTutorialKnowledge(event.target.checked)", }, "weak": { "name": "Entité faible (ou identification relative), cardinalités notées  ", "title": "Cochez pour inclure des entités faibles dans les MCD aléatoires et certaines opérations de décomposition. Une entité faible est une entité dont l'identifiant nécessite d'être renforcé par une ou plusieurs entités dont elle dépend fonctionnellement. NB : laisser cette option non cochée ne vous empêchera pas de créer vous-même des entités faibles.", "default": false }, "cluster": { "name": "Agrégation (ou pseudo-entité) / CIF", "title": "Cochez pour inclure une agrégation simple dans les MCD aléatoires et pour ajouter au menu « Révéler » une option permettant de la visualiser comme une Contrainte d'intégrité fonctionnelle. Ces notions voisines traitent du cas où une des entités participant à une association est complètement déterminée par la connaissance d'autres entités participantes : une ou plusieurs dans le cas général, toutes sous Mocodo.", "default": false, "onchange": "setClusterKnowledge(event.target.checked)", }, "constraints": { "name": "Contraintes d'unicité et d'optionalité", "title": "Cochez pour faire apparaître dans le schéma relationnel les contraintes d'unicité en exposant, d'optionalité comme des « ? », et de non-optionalité comme des « ! ». Ces notations sont non standard et peuvent gêner la lecture. NB : quel que soit votre choix, Mocodo ajoute systématiquement les contraintes UNIQUE, NULL ou NOT NULL appropriées dans le code SQL généré.", "default": false }, "assoc_ids": { "name": "Autoriser les identifiants supplémentaires dans les associations", "title": "Cochez pour activer cette possibilité, non prévue par Merise, mais qui permet dans certains cas de produire un même schéma relationnel à partir d'un schéma conceptuel plus simple.", "default": false }, "reproductibility": { "name": "Reproductibilité des tirages pseudo-aléatoires", "title": "Cochez pour que la longueur de la première ligne du texte-source soit prise comme germe du générateur pseudo-aléatoire. Ainsi, les algorithmes randomisés produiront toujours la même sortie sur un même texte-source.", "default": false }, "random": { "name": "Masquage et génération aléatoire", "title": "Cochez pour ajouter un bouton donnant accès à des opérations de masquage des libellés et de génération d'exercices aléatoires.", "default": false, "onchange": "setRandomKnowledge(event.target.checked)", }, "decomposition": { "name": "Décomposition d'associations", "title": "Cochez pour ajouter un bouton donnant accès à des opérations de réécriture de certains types d'associations.", "default": false, "onchange": "setDecompositionKnowledge(event.target.checked)", }, } function createTabs() { $('ul.tabs').each(function () { // based upon http://www.jacklmoore.com/notes/jquery-tabs $(this).find('a:not(.active)').each(function () { $($(this).attr('href')).hide(); }); $(this).on('click', 'a', function (e) { var $active = $(this).parent().siblings().children(".active"); var $content = $($active.attr('href')); $active.removeClass('active'); $content.hide(); $active = $(this); $content = $($(this).attr('href')); $active.addClass('active'); $content.show(); e.preventDefault(); }); }); } function activateTab(zone, target) { var $active = $(zone + ' a.active'); if ("#" + $active.attr("id") === target) { return; } var $content = $($active.attr('href')); $active.removeClass('active'); $content.hide(); $active = $(target); $content = $($active.attr('href')); $active.addClass('active'); $content.show(); } function createOptions(id, items, selected, on_demand_index) { $.each(items, function (i, elt) { $("#" + id).append("" + elt + "<\/option>"); }); } function createCheckboxes(group, group_name) { var s = ''; $.each(group, function (key, value) { s = "
  • "; s += '<\/span><\/li>"; if (value["advanced"]) { $("#" + group_name).find("details").append(s); } else { if ($("#" + group_name).find("details").length) { $("#" + group_name).find("details").before(s); } else { $("#" + group_name).append(s); } } }) } function appendMenuToInput(menuId, inputId) { document.getElementById(inputId).parentNode.appendChild(document.getElementById(menuId)); } function refreshSize(geo) { var s = '