Esempio n. 1
0
    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)
Esempio n. 2
0
    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)
Esempio n. 3
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
Esempio n. 4
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())
Esempio n. 5
0
 def __init__(self, evaluator: Evaluator, wish_list_fetcher: WishListFetcher):
     self._evaluator = evaluator
     self._wish_list_fetcher = wish_list_fetcher
     self._client = GoogleSheetClient(SHEET_ID)
Esempio n. 6
0
class SheetsUpdater(object):

    def __init__(self, evaluator: Evaluator, wish_list_fetcher: WishListFetcher):
        self._evaluator = evaluator
        self._wish_list_fetcher = wish_list_fetcher
        self._client = GoogleSheetClient(SHEET_ID)

    def _update_output_sheet(self) -> None:
        values = [
            list(
                itertools.chain(
                    *row
                )
            )
            for row in
            itertools.zip_longest(
                *(
                    _seller_to_grid(
                        concluded_seller.sorted_concluded_wishes,
                        concluded_seller.seller.name,
                        concluded_seller.price,
                        concluded_seller.value,
                    )
                    for concluded_seller in
                    self._evaluator.concluded_sellers[:TOP_SELLERS_AMOUNT]
                ),
                fillvalue = ('', '', ''),
            )
        ]

        self._client.clear_sheet(OUTPUT_SHEET_ID)
        self._client.update_sheet(
            sheet_name = OUTPUT_SHEET_NAME,
            start_column = 1,
            start_row = 1,
            values = values,
        )

        print('wish list sheet update done')

    def _update_knapsack_output_sheet(self) -> None:
        values = [
            list(
                itertools.chain(
                    *row
                )
            )
            for row in
            itertools.zip_longest(
                *(
                    _seller_to_grid(
                        sorted(
                            concluded_seller.get_knapsack_values(
                                KNAPSACK_CAPACITY
                            )[1],
                            key = lambda w: w.value,
                            reverse = True,
                        ),
                        concluded_seller.seller.name,
                        sum(
                            concluded_wish.price
                            for concluded_wish in
                            concluded_seller.get_knapsack_values(
                                KNAPSACK_CAPACITY
                            )[1]
                        ),
                        sum(
                            concluded_wish.value
                            for concluded_wish in
                            concluded_seller.get_knapsack_values(
                                KNAPSACK_CAPACITY
                            )[1]
                        ),
                    )
                    for concluded_seller in
                    sorted(
                        self._evaluator.concluded_sellers[:KNAPSACK_SEARCH_SPACE],
                        key = lambda s: s.get_knapsack_values(KNAPSACK_CAPACITY)[0],
                        reverse = True,
                    )[:TOP_SELLERS_AMOUNT]
                ),
                fillvalue = ('', '', ''),
            )
        ]

        self._client.clear_sheet(OUTPUT_KNAPSACK_SHEET_ID)

        self._client.update_sheet(
            sheet_name = OUTPUT_KNAPSACK_SHEET_NAME,
            start_column = 1,
            start_row = 1,
            values = values,
        )

        print('knapsack sheet updated')

    def _update_wish_list(self) -> None:
        wishes = list(
            self._wish_list_fetcher.fetch_wishes(
                included_suspended = True,
                collect_errors = False,
            )
        )

        grid = []

        for concluded_seller in self._evaluator.concluded_sellers[:TOP_SELLERS_AMOUNT]:

            wish_map: t.Dict[Wish, ConcludedWish] = {
                concluded_wish.wish:
                    concluded_wish
                for concluded_wish in
                concluded_seller.concluded_wishes
            }

            column = [concluded_seller.seller.name, '']

            for wish in wishes:
                if wish is None:
                    column.append('')
                    continue

                concluded_wish = wish_map.get(wish, None)

                if concluded_wish is None:
                    column.append('not in wish_list')
                    continue

                if not concluded_wish.include:
                    column.append('')
                    continue

                column.append(str(concluded_wish))

            grid.append(column)

        start_column = 5
        start_row = 2

        self._client.update_sheet(
            INPUT_SHEET_NAME,
            start_column = start_column,
            start_row = start_row,
            values = list(zip(*grid)),
        )

        print('wish list input sheet updated')

    def update_sheets(
        self,
        update_output_sheet: bool = True,
        update_knapsack_output_sheet: bool = True,
        update_wish_list: bool = True,
    ):
        if update_output_sheet:
            self._update_output_sheet()

        if update_knapsack_output_sheet:
            self._update_knapsack_output_sheet()

        if update_wish_list:
            self._update_wish_list()
Esempio n. 7
0
    def __init__(self, db: CardDatabase, spreadsheet_id: str, sheet_name: str):
        self._wish_parser = WishParser(db)
        self._sheet_client = GoogleSheetClient(spreadsheet_id)

        self._sheet_name = sheet_name
Esempio n. 8
0
class WishListFetcher(object):

    _START_COLUMN = 2
    _START_ROW = 4
    _END_COLUMN = _START_COLUMN + 2
    _END_ROW = 1000

    def __init__(self, db: CardDatabase, spreadsheet_id: str, sheet_name: str):
        self._wish_parser = WishParser(db)
        self._sheet_client = GoogleSheetClient(spreadsheet_id)

        self._sheet_name = sheet_name

    def fetch_wishes(
        self,
        included_suspended: bool = False,
        collect_errors: bool = True,
    ) -> t.Iterable[t.Optional[Wish]]:
        exceptions = []

        for values in self._sheet_client.read_sheet(
            sheet_name=self._sheet_name,
            start_column=self._START_COLUMN,
            start_row=self._START_ROW,
            end_column=self._END_COLUMN,
            end_row=self._END_ROW,
        ):
            if not values:
                continue
            s, weight = values[:2]
            status = values[2] if len(values) >= 3 else ''

            if status == 'suspended' or status == 'ignored':
                if included_suspended:
                    yield None
                continue

            try:
                weight = int(weight)
            except ValueError:
                e = WishParseException(f'Invalid weight "{weight}"')
                if collect_errors:
                    exceptions.append((s, weight, e))
                else:
                    raise e
                continue

            try:
                wish = self._wish_parser.parse(s)
            except WishParseException as e:
                if collect_errors:
                    exceptions.append((s, weight, e))
                else:
                    raise e
                continue

            wish.weight = weight
            yield wish

        if exceptions:
            raise WishParseException('\n'.join(str(item) for item in exceptions))

    def fetch(self) -> WishList:
        return WishList(
            wishes = list(
                self.fetch_wishes(
                    included_suspended = False,
                    collect_errors = True,
                )
            )
        )