""" Utilities to help load the vehicle/ordnance listings. """ import os import json import re import copy import logging from vasl_templates.webapp import app, globvars from vasl_templates.webapp.config.constants import DATA_DIR _vo_comments = None # --------------------------------------------------------------------- _NOTE_ID_PREFIXES = { "US": "american", "Br": "british", "Ge": "german", "Ru": "russian", "Fr": "french", "Ch": "chinese", "AllM": "allied-minor", } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _COMMENT_HANDLERS = { "russian": { "vehicles": { "N": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(a)": "American ESB", "(b)": "British ESB" } ), "LL": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(a)": "{? 01/1944- | Black TH# | Red TH# | Black TH#44+ ?}" }, "Black TH#" ), } }, "british": { "vehicles": { "A": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(a)": "American ESB+" } ) } }, "french": { "vehicles": { "F": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(a)": _french_veh_f, "(b)": _french_veh_f, "(f)": _french_veh_f, } ), } }, "finnish": { "vehicles": { "D": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(b)": "British ESB", "(g)": [ "German ESB", "Black TH#" ], "(r)": "Russian ESB", "(s)": [ "Swedish ESB", "Black TH#" ] } ) }, "ordnance": { "B": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(b)": "Black TH#", "(f)": "Black TH#", "(g)": [ "Black TH#", "No Captured Use penalty for Germans" ], "(r)": "No Captured Use penalty for Russians", "(s)": "Black TH#", "(t)": "Black TH#", } ) } }, "chinese": { "vehicles": { "A": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(a)": [ "American ESB, +1 DRM" ], "(b)": [ "British ESB, +1 DRM" ], "(g)": [ "German ESB, +1 DRM" ], "(i)": [ "Italian ESB, +1 DRM" ], "(r)": [ "Russian ESB, +1 DRM" ], } ), "D": "2 TK DR" } }, "allied-minor": { "vehicles": { "A": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(a)": "American ESB+", "(b)": "British ESB+", "(f)": "French ESB+", "(i)": "Italian ESB+", } ) } }, "axis-minor": { "vehicles": { "E": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(f)": "French ESB", "(g)": _axis_minor_veh_e, "(i)": "Italian ESB", "(r)": "Russian ESB", "(t)": _axis_minor_veh_e, } ), }, "ordnance": { "E": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(g)": _axis_minor_ord_e, "(t)": _axis_minor_ord_e, } ), } }, "kfw-un": { "vehicles": { "UU": lambda vo_entry, note_id: _check_name( vo_entry, note_id, { "(a)": "American ESB+", } ), } } } def _check_comment_handlers( vo_entry, nat, vo_type, note_id, comments, orig_nat ): """Add any multi-applicable note-specific comments to the vehicle/ordnance.""" val = _COMMENT_HANDLERS.get( nat, {} ).get( vo_type, {} ).get( note_id ) if not val: return if isinstance( val, str ): comments.append( val ) elif isinstance( val, list ): comments.extend( val ) else: assert callable( val ) val = val( vo_entry, orig_nat ) if val: assert isinstance( val, list ) comments.extend( val ) def _french_veh_f( vo_entry, orig_nat ): #pylint: disable=unused-argument """Handle French Vehicle Note F.""" # NOTE: French Vehicle Note F says things like: # "(a)" also indicates that this vehicle is treated as captured if crewed by other than Free French or U.S. # so we would like to be smart here and check the owning player's nationality and add a "Captured Use" comment # only if it applies. Unfortunately, while this technique works for the Allied/Axis Minor common vehicles/ordnance, # it won't here :-( # Consider a scenario where the British have an Ac de 40 CA(a). This piece won't appear in the list of # British vehicles, so the user has to set up a 2nd scenario, with a Free French player, to get access to # this piece. The code will detect the owning player is the Free French, and so conclude that it doesn't need # to add a "Captured Use" comment. There's no way of fixing this (other than adding the Free French # vehicles/ordnance to every nationality that could possibly use them), so we add the comment verbatim # and let the user figure it out. if "(a)" in vo_entry["name"]: comments = [ "Black TH#", "American ESB+" ] if vo_entry["id"] == "fr/v:020": # nb: AM Dodge(a) comments.append( "Captured Use (unless Vichy French)" ) else: comments.append( "Captured Use (unless Free French or US)" ) elif "(b)" in vo_entry["name"]: comments = [ "Black TH#", "British ESB+" ] comments.append( "Captured Use (unless Vichy French or British)" ) elif "(f)" in vo_entry["name"]: comments = [ "Red TH#", "French ESB+" ] comments.append( "Captured Use (unless Free/Vichy French)" ) else: comments = [] return comments def _axis_minor_veh_e( vo_entry, orig_nat ): """Handle Axis Minor Vehicle Note E (cases (g) and (t) only).""" assert "(g)" in vo_entry["name"] or "(t)" in vo_entry["name"] comments = [ "German ESB" if "(g)" in vo_entry["name"] else "Czech ESB" ] if orig_nat in ( "romanian", "hungarian", "slovakian" ): comments.append( "Black TH#" ) return comments def _axis_minor_ord_e( vo_entry, orig_nat ): """Handle Axis Minor Ordnance Note E.""" assert "(g)" in vo_entry["name"] or "(t)" in vo_entry["name"] if orig_nat in ( "romanian", "hungarian", "slovakian" ): return [ "Black TH#" ] return None def _check_name( vo_entry, nat, cases, defaultVal=None ): """Check the vehicle/ordnance's name.""" for key,val in cases.items(): if key in vo_entry.get("name",""): if isinstance( val, str ): return [val] elif isinstance( val, list ): return val else: assert callable( val ) return val( vo_entry, nat ) if defaultVal: return [defaultVal] return None # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def add_vo_comments( listings, vo_type, msg_store ): """Add comments to the vehicle/ordnance entries.""" # Melbourne, Australia (JUN/20) # initialize global _vo_comments if not _vo_comments: fname = os.path.join( app.config.get("DATA_DIR",DATA_DIR), "vo-comments.json" ) _vo_comments = json.load( open( fname, "r" ) ) # process each vehicle/ordnance for nat,vo_entries in listings.items(): for vo_entry in vo_entries: if "copy_from" in vo_entry: continue # nb: we do these later, when the entry is actually copied _do_add_vo_comments( vo_entry, nat, vo_type, msg_store ) def _do_add_vo_comments( vo_entry, nat, vo_type, msg_store ): #pylint: disable=too-many-locals,too-many-branches """Add comments to a vehicle/ordnance entry.""" # figure out which comments have been disabled disable_comments_for_note_ids = set() # disable all omments associated with these note ID's disabled_comments = set() # disable these specific comments prefixes = "|".join( _NOTE_ID_PREFIXES.keys() ) regex = re.compile( "^(({}) )?[A-Za-z]{{,2}}$".format( prefixes ) ) vals = vo_entry.get( "disabled_comments", [] ) for val in vals if isinstance(vals,list) else [vals]: if regex.search( val ): disable_comments_for_note_ids.add( val ) else: disabled_comments.add( val ) # get the vehicle/ordnance's manually-defined comments comments = vo_entry.get( "comments", [] ) if isinstance( comments, str ): comments = [ comments ] # add any generated comments comments.extend( _make_comments( vo_entry, nat, vo_type, disable_comments_for_note_ids ) ) # dedupe the comments # NOTE: This needs to be done in the front-end as well, since some comments will be generated # based on the scenario date, and we have no way of knowing what that is here. comments2, comment_index = [], set() for cmt in comments: if cmt in comment_index: continue comments2.append( cmt ) comment_index.add( cmt ) # remove comments that have been disabled # NOTE: This needs to be done in the front-end as well, since some comments will be generated # based on the scenario date, and we have no way of knowing what that is here. comments3 = [] def parse_cmd( cmt ): """Parse a disabled comment command.""" if cmt.startswith( "?:" ): return cmt[2:].strip(), False # nb: this is an optional comment (i.e. don't warn if it's not there) else: return cmt, True disabled_comments = dict( parse_cmd(c) for c in disabled_comments ) for cmt in comments2: if cmt in disabled_comments: del disabled_comments[ cmt ] else: comments3.append( cmt ) disabled_comments = { k: v for k,v in disabled_comments.items() if v } if disabled_comments: if msg_store: msg_store.warning( "Can't find disabled comments for {}: