Example #1
0
class ConstrainedNodeFetcher(object):
    SHEET_NAME = 'trapables'

    _value_value_map = {
        0: 0,
        1: 1,
        2: 5,
        3: 15,
        4: 30,
        5: 55,
    }

    _legal_groups = {
        'WHITE',
        'BLUE',
        'BLACK',
        'RED',
        'GREEN',
        'drawgo',
        'mud',
        'post',
        'midrange',
        'mill',
        'reanimate',
        'burn',
        'hatebear',
        'removal',
        'lock',
        'yardvalue',
        'ld',
        'storm',
        'tezz',
        'lands',
        'shatter',
        'bounce',
        'shadow',
        'stifle',
        'beat',
        'cheat',
        'pox',
        'counter',
        'discard',
        'cantrip',
        'balance',
        'stasis',
        'standstill',
        'whitehate',
        'bluehate',
        'blackhate',
        'redhate',
        'greenhate',
        'antiwaste',
        'delirium',
        'sacvalue',
        'lowtoughnesshate',
        'armageddon',
        'stax',
        'bloom',
        'weldingjar',
        'drawhate',
        'pluscard',
        'ramp',
        'devoteddruid',
        'fetchhate',
        'dragon',
        'company',
        'naturalorder',
        'flash',
        'wincon',
        'vial',
        'fixing',
        'colorlessvalue',
        'fetchable',
        'indestructable',
        'legendarymatters',
        'sol',
        'manland',
        'storage',
        'croprotate',
        'dnt',
        'equipment',
        'livingdeath',
        'eggskci',
        'hightide',
        'fatty',
        'walker',
        'blink',
        'miracles',
        'city',
        'wrath',
        'landtax',
        'discardvalue',
        'edict',
        'phoenix',
        'enchantress',
        'dork',
        'tinker',
        'landtax',
        'highpowerhate',
        'affinity',
        'academy',
        'stompy',
        'shardless',
        'lanterns',
        'depths',
        'survival',
        'landstill',
        'moat',
        'combo',
        'kite',
        'haste',
        'fog',
    }

    def __init__(self,
                 db: CardDatabase,
                 document_id: str = values.DOCUMENT_ID):
        self._db = db

        self._printing_tree_parser = PrintingTreeParser(db)
        self._sheet_client = GoogleSheetClient(document_id)

    def _parse_groups(self, s: str) -> t.FrozenSet[str]:
        groups = []

        if s:
            for group in s.split(','):
                group = group.rstrip().lstrip().replace('-', '')

                if not group in self._legal_groups:
                    raise ConstrainedCubeablesFetchException(
                        f'"{group}" not a legal group')

                groups.append(group)

        return frozenset(groups)

    def _fetch(self, start_column: int) -> t.List[ConstrainedNode]:
        exceptions = []
        constrained_cubeables = []

        for row in self._sheet_client.read_sheet(
                self.SHEET_NAME,
                start_column=start_column,
                start_row=4,
                end_column=start_column + 3,
                end_row=1000,
        ):
            if not row:
                print('Empty row in trapables input!')
                # TODO should probably be a runtime warning, but that requires logging...
                continue

            amount_cell, printings_cell, value_cell = row[:3]
            groups_cell = row[3] if len(row) > 3 else ''

            try:
                amount = int(amount_cell)
            except ValueError:
                amount = 1

            try:
                node = self._printing_tree_parser.parse(printings_cell)
            except PrintingTreeParserException as e:
                exceptions.append(e)
                continue

            try:
                value = self._value_value_map[int(value_cell)]
            except (ValueError, KeyError) as e:
                exceptions.append(e)
                continue

            try:
                groups = self._parse_groups(groups_cell)
            except ConstrainedCubeablesFetchException as e:
                exceptions.append(e)
                continue

            for _ in range(amount):
                constrained_cubeables.append(
                    ConstrainedNode(
                        node=node,
                        value=value,
                        groups=groups,
                    ))

        if exceptions:
            raise ConstrainedCubeablesFetchException(exceptions)

        return constrained_cubeables

    def fetch_garbage(self) -> t.List[ConstrainedNode]:
        return self._fetch(1)

    def fetch_garbage_lands(self) -> t.List[ConstrainedNode]:
        return self._fetch(5)

    def fetch_all(self) -> t.List[ConstrainedNode]:
        garbage = self.fetch_garbage()
        garbage.extend(self.fetch_garbage_lands())
        return garbage
Example #2
0
class CubeFetcher(object):
    def __init__(self,
                 db: CardDatabase,
                 document_id: str = values.DOCUMENT_ID,
                 sheet_id: str = SHEET_ID):
        self._db = db
        self._printing_tree_parser = PrintingTreeParser(self._db)
        self._document_id = document_id
        self._sheet_id = sheet_id
        self._printing_matcher = re.compile(
            "\\s*([\\w\\-': ,/]+)\\|([A-Z0-9_]+)")
        self._ticket_matcher = re.compile("([\\w ]+):(.*)")
        self._id_matcher = re.compile('\\d+$')

        self._sheet_client = GoogleSheetClient(document_id)

    def _get_cells(self) -> t.List[t.List[str]]:
        return self._sheet_client.read_sheet(
            sheet_name=SHEET_NAME,
            start_column=1,
            start_row=1,
            end_column=50,
            end_row=300,
            major_dimension='COLUMNS',
        )

    def _parse_trap_cell(self, cell: str, arg: t.Optional[str] = None) -> Trap:
        try:
            return Trap(
                node=self._printing_tree_parser.parse(cell),
                intention_type=(Trap.IntentionType.GARBAGE if arg is None
                                or arg == '' else Trap.IntentionType(
                                    arg.split('-')[0])),
            )
        except (PrintingTreeParserException, AttributeError) as e:
            raise CubeParseException(e)

    def _parse_printing(self, s: str, arg: str = None) -> Printing:
        m = self._printing_matcher.match(s)
        if not m:
            raise CubeParseException(f'Invalid printing "{s}"')

        if self._id_matcher.match(m.group(2)):
            try:
                return self._db.printings[int(m.group(2))]
            except KeyError:
                raise CubeParseException(f'Invalid printing id "{m.group(2)}"')

        try:
            cardboard = self._db.cardboards[m.group(1)]
        except KeyError:
            raise CubeParseException(f'Invalid cardboard "{m.group(1)}"')

        try:
            return cardboard.from_expansion(m.group(2))
        except KeyError:
            raise CubeParseException(
                f'Invalid expansion "{m.group(2)}" for cardboard "{cardboard}"'
            )

    def _parse_ticket(self, s: str, arg: str = None) -> Ticket:
        m = self._ticket_matcher.match(s)
        if not m:
            raise CubeParseException(f'Invalid ticket "{s}"')

        return Ticket([
            self._parse_printing(sm.group())
            for sm in self._printing_matcher.finditer(m.group(2))
        ], m.group(1))

    def _parse_purple(self, s: str, arg: str = None) -> Purple:
        return Purple(s)

    def _construct_cube(self, tsv: t.List[t.List[str]]) -> Cube:
        traps = []
        printings = []
        tickets = []
        purples = []

        exceptions = []  #type: t.List[t.Tuple[str, Exception]]

        def _parse_all_cells(
            cells: t.Iterable[str],
            arguments: t.Iterable[str],
            parser: t.Callable[[str, str], T],
        ) -> t.Iterable[T]:

            for _cell, arg in itertools.zip_longest(cells,
                                                    arguments,
                                                    fillvalue=''):
                if _cell:
                    try:
                        yield parser(_cell, arg)
                    except CubeParseException as e:
                        exceptions.append((_cell, e))

        for column, args in itertools.zip_longest(
                tsv, tsv[1:], fillvalue=['' for _ in range(len(tsv[-1]))]):

            if column[0] == 'TRAPS':
                traps.extend(
                    list(
                        _parse_all_cells(column[2:], args[2:],
                                         self._parse_trap_cell)))

            if column[0] == 'GARBAGE_TRAPS':
                traps.extend(
                    list(
                        _parse_all_cells(column[2:], (),
                                         self._parse_trap_cell)))

            elif column[0] in ('W', 'U', 'B', 'R', 'G', 'HYBRID', 'GOLD',
                               'COLORLESS', 'LAND'):
                printings.extend(
                    _parse_all_cells(column[2:], args[2:],
                                     self._parse_printing))

            elif column[0] == 'TICKETS':
                tickets = list(
                    _parse_all_cells(column[2:], args[2:], self._parse_ticket))

            elif column[0] == 'PURPLES':
                purples = list(
                    _parse_all_cells(column[2:], args[2:], self._parse_purple))

        if exceptions:
            raise CubeParseException(exceptions)

        return Cube(itertools.chain(printings, traps, tickets, purples))

    def fetch_cube(self) -> Cube:
        return self._construct_cube(self._get_cells())