import argparse, re from string import Template from typing import NamedTuple from jinja2 import Environment, PackageLoader, select_autoescape # global variables env = Environment( loader=PackageLoader("swjg"), autoescape=select_autoescape ) parser = argparse.ArgumentParser() # helper functions def get_identifiers_from_template(template): # Template.get_identifiers() doesn't appear until Python 3.11 # Don't substitute anything; get the raw string template_str = Template.safe_substitute() matches = re.findall("\$[a-z0-9_\.]+", template_str) matches = [x[1:] for x in matches] return matches # classes and such class Destination(NamedTuple): world: str system: str|None sector: str|None class Mission: output_destination_template = Template("to $world in the $system system") output_sector_template = Template(", part of the $sector,") value_template = Template("for $value credits") error_template = Template("You haven't given this mission some of the values it needs. Please check: $potential_values") def __init__(self, destination:Destination, value:int, *args, **kwargs): # destination: Destination (see above) self.world = destination.world if destination.world is not None else None self.system = destination.system if destination.system is not None else destination.world self.sector = destination.sector if destination.sector is not None else None self.value = value # for arg, val in kwargs.items(): # setattr(self, arg, val) def assemble_templates(self) -> list: # Override this in children template_list = [] if self.world != None: template_list.append(self.output_destination_template) if self.sector != None: template_list.append(self.output_sector_template) template_list.append(self.value_template) return template_list def get_current_values(self) -> dict: return { "world": self.world, "system": self.system, "sector": self.sector, "value": self.value } def missing_values(self) -> list: object_vars = vars(self) missing = [] for key, val in object_vars.items(): if val == None: missing.append(key) return missing def assemble_text(self) -> str: missing_vals = self.missing_values() templates = self.assemble_templates() if len(templates) == 0 or len(missing_vals) != 0: # either both of these should be true or neither should raise ValueError(self.error_template.substitute(potential_values=missing_vals)) current_values = self.get_current_values() output = [] for template in templates: output.append(template.substitute(**current_values)) out_text = " ".join(output) out_text = out_text.replace(" ,", ",") out_text += "." return out_text class PassengerMission(Mission): output_initial_template = Template("Bring $number passengers") def __init__(self, number:int, *args, **kwargs): super(PassengerMission, self).__init__(*args, **kwargs) self.number = number def get_current_values(self) -> dict: base_dict = super(PassengerMission, self).get_current_values() new_dict = { "number": self.number } base_dict.update(new_dict) return base_dict def assemble_templates(self): template_list = [self.output_initial_template] template_list.extend(super(PassengerMission, self).assemble_templates()) return template_list def assemble_text(self) -> str: return super(PassengerMission, self).assemble_text() class CargoMission(Mission): output_initial_template = Template("Deliver $tons tons of $item") output_time_timeplate = Template("in the next $time days") def __init__(self, tons:int, item:str, time:int, *args, **kwargs): # tons: integer # item: string (this will not be pluralized in the text) # time: integer (number of days the crew have to complete delivery) # value: integer (value in credits) super(CargoMission, self).__init__(*args, **kwargs) self.tons = tons self.item = item self.time = time def get_current_values(self) -> dict: base_dict = super(CargoMission, self).get_current_values() new_dict = { "tons": self.tons, "item": self.item, "time": self.time } base_dict.update(new_dict) return base_dict def assemble_templates(self): template_list = [self.output_initial_template] if self.time is not None: template_list.append(self.output_time_timeplate) template_list.extend(super(CargoMission, self).assemble_templates()) return template_list def assemble_text(self) -> str: return super(CargoMission, self).assemble_text() class GroupMission(Mission): output_initial_template = Template("Bring $number members of a $group") def __init__(self, number:int, group:str, *args, **kwargs): super(GroupMission, self).__init__(*args, **kwargs) self.number = number self.group = group def get_current_values(self) -> dict: base_dict = super(GroupMission, self).get_current_values() new_dict = { "number": self.number, "group": self.group } base_dict.update(new_dict) return base_dict def assemble_templates(self): template_list = [self.output_initial_template] template_list.extend(super(GroupMission, self).assemble_templates()) return template_list def assemble_text(self) -> str: return super(GroupMission, self).assemble_text() # the function that does the thing def main(): import random mission_types = [PassengerMission, CargoMission, GroupMission] missions = [] for _ in range(5): current_mission = random.choice(mission_types) destination = Destination("Alderaan", None, "Core Worlds") number = random.randint(1,8) tons = random.randint(4, 12) item = random.choice(["gravel", "computer chips", "spice", "live ysalamiri", "empty shipping containers"]) time = random.randint(7, 31) value = random.randint(20, 120) * 1000 group = None if current_mission == GroupMission: group = random.choice(["family", "performing troupe", "acrobatic troupe"]) missions.append(current_mission(destination=destination, number=number, tons=tons, item=item, time=time, value=value, group=group)) for mission in missions: # print(mission, mission.__dict__) print(mission.assemble_text()) # Don't do anything if the module is loaded wholesale into something else if __name__ == "__main__": main()