Core: change yaml_output to output a full csv (#3653)
* make yaml_output arg a bool instead of number * make yaml_output dump all player options as csv * it sorts by game so swap those columns * capitalize game and name headers * use a list and just add an if before adding instead of sorting * skip options that the world doesn't want displayed * check if the class is a subclass of Removed specifically instead of the none flag * don't create empty rows * add a header for every game option that isn't from the common ones even if they have the same name * add to webhost gen args so it can still gen
This commit is contained in:
		
							parent
							
								
									f73c0d9894
								
							
						
					
					
						commit
						69487661dd
					
				
							
								
								
									
										31
									
								
								Generate.py
								
								
								
								
							
							
						
						
									
										31
									
								
								Generate.py
								
								
								
								
							| 
						 | 
				
			
			@ -43,10 +43,10 @@ def mystery_argparse():
 | 
			
		|||
    parser.add_argument('--race', action='store_true', default=defaults.race)
 | 
			
		||||
    parser.add_argument('--meta_file_path', default=defaults.meta_file_path)
 | 
			
		||||
    parser.add_argument('--log_level', default='info', help='Sets log level')
 | 
			
		||||
    parser.add_argument('--yaml_output', default=0, type=lambda value: max(int(value), 0),
 | 
			
		||||
                        help='Output rolled mystery results to yaml up to specified number (made for async multiworld)')
 | 
			
		||||
    parser.add_argument('--plando', default=defaults.plando_options,
 | 
			
		||||
                        help='List of options that can be set manually. Can be combined, for example "bosses, items"')
 | 
			
		||||
    parser.add_argument("--yaml_output", action="store_true",
 | 
			
		||||
                        help="Output rolled player options to csv (made for async multiworld).")
 | 
			
		||||
    parser.add_argument("--plando", default=defaults.plando_options,
 | 
			
		||||
                        help="List of options that can be set manually. Can be combined, for example \"bosses, items\"")
 | 
			
		||||
    parser.add_argument("--skip_prog_balancing", action="store_true",
 | 
			
		||||
                        help="Skip progression balancing step during generation.")
 | 
			
		||||
    parser.add_argument("--skip_output", action="store_true",
 | 
			
		||||
| 
						 | 
				
			
			@ -156,6 +156,7 @@ def main(args=None) -> Tuple[argparse.Namespace, int]:
 | 
			
		|||
    erargs.skip_prog_balancing = args.skip_prog_balancing
 | 
			
		||||
    erargs.skip_output = args.skip_output
 | 
			
		||||
    erargs.name = {}
 | 
			
		||||
    erargs.yaml_output = args.yaml_output
 | 
			
		||||
 | 
			
		||||
    settings_cache: Dict[str, Tuple[argparse.Namespace, ...]] = \
 | 
			
		||||
        {fname: (tuple(roll_settings(yaml, args.plando) for yaml in yamls) if args.sameoptions else None)
 | 
			
		||||
| 
						 | 
				
			
			@ -216,28 +217,6 @@ def main(args=None) -> Tuple[argparse.Namespace, int]:
 | 
			
		|||
    if len(set(name.lower() for name in erargs.name.values())) != len(erargs.name):
 | 
			
		||||
        raise Exception(f"Names have to be unique. Names: {Counter(name.lower() for name in erargs.name.values())}")
 | 
			
		||||
 | 
			
		||||
    if args.yaml_output:
 | 
			
		||||
        import yaml
 | 
			
		||||
        important = {}
 | 
			
		||||
        for option, player_settings in vars(erargs).items():
 | 
			
		||||
            if type(player_settings) == dict:
 | 
			
		||||
                if all(type(value) != list for value in player_settings.values()):
 | 
			
		||||
                    if len(player_settings.values()) > 1:
 | 
			
		||||
                        important[option] = {player: value for player, value in player_settings.items() if
 | 
			
		||||
                                             player <= args.yaml_output}
 | 
			
		||||
                    else:
 | 
			
		||||
                        logging.debug(f"No player settings defined for option '{option}'")
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                if player_settings != "":  # is not empty name
 | 
			
		||||
                    important[option] = player_settings
 | 
			
		||||
                else:
 | 
			
		||||
                    logging.debug(f"No player settings defined for option '{option}'")
 | 
			
		||||
        if args.outputpath:
 | 
			
		||||
            os.makedirs(args.outputpath, exist_ok=True)
 | 
			
		||||
        with open(os.path.join(args.outputpath if args.outputpath else ".", f"generate_{seed_name}.yaml"), "wt") as f:
 | 
			
		||||
            yaml.dump(important, f)
 | 
			
		||||
 | 
			
		||||
    return erargs, seed
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								Main.py
								
								
								
								
							
							
						
						
									
										3
									
								
								Main.py
								
								
								
								
							| 
						 | 
				
			
			@ -46,6 +46,9 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
 | 
			
		|||
    multiworld.sprite_pool = args.sprite_pool.copy()
 | 
			
		||||
 | 
			
		||||
    multiworld.set_options(args)
 | 
			
		||||
    if args.yaml_output:
 | 
			
		||||
        from Options import dump_player_options
 | 
			
		||||
        dump_player_options(multiworld)
 | 
			
		||||
    multiworld.set_item_links()
 | 
			
		||||
    multiworld.state = CollectionState(multiworld)
 | 
			
		||||
    logger.info('Archipelago Version %s  -  Seed: %s\n', __version__, multiworld.seed)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										44
									
								
								Options.py
								
								
								
								
							
							
						
						
									
										44
									
								
								Options.py
								
								
								
								
							| 
						 | 
				
			
			@ -8,16 +8,17 @@ import numbers
 | 
			
		|||
import random
 | 
			
		||||
import typing
 | 
			
		||||
import enum
 | 
			
		||||
from collections import defaultdict
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
 | 
			
		||||
from schema import And, Optional, Or, Schema
 | 
			
		||||
from typing_extensions import Self
 | 
			
		||||
 | 
			
		||||
from Utils import get_fuzzy_results, is_iterable_except_str
 | 
			
		||||
from Utils import get_fuzzy_results, is_iterable_except_str, output_path
 | 
			
		||||
 | 
			
		||||
if typing.TYPE_CHECKING:
 | 
			
		||||
    from BaseClasses import PlandoOptions
 | 
			
		||||
    from BaseClasses import MultiWorld, PlandoOptions
 | 
			
		||||
    from worlds.AutoWorld import World
 | 
			
		||||
    import pathlib
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1532,3 +1533,42 @@ def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], ge
 | 
			
		|||
 | 
			
		||||
            with open(os.path.join(target_folder, game_name + ".yaml"), "w", encoding="utf-8-sig") as f:
 | 
			
		||||
                f.write(res)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def dump_player_options(multiworld: MultiWorld) -> None:
 | 
			
		||||
    from csv import DictWriter
 | 
			
		||||
 | 
			
		||||
    game_players = defaultdict(list)
 | 
			
		||||
    for player, game in multiworld.game.items():
 | 
			
		||||
        game_players[game].append(player)
 | 
			
		||||
    game_players = dict(sorted(game_players.items()))
 | 
			
		||||
 | 
			
		||||
    output = []
 | 
			
		||||
    per_game_option_names = [
 | 
			
		||||
        getattr(option, "display_name", option_key)
 | 
			
		||||
        for option_key, option in PerGameCommonOptions.type_hints.items()
 | 
			
		||||
    ]
 | 
			
		||||
    all_option_names = per_game_option_names.copy()
 | 
			
		||||
    for game, players in game_players.items():
 | 
			
		||||
        game_option_names = per_game_option_names.copy()
 | 
			
		||||
        for player in players:
 | 
			
		||||
            world = multiworld.worlds[player]
 | 
			
		||||
            player_output = {
 | 
			
		||||
                "Game": multiworld.game[player],
 | 
			
		||||
                "Name": multiworld.get_player_name(player),
 | 
			
		||||
            }
 | 
			
		||||
            output.append(player_output)
 | 
			
		||||
            for option_key, option in world.options_dataclass.type_hints.items():
 | 
			
		||||
                if issubclass(Removed, option):
 | 
			
		||||
                    continue
 | 
			
		||||
                display_name = getattr(option, "display_name", option_key)
 | 
			
		||||
                player_output[display_name] = getattr(world.options, option_key).current_option_name
 | 
			
		||||
                if display_name not in game_option_names:
 | 
			
		||||
                    all_option_names.append(display_name)
 | 
			
		||||
                    game_option_names.append(display_name)
 | 
			
		||||
 | 
			
		||||
    with open(output_path(f"generate_{multiworld.seed_name}.csv"), mode="w", newline="") as file:
 | 
			
		||||
        fields = ["Game", "Name", *all_option_names]
 | 
			
		||||
        writer = DictWriter(file, fields)
 | 
			
		||||
        writer.writeheader()
 | 
			
		||||
        writer.writerows(output)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -134,6 +134,7 @@ def gen_game(gen_options: dict, meta: Optional[Dict[str, Any]] = None, owner=Non
 | 
			
		|||
                                                                       {"bosses", "items", "connections", "texts"}))
 | 
			
		||||
        erargs.skip_prog_balancing = False
 | 
			
		||||
        erargs.skip_output = False
 | 
			
		||||
        erargs.yaml_output = False
 | 
			
		||||
 | 
			
		||||
        name_counter = Counter()
 | 
			
		||||
        for player, (playerfile, settings) in enumerate(gen_options.items(), 1):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue