Пример #1
0
    def _query_to_dict(self, query: MultiDict):
        """
        Return a dict with list as value from the MultiDict.

        The value will be wrapped in a list if the args spec is define as a list or if
        the multiple values are sent (i.e ?foo=1&foo=2)
        """
        return {
            key: values if len(values := query.getall(key)) > 1
            or key in self._is_multiple else value
            for key, value in query.items()
        }
Пример #2
0
    def parse(self, post_data: MultiDict):
        self.clear()
        if isinstance(post_data, dict):
            post_data = MultiDict(post_data)

        self.incr_fields = set()

        for k, v in post_data.items():
            # 提交多个相同值,等价于提交一个数组(用于formdata和urlencode形式)
            v_all = post_data.getall(k)
            if len(v_all) > 1:
                v = v_all

            if k.startswith('$'):
                continue
            elif k == 'returning':
                self.returning = True
                continue
            elif '.' in k:
                # TODO: 不允许 incr 和普通赋值同时出现
                k, op = k.rsplit('.', 1)
                if op == 'incr':
                    self.incr_fields.add(k)

            self[k] = v
Пример #3
0
    def _multi_matchup_bullets(self, section, points, matchups):
        def multi_matchup_string(primary_team, opponents):
            opponent_string = opponent_matchup_string(
                self._team_points(primary_team), opponents)
            return (f"{self._team_string(primary_team)}"
                    f"{';' if len(opponents) > 1 else ''} {opponent_string}")

        def opponent_matchup_string(primary_points, opponents):
            opponent_strings = [
                f"over {self._team_string(opponent)}"
                if self._team_points(opponent) < primary_points else
                f"to {self._team_string(opponent)}"
                if self._team_points(opponent) > primary_points else
                f"tied with {self._team_points(opponent)}"
                for opponent in opponents
            ]

            if len(opponent_strings) == 1:
                return opponent_strings[0]
            elif len(opponent_strings) > 2:
                first_string = "; ".join(opponent_strings[:-2])
                last_string = "; and ".join(opponent_strings[-2:])
                return f"{first_string}; {last_string}"

            return ", and ".join(opponent_strings)

        matchup_dict = MultiDict(matchups)
        return [
            multi_matchup_string(team, matchup_dict.getall(team))
            for team in list(dict.fromkeys(matchup_dict.keys()))
        ]
Пример #4
0
def multidict_to_dict(x: MultiDict) -> dict:
    """
    Funzione che converte un MultiDict in un dict

    :param x: MultiDict da convertire
    :type: MultiDict
    :return: dict corrispondente al MultiDict
    :rtype: dict
    """
    return {
        k: v if hasattr(v, "__len__") and len(v) <= 1 else x.getall(k)
        for k, v in x.items()
    }
Пример #5
0
 async def add_relations(self, rel_attr: InstrumentedAttribute,
                         data: MultiDict, conn):
     # rel_attr - атрибут связи many to many (например User.groups, User.permissions)
     relations_table = rel_attr.property.secondary  # класс таблицы связей many-to-many
     ident_col, rel_col = tuple(
         (item[1] for item in rel_attr.prop.local_remote_pairs))
     # rel_attr.prop.local_remote_pairs - список кортежей (по 2 элемента) связей таблиц
     relations = [{
         'id': None,
         ident_col.name: self.id,
         rel_col.name: rel_id
     } for rel_id in data.getall(rel_col.name, [])]
     await conn.execute(relations_table.delete(ident_col == self.id))
     if len(relations) == 0:
         return
     await conn.execute(relations_table.insert(), relations)
Пример #6
0
class DivisionTableFormatter:
    def __init__(self, teams):
        self._divisions = MultiDict(
            (team.division, team) for team in teams.values())

    def markdown(self):
        markdown_string = "### Division Stats\n"
        all_stats = self._all_division_stats()
        for index, entries in enumerate(list(zip(*all_stats))):
            columns = [
                f"**{entry}**" if index == 0 else f"{entry}"
                for entry in entries
            ]
            markdown_string = f"{markdown_string}\n| {' | '.join(columns)} |"
            if index == 0:
                alignment = " | ".join(len(columns) * [":---:"])
                markdown_string = f"{markdown_string}\n| {alignment} |"

        return [markdown_string]

    def _all_division_stats(self):
        stats = [
            DivisionStats("Division", "Points", "Points/Team", "Std. Dev.",
                          "Record")
        ]

        stats.extend(
            self._division_stats(division) for division in sorted(
                list(dict.fromkeys(self._divisions.keys()))))

        stats.append(self._division_stats("League"))
        return stats

    def _division_stats(self, division_name):
        teams = (self._divisions.values() if division_name == "League" else
                 self._divisions.getall(division_name))
        use_record = division_name != "League"

        scores = list(map(lambda team: team.total_points, teams))
        wins = sum(map(lambda team: len(team.wins), teams), 0)
        losses = sum(map(lambda team: len(team.losses), teams), 0)
        ties = sum(map(lambda team: len(team.ties), teams), 0)
        record_string = ("" if not use_record else f"{wins}-{losses}"
                         if ties == 0 else f"{wins}-{losses}-{ties}")
        return DivisionStats(division_name, sum(scores, 0),
                             round(statistics.mean(scores), 1),
                             round(statistics.stdev(scores), 1), record_string)
Пример #7
0
    def _dict_convert(self, data) -> Dict:
        if isinstance(data, dict):
            return data

        elif isinstance(data, MultiDict):
            data = MultiDict(data)
            tmp = {}

            for k, v in data.items():
                # 提交多个相同值,等价于提交一个数组(用于formdata和urlencode形式)
                v_all = data.getall(k)
                if len(v_all) > 1:
                    v = v_all
                tmp[k] = v

            return tmp
        else:
            return dict(data)
Пример #8
0
    def parse(self, post_data: MultiDict):
        self.clear()
        if isinstance(post_data, dict):
            post_data = MultiDict(post_data)

        for k, v in post_data.items():
            v_all = post_data.getall(k)
            if len(v_all) > 1:
                v = v_all

            if k.startswith('$'):
                continue
            elif k == '_inner_data':
                continue
            elif k == 'returning':
                self.returning = True
                continue
            elif '.' in k:
                k, op = k.rsplit('.', 1)
                v = UpdateInfo(k, op, v)

            self[k] = v
def main():
    with open(cClean_no_meir_list_name, 'r',
              errors='replace') as cClean_no_meir_list_file:
        csvfile = csv.reader(cClean_no_meir_list_file)
        cClean_no_meir_list = []
        site_names = set()
        site_names_and_participants = set()
        for row in csvfile:
            if len(row) == 0:
                continue
            cClean_no_meir_list.append(row[0])
            site_participant, = row
            site_name, partipant_name = extract_site_name_and_participant(
                site_participant)
            site_names.add(site_name)
            site_names_and_participants.add((site_name, partipant_name))

    cclean_site_names_list = list(site_names)
    cclean_site_names_list.sort()
    image_descriptions = []
    #print ("cClean site names: \n", '\n'.join(cclean_site_names_list))

    with open(zip_list_name, 'r', errors='replace') as zip_list:
        csvfile = csv.reader(zip_list)
        site_names = dict()

        with open(zip_and_cClean_no_meir_list,
                  'wt') as zip_ccclean_noMeir_file:
            csvwriter = csv.writer(zip_ccclean_noMeir_file)
            csvwriter.writerow(ImageDesc._fields)

            for row in csvfile:
                site_participant, filename = row

                separator = site_participant.rfind('_')
                site = site_participant[:separator]

                filename = filename.replace('\\', '/')
                filename_base = filename[:-4]  # cut off '.jpg'
                parts = filename_base.split('/')
                alt_site_name, participant, event, img_index = parts[-4:]

                if site == "SingTel":
                    alt_site_name = "SingTel"

                if site == "KippsBay":
                    alt_site_name = "KBCBMSVR1"

                if site == "SeaView":
                    alt_site_name = "SVS1-HZ-ISR"

                match = extension_regex.search(filename)
                if match is None:
                    raise Exception('Not an archive')

                zip_file = filename[:match.end() - 1]
                path_in_zip = filename[match.end():]

                desc = ImageDesc(site, alt_site_name, participant, event,
                                 img_index, zip_file, path_in_zip)
                site_names[site] = alt_site_name
                site_names[alt_site_name] = site

                if (site, participant) in site_names_and_participants or (
                        alt_site_name,
                        participant) in site_names_and_participants:
                    image_descriptions.append(desc)
                    csvwriter.writerow(desc)

    participant_to_images = MultiDict()

    for desc in image_descriptions:
        participant_to_images.add(str((desc.site, desc.participant)), desc)

    chosen_site_to_participant = MultiDict()
    selected_image_descriptions = []
    for participant in set(participant_to_images.keys()):
        images = participant_to_images.getall(participant)
        if len(images) > MIN_NUM_IMAGES and len(images) <= MAX_NUM_IMAGES:
            # selected_image_descriptions += images
            chosen_site_to_participant.add(images[0].site,
                                           images[0].participant)

    num_chosen_participants_per_site = dict()
    for site in set(chosen_site_to_participant.keys()):
        num_chosen_participants_per_site[site] = len(
            chosen_site_to_participant.getall(site))
        print(site, num_chosen_participants_per_site[site])

    total_num_chosen_participants = sum(
        num_chosen_participants_per_site.values())
    print(total_num_chosen_participants, 'total')

    for site, num_part in num_chosen_participants_per_site.items():
        ratio = num_part / total_num_chosen_participants
        num_participants_to_select = round(ratio * NUM_CHOSEN_PARTICIPANTS)
        print(site, num_part, ratio, num_participants_to_select)

        if num_participants_to_select == 0:
            continue

        participants = chosen_site_to_participant.getall(site)
        participants = participants[:num_participants_to_select]

        for participant in participants:
            images = participant_to_images.getall(str((site, participant)))

            per_event = MultiDict()
            for img in images:
                per_event.add(img.event, img)

            num_events = len(set(per_event.keys()))
            images_per_event = ceil(MIN_NUM_IMAGES / num_events)
            selected_images_for_this_participant = []
            for event in set(per_event.keys()):
                images_for_this_event = per_event.getall(event)
                selected_images_for_this_participant += images_for_this_event[:
                                                                              images_per_event]

            if len(selected_images_for_this_participant) >= MIN_NUM_IMAGES:
                selected_images_for_this_participant = selected_images_for_this_participant[:
                                                                                            MIN_NUM_IMAGES]
            else:
                selected_images_set = set(selected_images_for_this_participant)
                for img in images:
                    selected_images_set.add(img)
                    if len(selected_images_set) == MIN_NUM_IMAGES:
                        break
                selected_images_for_this_participant = list(
                    selected_images_set)

            selected_image_descriptions += selected_images_for_this_participant

    outfile = open(
        r'C:\Users\leahb\Documents\Leah\MyDataSets\FileSelection\selected_images_for_unknown.csv',
        'wt',
        encoding='ascii')
    csv_out = csv.writer(outfile)
    csv_out.writerow(ImageDesc._fields)
    for rec in selected_image_descriptions:
        csv_out.writerow(rec)
Пример #10
0
class EventManager(object):
    """
    A manager for events.

    This deals with firing of events and temporary listeners.
    """
    def __init__(self):
        #: The task manager used to spawn events.
        self.task_manager = None

        #: A list of event hooks.
        self.event_hooks = set()

        #: A MultiDict of event listeners.
        self.event_listeners = MultiDict()

        #: A MultiDict of temporary listeners.
        self.temporary_listeners = MultiDict()

    # add or removal functions
    # Events
    def add_event(self, func, name: str = None):
        """
        Add an event to the internal registry of events.

        :param name: The event name to register under.
        :param func: The function to add.
        """
        if not inspect.iscoroutinefunction(func):
            raise TypeError("Event must be an async function")

        if name is None:
            evs = func.events
        else:
            evs = [name]

        for ev_name in evs:
            logger.debug("Registered event `{}` handling `{}`".format(
                func, ev_name))
            self.event_listeners.add(ev_name, func)

    def remove_event(self, name: str, func):
        """
        Removes a function event.

        :param name: The name the event is registered under.
        :param func: The function to remove.
        """
        self.event_listeners = remove_from_multidict(self.event_listeners,
                                                     key=name,
                                                     item=func)

    # listeners
    def add_temporary_listener(self, name: str, listener):
        """
        Adds a new temporary listener.

        To remove the listener, you can raise ListenerExit which will exit it and remove the
        listener from the list.

        :param name: The name of the event to listen to.
        :param listener: The listener function.
        """
        self.temporary_listeners.add(name, listener)

    def remove_listener_early(self, name: str, listener):
        """
        Removes a temporary listener early.

        :param name: The name of the event the listener is registered under.
        :param listener: The listener function.
        """
        self.event_listeners = remove_from_multidict(self.event_listeners,
                                                     key=name,
                                                     item=listener)

    def add_event_hook(self, listener):
        """
        Adds an event hook.

        :param listener: The event hook callable to use.
        """
        logger.warning("Adding event hook '%s'", listener)
        self.event_hooks.add(listener)

    def remove_event_hook(self, listener):
        """
        Removes an event hook.
        """
        self.event_hooks.remove(listener)

    # wrapper functions
    async def _safety_wrapper(self, func, *args, **kwargs):
        """
        Ensures a coro's error is caught and doesn't balloon out.
        """
        try:
            await func(*args, **kwargs)
        except Exception as e:
            logger.exception("Unhandled exception in {}!".format(
                func.__name__),
                             exc_info=True)

    async def _listener_wrapper(self, key: str, func, *args, **kwargs):
        """
        Wraps a listener, ensuring ListenerExit is handled properly.
        """
        try:
            await func(*args, **kwargs)
        except ListenerExit:
            # remove the function
            self.temporary_listeners = remove_from_multidict(
                self.temporary_listeners, key, func)
        except Exception:
            logger.exception("Unhandled exception in listener {}!".format(
                func.__name__),
                             exc_info=True)
            self.temporary_listeners = remove_from_multidict(
                self.temporary_listeners, key, func)

    async def wait_for(self, event_name: str, predicate=None):
        """
        Waits for an event.

        Returning a truthy value from the predicate will cause it to exit and return.

        :param event_name: The name of the event.
        :param predicate: The predicate to use to check for the event.
        """
        p = multio.Promise()
        errored = False

        async def listener(ctx, *args):
            # exit immediately if the predicate is none
            if predicate is None:
                await p.set(args)
                raise ListenerExit

            try:
                res = predicate(*args)
                if inspect.isawaitable(res):
                    res = await res
            except ListenerExit:
                # ???
                await p.set(args)
                raise
            except Exception as e:
                # something bad happened, set exception and exit
                logger.exception("Exception in wait_for predicate!")
                # signal that an error happened
                nonlocal errored
                errored = True
                await p.set(e)
                raise ListenerExit
            else:
                # exit now if result is true
                if res is True:
                    await p.set(args)
                    raise ListenerExit

        self.add_temporary_listener(name=event_name, listener=listener)
        output = await p.wait()
        if errored:
            raise output

        # unwrap tuples, if applicable
        if len(output) == 1:
            return output[0]
        return output

    def wait_for_manager(self, event_name: str,
                         predicate) -> 'typing.AsyncContextManager[None]':
        """
        Returns a context manager that can be used to run some steps whilst waiting for a
        temporary listener.

        .. code-block:: python

            async with client.events.wait_for_manager("member_update", predicate=...):
                await member.nickname.set("Test")

        This probably won't be needed outside of internal library functions.
        """
        return _wait_for_manager(self, event_name, predicate)

    async def spawn(self, cofunc, *args) -> typing.Any:
        """
        Spawns a new async function using our task manager.

        Usage::

            async def myfn(a, b):
                await do_some_operation(a + b)

            await events.spawn(myfn, 1, 2)

        :param cofunc: The async function to spawn.
        :param args: Args to provide to the async function.
        """
        return await multio.asynclib.spawn(self.task_manager, cofunc, *args)

    async def fire_event(self, event_name: str, *args, **kwargs):
        """
        Fires an event.

        :param event_name: The name of the event to fire.
        """
        if "ctx" not in kwargs:
            gateway = kwargs.pop("gateway")
            client = kwargs.pop("client")
            ctx = EventContext(client, gateway.gw_state.shard_id, event_name)
        else:
            ctx = kwargs.pop("ctx")

        # clobber event name
        ctx.event_name = event_name

        # always ensure hooks are ran first
        for hook in self.event_hooks:
            cofunc = functools.partial(hook, ctx, *args, **kwargs)
            await self.spawn(cofunc)

        for handler in self.event_listeners.getall(event_name, []):
            coro = functools.partial(handler, ctx, *args, **kwargs)
            coro.__name__ = handler.__name__
            await self.spawn(self._safety_wrapper, coro)

        for listener in self.temporary_listeners.getall(event_name, []):
            coro = functools.partial(self._listener_wrapper, event_name,
                                     listener, ctx, *args, **kwargs)
            await self.spawn(coro)
Пример #11
0
class MatchupSectionFormatter:

    MATCHUP_HEADERS = [
        "Blowout of the Week",
        "Closest Matchup of the Week",
        "Strongest Loss",
        "No Wins for the Effort",
        "Weakest Win",
        "Dirty Cheater",
    ]

    def __init__(self, teams):
        self._teams = teams
        self._winners = MultiDict()
        self._losers = MultiDict()
        self._ties = MultiDict()
        for team in teams.values():
            [self._winners.add(team.name, loser) for loser in team.wins]
            [self._losers.add(team.name, winner) for winner in team.losses]
            [self._ties.add(team.name, other_team) for other_team in team.ties]

    def markdown(self):
        def section_markdown(section):
            sorted_matchups = self._sort_matchups(section)[0]
            bullets = (self._comparison_matchup_bullets(
                section, *sorted_matchups) if section in [
                    "Blowout of the Week",
                    "Closest Matchup of the Week",
                ] else self._multi_matchup_bullets(section, *sorted_matchups))
            return markdown_for_section(section, bullets)

        return list(map(section_markdown, self.MATCHUP_HEADERS))

    def _sort_matchups(self, section):
        def filter_key(matchup_entry):
            primary_team, opponent = matchup_entry
            if section == "Strongest Loss":
                return opponent in self._losers.getall(primary_team, [])
            elif section == "No Wins for the Effort":
                return primary_team not in self._winners
            elif section == "Dirty Cheater":
                return primary_team not in self._losers
            else:
                return opponent in self._winners.getall(primary_team, [])

        def groupby_key(matchup_entry):
            primary_team, opponent = matchup_entry
            primary_points = self._team_points(primary_team)
            opponent_points = self._team_points(opponent)
            if section in [
                    "Blowout of the Week", "Closest Matchup of the Week"
            ]:
                return primary_points - opponent_points
            else:
                return primary_points

        def sort_key(matchup_entry):
            primary_team, opponent = matchup_entry
            return (
                groupby_key(matchup_entry),
                self._team_points(primary_team),
                self._team_points(opponent),
            )

        if section == "Strongest Loss":
            entries = self._losers
        elif section == "No Wins for the Effort":
            entries = MultiDict(self._losers, **self._ties)
        elif section in ["Closest Matchup", "Dirty Cheater"]:
            entries = MultiDict(self._winners, **self._ties)
        else:
            entries = self._winners

        sorted_matchups = sorted(filter(filter_key, entries.items()),
                                 key=sort_key,
                                 reverse=self._reverse_sort(section))
        return [(points, list(group))
                for points, group in groupby(sorted_matchups, key=groupby_key)]

    def _team_points(self, team):
        return self._teams[team].total_points

    @staticmethod
    def _reverse_sort(section):
        return section in [
            "Blowout of the Week",
            "Strongest Loss",
            "No Wins for the Effort",
        ]

    def _comparison_matchup_bullets(self, section, points, matchups):
        def comparison_matchup_string(matchup_entry):
            primary_team, opponent = matchup_entry

            primary_points = self._team_points(primary_team)
            opponent_points = self._team_points(opponent)

            point_diff = primary_points - opponent_points
            if point_diff == 0:
                return (f"Tied at {points_string(primary_points)}:"
                        f" {primary_team} and {opponent}")

            return (f"{self._team_string(primary_team)}"
                    f" over {self._team_string(opponent)}"
                    f" by {points_string(point_diff)}.")

        deduplicated = []
        [
            deduplicated.append((primary_team, opponent))
            for primary_team, opponent in matchups
            if (opponent, primary_team) not in deduplicated
        ]
        return list(map(comparison_matchup_string, deduplicated))

    def _team_string(self, team):
        return team_string(team, self._team_points(team))

    def _multi_matchup_bullets(self, section, points, matchups):
        def multi_matchup_string(primary_team, opponents):
            opponent_string = opponent_matchup_string(
                self._team_points(primary_team), opponents)
            return (f"{self._team_string(primary_team)}"
                    f"{';' if len(opponents) > 1 else ''} {opponent_string}")

        def opponent_matchup_string(primary_points, opponents):
            opponent_strings = [
                f"over {self._team_string(opponent)}"
                if self._team_points(opponent) < primary_points else
                f"to {self._team_string(opponent)}"
                if self._team_points(opponent) > primary_points else
                f"tied with {self._team_points(opponent)}"
                for opponent in opponents
            ]

            if len(opponent_strings) == 1:
                return opponent_strings[0]
            elif len(opponent_strings) > 2:
                first_string = "; ".join(opponent_strings[:-2])
                last_string = "; and ".join(opponent_strings[-2:])
                return f"{first_string}; {last_string}"

            return ", and ".join(opponent_strings)

        matchup_dict = MultiDict(matchups)
        return [
            multi_matchup_string(team, matchup_dict.getall(team))
            for team in list(dict.fromkeys(matchup_dict.keys()))
        ]
Пример #12
0
    def collection(self, user_name, extra_params):
        collection_data = []
        plays_data = []

        if isinstance(extra_params, list):
            for params in extra_params:
                collection_data += self.client.collection(
                    user_name=user_name,
                    **params,
                )
        else:
            collection_data = self.client.collection(
                user_name=user_name,
                **extra_params,
            )

        # Dummy game for linking extra promos and accessories
        collection_data.append(
            _create_blank_collection(EXTRA_EXPANSIONS_GAME_ID,
                                     "ZZZ: Expansions without Game (A-I)"))

        params = {"subtype": "boardgameaccessory", "own": 1}
        accessory_collection = self.client.collection(user_name=user_name,
                                                      **params)
        accessory_list_data = self.client.game_list([
            game_in_collection["id"]
            for game_in_collection in accessory_collection
        ])
        accessory_collection_by_id = MultiDict()
        for acc in accessory_collection:
            accessory_collection_by_id.add(str(acc["id"]), acc)

        plays_data = self.client.plays(user_name=user_name, )

        game_list_data = self.client.game_list([
            game_in_collection["id"] for game_in_collection in collection_data
        ])

        collection_by_id = MultiDict()
        for item in collection_data:
            item["players"] = []
            collection_by_id.add(str(item["id"]), item)

        for play in plays_data:
            play_id = str(play["game"]["gameid"])
            if play_id in collection_by_id:
                collection_by_id[play_id]["players"].extend(play["players"])

        games_data = list(
            filter(lambda x: x["type"] == "boardgame", game_list_data))
        expansions_data = list(
            filter(lambda x: x["type"] == "boardgameexpansion",
                   game_list_data))

        game_data_by_id = {}
        expansion_data_by_id = {}

        for game in games_data:
            game["accessories_collection"] = []
            game["expansions_collection"] = []
            game_data_by_id[game["id"]] = game

        for expansion in expansions_data:
            expansion["accessories_collection"] = []
            expansion["expansions_collection"] = []
            expansion_data_by_id[expansion["id"]] = expansion

        expansion_data_by_id = custom_expansion_mappings(expansion_data_by_id)

        for expansion_data in expansion_data_by_id.values():
            if is_promo_box(expansion_data):
                game_data_by_id[expansion_data["id"]] = expansion_data
            for expansion in expansion_data["expansions"]:
                id = expansion["id"]
                if expansion["inbound"] and id in expansion_data_by_id:
                    expansion_data_by_id[id]["expansions_collection"].append(
                        expansion_data)

        for accessory_data in accessory_list_data:
            own_game = False
            for accessory in accessory_data["accessories"]:
                id = accessory["id"]
                if accessory["inbound"]:
                    if id in game_data_by_id:
                        game_data_by_id[id]["accessories_collection"].append(
                            accessory_data)
                        own_game = True
                    elif id in expansion_data_by_id:
                        expansion_data_by_id[id][
                            "accessories_collection"].append(accessory_data)
                        own_game = True
            if not own_game:
                game_data_by_id[EXTRA_EXPANSIONS_GAME_ID][
                    "accessories_collection"].append(accessory_data)

        for expansion_data in expansion_data_by_id.values():
            own_base_game = False
            for expansion in expansion_data["expansions"]:
                id = expansion["id"]
                if expansion["inbound"]:
                    if id in game_data_by_id:
                        own_base_game = True
                        if not is_promo_box(expansion_data):
                            game_data_by_id[id][
                                "expansions_collection"].append(expansion_data)
                            game_data_by_id[id][
                                "expansions_collection"].extend(
                                    expansion_data_by_id[expansion_data["id"]]
                                    ["expansions_collection"])
                            game_data_by_id[id][
                                "accessories_collection"].extend(
                                    expansion_data_by_id[expansion_data["id"]]
                                    ["accessories_collection"])
                    elif id in expansion_data_by_id:
                        own_base_game = True
            if not own_base_game:
                id = EXTRA_EXPANSIONS_GAME_ID
                expansion_data["suggested_numplayers"] = []
                game_data_by_id[id]["expansions_collection"].append(
                    expansion_data)
                game_data_by_id[id]["expansions_collection"].extend(
                    expansion_data_by_id[
                        expansion_data["id"]]["expansions_collection"])
                game_data_by_id[id]["accessories_collection"].extend(
                    expansion_data_by_id[
                        expansion_data["id"]]["accessories_collection"])

        games_collection = list(
            filter(lambda x: x["id"] in game_data_by_id,
                   collection_by_id.values()))

        games = [
            BoardGame(game_data_by_id[collection["id"]],
                      collection,
                      expansions=[
                          BoardGame(expansion_data, collection)
                          for expansion_data in _uniq(game_data_by_id[
                              collection["id"]]["expansions_collection"])
                          for collection in collection_by_id.getall(
                              str(expansion_data["id"]))
                      ],
                      accessories=[
                          BoardGame(accessory_data, collection)
                          for accessory_data in _uniq(game_data_by_id[
                              collection["id"]]["accessories_collection"])
                          for collection in accessory_collection_by_id.getall(
                              str(accessory_data["id"]))
                      ]) for collection in games_collection
        ]

        newGames = []

        # Cleanup the game
        for game in games:
            for exp in game.expansions:
                exp.name = remove_prefix(exp.name, game)
            for acc in game.accessories:
                acc.name = remove_prefix(acc.name, game)
            contained_list = []
            for con in game.contained:
                if con["inbound"]:
                    con["name"] = remove_prefix(con["name"], game)
                    contained_list.append(con)
            game.contained = sorted(contained_list, key=lambda x: x["name"])

            integrates_list = []
            for integrate in game.integrates:
                # Filter integrates to owned games
                if str(integrate["id"]) in collection_by_id:
                    integrate["name"] = name_scrubber(integrate["name"])
                    integrates_list.append(integrate)
            game.integrates = sorted(integrates_list, key=lambda x: x["name"])

            for reimps in game.reimplements:
                reimps["name"] = name_scrubber(reimps["name"])
            for reimpby in game.reimplementedby:
                reimpby["name"] = name_scrubber(reimpby["name"])

            family_list = []
            for fam in game.families:
                newFam = family_filter(fam)
                if newFam:
                    family_list.append(newFam)
            game.families = family_list

            game.publishers = publisher_filter(game.publishers,
                                               collection_by_id[str(game.id)])

            # TODO This is terrible, but split the extra expansions by letter
            if game.id == EXTRA_EXPANSIONS_GAME_ID:

                game.description = ""
                game.players = []
                for exp in game.expansions:
                    exp.players.clear()

                newGame = copy.deepcopy(game)
                newGame.name = "ZZZ: Expansions without Game (J-Q)"
                newGame.collection_id = str(game.collection_id) + "jq"
                newGame.expansions = list(
                    filter(lambda x: re.search(r"^[j-qJ-Q]", x.name),
                           game.expansions))
                newGame.accessories = list(
                    filter(lambda x: re.search(r"^[j-qJ-Q]", x.name),
                           game.accessories))
                newGame.expansions = sorted(newGame.expansions,
                                            key=lambda x: x.name)
                newGame.accessories = sorted(newGame.accessories,
                                             key=lambda x: x.name)
                game.expansions = list(
                    set(game.expansions) - set(newGame.expansions))
                game.accessories = list(
                    set(game.accessories) - set(newGame.accessories))
                newGames.append(newGame)

                newGame = copy.deepcopy(game)
                newGame.name = "ZZZ: Expansions without Game (R-Z)"
                newGame.collection_id = str(game.collection_id) + "rz"
                newGame.expansions = list(
                    filter(lambda x: re.search(r"^[r-zR-Z]", x.name),
                           game.expansions))
                newGame.accessories = list(
                    filter(lambda x: re.search(r"^[r-zR-Z]", x.name),
                           game.accessories))
                newGame.expansions = sorted(newGame.expansions,
                                            key=lambda x: x.name)
                newGame.accessories = sorted(newGame.accessories,
                                             key=lambda x: x.name)
                game.expansions = list(
                    set(game.expansions) - set(newGame.expansions))
                game.accessories = list(
                    set(game.accessories) - set(newGame.accessories))
                newGames.append(newGame)

            # Resort the list after updating the names
            game.expansions = sorted(game.expansions, key=lambda x: x.name)
            game.accessories = sorted(game.accessories, key=lambda x: x.name)
            game.contained = sorted(game.contained, key=lambda x: x["name"])
            game.families = sorted(game.families, key=lambda x: x["name"])
            game.reimplements = sorted(game.reimplements,
                                       key=lambda x: x["name"])
            game.reimplementedby = sorted(game.reimplementedby,
                                          key=lambda x: x["name"])

        games.extend(newGames)

        return games