commit bf9156b973d028da6b7dc67381d815e9f4f999aa Author: Marc Wempe Date: Fri Apr 3 23:08:57 2026 +0200 🎉 Initialize module repository diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c5f867 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +__pycache__/ +*.py[cod] +.DS_Store +.pytest_cache/ +.ruff_cache/ +*.log +*.swp +*~ diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/__manifest__.py b/__manifest__.py new file mode 100644 index 0000000..17f1290 --- /dev/null +++ b/__manifest__.py @@ -0,0 +1,39 @@ +{ + "name": "MVD TCG MTG", + "summary": "Magic: The Gathering reference adapter for the MVD TCG suite", + "version": "19.0.9.5.1", + "description": """ +Magic: The Gathering adapter built on top of MVD TCG Base. + +This module adds MTG-specific reference data and presentation: +- sets, colors, rarities, finishes, formats, card types, and keywords +- MTG card fields such as mana cost, oracle text, collector number, and faces +- MTG-focused search, views, and symbol rendering in the Odoo backend + +It does not import data on its own. External sources such as Scryfall are +handled by dedicated connector modules. +""", + "category": "Tools", + "author": "Mantjeverse Digital", + "license": "LGPL-3", + "depends": ["mvd_tcg_base", "web"], + "data": [ + "security/ir.model.access.csv", + "data/mvd_tcg_game_data.xml", + "data/mvd_tcg_mtg_taxonomy_data.xml", + "views/mvd_tcg_mtg_set_views.xml", + "views/mvd_tcg_mtg_taxonomy_views.xml", + "views/mvd_tcg_mtg_card_views.xml", + "views/menu_views.xml", + ], + "assets": { + "web.assets_backend": [ + "mvd_tcg_mtg/static/src/js/fields/mtg_symbol_catalog.js", + "mvd_tcg_mtg/static/src/js/fields/mtg_symbols_field.js", + "mvd_tcg_mtg/static/src/xml/mtg_symbols_field.xml", + "mvd_tcg_mtg/static/src/scss/mtg_symbols_field.scss", + ], + }, + "application": False, + "installable": True, +} diff --git a/data/mvd_tcg_game_data.xml b/data/mvd_tcg_game_data.xml new file mode 100644 index 0000000..8e1be7a --- /dev/null +++ b/data/mvd_tcg_game_data.xml @@ -0,0 +1,9 @@ + + + + Magic: The Gathering + mtg + 10 + Magic: The Gathering reference root for MTG-specific adapters. + + diff --git a/data/mvd_tcg_mtg_taxonomy_data.xml b/data/mvd_tcg_mtg_taxonomy_data.xml new file mode 100644 index 0000000..6f6c48d --- /dev/null +++ b/data/mvd_tcg_mtg_taxonomy_data.xml @@ -0,0 +1,248 @@ + + + + Common + common + 10 + + + Uncommon + uncommon + 20 + + + Rare + rare + 30 + + + Mythic Rare + mythic + 40 + + + Special + special + 50 + + + Bonus + bonus + 60 + + + + White + W + 10 + + + Blue + U + 20 + + + Black + B + 30 + + + Red + R + 40 + + + Green + G + 50 + + + Colorless + C + 60 + + + + Artifact + artifact + 10 + + + Battle + battle + 20 + + + Creature + creature + 30 + + + Enchantment + enchantment + 40 + + + Instant + instant + 50 + + + Kindred + kindred + 60 + + + Land + land + 70 + + + Planeswalker + planeswalker + 80 + + + Sorcery + sorcery + 90 + + + + Nonfoil + nonfoil + 10 + + + Foil + foil + 20 + + + Etched + etched + 30 + + + + Paper + paper + 10 + + + Arena + arena + 20 + + + MTGO + mtgo + 30 + + + + Standard + standard + 10 + + + Future + future + 20 + + + Historic + historic + 30 + + + Timeless + timeless + 40 + + + Gladiator + gladiator + 50 + + + Pioneer + pioneer + 60 + + + Modern + modern + 70 + + + Legacy + legacy + 80 + + + Pauper + pauper + 90 + + + Vintage + vintage + 100 + + + Penny + penny + 110 + + + Commander + commander + 120 + + + Oathbreaker + oathbreaker + 130 + + + Standard Brawl + standardbrawl + 140 + + + Brawl + brawl + 150 + + + Alchemy + alchemy + 160 + + + Pauper Commander + paupercommander + 170 + + + Duel Commander + duel + 180 + + + Old School + oldschool + 190 + + + Premodern + premodern + 200 + + + PreDH + predh + 210 + + diff --git a/i18n/de.po b/i18n/de.po new file mode 100644 index 0000000..8550d79 --- /dev/null +++ b/i18n/de.po @@ -0,0 +1,1144 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mvd_tcg_mtg +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.0+e-20260324\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-04-03 05:05+0000\n" +"PO-Revision-Date: 2026-04-03 05:05+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Language: de\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "#" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Set" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__active +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__active +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__active +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__active +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__active +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__active +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__active +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__active +msgid "Active" +msgstr "Aktiv" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_form +msgid "Add MTG-set-specific internal notes." +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Add a neutral shared description." +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Add internal MTG card notes." +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Add oracle text for this MTG card." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_alchemy +msgid "Alchemy" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_type_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_color_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_finish_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_format_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_keyword_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_platform_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_rarity_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_search +msgid "Archived" +msgstr "Archiviert" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.platform,name:mvd_tcg_mtg.mvd_tcg_mtg_platform_arena +msgid "Arena" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_operations_menu +msgid "Reference Data" +msgstr "Referenzdaten" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.card.type,name:mvd_tcg_mtg.mvd_tcg_mtg_card_type_artifact +msgid "Artifact" +msgstr "Artefakt" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_artist +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__artist +msgid "Artist" +msgstr "Illustration" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields.selection,name:mvd_tcg_mtg.selection__mvd_tcg_mtg_card_legality__status__banned +msgid "Banned" +msgstr "Gebannt" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.card.type,name:mvd_tcg_mtg.mvd_tcg_mtg_card_type_battle +msgid "Battle" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.color,name:mvd_tcg_mtg.mvd_tcg_mtg_color_black +msgid "Black" +msgstr "Schwarz" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.color,name:mvd_tcg_mtg.mvd_tcg_mtg_color_blue +msgid "Blue" +msgstr "Blau" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.rarity,name:mvd_tcg_mtg.mvd_tcg_mtg_rarity_bonus +msgid "Bonus" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_brawl +msgid "Brawl" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__card_id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__card_id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__card_ids +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__card_ids +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__card_ids +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__card_ids +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__card_ids +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__card_ids +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Card" +msgstr "Karte" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_type_view_form +msgid "Card Type" +msgstr "Kartentyp" + +#. module: mvd_tcg_mtg +#: model:ir.actions.act_window,name:mvd_tcg_mtg.mvd_tcg_mtg_card_type_action +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_card_type_ids +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_card_types_menu +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_type_view_list +msgid "Card Types" +msgstr "Kartentypen" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_form +msgid "Card Count" +msgstr "Anzahl Karten" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__format_code +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__code +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__code +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__code +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__code +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__code +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__code +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__code +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__code +msgid "Code" +msgstr "Code" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_collector_number +msgid "Collector Number" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_color_view_form +msgid "Color" +msgstr "Farbe" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_color_identity_ids +msgid "Color Identity" +msgstr "Farbidentität" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.color,name:mvd_tcg_mtg.mvd_tcg_mtg_color_colorless +msgid "Colorless" +msgstr "Farblos" + +#. module: mvd_tcg_mtg +#: model:ir.actions.act_window,name:mvd_tcg_mtg.mvd_tcg_mtg_color_action +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_color_ids +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_colors_menu +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_color_view_list +msgid "Colors" +msgstr "Farben" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_commander +msgid "Commander" +msgstr "Commander" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.rarity,name:mvd_tcg_mtg.mvd_tcg_mtg_rarity_common +msgid "Common" +msgstr "Gewöhnlich" + +#. module: mvd_tcg_mtg +#: model_terms:ir.actions.act_window,help:mvd_tcg_mtg.mvd_tcg_mtg_card_action +msgid "Create a new Magic card reference" +msgstr "Neue Magic-Kartenreferenz anlegen" + +#. module: mvd_tcg_mtg +#: model_terms:ir.actions.act_window,help:mvd_tcg_mtg.mvd_tcg_mtg_set_action +msgid "Create a new Magic set" +msgstr "Neues Magic-Set anlegen" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__create_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__create_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__create_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__create_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__create_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__create_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__create_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__create_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__create_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__create_uid +msgid "Created by" +msgstr "Erstellt von" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__create_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__create_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__create_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__create_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__create_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__create_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__create_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__create_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__create_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__create_date +msgid "Created on" +msgstr "Erstellt am" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.card.type,name:mvd_tcg_mtg.mvd_tcg_mtg_card_type_creature +msgid "Creature" +msgstr "Kreatur" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_is_digital +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Digital" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_game__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__display_name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_taxonomy_mixin__display_name +msgid "Display Name" +msgstr "Anzeigename" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Draft" +msgstr "Entwurf" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_duel +msgid "Duel Commander" +msgstr "Duel Commander" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.card.type,name:mvd_tcg_mtg.mvd_tcg_mtg_card_type_enchantment +msgid "Enchantment" +msgstr "Verzauberung" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.finish,name:mvd_tcg_mtg.mvd_tcg_mtg_finish_etched +msgid "Etched" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Face" +msgstr "Seite" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_face_ids +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Faces" +msgstr "Seiten" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_finish_view_form +msgid "Finish" +msgstr "Finish" + +#. module: mvd_tcg_mtg +#: model:ir.actions.act_window,name:mvd_tcg_mtg.mvd_tcg_mtg_finish_action +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_finish_ids +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_finishes_menu +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_finish_view_list +msgid "Finishes" +msgstr "Finishes" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_flavor_text +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__flavor_text +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Flavor Text" +msgstr "Flavortext" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.finish,name:mvd_tcg_mtg.mvd_tcg_mtg_finish_foil +msgid "Foil" +msgstr "Foil" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__format_id +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_format_view_form +msgid "Format" +msgstr "Format" + +#. module: mvd_tcg_mtg +#: model:ir.actions.act_window,name:mvd_tcg_mtg.mvd_tcg_mtg_format_action +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_formats_menu +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_format_view_list +msgid "Formats" +msgstr "Formate" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_is_full_art +msgid "Full Art" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_future +msgid "Future" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__game_id +msgid "Game" +msgstr "Spiel" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_platform_view_form +msgid "Game Platform" +msgstr "Plattform" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_platform_view_list +msgid "Game Platforms" +msgstr "Plattformen" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Gameplay" +msgstr "Spielinformationen" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_game_platform_ids +msgid "Games" +msgstr "Spiele" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_form +msgid "General Information" +msgstr "Allgemeine Informationen" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_gladiator +msgid "Gladiator" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.color,name:mvd_tcg_mtg.mvd_tcg_mtg_color_green +msgid "Green" +msgstr "Grün" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_historic +msgid "Historic" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_game__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__id +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_taxonomy_mixin__id +msgid "ID" +msgstr "ID" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__icon_svg_uri +msgid "Icon Svg Uri" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.card.type,name:mvd_tcg_mtg.mvd_tcg_mtg_card_type_instant +msgid "Instant" +msgstr "Spontanzauber" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_form +msgid "Internal Notes" +msgstr "Interne Notizen" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__is_mtg_game +msgid "Is Mtg Game" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_keyword_view_form +msgid "Keyword" +msgstr "Schlüsselwort" + +#. module: mvd_tcg_mtg +#: model:ir.actions.act_window,name:mvd_tcg_mtg.mvd_tcg_mtg_keyword_action +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_keyword_ids +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_keywords_menu +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_keyword_view_list +msgid "Keywords" +msgstr "Schlüsselwörter" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.card.type,name:mvd_tcg_mtg.mvd_tcg_mtg_card_type_kindred +msgid "Kindred" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.card.type,name:mvd_tcg_mtg.mvd_tcg_mtg_card_type_land +msgid "Land" +msgstr "Land" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__write_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__write_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__write_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__write_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__write_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__write_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__write_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__write_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__write_uid +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__write_uid +msgid "Last Updated by" +msgstr "Zuletzt aktualisiert von" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__write_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__write_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__write_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__write_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__write_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__write_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__write_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__write_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__write_date +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__write_date +msgid "Last Updated on" +msgstr "Zuletzt aktualisiert am" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_layout +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Layout" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_legacy +msgid "Legacy" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields.selection,name:mvd_tcg_mtg.selection__mvd_tcg_mtg_card_legality__status__legal +msgid "Legal" +msgstr "Legal" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_legality_ids +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Legalities" +msgstr "Legalitäten" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__legality_ids +msgid "Legality" +msgstr "Legalität" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Legendary" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_kanban +msgid "Loaded Cards" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_loyalty +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__loyalty +msgid "Loyalty" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_card_face +msgid "MTG Card Face" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_card_legality +msgid "MTG Card Legality" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_card_type +msgid "MTG Card Type" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_color +msgid "MTG Color" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_finish +msgid "MTG Finish" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_format +msgid "MTG Format" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_keyword +msgid "MTG Keyword" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_platform +msgid "MTG Platform" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_rarity +msgid "MTG Rarity" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_set +msgid "MTG Set" +msgstr "" + +#. module: mvd_tcg_mtg +#. odoo-javascript +#: code:addons/mvd_tcg_mtg/static/src/js/fields/mtg_symbols_field.js:0 +msgid "MTG Symbols" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_mtg_taxonomy_mixin +msgid "MTG Taxonomy Technical Code Mixin" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.actions.act_window,help:mvd_tcg_mtg.mvd_tcg_mtg_set_action +msgid "" +"MTG sets define the release-specific reference layer used by cards\n" +" and later Scryfall-based import logic." +msgstr "" + +#. module: mvd_tcg_mtg +#. odoo-python +#: code:addons/mvd_tcg_mtg/models/mvd_tcg_mtg_taxonomy.py:0 +msgid "" +"MTG taxonomy codes are technical identifiers and can only be changed by TCG " +"administrators." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.platform,name:mvd_tcg_mtg.mvd_tcg_mtg_platform_mtgo +msgid "MTGO" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_configuration_menu +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_root_menu +msgid "Magic" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_kanban +msgid "Magic Card" +msgstr "Magic-Karte" + +#. module: mvd_tcg_mtg +#: model:ir.actions.act_window,name:mvd_tcg_mtg.mvd_tcg_mtg_card_action +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_list +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Magic Cards" +msgstr "Magic-Karten" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_form +msgid "Magic Set" +msgstr "Magic-Set" + +#. module: mvd_tcg_mtg +#: model:ir.actions.act_window,name:mvd_tcg_mtg.mvd_tcg_mtg_set_action +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_list +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_search +msgid "Magic Sets" +msgstr "Magic-Sets" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.game,name:mvd_tcg_mtg.mvd_tcg_game_mtg +msgid "Magic: The Gathering" +msgstr "Magic: The Gathering" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.game,description:mvd_tcg_mtg.mvd_tcg_game_mtg +msgid "Magic: The Gathering reference root for MTG-specific adapters." +msgstr "Referenzwurzel für Magic: The Gathering und MTG-spezifische Adapter." + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_mana_cost +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__mana_cost +msgid "Mana Cost" +msgstr "Manakosten" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_mana_value +msgid "Mana Value" +msgstr "Manawert" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_modern +msgid "Modern" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__mtg_card_ids +msgid "Cards" +msgstr "Karten" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__mtg_card_count +msgid "Cards" +msgstr "Karten" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_collector_sort_key +msgid "Collector Sort Key" +msgstr "Sammler-Sortierschlüssel" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_color_identity_signature +msgid "Color Identity Signature" +msgstr "Farbidentitäts-Signatur" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_color_signature +msgid "Color Signature" +msgstr "Farbsignatur" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_face_count +msgid "Face Count" +msgstr "Anzahl Seiten" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Multi-Faced" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.rarity,name:mvd_tcg_mtg.mvd_tcg_mtg_rarity_mythic +msgid "Mythic Rare" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__name +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__name +msgid "Name" +msgstr "Name" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Neutral Description" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "No flavor text is currently stored." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.finish,name:mvd_tcg_mtg.mvd_tcg_mtg_finish_nonfoil +msgid "Nonfoil" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields.selection,name:mvd_tcg_mtg.selection__mvd_tcg_mtg_card_legality__status__not_legal +msgid "Not Legal" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__note +msgid "Note" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_oathbreaker +msgid "Oathbreaker" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__official_card_count +msgid "Official Card Count" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_oldschool +msgid "Old School" +msgstr "" + +#. module: mvd_tcg_mtg +msgid "Operations" +msgstr "Operationen" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_oracle_id +msgid "Oracle ID" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_oracle_text +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__oracle_text +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Oracle Text" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,help:mvd_tcg_mtg.field_mvd_tcg_card__mtg_color_identity_signature +msgid "Ordered MTG color identity signature, for example W, UB or WUBRG." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,help:mvd_tcg_mtg.field_mvd_tcg_card__mtg_color_signature +msgid "Ordered MTG color signature, for example W, UB or WUBRG." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.platform,name:mvd_tcg_mtg.mvd_tcg_mtg_platform_paper +msgid "Paper" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_pauper +msgid "Pauper" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_paupercommander +msgid "Pauper Commander" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_penny +msgid "Penny" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_pioneer +msgid "Pioneer" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.card.type,name:mvd_tcg_mtg.mvd_tcg_mtg_card_type_planeswalker +msgid "Planeswalker" +msgstr "Planeswalker" + +#. module: mvd_tcg_mtg +#: model:ir.actions.act_window,name:mvd_tcg_mtg.mvd_tcg_mtg_platform_action +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_platforms_menu +msgid "Platforms" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_power +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__power +msgid "Power" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_predh +msgid "PreDH" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_premodern +msgid "Premodern" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_is_promo +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Promo" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Promos" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.rarity,name:mvd_tcg_mtg.mvd_tcg_mtg_rarity_rare +msgid "Rare" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.actions.act_window,name:mvd_tcg_mtg.mvd_tcg_mtg_rarity_action +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_rarities_menu +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_rarity_view_list +msgid "Rarities" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_rarity_id +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_rarity_view_form +msgid "Rarity" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_rarity_code +msgid "Rarity Code" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.color,name:mvd_tcg_mtg.mvd_tcg_mtg_color_red +msgid "Red" +msgstr "Rot" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_search +msgid "Release Date" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_set_released_on +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__released_on +msgid "Released On" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_is_reprint +msgid "Reprint" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Reprints" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Reset to Draft" +msgstr "Auf Entwurf zurücksetzen" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields.selection,name:mvd_tcg_mtg.selection__mvd_tcg_mtg_card_legality__status__restricted +msgid "Restricted" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__sequence +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__format_sequence +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_type__sequence +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_color__sequence +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_finish__sequence +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_format__sequence +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_keyword__sequence +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_platform__sequence +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_rarity__sequence +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__sequence +msgid "Sequence" +msgstr "Reihenfolge" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_set_id +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_search +msgid "Set" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_set_code +msgid "Set Code" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_set_type +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_set__set_type +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_search +msgid "Set Type" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.ui.menu,name:mvd_tcg_mtg.mvd_tcg_mtg_sets_menu +msgid "Sets" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.card.type,name:mvd_tcg_mtg.mvd_tcg_mtg_card_type_sorcery +msgid "Sorcery" +msgstr "Hexerei" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.rarity,name:mvd_tcg_mtg.mvd_tcg_mtg_rarity_special +msgid "Special" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_standard +msgid "Standard" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_standardbrawl +msgid "Standard Brawl" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "State" +msgstr "Status" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_legality__status +msgid "Status" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_card +msgid "TCG Card" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model,name:mvd_tcg_mtg.model_mvd_tcg_game +msgid "TCG Game" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_type_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_color_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_finish_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_format_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_keyword_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_platform_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_rarity_view_form +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_form +msgid "Technical" +msgstr "Technisch" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_is_textless +msgid "Textless" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.actions.act_window,help:mvd_tcg_mtg.mvd_tcg_mtg_card_action +msgid "" +"The MTG adapter enriches the neutral card model with set,\n" +" collector and oracle metadata while keeping the suite-wide card\n" +" identity on mvd.tcg.card." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_mtg_card_type_code_unique +msgid "The MTG card type code must be unique." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_mtg_color_code_unique +msgid "The MTG color code must be unique." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_mtg_card_face_card_sequence_unique +msgid "The MTG face sequence must be unique per card." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_mtg_finish_code_unique +msgid "The MTG finish code must be unique." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_mtg_format_code_unique +msgid "The MTG format code must be unique." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_mtg_keyword_code_unique +msgid "The MTG keyword code must be unique." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_mtg_card_legality_card_format_unique +msgid "The MTG legality format must be unique per card." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_mtg_platform_code_unique +msgid "The MTG platform code must be unique." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_mtg_rarity_code_unique +msgid "The MTG rarity code must be unique." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_mtg_set_code_unique +msgid "The MTG set code must be unique per game." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.constraint,message:mvd_tcg_mtg.constraint_mvd_tcg_card_mtg_set_collector_unique +msgid "The collector number must be unique inside an MTG set." +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_timeless +msgid "Timeless" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_is_token +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Token" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Tokens" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_toughness +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__toughness +msgid "Toughness" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_card__mtg_type_line +#: model:ir.model.fields,field_description:mvd_tcg_mtg.field_mvd_tcg_mtg_card_face__type_line +msgid "Type Line" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.rarity,name:mvd_tcg_mtg.mvd_tcg_mtg_rarity_uncommon +msgid "Uncommon" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "Validate" +msgstr "Validieren" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "Validated" +msgstr "Validiert" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.format,name:mvd_tcg_mtg.mvd_tcg_mtg_format_vintage +msgid "Vintage" +msgstr "" + +#. module: mvd_tcg_mtg +#: model:mvd.tcg.mtg.color,name:mvd_tcg_mtg.mvd_tcg_mtg_color_white +msgid "White" +msgstr "Weiß" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_search +msgid "With Flavor Text" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_card_view_form +msgid "e.g. Black Lotus" +msgstr "" + +#. module: mvd_tcg_mtg +#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg.mvd_tcg_mtg_set_view_form +msgid "e.g. Tarkir: Dragonstorm" +msgstr "" diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..4cdee43 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,6 @@ +from . import mvd_tcg_game +from . import mvd_tcg_card +from . import mvd_tcg_mtg_card_face +from . import mvd_tcg_mtg_card_legality +from . import mvd_tcg_mtg_set +from . import mvd_tcg_mtg_taxonomy diff --git a/models/mvd_tcg_card.py b/models/mvd_tcg_card.py new file mode 100644 index 0000000..be7a29f --- /dev/null +++ b/models/mvd_tcg_card.py @@ -0,0 +1,499 @@ +"""Magic: The Gathering card extensions for the TCG suite.""" + +import re + +from odoo import api, fields, models + +COLLECTOR_NUMBER_PATTERN = re.compile(r"^(\d+)(.*)$") + + +class MvdTcgCard(models.Model): + """Extend neutral card references with MTG-specific metadata.""" + + _inherit = "mvd.tcg.card" + + is_mtg_game = fields.Boolean(compute="_compute_is_mtg_game") + mtg_set_id = fields.Many2one( + "mvd.tcg.mtg.set", + string="Set", + index=True, + ondelete="restrict", + ) + mtg_set_code = fields.Char( + related="mtg_set_id.code", + string="Set Code", + store=True, + index=True, + ) + mtg_rarity_id = fields.Many2one( + "mvd.tcg.mtg.rarity", + string="Rarity", + index=True, + ondelete="restrict", + ) + mtg_rarity_code = fields.Char( + related="mtg_rarity_id.code", + string="Rarity Code", + store=True, + index=True, + ) + mtg_collector_number = fields.Char(string="Collector Number", index=True) + mtg_collector_sort_key = fields.Char( + string="Collector Sort Key", + compute="_compute_mtg_collector_sort_key", + store=True, + index=True, + ) + mtg_oracle_id = fields.Char(string="Oracle ID", index=True) + mtg_layout = fields.Char(string="Layout", index=True) + mtg_mana_cost = fields.Char(string="Mana Cost") + mtg_mana_value = fields.Float(string="Mana Value") + mtg_type_line = fields.Char(string="Type Line", translate=True) + mtg_oracle_text = fields.Text(string="Oracle Text", translate=True) + mtg_flavor_text = fields.Text(string="Flavor Text", translate=True) + mtg_color_ids = fields.Many2many( + "mvd.tcg.mtg.color", + "mvd_tcg_card_mtg_color_rel", + "card_id", + "color_id", + string="Colors", + ) + mtg_color_identity_ids = fields.Many2many( + "mvd.tcg.mtg.color", + "mvd_tcg_card_mtg_color_identity_rel", + "card_id", + "color_id", + string="Color Identity", + ) + mtg_color_identity_signature = fields.Char( + string="Color Identity Signature", + compute="_compute_mtg_color_identity_signature", + store=True, + index=True, + help="Ordered MTG color identity signature, for example W, UB or WUBRG.", + ) + mtg_card_type_ids = fields.Many2many( + "mvd.tcg.mtg.card.type", + "mvd_tcg_card_mtg_card_type_rel", + "card_id", + "type_id", + string="Card Types", + ) + mtg_keyword_ids = fields.Many2many( + "mvd.tcg.mtg.keyword", + "mvd_tcg_card_mtg_keyword_rel", + "card_id", + "keyword_id", + string="Keywords", + ) + mtg_game_platform_ids = fields.Many2many( + "mvd.tcg.mtg.platform", + "mvd_tcg_card_mtg_platform_rel", + "card_id", + "platform_id", + string="Games", + ) + mtg_finish_ids = fields.Many2many( + "mvd.tcg.mtg.finish", + "mvd_tcg_card_mtg_finish_rel", + "card_id", + "finish_id", + string="Finishes", + ) + mtg_power = fields.Char(string="Power") + mtg_toughness = fields.Char(string="Toughness") + mtg_loyalty = fields.Char(string="Loyalty") + mtg_artist = fields.Char(string="Artist", index="trigram") + mtg_color_signature = fields.Char( + string="Color Signature", + compute="_compute_mtg_color_signature", + store=True, + index=True, + help="Ordered MTG color signature, for example W, UB or WUBRG.", + ) + mtg_face_ids = fields.One2many("mvd.tcg.mtg.card.face", "card_id", string="Faces") + mtg_face_count = fields.Integer( + string="Face Count", + compute="_compute_mtg_face_count", + store=True, + ) + mtg_legality_ids = fields.One2many( + "mvd.tcg.mtg.card.legality", + "card_id", + string="Legalities", + ) + mtg_is_token = fields.Boolean(string="Token") + mtg_is_reprint = fields.Boolean(string="Reprint") + mtg_is_promo = fields.Boolean(string="Promo") + mtg_is_digital = fields.Boolean(string="Digital") + mtg_is_full_art = fields.Boolean(string="Full Art") + mtg_is_textless = fields.Boolean(string="Textless") + mtg_set_released_on = fields.Date( + related="mtg_set_id.released_on", + string="Released On", + store=True, + index=True, + ) + mtg_set_type = fields.Char( + related="mtg_set_id.set_type", + string="Set Type", + store=True, + index=True, + ) + + _mtg_set_collector_unique = models.Constraint( + "UNIQUE (mtg_set_id, mtg_collector_number)", + "The collector number must be unique inside an MTG set.", + ) + + @api.depends("game_id.code") + def _compute_is_mtg_game(self): + """Flag whether the current card belongs to the MTG adapter.""" + for card in self: + card.is_mtg_game = card.game_id.code == "mtg" + + @api.depends("mtg_collector_number") + def _compute_mtg_collector_sort_key(self): + """Build a stable natural-sort key for MTG collector numbers. + + Returns: + None: The compute updates records in place. + """ + for card in self: + collector_number = (card.mtg_collector_number or "").strip().lower() + if not collector_number: + card.mtg_collector_sort_key = "99999999:" + continue + + match = COLLECTOR_NUMBER_PATTERN.match(collector_number) + if match: + numeric_part, suffix = match.groups() + card.mtg_collector_sort_key = ( + f"{int(numeric_part):08d}:{suffix.strip()}" + ) + continue + + card.mtg_collector_sort_key = f"99999999:{collector_number}" + + @api.depends("mtg_color_ids", "mtg_color_ids.sequence", "mtg_color_ids.code") + def _compute_mtg_color_signature(self): + """Build the canonical MTG color signature from selected colors. + + Returns: + None: The compute updates records in place. + """ + for card in self: + colors = card.mtg_color_ids.sorted( + key=lambda color: (color.sequence, color.code or "", color.id) + ) + card.mtg_color_signature = "".join( + (color.code or "").strip().upper() for color in colors + ) or False + + @api.depends( + "mtg_color_identity_ids", + "mtg_color_identity_ids.sequence", + "mtg_color_identity_ids.code", + ) + def _compute_mtg_color_identity_signature(self): + """Build the canonical MTG color identity signature. + + Returns: + None: The compute updates records in place. + """ + for card in self: + colors = card.mtg_color_identity_ids.sorted( + key=lambda color: (color.sequence, color.code or "", color.id) + ) + card.mtg_color_identity_signature = "".join( + (color.code or "").strip().upper() for color in colors + ) or False + + @api.depends("mtg_face_ids") + def _compute_mtg_face_count(self): + """Compute how many explicit faces are linked to each card. + + Returns: + None: The compute updates records in place. + """ + face_data = self.env["mvd.tcg.mtg.card.face"]._read_group( + [("card_id", "in", self.ids)], + ["card_id"], + ["__count"], + ) + counts_by_card = {record.id: count for record, count in face_data} + for card in self: + card.mtg_face_count = counts_by_card.get(card.id, 0) + + def action_open_mtg_set(self): + """Open the linked MTG set in form view. + + Returns: + dict: Window action for the linked MTG set. + """ + self.ensure_one() + if not self.mtg_set_id: + return False + + return { + "type": "ir.actions.act_window", + "name": self.mtg_set_id.display_name, + "res_model": "mvd.tcg.mtg.set", + "view_mode": "form", + "res_id": self.mtg_set_id.id, + "target": "current", + } + + def mtg_get_rules_sections(self): + """Return one face-aware MTG rules structure for the current card. + + Returns: + list[dict[str, object]]: Ordered rules sections. Multi-face cards + return one section per face, while single-face cards return one + fallback section from the card header fields. + """ + self.ensure_one() + face_sections = [] + for face in self.mtg_face_ids.sorted(lambda current_face: (current_face.sequence, current_face.id)): + section = { + "sequence": face.sequence, + "name": face.name or False, + "mana_cost": face.mana_cost or False, + "type_line": face.type_line or False, + "oracle_text": face.oracle_text or False, + "flavor_text": face.flavor_text or False, + "power": face.power or False, + "toughness": face.toughness or False, + "loyalty": face.loyalty or False, + } + if any(section.values()): + face_sections.append(section) + if face_sections: + return face_sections + return [ + { + "sequence": 10, + "name": self.name or False, + "mana_cost": self.mtg_mana_cost or False, + "type_line": self.mtg_type_line or False, + "oracle_text": self.mtg_oracle_text or False, + "flavor_text": self.mtg_flavor_text or False, + "power": self.mtg_power or False, + "toughness": self.mtg_toughness or False, + "loyalty": self.mtg_loyalty or False, + } + ] + + def mtg_get_rules_summary(self): + """Return one analysis-friendly MTG rules summary. + + Returns: + str | bool: Single-face cards return their oracle text directly. + Multi-face cards return a combined face-aware rules summary. + """ + self.ensure_one() + rule_sections = self.mtg_get_rules_sections() + if len(rule_sections) <= 1: + return rule_sections and rule_sections[0]["oracle_text"] or False + + rendered_sections = [] + for section in rule_sections: + section_lines = [] + heading_parts = [section["name"]] + if section["mana_cost"]: + heading_parts.append(section["mana_cost"]) + heading = " ".join(part for part in heading_parts if part) + if heading: + section_lines.append(heading) + if section["type_line"]: + section_lines.append(section["type_line"]) + if section["oracle_text"]: + section_lines.append(section["oracle_text"]) + if section_lines: + rendered_sections.append("\n".join(section_lines)) + return "\n\n//\n\n".join(rendered_sections) or False + + def mtg_get_singleton_key_aliases(self): + """Return stable MTG identity aliases across printings and styles. + + Returns: + tuple[str, ...]: Ordered identity aliases. Cards with a missing + Oracle ID still expose rules-based fallbacks that can match + styled variants of the same card. + """ + self.ensure_one() + english_card = self.with_context(lang="en_US") + aliases = [] + oracle_id = (english_card.mtg_oracle_id or "").strip() + if oracle_id: + aliases.append(f"oracle:{oracle_id}") + + rules_summary = re.sub( + r"\s+", + " ", + (english_card.mtg_get_rules_summary() or "").strip(), + ).strip() + if rules_summary: + aliases.append(f"rules:{rules_summary}") + + deduplicated_oracle_chunks = [] + for section in english_card.mtg_get_rules_sections(): + oracle_text = re.sub( + r"\s+", + " ", + (section["oracle_text"] or "").strip(), + ).strip() + if oracle_text and oracle_text not in deduplicated_oracle_chunks: + deduplicated_oracle_chunks.append(oracle_text) + if deduplicated_oracle_chunks: + aliases.append(f"oracletext:{' // '.join(deduplicated_oracle_chunks)}") + + face_names = " // ".join( + section["name"] + for section in english_card.mtg_get_rules_sections() + if section["name"] + ) + fallback_name = face_names or (english_card.name or "").strip() + fallback_type = (english_card.mtg_type_line or "").strip() + fallback_cost = (english_card.mtg_mana_cost or "").strip() + if any((fallback_name, fallback_type, fallback_cost)): + aliases.append(f"fallback:{fallback_name}|{fallback_type}|{fallback_cost}") + + deduplicated_aliases = [] + for alias in aliases: + if alias and alias not in deduplicated_aliases: + deduplicated_aliases.append(alias) + return tuple(deduplicated_aliases) + + def mtg_get_singleton_key(self): + """Return the primary MTG singleton key for the current card. + + Returns: + str | bool: First stable singleton alias, if any. + """ + self.ensure_one() + aliases = self.mtg_get_singleton_key_aliases() + return aliases[0] if aliases else False + + def mtg_allows_unlimited_copies(self): + """Return whether one MTG card bypasses singleton restrictions. + + Returns: + bool: ``True`` for basic lands and cards with explicit unlimited- + copy rules text. + """ + self.ensure_one() + english_card = self.with_context(lang="en_US") + combined_type_line = " ".join( + section["type_line"] + for section in english_card.mtg_get_rules_sections() + if section["type_line"] + ).lower() + rules_summary = (english_card.mtg_get_rules_summary() or "").lower() + is_basic_land = "land" in combined_type_line and "basic" in combined_type_line + allows_unlimited_copies = ( + "a deck can have any number of cards named" in rules_summary + ) + return is_basic_land or allows_unlimited_copies + + @api.model + def _mtg_exact_color_signature_from_ids(self, color_ids): + """Build the canonical color signature for selected color records. + + Args: + color_ids: MTG color record ids from a filter domain. + + Returns: + str | bool: Canonical signature like ``WB`` or ``False`` when invalid. + """ + normalized_ids = [int(color_id) for color_id in color_ids or [] if color_id] + if not normalized_ids: + return False + + colors = self.env["mvd.tcg.mtg.color"].browse(normalized_ids).exists().sorted( + key=lambda color: (color.sequence, color.code or "", color.id) + ) + if len(colors) != len(set(normalized_ids)): + return False + return "".join((color.code or "").strip().upper() for color in colors) + + @api.model + def _mtg_transform_exact_color_domain(self, domain): + """Rewrite MTG color filters into exact color-signature filters. + + Args: + domain: Original ORM domain. + + Returns: + list: Domain with exact MTG color matching where applicable. + """ + domain_object = fields.Domain(domain or []) + selected_color_ids = [] + + for condition in domain_object.iter_conditions(): + if condition.field_expr != "mtg_color_ids": + continue + if condition.operator == "=" and condition.value: + color_id = int(condition.value) + if color_id not in selected_color_ids: + selected_color_ids.append(color_id) + continue + if condition.operator == "in" and isinstance(condition.value, (list, tuple)): + for color_id in condition.value: + normalized_id = int(color_id) + if normalized_id not in selected_color_ids: + selected_color_ids.append(normalized_id) + + if not selected_color_ids: + return domain + + exact_signature = self._mtg_exact_color_signature_from_ids(selected_color_ids) + if not exact_signature: + return list(fields.Domain.FALSE) + + remaining_domain = domain_object.map_conditions( + lambda condition: ( + fields.Domain.TRUE + if condition.field_expr == "mtg_color_ids" + else fields.Domain(condition) + ) + ).optimize(self) + exact_domain = fields.Domain( + [("mtg_color_signature", "=", exact_signature)] + ) + return list(fields.Domain.AND([remaining_domain, exact_domain]).optimize(self)) + + @api.model + def _search( + self, + domain, + offset=0, + limit=None, + order=None, + *, + active_test=True, + bypass_access=False, + ): + """Apply exact MTG color matching for the Magic card action. + + Args: + domain: ORM domain for the current search. + offset: Search offset. + limit: Optional maximal number of records. + order: SQL order clause. + active_test: Whether active records should be filtered implicitly. + bypass_access: Whether access rules should be bypassed. + + Returns: + Query: Matching search query. + """ + if self.env.context.get("mvd_mtg_exact_color_filter"): + domain = self._mtg_transform_exact_color_domain(domain) + return super()._search( + domain, + offset=offset, + limit=limit, + order=order, + active_test=active_test, + bypass_access=bypass_access, + ) diff --git a/models/mvd_tcg_game.py b/models/mvd_tcg_game.py new file mode 100644 index 0000000..590c245 --- /dev/null +++ b/models/mvd_tcg_game.py @@ -0,0 +1,14 @@ +"""MTG-specific helpers on the neutral game model.""" + +from odoo import api, models + + +class MvdTcgGame(models.Model): + """Provide stable access to the seeded MTG game record.""" + + _inherit = "mvd.tcg.game" + + @api.model + def _mvd_tcg_get_mtg_game(self): + """Return the seeded MTG game record.""" + return self.env.ref("mvd_tcg_mtg.mvd_tcg_game_mtg") diff --git a/models/mvd_tcg_mtg_card_face.py b/models/mvd_tcg_mtg_card_face.py new file mode 100644 index 0000000..4f30b36 --- /dev/null +++ b/models/mvd_tcg_mtg_card_face.py @@ -0,0 +1,33 @@ +"""Magic: The Gathering face-level reference models.""" + +from odoo import fields, models + + +class MvdTcgMtgCardFace(models.Model): + """Represent one ordered printed face of an MTG card reference.""" + + _name = "mvd.tcg.mtg.card.face" + _description = "MTG Card Face" + _order = "card_id, sequence, id" + + card_id = fields.Many2one( + "mvd.tcg.card", + required=True, + index=True, + ondelete="cascade", + ) + sequence = fields.Integer(default=10, index=True) + name = fields.Char(required=True, translate=True, index="trigram") + mana_cost = fields.Char() + type_line = fields.Char(translate=True) + oracle_text = fields.Text(translate=True) + flavor_text = fields.Text(translate=True) + power = fields.Char() + toughness = fields.Char() + loyalty = fields.Char() + artist = fields.Char(index="trigram") + + _card_sequence_unique = models.Constraint( + "UNIQUE (card_id, sequence)", + "The MTG face sequence must be unique per card.", + ) diff --git a/models/mvd_tcg_mtg_card_legality.py b/models/mvd_tcg_mtg_card_legality.py new file mode 100644 index 0000000..0707e73 --- /dev/null +++ b/models/mvd_tcg_mtg_card_legality.py @@ -0,0 +1,52 @@ +"""Magic: The Gathering legality models.""" + +from odoo import fields, models + +LEGALITY_SELECTION = [ + ("legal", "Legal"), + ("not_legal", "Not Legal"), + ("restricted", "Restricted"), + ("banned", "Banned"), +] + + +class MvdTcgMtgCardLegality(models.Model): + """Store one legality status per MTG card and constructed format.""" + + _name = "mvd.tcg.mtg.card.legality" + _description = "MTG Card Legality" + _order = "format_sequence, id" + + card_id = fields.Many2one( + "mvd.tcg.card", + required=True, + index=True, + ondelete="cascade", + ) + format_id = fields.Many2one( + "mvd.tcg.mtg.format", + required=True, + index=True, + ondelete="restrict", + ) + format_code = fields.Char( + related="format_id.code", + store=True, + index=True, + ) + format_sequence = fields.Integer( + related="format_id.sequence", + store=True, + index=True, + ) + status = fields.Selection( + selection=LEGALITY_SELECTION, + required=True, + default="not_legal", + index=True, + ) + + _card_format_unique = models.Constraint( + "UNIQUE (card_id, format_id)", + "The MTG legality format must be unique per card.", + ) diff --git a/models/mvd_tcg_mtg_set.py b/models/mvd_tcg_mtg_set.py new file mode 100644 index 0000000..4a84105 --- /dev/null +++ b/models/mvd_tcg_mtg_set.py @@ -0,0 +1,72 @@ +"""Magic: The Gathering set models for the TCG suite.""" + +from odoo import api, fields, models + + +class MvdTcgMtgSet(models.Model): + """Represent a Magic: The Gathering set within the MTG adapter.""" + + _name = "mvd.tcg.mtg.set" + _description = "MTG Set" + _order = "released_on desc, code, id" + + name = fields.Char(required=True, translate=True, index="trigram") + code = fields.Char(required=True, index=True) + active = fields.Boolean(default=True) + sequence = fields.Integer(default=10) + game_id = fields.Many2one( + "mvd.tcg.game", + required=True, + index=True, + default=lambda self: self._default_game_id(), + ondelete="restrict", + ) + released_on = fields.Date(index=True) + set_type = fields.Char(index=True) + official_card_count = fields.Integer() + icon_svg_uri = fields.Char() + note = fields.Text(translate=True) + mtg_card_ids = fields.One2many( + "mvd.tcg.card", + "mtg_set_id", + string="Cards", + ) + mtg_card_count = fields.Integer( + string="Card Count", + compute="_compute_mtg_card_count", + ) + + _code_unique = models.Constraint( + "UNIQUE (game_id, code)", + "The MTG set code must be unique per game.", + ) + + @api.model + def _default_game_id(self): + """Return the seeded MTG game record.""" + return self.env["mvd.tcg.game"]._mvd_tcg_get_mtg_game().id + + @api.depends("mtg_card_ids") + def _compute_mtg_card_count(self): + """Compute how many MTG cards are currently linked to each set.""" + card_data = self.env["mvd.tcg.card"]._read_group( + [("mtg_set_id", "in", self.ids)], + ["mtg_set_id"], + ["__count"], + ) + counts_by_set = {record.id: count for record, count in card_data} + for mtg_set in self: + mtg_set.mtg_card_count = counts_by_set.get(mtg_set.id, 0) + + def action_open_cards(self): + """Open the MTG cards catalog filtered on the selected set.""" + self.ensure_one() + action = self.env["ir.actions.actions"]._for_xml_id( + "mvd_tcg_mtg.mvd_tcg_mtg_card_action" + ) + action["domain"] = [("mtg_set_id", "=", self.id)] + action["context"] = { + "default_game_id": self.game_id.id, + "default_mtg_set_id": self.id, + } + return action diff --git a/models/mvd_tcg_mtg_taxonomy.py b/models/mvd_tcg_mtg_taxonomy.py new file mode 100644 index 0000000..c65efa3 --- /dev/null +++ b/models/mvd_tcg_mtg_taxonomy.py @@ -0,0 +1,230 @@ +"""Magic: The Gathering taxonomy models for cards and faceting.""" + +import re + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + + +class MvdTcgMtgTaxonomyMixin(models.AbstractModel): + """Share technical code handling across MTG taxonomy models.""" + + _name = "mvd.tcg.mtg.taxonomy.mixin" + _description = "MTG Taxonomy Technical Code Mixin" + + @api.model + def _mvd_tcg_generate_code(self, name): + """Return a slug-like technical code for one taxonomy name.""" + return re.sub(r"[^a-z0-9]+", "_", (name or "").strip().lower()).strip("_") or "item" + + @api.model + def _mvd_tcg_get_unique_code(self, name): + """Return a unique technical code for one taxonomy record name.""" + base_code = self._mvd_tcg_generate_code(name) + existing_codes = set(self.search([]).mapped("code")) + if base_code not in existing_codes: + return base_code + suffix = 2 + while f"{base_code}_{suffix}" in existing_codes: + suffix += 1 + return f"{base_code}_{suffix}" + + def _mvd_tcg_can_edit_code(self): + """Return whether the current user may edit taxonomy codes.""" + return self.env.is_superuser() or any( + self.env.user.has_group(xmlid) + for xmlid in ( + "mvd_tcg_base.mvd_tcg_base_group_administrator", + "base.group_system", + ) + ) + + @api.model_create_multi + def create(self, vals_list): + """Generate missing technical codes for manually created taxonomy records.""" + prepared_vals_list = [] + for vals in vals_list: + prepared_vals = dict(vals) + if not prepared_vals.get("code"): + prepared_vals["code"] = self._mvd_tcg_get_unique_code( + prepared_vals.get("name") + ) + prepared_vals_list.append(prepared_vals) + return super().create(prepared_vals_list) + + def write(self, vals): + """Protect taxonomy codes from normal business edits.""" + if "code" in vals and not self.env.context.get("mvd_tcg_bypass_taxonomy_code_write"): + if not self._mvd_tcg_can_edit_code(): + raise UserError( + _( + "MTG taxonomy codes are technical identifiers and can only be " + "changed by TCG administrators." + ) + ) + return super().write(vals) + + +class MvdTcgMtgRarity(models.Model): + """Store normalized MTG rarity records.""" + + _name = "mvd.tcg.mtg.rarity" + _inherit = "mvd.tcg.mtg.taxonomy.mixin" + _description = "MTG Rarity" + _order = "sequence, name, id" + + name = fields.Char(required=True, translate=True, index="trigram") + code = fields.Char(required=True, index=True) + active = fields.Boolean(default=True) + sequence = fields.Integer(default=10) + card_ids = fields.One2many("mvd.tcg.card", "mtg_rarity_id") + + _code_unique = models.Constraint( + "UNIQUE (code)", + "The MTG rarity code must be unique.", + ) + + +class MvdTcgMtgColor(models.Model): + """Store normalized MTG colors for faceting and card metadata.""" + + _name = "mvd.tcg.mtg.color" + _inherit = "mvd.tcg.mtg.taxonomy.mixin" + _description = "MTG Color" + _order = "sequence, name, id" + + name = fields.Char(required=True, translate=True, index="trigram") + code = fields.Char(required=True, index=True) + active = fields.Boolean(default=True) + sequence = fields.Integer(default=10) + card_ids = fields.Many2many( + "mvd.tcg.card", + "mvd_tcg_card_mtg_color_rel", + "color_id", + "card_id", + ) + + _code_unique = models.Constraint( + "UNIQUE (code)", + "The MTG color code must be unique.", + ) + + +class MvdTcgMtgCardType(models.Model): + """Store normalized MTG card types for faceting and grouping.""" + + _name = "mvd.tcg.mtg.card.type" + _inherit = "mvd.tcg.mtg.taxonomy.mixin" + _description = "MTG Card Type" + _order = "sequence, name, id" + + name = fields.Char(required=True, translate=True, index="trigram") + code = fields.Char(required=True, index=True) + active = fields.Boolean(default=True) + sequence = fields.Integer(default=10) + card_ids = fields.Many2many( + "mvd.tcg.card", + "mvd_tcg_card_mtg_card_type_rel", + "type_id", + "card_id", + ) + + _code_unique = models.Constraint( + "UNIQUE (code)", + "The MTG card type code must be unique.", + ) + + +class MvdTcgMtgKeyword(models.Model): + """Store normalized MTG keywords imported from Scryfall.""" + + _name = "mvd.tcg.mtg.keyword" + _inherit = "mvd.tcg.mtg.taxonomy.mixin" + _description = "MTG Keyword" + _order = "sequence, name, id" + + name = fields.Char(required=True, translate=True, index="trigram") + code = fields.Char(required=True, index=True) + active = fields.Boolean(default=True) + sequence = fields.Integer(default=10) + card_ids = fields.Many2many( + "mvd.tcg.card", + "mvd_tcg_card_mtg_keyword_rel", + "keyword_id", + "card_id", + ) + + _code_unique = models.Constraint( + "UNIQUE (code)", + "The MTG keyword code must be unique.", + ) + + +class MvdTcgMtgFormat(models.Model): + """Store normalized MTG formats for legality information.""" + + _name = "mvd.tcg.mtg.format" + _inherit = "mvd.tcg.mtg.taxonomy.mixin" + _description = "MTG Format" + _order = "sequence, name, id" + + name = fields.Char(required=True, translate=True, index="trigram") + code = fields.Char(required=True, index=True) + active = fields.Boolean(default=True) + sequence = fields.Integer(default=10) + legality_ids = fields.One2many("mvd.tcg.mtg.card.legality", "format_id") + + _code_unique = models.Constraint( + "UNIQUE (code)", + "The MTG format code must be unique.", + ) + + +class MvdTcgMtgFinish(models.Model): + """Store normalized MTG finishes such as foil and etched.""" + + _name = "mvd.tcg.mtg.finish" + _inherit = "mvd.tcg.mtg.taxonomy.mixin" + _description = "MTG Finish" + _order = "sequence, name, id" + + name = fields.Char(required=True, translate=True, index="trigram") + code = fields.Char(required=True, index=True) + active = fields.Boolean(default=True) + sequence = fields.Integer(default=10) + card_ids = fields.Many2many( + "mvd.tcg.card", + "mvd_tcg_card_mtg_finish_rel", + "finish_id", + "card_id", + ) + + _code_unique = models.Constraint( + "UNIQUE (code)", + "The MTG finish code must be unique.", + ) + + +class MvdTcgMtgPlatform(models.Model): + """Store normalized MTG game platforms such as paper and Arena.""" + + _name = "mvd.tcg.mtg.platform" + _inherit = "mvd.tcg.mtg.taxonomy.mixin" + _description = "MTG Platform" + _order = "sequence, name, id" + + name = fields.Char(required=True, translate=True, index="trigram") + code = fields.Char(required=True, index=True) + active = fields.Boolean(default=True) + sequence = fields.Integer(default=10) + card_ids = fields.Many2many( + "mvd.tcg.card", + "mvd_tcg_card_mtg_platform_rel", + "platform_id", + "card_id", + ) + + _code_unique = models.Constraint( + "UNIQUE (code)", + "The MTG platform code must be unique.", + ) diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv new file mode 100644 index 0000000..ece57ea --- /dev/null +++ b/security/ir.model.access.csv @@ -0,0 +1,39 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_mvd_tcg_mtg_set_user,mvd.tcg.mtg.set.user,model_mvd_tcg_mtg_set,mvd_tcg_base.mvd_tcg_base_group_user,1,0,0,0 +access_mvd_tcg_mtg_set_operator,mvd.tcg.mtg.set.operator,model_mvd_tcg_mtg_set,mvd_tcg_base.mvd_tcg_base_group_operator,1,0,0,0 +access_mvd_tcg_mtg_set_manager,mvd.tcg.mtg.set.manager,model_mvd_tcg_mtg_set,mvd_tcg_base.mvd_tcg_base_group_manager,1,1,1,1 +access_mvd_tcg_mtg_set_system,mvd.tcg.mtg.set.system,model_mvd_tcg_mtg_set,base.group_system,1,1,1,1 +access_mvd_tcg_mtg_rarity_user,mvd.tcg.mtg.rarity.user,model_mvd_tcg_mtg_rarity,mvd_tcg_base.mvd_tcg_base_group_user,1,0,0,0 +access_mvd_tcg_mtg_rarity_operator,mvd.tcg.mtg.rarity.operator,model_mvd_tcg_mtg_rarity,mvd_tcg_base.mvd_tcg_base_group_operator,1,0,0,0 +access_mvd_tcg_mtg_rarity_manager,mvd.tcg.mtg.rarity.manager,model_mvd_tcg_mtg_rarity,mvd_tcg_base.mvd_tcg_base_group_manager,1,1,1,1 +access_mvd_tcg_mtg_rarity_system,mvd.tcg.mtg.rarity.system,model_mvd_tcg_mtg_rarity,base.group_system,1,1,1,1 +access_mvd_tcg_mtg_color_user,mvd.tcg.mtg.color.user,model_mvd_tcg_mtg_color,mvd_tcg_base.mvd_tcg_base_group_user,1,0,0,0 +access_mvd_tcg_mtg_color_operator,mvd.tcg.mtg.color.operator,model_mvd_tcg_mtg_color,mvd_tcg_base.mvd_tcg_base_group_operator,1,0,0,0 +access_mvd_tcg_mtg_color_manager,mvd.tcg.mtg.color.manager,model_mvd_tcg_mtg_color,mvd_tcg_base.mvd_tcg_base_group_manager,1,1,1,1 +access_mvd_tcg_mtg_color_system,mvd.tcg.mtg.color.system,model_mvd_tcg_mtg_color,base.group_system,1,1,1,1 +access_mvd_tcg_mtg_card_type_user,mvd.tcg.mtg.card.type.user,model_mvd_tcg_mtg_card_type,mvd_tcg_base.mvd_tcg_base_group_user,1,0,0,0 +access_mvd_tcg_mtg_card_type_operator,mvd.tcg.mtg.card.type.operator,model_mvd_tcg_mtg_card_type,mvd_tcg_base.mvd_tcg_base_group_operator,1,0,0,0 +access_mvd_tcg_mtg_card_type_manager,mvd.tcg.mtg.card.type.manager,model_mvd_tcg_mtg_card_type,mvd_tcg_base.mvd_tcg_base_group_manager,1,1,1,1 +access_mvd_tcg_mtg_card_type_system,mvd.tcg.mtg.card.type.system,model_mvd_tcg_mtg_card_type,base.group_system,1,1,1,1 +access_mvd_tcg_mtg_keyword_user,mvd.tcg.mtg.keyword.user,model_mvd_tcg_mtg_keyword,mvd_tcg_base.mvd_tcg_base_group_user,1,0,0,0 +access_mvd_tcg_mtg_keyword_operator,mvd.tcg.mtg.keyword.operator,model_mvd_tcg_mtg_keyword,mvd_tcg_base.mvd_tcg_base_group_operator,1,0,0,0 +access_mvd_tcg_mtg_keyword_manager,mvd.tcg.mtg.keyword.manager,model_mvd_tcg_mtg_keyword,mvd_tcg_base.mvd_tcg_base_group_manager,1,1,1,1 +access_mvd_tcg_mtg_keyword_system,mvd.tcg.mtg.keyword.system,model_mvd_tcg_mtg_keyword,base.group_system,1,1,1,1 +access_mvd_tcg_mtg_format_user,mvd.tcg.mtg.format.user,model_mvd_tcg_mtg_format,mvd_tcg_base.mvd_tcg_base_group_user,1,0,0,0 +access_mvd_tcg_mtg_format_operator,mvd.tcg.mtg.format.operator,model_mvd_tcg_mtg_format,mvd_tcg_base.mvd_tcg_base_group_operator,1,0,0,0 +access_mvd_tcg_mtg_format_manager,mvd.tcg.mtg.format.manager,model_mvd_tcg_mtg_format,mvd_tcg_base.mvd_tcg_base_group_manager,1,1,1,1 +access_mvd_tcg_mtg_format_system,mvd.tcg.mtg.format.system,model_mvd_tcg_mtg_format,base.group_system,1,1,1,1 +access_mvd_tcg_mtg_finish_user,mvd.tcg.mtg.finish.user,model_mvd_tcg_mtg_finish,mvd_tcg_base.mvd_tcg_base_group_user,1,0,0,0 +access_mvd_tcg_mtg_finish_operator,mvd.tcg.mtg.finish.operator,model_mvd_tcg_mtg_finish,mvd_tcg_base.mvd_tcg_base_group_operator,1,0,0,0 +access_mvd_tcg_mtg_finish_manager,mvd.tcg.mtg.finish.manager,model_mvd_tcg_mtg_finish,mvd_tcg_base.mvd_tcg_base_group_manager,1,1,1,1 +access_mvd_tcg_mtg_finish_system,mvd.tcg.mtg.finish.system,model_mvd_tcg_mtg_finish,base.group_system,1,1,1,1 +access_mvd_tcg_mtg_platform_user,mvd.tcg.mtg.platform.user,model_mvd_tcg_mtg_platform,mvd_tcg_base.mvd_tcg_base_group_user,1,0,0,0 +access_mvd_tcg_mtg_platform_operator,mvd.tcg.mtg.platform.operator,model_mvd_tcg_mtg_platform,mvd_tcg_base.mvd_tcg_base_group_operator,1,0,0,0 +access_mvd_tcg_mtg_platform_manager,mvd.tcg.mtg.platform.manager,model_mvd_tcg_mtg_platform,mvd_tcg_base.mvd_tcg_base_group_manager,1,1,1,1 +access_mvd_tcg_mtg_platform_system,mvd.tcg.mtg.platform.system,model_mvd_tcg_mtg_platform,base.group_system,1,1,1,1 +access_mvd_tcg_mtg_card_face_user,mvd.tcg.mtg.card.face.user,model_mvd_tcg_mtg_card_face,mvd_tcg_base.mvd_tcg_base_group_user,1,0,0,0 +access_mvd_tcg_mtg_card_face_manager,mvd.tcg.mtg.card.face.manager,model_mvd_tcg_mtg_card_face,mvd_tcg_base.mvd_tcg_base_group_manager,1,1,1,1 +access_mvd_tcg_mtg_card_face_system,mvd.tcg.mtg.card.face.system,model_mvd_tcg_mtg_card_face,base.group_system,1,1,1,1 +access_mvd_tcg_mtg_card_legality_user,mvd.tcg.mtg.card.legality.user,model_mvd_tcg_mtg_card_legality,mvd_tcg_base.mvd_tcg_base_group_user,1,0,0,0 +access_mvd_tcg_mtg_card_legality_manager,mvd.tcg.mtg.card.legality.manager,model_mvd_tcg_mtg_card_legality,mvd_tcg_base.mvd_tcg_base_group_manager,1,1,1,1 +access_mvd_tcg_mtg_card_legality_system,mvd.tcg.mtg.card.legality.system,model_mvd_tcg_mtg_card_legality,base.group_system,1,1,1,1 diff --git a/static/src/img/card-symbols-report/0.png b/static/src/img/card-symbols-report/0.png new file mode 100644 index 0000000..e6c169e Binary files /dev/null and b/static/src/img/card-symbols-report/0.png differ diff --git a/static/src/img/card-symbols-report/1.png b/static/src/img/card-symbols-report/1.png new file mode 100644 index 0000000..9e6b364 Binary files /dev/null and b/static/src/img/card-symbols-report/1.png differ diff --git a/static/src/img/card-symbols-report/10.png b/static/src/img/card-symbols-report/10.png new file mode 100644 index 0000000..f41673d Binary files /dev/null and b/static/src/img/card-symbols-report/10.png differ diff --git a/static/src/img/card-symbols-report/100.png b/static/src/img/card-symbols-report/100.png new file mode 100644 index 0000000..d228e92 Binary files /dev/null and b/static/src/img/card-symbols-report/100.png differ diff --git a/static/src/img/card-symbols-report/1000000.png b/static/src/img/card-symbols-report/1000000.png new file mode 100644 index 0000000..c06814d Binary files /dev/null and b/static/src/img/card-symbols-report/1000000.png differ diff --git a/static/src/img/card-symbols-report/11.png b/static/src/img/card-symbols-report/11.png new file mode 100644 index 0000000..d4817a9 Binary files /dev/null and b/static/src/img/card-symbols-report/11.png differ diff --git a/static/src/img/card-symbols-report/12.png b/static/src/img/card-symbols-report/12.png new file mode 100644 index 0000000..339a86d Binary files /dev/null and b/static/src/img/card-symbols-report/12.png differ diff --git a/static/src/img/card-symbols-report/13.png b/static/src/img/card-symbols-report/13.png new file mode 100644 index 0000000..ed8a5fa Binary files /dev/null and b/static/src/img/card-symbols-report/13.png differ diff --git a/static/src/img/card-symbols-report/14.png b/static/src/img/card-symbols-report/14.png new file mode 100644 index 0000000..509975a Binary files /dev/null and b/static/src/img/card-symbols-report/14.png differ diff --git a/static/src/img/card-symbols-report/15.png b/static/src/img/card-symbols-report/15.png new file mode 100644 index 0000000..f8685e0 Binary files /dev/null and b/static/src/img/card-symbols-report/15.png differ diff --git a/static/src/img/card-symbols-report/16.png b/static/src/img/card-symbols-report/16.png new file mode 100644 index 0000000..58c85dc Binary files /dev/null and b/static/src/img/card-symbols-report/16.png differ diff --git a/static/src/img/card-symbols-report/17.png b/static/src/img/card-symbols-report/17.png new file mode 100644 index 0000000..7a2f06a Binary files /dev/null and b/static/src/img/card-symbols-report/17.png differ diff --git a/static/src/img/card-symbols-report/18.png b/static/src/img/card-symbols-report/18.png new file mode 100644 index 0000000..625580f Binary files /dev/null and b/static/src/img/card-symbols-report/18.png differ diff --git a/static/src/img/card-symbols-report/19.png b/static/src/img/card-symbols-report/19.png new file mode 100644 index 0000000..f3df809 Binary files /dev/null and b/static/src/img/card-symbols-report/19.png differ diff --git a/static/src/img/card-symbols-report/2.png b/static/src/img/card-symbols-report/2.png new file mode 100644 index 0000000..c691876 Binary files /dev/null and b/static/src/img/card-symbols-report/2.png differ diff --git a/static/src/img/card-symbols-report/20.png b/static/src/img/card-symbols-report/20.png new file mode 100644 index 0000000..904aa6d Binary files /dev/null and b/static/src/img/card-symbols-report/20.png differ diff --git a/static/src/img/card-symbols-report/2B.png b/static/src/img/card-symbols-report/2B.png new file mode 100644 index 0000000..7a27ee3 Binary files /dev/null and b/static/src/img/card-symbols-report/2B.png differ diff --git a/static/src/img/card-symbols-report/2G.png b/static/src/img/card-symbols-report/2G.png new file mode 100644 index 0000000..505a7f0 Binary files /dev/null and b/static/src/img/card-symbols-report/2G.png differ diff --git a/static/src/img/card-symbols-report/2R.png b/static/src/img/card-symbols-report/2R.png new file mode 100644 index 0000000..bb1fc8a Binary files /dev/null and b/static/src/img/card-symbols-report/2R.png differ diff --git a/static/src/img/card-symbols-report/2U.png b/static/src/img/card-symbols-report/2U.png new file mode 100644 index 0000000..b3030bc Binary files /dev/null and b/static/src/img/card-symbols-report/2U.png differ diff --git a/static/src/img/card-symbols-report/2W.png b/static/src/img/card-symbols-report/2W.png new file mode 100644 index 0000000..9dd0703 Binary files /dev/null and b/static/src/img/card-symbols-report/2W.png differ diff --git a/static/src/img/card-symbols-report/3.png b/static/src/img/card-symbols-report/3.png new file mode 100644 index 0000000..534d09a Binary files /dev/null and b/static/src/img/card-symbols-report/3.png differ diff --git a/static/src/img/card-symbols-report/4.png b/static/src/img/card-symbols-report/4.png new file mode 100644 index 0000000..60aefc0 Binary files /dev/null and b/static/src/img/card-symbols-report/4.png differ diff --git a/static/src/img/card-symbols-report/5.png b/static/src/img/card-symbols-report/5.png new file mode 100644 index 0000000..d862d44 Binary files /dev/null and b/static/src/img/card-symbols-report/5.png differ diff --git a/static/src/img/card-symbols-report/6.png b/static/src/img/card-symbols-report/6.png new file mode 100644 index 0000000..fe204c8 Binary files /dev/null and b/static/src/img/card-symbols-report/6.png differ diff --git a/static/src/img/card-symbols-report/7.png b/static/src/img/card-symbols-report/7.png new file mode 100644 index 0000000..b400797 Binary files /dev/null and b/static/src/img/card-symbols-report/7.png differ diff --git a/static/src/img/card-symbols-report/8.png b/static/src/img/card-symbols-report/8.png new file mode 100644 index 0000000..774ce87 Binary files /dev/null and b/static/src/img/card-symbols-report/8.png differ diff --git a/static/src/img/card-symbols-report/9.png b/static/src/img/card-symbols-report/9.png new file mode 100644 index 0000000..f128206 Binary files /dev/null and b/static/src/img/card-symbols-report/9.png differ diff --git a/static/src/img/card-symbols-report/A.png b/static/src/img/card-symbols-report/A.png new file mode 100644 index 0000000..9bf2112 Binary files /dev/null and b/static/src/img/card-symbols-report/A.png differ diff --git a/static/src/img/card-symbols-report/B.png b/static/src/img/card-symbols-report/B.png new file mode 100644 index 0000000..31c7203 Binary files /dev/null and b/static/src/img/card-symbols-report/B.png differ diff --git a/static/src/img/card-symbols-report/BG.png b/static/src/img/card-symbols-report/BG.png new file mode 100644 index 0000000..4864b1a Binary files /dev/null and b/static/src/img/card-symbols-report/BG.png differ diff --git a/static/src/img/card-symbols-report/BGP.png b/static/src/img/card-symbols-report/BGP.png new file mode 100644 index 0000000..4f3d1ef Binary files /dev/null and b/static/src/img/card-symbols-report/BGP.png differ diff --git a/static/src/img/card-symbols-report/BP.png b/static/src/img/card-symbols-report/BP.png new file mode 100644 index 0000000..acc295c Binary files /dev/null and b/static/src/img/card-symbols-report/BP.png differ diff --git a/static/src/img/card-symbols-report/BR.png b/static/src/img/card-symbols-report/BR.png new file mode 100644 index 0000000..f102c65 Binary files /dev/null and b/static/src/img/card-symbols-report/BR.png differ diff --git a/static/src/img/card-symbols-report/BRP.png b/static/src/img/card-symbols-report/BRP.png new file mode 100644 index 0000000..fdfa41e Binary files /dev/null and b/static/src/img/card-symbols-report/BRP.png differ diff --git a/static/src/img/card-symbols-report/C.png b/static/src/img/card-symbols-report/C.png new file mode 100644 index 0000000..3ad751b Binary files /dev/null and b/static/src/img/card-symbols-report/C.png differ diff --git a/static/src/img/card-symbols-report/CB.png b/static/src/img/card-symbols-report/CB.png new file mode 100644 index 0000000..b846a35 Binary files /dev/null and b/static/src/img/card-symbols-report/CB.png differ diff --git a/static/src/img/card-symbols-report/CG.png b/static/src/img/card-symbols-report/CG.png new file mode 100644 index 0000000..764fa3c Binary files /dev/null and b/static/src/img/card-symbols-report/CG.png differ diff --git a/static/src/img/card-symbols-report/CHAOS.png b/static/src/img/card-symbols-report/CHAOS.png new file mode 100644 index 0000000..9fd485a Binary files /dev/null and b/static/src/img/card-symbols-report/CHAOS.png differ diff --git a/static/src/img/card-symbols-report/CP.png b/static/src/img/card-symbols-report/CP.png new file mode 100644 index 0000000..b7afb15 Binary files /dev/null and b/static/src/img/card-symbols-report/CP.png differ diff --git a/static/src/img/card-symbols-report/CR.png b/static/src/img/card-symbols-report/CR.png new file mode 100644 index 0000000..b0d8f07 Binary files /dev/null and b/static/src/img/card-symbols-report/CR.png differ diff --git a/static/src/img/card-symbols-report/CU.png b/static/src/img/card-symbols-report/CU.png new file mode 100644 index 0000000..3360290 Binary files /dev/null and b/static/src/img/card-symbols-report/CU.png differ diff --git a/static/src/img/card-symbols-report/CW.png b/static/src/img/card-symbols-report/CW.png new file mode 100644 index 0000000..82f0018 Binary files /dev/null and b/static/src/img/card-symbols-report/CW.png differ diff --git a/static/src/img/card-symbols-report/D.png b/static/src/img/card-symbols-report/D.png new file mode 100644 index 0000000..e21bdc8 Binary files /dev/null and b/static/src/img/card-symbols-report/D.png differ diff --git a/static/src/img/card-symbols-report/E.png b/static/src/img/card-symbols-report/E.png new file mode 100644 index 0000000..78d2b30 Binary files /dev/null and b/static/src/img/card-symbols-report/E.png differ diff --git a/static/src/img/card-symbols-report/G.png b/static/src/img/card-symbols-report/G.png new file mode 100644 index 0000000..d8c3be3 Binary files /dev/null and b/static/src/img/card-symbols-report/G.png differ diff --git a/static/src/img/card-symbols-report/GP.png b/static/src/img/card-symbols-report/GP.png new file mode 100644 index 0000000..9c82150 Binary files /dev/null and b/static/src/img/card-symbols-report/GP.png differ diff --git a/static/src/img/card-symbols-report/GU.png b/static/src/img/card-symbols-report/GU.png new file mode 100644 index 0000000..4220029 Binary files /dev/null and b/static/src/img/card-symbols-report/GU.png differ diff --git a/static/src/img/card-symbols-report/GUP.png b/static/src/img/card-symbols-report/GUP.png new file mode 100644 index 0000000..6012818 Binary files /dev/null and b/static/src/img/card-symbols-report/GUP.png differ diff --git a/static/src/img/card-symbols-report/GW.png b/static/src/img/card-symbols-report/GW.png new file mode 100644 index 0000000..9c2d6a2 Binary files /dev/null and b/static/src/img/card-symbols-report/GW.png differ diff --git a/static/src/img/card-symbols-report/GWP.png b/static/src/img/card-symbols-report/GWP.png new file mode 100644 index 0000000..d571816 Binary files /dev/null and b/static/src/img/card-symbols-report/GWP.png differ diff --git a/static/src/img/card-symbols-report/H.png b/static/src/img/card-symbols-report/H.png new file mode 100644 index 0000000..5489b7c Binary files /dev/null and b/static/src/img/card-symbols-report/H.png differ diff --git a/static/src/img/card-symbols-report/HALF.png b/static/src/img/card-symbols-report/HALF.png new file mode 100644 index 0000000..1c6a871 Binary files /dev/null and b/static/src/img/card-symbols-report/HALF.png differ diff --git a/static/src/img/card-symbols-report/HR.png b/static/src/img/card-symbols-report/HR.png new file mode 100644 index 0000000..1a336e8 Binary files /dev/null and b/static/src/img/card-symbols-report/HR.png differ diff --git a/static/src/img/card-symbols-report/HW.png b/static/src/img/card-symbols-report/HW.png new file mode 100644 index 0000000..3ef45fc Binary files /dev/null and b/static/src/img/card-symbols-report/HW.png differ diff --git a/static/src/img/card-symbols-report/INFINITY.png b/static/src/img/card-symbols-report/INFINITY.png new file mode 100644 index 0000000..eaf380c Binary files /dev/null and b/static/src/img/card-symbols-report/INFINITY.png differ diff --git a/static/src/img/card-symbols-report/L.png b/static/src/img/card-symbols-report/L.png new file mode 100644 index 0000000..2bb4861 Binary files /dev/null and b/static/src/img/card-symbols-report/L.png differ diff --git a/static/src/img/card-symbols-report/P.png b/static/src/img/card-symbols-report/P.png new file mode 100644 index 0000000..7985d61 Binary files /dev/null and b/static/src/img/card-symbols-report/P.png differ diff --git a/static/src/img/card-symbols-report/PW.png b/static/src/img/card-symbols-report/PW.png new file mode 100644 index 0000000..2bc0367 Binary files /dev/null and b/static/src/img/card-symbols-report/PW.png differ diff --git a/static/src/img/card-symbols-report/Q.png b/static/src/img/card-symbols-report/Q.png new file mode 100644 index 0000000..e85da44 Binary files /dev/null and b/static/src/img/card-symbols-report/Q.png differ diff --git a/static/src/img/card-symbols-report/R.png b/static/src/img/card-symbols-report/R.png new file mode 100644 index 0000000..bfbbfee Binary files /dev/null and b/static/src/img/card-symbols-report/R.png differ diff --git a/static/src/img/card-symbols-report/RG.png b/static/src/img/card-symbols-report/RG.png new file mode 100644 index 0000000..652e7d1 Binary files /dev/null and b/static/src/img/card-symbols-report/RG.png differ diff --git a/static/src/img/card-symbols-report/RGP.png b/static/src/img/card-symbols-report/RGP.png new file mode 100644 index 0000000..e553878 Binary files /dev/null and b/static/src/img/card-symbols-report/RGP.png differ diff --git a/static/src/img/card-symbols-report/RP.png b/static/src/img/card-symbols-report/RP.png new file mode 100644 index 0000000..65bcb3f Binary files /dev/null and b/static/src/img/card-symbols-report/RP.png differ diff --git a/static/src/img/card-symbols-report/RW.png b/static/src/img/card-symbols-report/RW.png new file mode 100644 index 0000000..50187c6 Binary files /dev/null and b/static/src/img/card-symbols-report/RW.png differ diff --git a/static/src/img/card-symbols-report/RWP.png b/static/src/img/card-symbols-report/RWP.png new file mode 100644 index 0000000..69a6916 Binary files /dev/null and b/static/src/img/card-symbols-report/RWP.png differ diff --git a/static/src/img/card-symbols-report/S.png b/static/src/img/card-symbols-report/S.png new file mode 100644 index 0000000..109a630 Binary files /dev/null and b/static/src/img/card-symbols-report/S.png differ diff --git a/static/src/img/card-symbols-report/T.png b/static/src/img/card-symbols-report/T.png new file mode 100644 index 0000000..e11238b Binary files /dev/null and b/static/src/img/card-symbols-report/T.png differ diff --git a/static/src/img/card-symbols-report/TK.png b/static/src/img/card-symbols-report/TK.png new file mode 100644 index 0000000..317cc04 Binary files /dev/null and b/static/src/img/card-symbols-report/TK.png differ diff --git a/static/src/img/card-symbols-report/U.png b/static/src/img/card-symbols-report/U.png new file mode 100644 index 0000000..40b22d2 Binary files /dev/null and b/static/src/img/card-symbols-report/U.png differ diff --git a/static/src/img/card-symbols-report/UB.png b/static/src/img/card-symbols-report/UB.png new file mode 100644 index 0000000..962ca27 Binary files /dev/null and b/static/src/img/card-symbols-report/UB.png differ diff --git a/static/src/img/card-symbols-report/UBP.png b/static/src/img/card-symbols-report/UBP.png new file mode 100644 index 0000000..44e22fc Binary files /dev/null and b/static/src/img/card-symbols-report/UBP.png differ diff --git a/static/src/img/card-symbols-report/UP.png b/static/src/img/card-symbols-report/UP.png new file mode 100644 index 0000000..54bf470 Binary files /dev/null and b/static/src/img/card-symbols-report/UP.png differ diff --git a/static/src/img/card-symbols-report/UR.png b/static/src/img/card-symbols-report/UR.png new file mode 100644 index 0000000..38119b3 Binary files /dev/null and b/static/src/img/card-symbols-report/UR.png differ diff --git a/static/src/img/card-symbols-report/URP.png b/static/src/img/card-symbols-report/URP.png new file mode 100644 index 0000000..0cffd4d Binary files /dev/null and b/static/src/img/card-symbols-report/URP.png differ diff --git a/static/src/img/card-symbols-report/W.png b/static/src/img/card-symbols-report/W.png new file mode 100644 index 0000000..57f9e8d Binary files /dev/null and b/static/src/img/card-symbols-report/W.png differ diff --git a/static/src/img/card-symbols-report/WB.png b/static/src/img/card-symbols-report/WB.png new file mode 100644 index 0000000..e1959cf Binary files /dev/null and b/static/src/img/card-symbols-report/WB.png differ diff --git a/static/src/img/card-symbols-report/WBP.png b/static/src/img/card-symbols-report/WBP.png new file mode 100644 index 0000000..6c13c62 Binary files /dev/null and b/static/src/img/card-symbols-report/WBP.png differ diff --git a/static/src/img/card-symbols-report/WP.png b/static/src/img/card-symbols-report/WP.png new file mode 100644 index 0000000..161f8f9 Binary files /dev/null and b/static/src/img/card-symbols-report/WP.png differ diff --git a/static/src/img/card-symbols-report/WU.png b/static/src/img/card-symbols-report/WU.png new file mode 100644 index 0000000..ec453f4 Binary files /dev/null and b/static/src/img/card-symbols-report/WU.png differ diff --git a/static/src/img/card-symbols-report/WUP.png b/static/src/img/card-symbols-report/WUP.png new file mode 100644 index 0000000..d3d9d4f Binary files /dev/null and b/static/src/img/card-symbols-report/WUP.png differ diff --git a/static/src/img/card-symbols-report/X.png b/static/src/img/card-symbols-report/X.png new file mode 100644 index 0000000..2b2feff Binary files /dev/null and b/static/src/img/card-symbols-report/X.png differ diff --git a/static/src/img/card-symbols-report/Y.png b/static/src/img/card-symbols-report/Y.png new file mode 100644 index 0000000..59b9a81 Binary files /dev/null and b/static/src/img/card-symbols-report/Y.png differ diff --git a/static/src/img/card-symbols-report/Z.png b/static/src/img/card-symbols-report/Z.png new file mode 100644 index 0000000..dd2c362 Binary files /dev/null and b/static/src/img/card-symbols-report/Z.png differ diff --git a/static/src/img/card-symbols/0.svg b/static/src/img/card-symbols/0.svg new file mode 100644 index 0000000..7f986c8 --- /dev/null +++ b/static/src/img/card-symbols/0.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/1.svg b/static/src/img/card-symbols/1.svg new file mode 100644 index 0000000..26d5ac9 --- /dev/null +++ b/static/src/img/card-symbols/1.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/10.svg b/static/src/img/card-symbols/10.svg new file mode 100644 index 0000000..9292cf8 --- /dev/null +++ b/static/src/img/card-symbols/10.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/100.svg b/static/src/img/card-symbols/100.svg new file mode 100644 index 0000000..95ab348 --- /dev/null +++ b/static/src/img/card-symbols/100.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/1000000.svg b/static/src/img/card-symbols/1000000.svg new file mode 100644 index 0000000..772d346 --- /dev/null +++ b/static/src/img/card-symbols/1000000.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/11.svg b/static/src/img/card-symbols/11.svg new file mode 100644 index 0000000..12c7604 --- /dev/null +++ b/static/src/img/card-symbols/11.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/12.svg b/static/src/img/card-symbols/12.svg new file mode 100644 index 0000000..853cb04 --- /dev/null +++ b/static/src/img/card-symbols/12.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/13.svg b/static/src/img/card-symbols/13.svg new file mode 100644 index 0000000..6cc92fb --- /dev/null +++ b/static/src/img/card-symbols/13.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/14.svg b/static/src/img/card-symbols/14.svg new file mode 100644 index 0000000..b8d3442 --- /dev/null +++ b/static/src/img/card-symbols/14.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/15.svg b/static/src/img/card-symbols/15.svg new file mode 100644 index 0000000..6a06e1b --- /dev/null +++ b/static/src/img/card-symbols/15.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/16.svg b/static/src/img/card-symbols/16.svg new file mode 100644 index 0000000..34c9001 --- /dev/null +++ b/static/src/img/card-symbols/16.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/17.svg b/static/src/img/card-symbols/17.svg new file mode 100644 index 0000000..48515fb --- /dev/null +++ b/static/src/img/card-symbols/17.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/18.svg b/static/src/img/card-symbols/18.svg new file mode 100644 index 0000000..37dc1d7 --- /dev/null +++ b/static/src/img/card-symbols/18.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/19.svg b/static/src/img/card-symbols/19.svg new file mode 100644 index 0000000..6e139d8 --- /dev/null +++ b/static/src/img/card-symbols/19.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/2.svg b/static/src/img/card-symbols/2.svg new file mode 100644 index 0000000..94647e0 --- /dev/null +++ b/static/src/img/card-symbols/2.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/20.svg b/static/src/img/card-symbols/20.svg new file mode 100644 index 0000000..f6e6649 --- /dev/null +++ b/static/src/img/card-symbols/20.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/2B.svg b/static/src/img/card-symbols/2B.svg new file mode 100644 index 0000000..e1e4618 --- /dev/null +++ b/static/src/img/card-symbols/2B.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/2G.svg b/static/src/img/card-symbols/2G.svg new file mode 100644 index 0000000..91e0122 --- /dev/null +++ b/static/src/img/card-symbols/2G.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/2R.svg b/static/src/img/card-symbols/2R.svg new file mode 100644 index 0000000..b8cb3db --- /dev/null +++ b/static/src/img/card-symbols/2R.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/2U.svg b/static/src/img/card-symbols/2U.svg new file mode 100644 index 0000000..12d2ec5 --- /dev/null +++ b/static/src/img/card-symbols/2U.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/2W.svg b/static/src/img/card-symbols/2W.svg new file mode 100644 index 0000000..87334de --- /dev/null +++ b/static/src/img/card-symbols/2W.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/3.svg b/static/src/img/card-symbols/3.svg new file mode 100644 index 0000000..90ce75d --- /dev/null +++ b/static/src/img/card-symbols/3.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/4.svg b/static/src/img/card-symbols/4.svg new file mode 100644 index 0000000..aa5e02f --- /dev/null +++ b/static/src/img/card-symbols/4.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/5.svg b/static/src/img/card-symbols/5.svg new file mode 100644 index 0000000..e13fac6 --- /dev/null +++ b/static/src/img/card-symbols/5.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/6.svg b/static/src/img/card-symbols/6.svg new file mode 100644 index 0000000..c4b891a --- /dev/null +++ b/static/src/img/card-symbols/6.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/7.svg b/static/src/img/card-symbols/7.svg new file mode 100644 index 0000000..0c033d8 --- /dev/null +++ b/static/src/img/card-symbols/7.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/8.svg b/static/src/img/card-symbols/8.svg new file mode 100644 index 0000000..d593ab4 --- /dev/null +++ b/static/src/img/card-symbols/8.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/9.svg b/static/src/img/card-symbols/9.svg new file mode 100644 index 0000000..695e71d --- /dev/null +++ b/static/src/img/card-symbols/9.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/A.svg b/static/src/img/card-symbols/A.svg new file mode 100644 index 0000000..890f822 --- /dev/null +++ b/static/src/img/card-symbols/A.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/B.svg b/static/src/img/card-symbols/B.svg new file mode 100644 index 0000000..2e8b71b --- /dev/null +++ b/static/src/img/card-symbols/B.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/BG.svg b/static/src/img/card-symbols/BG.svg new file mode 100644 index 0000000..579e078 --- /dev/null +++ b/static/src/img/card-symbols/BG.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/BGP.svg b/static/src/img/card-symbols/BGP.svg new file mode 100644 index 0000000..6997fb6 --- /dev/null +++ b/static/src/img/card-symbols/BGP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/BP.svg b/static/src/img/card-symbols/BP.svg new file mode 100644 index 0000000..a4943ea --- /dev/null +++ b/static/src/img/card-symbols/BP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/BR.svg b/static/src/img/card-symbols/BR.svg new file mode 100644 index 0000000..a049695 --- /dev/null +++ b/static/src/img/card-symbols/BR.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/BRP.svg b/static/src/img/card-symbols/BRP.svg new file mode 100644 index 0000000..87784fd --- /dev/null +++ b/static/src/img/card-symbols/BRP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/C.svg b/static/src/img/card-symbols/C.svg new file mode 100644 index 0000000..145461b --- /dev/null +++ b/static/src/img/card-symbols/C.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/CB.svg b/static/src/img/card-symbols/CB.svg new file mode 100644 index 0000000..d2c179d --- /dev/null +++ b/static/src/img/card-symbols/CB.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/CG.svg b/static/src/img/card-symbols/CG.svg new file mode 100644 index 0000000..1284e3f --- /dev/null +++ b/static/src/img/card-symbols/CG.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/CHAOS.svg b/static/src/img/card-symbols/CHAOS.svg new file mode 100644 index 0000000..6e686fc --- /dev/null +++ b/static/src/img/card-symbols/CHAOS.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/CP.svg b/static/src/img/card-symbols/CP.svg new file mode 100644 index 0000000..6a8ded5 --- /dev/null +++ b/static/src/img/card-symbols/CP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/CR.svg b/static/src/img/card-symbols/CR.svg new file mode 100644 index 0000000..623055b --- /dev/null +++ b/static/src/img/card-symbols/CR.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/CU.svg b/static/src/img/card-symbols/CU.svg new file mode 100644 index 0000000..7182bb9 --- /dev/null +++ b/static/src/img/card-symbols/CU.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/CW.svg b/static/src/img/card-symbols/CW.svg new file mode 100644 index 0000000..3e82592 --- /dev/null +++ b/static/src/img/card-symbols/CW.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/D.svg b/static/src/img/card-symbols/D.svg new file mode 100644 index 0000000..16675d7 --- /dev/null +++ b/static/src/img/card-symbols/D.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/E.svg b/static/src/img/card-symbols/E.svg new file mode 100644 index 0000000..5717bbb --- /dev/null +++ b/static/src/img/card-symbols/E.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/G.svg b/static/src/img/card-symbols/G.svg new file mode 100644 index 0000000..618094a --- /dev/null +++ b/static/src/img/card-symbols/G.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/GP.svg b/static/src/img/card-symbols/GP.svg new file mode 100644 index 0000000..4dcd234 --- /dev/null +++ b/static/src/img/card-symbols/GP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/GU.svg b/static/src/img/card-symbols/GU.svg new file mode 100644 index 0000000..c80cd44 --- /dev/null +++ b/static/src/img/card-symbols/GU.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/GUP.svg b/static/src/img/card-symbols/GUP.svg new file mode 100644 index 0000000..66bef39 --- /dev/null +++ b/static/src/img/card-symbols/GUP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/GW.svg b/static/src/img/card-symbols/GW.svg new file mode 100644 index 0000000..56ac5d4 --- /dev/null +++ b/static/src/img/card-symbols/GW.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/GWP.svg b/static/src/img/card-symbols/GWP.svg new file mode 100644 index 0000000..c0e55bf --- /dev/null +++ b/static/src/img/card-symbols/GWP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/H.svg b/static/src/img/card-symbols/H.svg new file mode 100644 index 0000000..8732009 --- /dev/null +++ b/static/src/img/card-symbols/H.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/HALF.svg b/static/src/img/card-symbols/HALF.svg new file mode 100644 index 0000000..8ece9fc --- /dev/null +++ b/static/src/img/card-symbols/HALF.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/HR.svg b/static/src/img/card-symbols/HR.svg new file mode 100644 index 0000000..07d1cca --- /dev/null +++ b/static/src/img/card-symbols/HR.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/HW.svg b/static/src/img/card-symbols/HW.svg new file mode 100644 index 0000000..b473cb2 --- /dev/null +++ b/static/src/img/card-symbols/HW.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/INFINITY.svg b/static/src/img/card-symbols/INFINITY.svg new file mode 100644 index 0000000..ebab618 --- /dev/null +++ b/static/src/img/card-symbols/INFINITY.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/L.svg b/static/src/img/card-symbols/L.svg new file mode 100644 index 0000000..c199f21 --- /dev/null +++ b/static/src/img/card-symbols/L.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/P.svg b/static/src/img/card-symbols/P.svg new file mode 100644 index 0000000..b3eedb3 --- /dev/null +++ b/static/src/img/card-symbols/P.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/PW.svg b/static/src/img/card-symbols/PW.svg new file mode 100644 index 0000000..99b49ee --- /dev/null +++ b/static/src/img/card-symbols/PW.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/Q.svg b/static/src/img/card-symbols/Q.svg new file mode 100644 index 0000000..07600a2 --- /dev/null +++ b/static/src/img/card-symbols/Q.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/R.svg b/static/src/img/card-symbols/R.svg new file mode 100644 index 0000000..c330b01 --- /dev/null +++ b/static/src/img/card-symbols/R.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/RG.svg b/static/src/img/card-symbols/RG.svg new file mode 100644 index 0000000..5024a00 --- /dev/null +++ b/static/src/img/card-symbols/RG.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/RGP.svg b/static/src/img/card-symbols/RGP.svg new file mode 100644 index 0000000..c2b534a --- /dev/null +++ b/static/src/img/card-symbols/RGP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/RP.svg b/static/src/img/card-symbols/RP.svg new file mode 100644 index 0000000..d1f108f --- /dev/null +++ b/static/src/img/card-symbols/RP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/RW.svg b/static/src/img/card-symbols/RW.svg new file mode 100644 index 0000000..c1457e3 --- /dev/null +++ b/static/src/img/card-symbols/RW.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/RWP.svg b/static/src/img/card-symbols/RWP.svg new file mode 100644 index 0000000..a5ab53f --- /dev/null +++ b/static/src/img/card-symbols/RWP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/S.svg b/static/src/img/card-symbols/S.svg new file mode 100644 index 0000000..066d003 --- /dev/null +++ b/static/src/img/card-symbols/S.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/T.svg b/static/src/img/card-symbols/T.svg new file mode 100644 index 0000000..a2189dd --- /dev/null +++ b/static/src/img/card-symbols/T.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/TK.svg b/static/src/img/card-symbols/TK.svg new file mode 100644 index 0000000..bb5446a --- /dev/null +++ b/static/src/img/card-symbols/TK.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/U.svg b/static/src/img/card-symbols/U.svg new file mode 100644 index 0000000..e693d70 --- /dev/null +++ b/static/src/img/card-symbols/U.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/UB.svg b/static/src/img/card-symbols/UB.svg new file mode 100644 index 0000000..c3b840b --- /dev/null +++ b/static/src/img/card-symbols/UB.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/UBP.svg b/static/src/img/card-symbols/UBP.svg new file mode 100644 index 0000000..447280f --- /dev/null +++ b/static/src/img/card-symbols/UBP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/UP.svg b/static/src/img/card-symbols/UP.svg new file mode 100644 index 0000000..f945bf4 --- /dev/null +++ b/static/src/img/card-symbols/UP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/UR.svg b/static/src/img/card-symbols/UR.svg new file mode 100644 index 0000000..0b997fc --- /dev/null +++ b/static/src/img/card-symbols/UR.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/URP.svg b/static/src/img/card-symbols/URP.svg new file mode 100644 index 0000000..0386ad9 --- /dev/null +++ b/static/src/img/card-symbols/URP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/W.svg b/static/src/img/card-symbols/W.svg new file mode 100644 index 0000000..0ca88a5 --- /dev/null +++ b/static/src/img/card-symbols/W.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/WB.svg b/static/src/img/card-symbols/WB.svg new file mode 100644 index 0000000..eff6e0a --- /dev/null +++ b/static/src/img/card-symbols/WB.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/WBP.svg b/static/src/img/card-symbols/WBP.svg new file mode 100644 index 0000000..cfe880d --- /dev/null +++ b/static/src/img/card-symbols/WBP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/WP.svg b/static/src/img/card-symbols/WP.svg new file mode 100644 index 0000000..9f6f4e1 --- /dev/null +++ b/static/src/img/card-symbols/WP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/WU.svg b/static/src/img/card-symbols/WU.svg new file mode 100644 index 0000000..6765419 --- /dev/null +++ b/static/src/img/card-symbols/WU.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/WUP.svg b/static/src/img/card-symbols/WUP.svg new file mode 100644 index 0000000..cfe913b --- /dev/null +++ b/static/src/img/card-symbols/WUP.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/X.svg b/static/src/img/card-symbols/X.svg new file mode 100644 index 0000000..9738680 --- /dev/null +++ b/static/src/img/card-symbols/X.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/Y.svg b/static/src/img/card-symbols/Y.svg new file mode 100644 index 0000000..5ac2539 --- /dev/null +++ b/static/src/img/card-symbols/Y.svg @@ -0,0 +1 @@ + diff --git a/static/src/img/card-symbols/Z.svg b/static/src/img/card-symbols/Z.svg new file mode 100644 index 0000000..d46228e --- /dev/null +++ b/static/src/img/card-symbols/Z.svg @@ -0,0 +1 @@ + diff --git a/static/src/js/fields/mtg_symbol_catalog.js b/static/src/js/fields/mtg_symbol_catalog.js new file mode 100644 index 0000000..8f305e8 --- /dev/null +++ b/static/src/js/fields/mtg_symbol_catalog.js @@ -0,0 +1,90 @@ +/** @odoo-module **/ + +// Generated from https://api.scryfall.com/symbology on 2026-04-01. + +export const MTG_SYMBOL_CATALOG = Object.freeze({ + "T": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/T.svg", title: "tap this permanent" }), + "Q": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/Q.svg", title: "untap this permanent" }), + "E": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/E.svg", title: "an energy counter" }), + "P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/P.svg", title: "modal budget pawprint" }), + "PW": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/PW.svg", title: "planeswalker" }), + "CHAOS": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/CHAOS.svg", title: "chaos" }), + "A": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/A.svg", title: "an acorn counter" }), + "TK": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/TK.svg", title: "a ticket counter" }), + "X": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/X.svg", title: "X generic mana" }), + "Y": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/Y.svg", title: "Y generic mana" }), + "Z": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/Z.svg", title: "Z generic mana" }), + "0": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/0.svg", title: "zero mana" }), + "½": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/HALF.svg", title: "one-half generic mana" }), + "1": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/1.svg", title: "one generic mana" }), + "2": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/2.svg", title: "two generic mana" }), + "3": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/3.svg", title: "three generic mana" }), + "4": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/4.svg", title: "four generic mana" }), + "5": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/5.svg", title: "five generic mana" }), + "6": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/6.svg", title: "six generic mana" }), + "7": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/7.svg", title: "seven generic mana" }), + "8": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/8.svg", title: "eight generic mana" }), + "9": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/9.svg", title: "nine generic mana" }), + "10": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/10.svg", title: "ten generic mana" }), + "11": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/11.svg", title: "eleven generic mana" }), + "12": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/12.svg", title: "twelve generic mana" }), + "13": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/13.svg", title: "thirteen generic mana" }), + "14": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/14.svg", title: "fourteen generic mana" }), + "15": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/15.svg", title: "fifteen generic mana" }), + "16": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/16.svg", title: "sixteen generic mana" }), + "17": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/17.svg", title: "seventeen generic mana" }), + "18": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/18.svg", title: "eighteen generic mana" }), + "19": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/19.svg", title: "nineteen generic mana" }), + "20": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/20.svg", title: "twenty generic mana" }), + "100": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/100.svg", title: "one hundred generic mana" }), + "1000000": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/1000000.svg", title: "one million generic mana" }), + "∞": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/INFINITY.svg", title: "infinite generic mana" }), + "W/U": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/WU.svg", title: "one white or blue mana" }), + "W/B": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/WB.svg", title: "one white or black mana" }), + "B/R": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/BR.svg", title: "one black or red mana" }), + "B/G": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/BG.svg", title: "one black or green mana" }), + "U/B": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/UB.svg", title: "one blue or black mana" }), + "U/R": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/UR.svg", title: "one blue or red mana" }), + "R/G": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/RG.svg", title: "one red or green mana" }), + "R/W": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/RW.svg", title: "one red or white mana" }), + "G/W": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/GW.svg", title: "one green or white mana" }), + "G/U": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/GU.svg", title: "one green or blue mana" }), + "B/G/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/BGP.svg", title: "one black mana, one green mana, or 2 life" }), + "B/R/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/BRP.svg", title: "one black mana, one red mana, or 2 life" }), + "G/U/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/GUP.svg", title: "one green mana, one blue mana, or 2 life" }), + "G/W/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/GWP.svg", title: "one green mana, one white mana, or 2 life" }), + "R/G/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/RGP.svg", title: "one red mana, one green mana, or 2 life" }), + "R/W/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/RWP.svg", title: "one red mana, one white mana, or 2 life" }), + "U/B/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/UBP.svg", title: "one blue mana, one black mana, or 2 life" }), + "U/R/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/URP.svg", title: "one blue mana, one red mana, or 2 life" }), + "W/B/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/WBP.svg", title: "one white mana, one black mana, or 2 life" }), + "W/U/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/WUP.svg", title: "one white mana, one blue mana, or 2 life" }), + "C/W": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/CW.svg", title: "one colorless mana or one white mana" }), + "C/U": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/CU.svg", title: "one colorless mana or one blue mana" }), + "C/B": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/CB.svg", title: "one colorless mana or one black mana" }), + "C/R": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/CR.svg", title: "one colorless mana or one red mana" }), + "C/G": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/CG.svg", title: "one colorless mana or one green mana" }), + "2/W": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/2W.svg", title: "two generic mana or one white mana" }), + "2/U": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/2U.svg", title: "two generic mana or one blue mana" }), + "2/B": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/2B.svg", title: "two generic mana or one black mana" }), + "2/R": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/2R.svg", title: "two generic mana or one red mana" }), + "2/G": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/2G.svg", title: "two generic mana or one green mana" }), + "H": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/H.svg", title: "one colored mana or two life" }), + "W/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/WP.svg", title: "one white mana or two life" }), + "U/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/UP.svg", title: "one blue mana or two life" }), + "B/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/BP.svg", title: "one black mana or two life" }), + "R/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/RP.svg", title: "one red mana or two life" }), + "G/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/GP.svg", title: "one green mana or two life" }), + "C/P": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/CP.svg", title: "one colorless mana or two life" }), + "HW": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/HW.svg", title: "one-half white mana" }), + "HR": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/HR.svg", title: "one-half red mana" }), + "W": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/W.svg", title: "one white mana" }), + "U": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/U.svg", title: "one blue mana" }), + "B": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/B.svg", title: "one black mana" }), + "R": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/R.svg", title: "one red mana" }), + "G": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/G.svg", title: "one green mana" }), + "C": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/C.svg", title: "one colorless mana" }), + "S": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/S.svg", title: "one snow mana" }), + "L": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/L.svg", title: "one mana from a legendary source" }), + "D": Object.freeze({ src: "/mvd_tcg_mtg/static/src/img/card-symbols/D.svg", title: "one potential land drop" }), +}); diff --git a/static/src/js/fields/mtg_symbols_field.js b/static/src/js/fields/mtg_symbols_field.js new file mode 100644 index 0000000..3dad1f5 --- /dev/null +++ b/static/src/js/fields/mtg_symbols_field.js @@ -0,0 +1,248 @@ +/** @odoo-module **/ + +import { _t } from "@web/core/l10n/translation"; +import { registry } from "@web/core/registry"; +import { useAutoresize } from "@web/core/utils/autoresize"; +import { useSpellCheck } from "@web/core/utils/hooks"; +import { useInputField } from "@web/views/fields/input_field_hook"; +import { parseInteger } from "@web/views/fields/parsers"; +import { standardFieldProps } from "@web/views/fields/standard_field_props"; +import { TranslationButton } from "@web/views/fields/translation_button"; + +import { Component, useRef } from "@odoo/owl"; + +import { MTG_SYMBOL_CATALOG } from "./mtg_symbol_catalog"; + +const SYMBOL_PATTERN = /\{([^}]+)\}/g; +const NUMERIC_SYMBOL_PATTERN = /^(?:\d+|½|∞)$/; + +/** + * Normalize an MTG symbol code from ``{W}``-style notation. + * + * Args: + * rawCode (string): Symbol content without braces. + * + * Returns: + * string: Uppercase normalized symbol code. + */ +function normalizeSymbolCode(rawCode) { + return String(rawCode || "").trim().toUpperCase(); +} + +/** + * Build a display descriptor for one MTG symbol token. + * + * Args: + * rawCode (string): Symbol content without braces. + * index (number): Stable rendering index. + * + * Returns: + * Object: Render descriptor for the widget template. + */ +function buildSymbolPart(rawCode, index) { + const code = normalizeSymbolCode(rawCode); + const key = `symbol-${index}`; + if (!code) { + return { + key, + type: "text", + text: "{}", + }; + } + + if (NUMERIC_SYMBOL_PATTERN.test(code) && code in MTG_SYMBOL_CATALOG) { + const symbolInfo = MTG_SYMBOL_CATALOG[code]; + return { + key, + type: "symbol", + alt: symbolInfo.title || `{${code}}`, + className: "o_mvd_mtg_symbol o_mvd_mtg_symbol--numeric", + src: symbolInfo.src, + title: symbolInfo.title || `{${code}}`, + }; + } + + if (code in MTG_SYMBOL_CATALOG) { + const symbolInfo = MTG_SYMBOL_CATALOG[code]; + return { + key, + type: "symbol", + alt: symbolInfo.title || `{${code}}`, + className: "o_mvd_mtg_symbol", + src: symbolInfo.src, + title: symbolInfo.title || `{${code}}`, + }; + } + + return { + key, + type: "symbol", + alt: `{${code}}`, + className: "o_mvd_mtg_symbol o_mvd_mtg_symbol--fallback", + display: code, + title: `{${code}}`, + }; +} + +/** + * Split plain MTG text into text and symbol segments. + * + * Args: + * value (string): Source text with ``{...}`` symbol markers. + * + * Returns: + * Array: Ordered render parts for the widget. + */ +function tokenizeSymbolText(value) { + const text = value || ""; + const parts = []; + let cursor = 0; + let index = 0; + + for (const match of text.matchAll(SYMBOL_PATTERN)) { + if (match.index > cursor) { + parts.push({ + key: `text-${index}`, + type: "text", + text: text.slice(cursor, match.index), + }); + index += 1; + } + parts.push(buildSymbolPart(match[1], index)); + index += 1; + cursor = match.index + match[0].length; + } + + if (cursor < text.length || !parts.length) { + parts.push({ + key: `text-${index}`, + type: "text", + text: text.slice(cursor), + }); + } + + return parts; +} + +export class MtgSymbolsField extends Component { + static template = "mvd_tcg_mtg.MtgSymbolsField"; + static components = { + TranslationButton, + }; + static props = { + ...standardFieldProps, + lineBreaks: { type: Boolean, optional: true }, + placeholder: { type: String, optional: true }, + rowCount: { type: Number, optional: true }, + }; + static defaultProps = { + lineBreaks: true, + rowCount: 2, + }; + + setup() { + this.inputRef = useRef("input"); + this.textareaRef = useRef("textarea"); + + useInputField({ + getValue: () => this.rawValue, + refName: this.isMultiline ? "textarea" : "input", + parse: (value) => this.parse(value), + preventLineBreaks: !this.isMultiline || !this.props.lineBreaks, + }); + + if (this.isMultiline) { + useSpellCheck({ refName: "textarea" }); + useAutoresize(this.textareaRef, { minimumHeight: this.minimumHeight }); + } + } + + /** + * Return the current field value as a string. + * + * Returns: + * string: Current record value or an empty string. + */ + get rawValue() { + return this.props.record.data[this.props.name] || ""; + } + + /** + * Flag whether this widget should edit as a textarea. + * + * Returns: + * boolean: ``True`` for text fields, else ``False``. + */ + get isMultiline() { + return this.props.record.fields[this.props.name].type === "text"; + } + + /** + * Flag whether Odoo translations are enabled for the field. + * + * Returns: + * boolean: ``True`` when the field is translatable. + */ + get isTranslatable() { + return Boolean(this.props.record.fields[this.props.name].translate); + } + + /** + * Resolve rendered readonly parts for the current field value. + * + * Returns: + * Array: Render segments for plain text and symbols. + */ + get renderedParts() { + return tokenizeSymbolText(this.rawValue); + } + + /** + * Compute the textarea minimum height for multiline fields. + * + * Returns: + * number: Minimum textarea height in pixels. + */ + get minimumHeight() { + return this.props.lineBreaks && this.isMultiline ? 64 : 0; + } + + /** + * Compute the visible row count for multiline fields. + * + * Returns: + * number: Visible row count. + */ + get rowCount() { + return this.props.lineBreaks && this.isMultiline ? this.props.rowCount : 1; + } + + /** + * Parse input values back into the model. + * + * Args: + * value (string): User-provided field value. + * + * Returns: + * string: Trimmed or raw field value. + */ + parse(value) { + if (this.props.record.fields[this.props.name].trim) { + return value.trim(); + } + return value; + } +} + +export const mtgSymbolsField = { + component: MtgSymbolsField, + displayName: _t("MTG Symbols"), + supportedTypes: ["char", "text"], + extractProps: ({ attrs, options, placeholder }) => ({ + lineBreaks: options?.line_breaks !== undefined ? Boolean(options.line_breaks) : true, + placeholder, + rowCount: attrs.rows && parseInteger(attrs.rows), + }), +}; + +registry.category("fields").add("mtg_symbols", mtgSymbolsField); diff --git a/static/src/scss/mtg_symbols_field.scss b/static/src/scss/mtg_symbols_field.scss new file mode 100644 index 0000000..322e536 --- /dev/null +++ b/static/src/scss/mtg_symbols_field.scss @@ -0,0 +1,50 @@ +.o_mvd_mtg_symbols { + display: inline; + white-space: pre-wrap; + line-height: 1.5; +} + +.o_mvd_mtg_symbols--multiline { + display: block; +} + +.o_mvd_mtg_symbol { + display: inline-block; + width: 1.25em; + height: 1.25em; + margin: 0 0.06em; + vertical-align: -0.2em; +} + +.o_mvd_mtg_symbol--fallback { + display: inline-flex; + align-items: center; + justify-content: center; + width: auto; + min-width: 1.5em; + padding: 0 0.2em; + border: 1px solid #9ca3af; + border-radius: 999px; + background: #f3f4f6; + color: #111827; + font-size: 0.8em; + font-weight: 700; + line-height: 1.2; + vertical-align: text-top; +} + +.o_mvd_mtg_symbol--numeric { + width: 1.25em; + height: 1.25em; + margin: 0 0.06em; + vertical-align: -0.2em; +} + +.o_mvd_mtg_symbols_preview { + min-height: 1.75rem; +} + +.o_mvd_mtg_symbols_preview .o_mvd_mtg_symbol { + width: 1.35em; + height: 1.35em; +} diff --git a/static/src/xml/mtg_symbols_field.xml b/static/src/xml/mtg_symbols_field.xml new file mode 100644 index 0000000..7614884 --- /dev/null +++ b/static/src/xml/mtg_symbols_field.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+