diff --git a/.travis.yml b/.travis.yml index 1f6b0b0..318b57d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,6 @@ script: | else error_without_calling_exit; fi -# - pandoc --from=markdown --to=rst README.md | diff DESCRIPTION.rst - after_success: | if [ "${TEST_SUITE}" = "test" ]; then diff --git a/mtg_ssm/serialization/xlsx.py b/mtg_ssm/serialization/xlsx.py index b16219e..dd23222 100644 --- a/mtg_ssm/serialization/xlsx.py +++ b/mtg_ssm/serialization/xlsx.py @@ -21,17 +21,11 @@ 'count', ] -ALL_SETS_SHEET_TOTALS = [ - 'Total', - None, - None, - None, - None, - '=SUM(F3:F65535)', - '=SUM(G3:G65535)', - '=SUM(H3:H65535)', - '=SUM(I3:I65535)', -] +ALL_SETS_SHEET_TOTALS = ( + ['Total'] + + [None] * 4 + + ['=SUM({c}3:{c}65535)'.format(c=c) for c in 'FGHI'] +) def create_all_sets(sheet, collection): @@ -74,23 +68,6 @@ def style_all_sets(sheet): cdim.hidden = hidden -SET_SHEET_HEADER = [ - 'have', - 'name', - 'id', - 'multiverseid', - 'number', - 'artist', -] + [ct.name for ct in models.CountTypes] + [ - 'others', -] -COUNT_COLS = [ - string.ascii_uppercase[SET_SHEET_HEADER.index(ct.name)] - for ct in models.CountTypes] -HAVE_TMPL = '=' + '+'.join(c + '{0}' for c in COUNT_COLS) -ROW_OFFSET = 2 - - def create_haverefs(printings): """Create a reference to the have cells for printings in a single set.""" card_set = printings[0].set @@ -101,26 +78,80 @@ def create_haverefs(printings): return '+'.join(haverefs) -def get_other_print_references(printing): - """Get an xlsx formula to list counts of a card from other sets.""" - if printing.card.strict_basic: +def get_references(card, exclude_sets=None): + """Get an equation for the references to a card.""" + if card.strict_basic: return None # Basics are so prolific that they overwhelm Excel - set_to_others = collections.defaultdict(list) - for other in printing.card.printings: - if other.set_code != printing.set_code: - set_to_others[other.set].append(other) + if exclude_sets is None: + exclude_sets = set() - set_to_haveref = {k: create_haverefs(v) for k, v in set_to_others.items()} + set_to_prints = collections.defaultdict(list) + for printing in card.printings: + if printing.set not in exclude_sets: + set_to_prints[printing.set].append(printing) + + set_to_haveref = {k: create_haverefs(v) for k, v in set_to_prints.items()} if not set_to_haveref: return None - other_references = [] + references = [] for card_set in sorted(set_to_haveref, key=lambda cset: cset.release_date): - other_reference = 'IF({count}>0,"{setcode}: "&{count}&", ","")'.format( + reference = 'IF({count}>0,"{setcode}: "&{count}&", ","")'.format( setcode=card_set.code, count=set_to_haveref[card_set]) - other_references.append(other_reference) - return '=' + '&'.join(other_references) + references.append(reference) + return '=' + '&'.join(references) + + +ALL_CARDS_SHEET_HEADER = [ + 'name', + 'have', +] + + +def create_all_cards(sheet, collection): + """Create all cards sheet from collection.""" + sheet.title = 'All Cards' + sheet.append(ALL_CARDS_SHEET_HEADER) + # Should this be done in the collection class indexes? + # Should card_sets not be done in the collection indexes? + cards = sorted(collection.name_to_card.values(), key=lambda c: c.name) + for card in cards: + row = [ + card.name, + get_references(card), + ] + sheet.append(row) + + +def style_all_cards(sheet): + """Apply styles to the all cards sheet.""" + sheet.freeze_panes = sheet['B2'] + col_width_hidden = [ + ('A', 24, False), + ('B', 32, False), + ] + for col, width, hidden in col_width_hidden: + cdim = sheet.column_dimensions[col] + cdim.width = width + cdim.hidden = hidden + + +SET_SHEET_HEADER = [ + 'have', + 'name', + 'id', + 'multiverseid', + 'number', + 'artist', +] + [ct.name for ct in models.CountTypes] + [ + 'others', +] +COUNT_COLS = [ + string.ascii_uppercase[SET_SHEET_HEADER.index(ct.name)] + for ct in models.CountTypes] +HAVE_TMPL = '=' + '+'.join(c + '{0}' for c in COUNT_COLS) +ROW_OFFSET = 2 def create_set_sheet(sheet, card_set): @@ -139,7 +170,7 @@ def create_set_sheet(sheet, card_set): ] for counttype in models.CountTypes: row.append(printing.counts.get(counttype)) - row.append(get_other_print_references(printing)) + row.append(get_references(printing.card, exclude_sets={card_set})) sheet.append(row) @@ -184,6 +215,9 @@ def write_to_file(self, filename: str) -> None: all_sets_sheet = workbook.create_sheet() create_all_sets(all_sets_sheet, self.collection) style_all_sets(all_sets_sheet) + all_cards_sheet = workbook.create_sheet() + create_all_cards(all_cards_sheet, self.collection) + style_all_cards(all_cards_sheet) for card_set in self.collection.card_sets: set_sheet = workbook.create_sheet() create_set_sheet(set_sheet, card_set) @@ -196,7 +230,7 @@ def read_from_file(self, filename: str) -> None: workbook = openpyxl.load_workbook(filename=filename, read_only=True) for sheet in workbook.worksheets: if sheet.title not in self.collection.code_to_card_set: - if sheet.title in ['Sets', 'All Sets']: + if sheet.title in {'Sets', 'All Sets', 'All Cards'}: continue raise interface.DeserializationError( 'No known set with code {}'.format(sheet.title)) diff --git a/tests/serialization/test_xlsx.py b/tests/serialization/test_xlsx.py index 5e9f335..b4fb85b 100644 --- a/tests/serialization/test_xlsx.py +++ b/tests/serialization/test_xlsx.py @@ -19,7 +19,10 @@ class XlsxSerializerTest(mtgjson_testcase.MtgJsonTestCase): def setUp(self): super().setUp() - self.collection = collection.Collection(self.mtg_data) + set_data = { + k: v for k, v in self.mtg_data.items() + if k in {'LEA', 'FEM', 'S00', 'ICE', 'HOP'}} + self.collection = collection.Collection(set_data) def test_create_all_sets(self): # Setup @@ -35,20 +38,9 @@ def test_create_all_sets(self): ['Total', None, None, None, None, '=SUM(F3:F65535)', '=SUM(G3:G65535)', '=SUM(H3:H65535)', '=SUM(I3:I65535)'], ['LEA', 'Limited Edition Alpha', dt.datetime(1993, 8, 5), None, 'core', 4, '=COUNTIF(\'LEA\'!A:A,">0")', '=COUNTIF(\'LEA\'!A:A,">=4")', "=SUM('LEA'!A:A)"], ['FEM', 'Fallen Empires', dt.datetime(1994, 11, 1), None, 'expansion', 4, '=COUNTIF(\'FEM\'!A:A,">0")', '=COUNTIF(\'FEM\'!A:A,">=4")', "=SUM('FEM'!A:A)"], - ['pMEI', 'Media Inserts', dt.datetime(1995, 1, 1), None, 'promo', 1, '=COUNTIF(\'pMEI\'!A:A,">0")', '=COUNTIF(\'pMEI\'!A:A,">=4")', "=SUM('pMEI'!A:A)"], ['ICE', 'Ice Age', dt.datetime(1995, 6, 1), 'Ice Age', 'expansion', 5, '=COUNTIF(\'ICE\'!A:A,">0")', '=COUNTIF(\'ICE\'!A:A,">=4")', "=SUM('ICE'!A:A)"], - ['HML', 'Homelands', dt.datetime(1995, 10, 1), None, 'expansion', 2, '=COUNTIF(\'HML\'!A:A,">0")', '=COUNTIF(\'HML\'!A:A,">=4")', "=SUM('HML'!A:A)"], ['S00', 'Starter 2000', dt.datetime(2000, 4, 1), None, 'starter', 1, '=COUNTIF(\'S00\'!A:A,">0")', '=COUNTIF(\'S00\'!A:A,">=4")', "=SUM('S00'!A:A)"], - ['PLS', 'Planeshift', dt.datetime(2001, 2, 5), 'Invasion', 'expansion', 2, '=COUNTIF(\'PLS\'!A:A,">0")', '=COUNTIF(\'PLS\'!A:A,">=4")', "=SUM('PLS'!A:A)"], - ['CHK', 'Champions of Kamigawa', dt.datetime(2004, 10, 1, 0, 0), 'Kamigawa', 'expansion', 2, '=COUNTIF(\'CHK\'!A:A,">0")', '=COUNTIF(\'CHK\'!A:A,">=4")', "=SUM('CHK'!A:A)"], - ['PLC', 'Planar Chaos', dt.datetime(2007, 2, 2, 0, 0), 'Time Spiral', 'expansion', 2, '=COUNTIF(\'PLC\'!A:A,">0")', '=COUNTIF(\'PLC\'!A:A,">=4")', "=SUM('PLC'!A:A)"], - ['pMGD', 'Magic Game Day', dt.datetime(2007, 7, 14), None, 'promo', 1, '=COUNTIF(\'pMGD\'!A:A,">0")', '=COUNTIF(\'pMGD\'!A:A,">=4")', "=SUM('pMGD'!A:A)"], ['HOP', 'Planechase', dt.datetime(2009, 9, 4), None, 'planechase', 3, '=COUNTIF(\'HOP\'!A:A,">0")', '=COUNTIF(\'HOP\'!A:A,">=4")', "=SUM('HOP'!A:A)"], - ['ARC', 'Archenemy', dt.datetime(2010, 6, 18), None, 'archenemy', 2, '=COUNTIF(\'ARC\'!A:A,">0")', '=COUNTIF(\'ARC\'!A:A,">=4")', "=SUM('ARC'!A:A)"], - ['ISD', 'Innistrad', dt.datetime(2011, 9, 30), 'Innistrad', 'expansion', 6, '=COUNTIF(\'ISD\'!A:A,">0")', '=COUNTIF(\'ISD\'!A:A,">=4")', "=SUM('ISD'!A:A)"], - ['PC2', 'Planechase 2012 Edition', dt.datetime(2012, 6, 1), None, 'planechase', 4, '=COUNTIF(\'PC2\'!A:A,">0")', '=COUNTIF(\'PC2\'!A:A,">=4")', "=SUM('PC2'!A:A)"], - ['MMA', 'Modern Masters', dt.datetime(2013, 6, 7, 0, 0), None, 'reprint', 1, '=COUNTIF(\'MMA\'!A:A,">0")', '=COUNTIF(\'MMA\'!A:A,">=4")', "=SUM('MMA'!A:A)"], - ['OGW', 'Oath of the Gatewatch', dt.datetime(2016, 1, 22, 0, 0), 'Battle for Zendikar', 'expansion', 4, '=COUNTIF(\'OGW\'!A:A,">0")', '=COUNTIF(\'OGW\'!A:A,">=4")', "=SUM('OGW'!A:A)"], ] self.assertEqual(expected, rows) self.assertEqual('All Sets', sheet.title) @@ -72,48 +64,91 @@ def test_create_haverefs(self): def test_get_refs_basic_land(self): # Setup - lea_forest = self.collection.id_to_printing[ - '5ede9781b0c5d157c28a15c3153a455d7d6180fa'] + forest = self.collection.name_to_card['Forest'] # Execute - print_refs = xlsx.get_other_print_references(lea_forest) + print_refs = xlsx.get_references(forest) # Verify self.assertIsNone(print_refs) - def test_get_refs_no_others(self): + def test_get_refs_singular(self): # Setup - rhox = self.collection.id_to_printing[ - '536d407161fa03eddee7da0e823c2042a8fa0262'] + rhox = self.collection.name_to_card['Rhox'] # Execute - print_refs = xlsx.get_other_print_references(rhox) + print_refs = xlsx.get_references(rhox) # Verify - self.assertIsNone(print_refs) + expected = '=IF(\'S00\'!A2>0,"S00: "&\'S00\'!A2&", ","")' + self.assertEqual(expected, print_refs) - def test_ref_refs_multiple_sets(self): + def test_get_refs_exclude_only(self): # Setup - lea_dark_rit = self.collection.id_to_printing[ - 'fff0b8e8fea06ee1ac5c35f048a0a459b1222673'] + rhox = self.collection.name_to_card['Rhox'] + s00 = self.collection.code_to_card_set['S00'] # Execute - print_refs = xlsx.get_other_print_references(lea_dark_rit) + print_refs = xlsx.get_references(rhox, exclude_sets={s00}) + # Verify' + self.assertFalse(print_refs) + + def test_get_refs_multiple_sets(self): + # Setup + dark_rit = self.collection.name_to_card['Dark Ritual'] + lea = self.collection.code_to_card_set['LEA'] + # Execute + print_refs = xlsx.get_references(dark_rit, exclude_sets={lea}) # Verify expected = ( '=IF(\'ICE\'!A2>0,"ICE: "&\'ICE\'!A2&", ","")' '&IF(\'HOP\'!A4>0,"HOP: "&\'HOP\'!A4&", ","")') self.assertEqual(expected, print_refs) - def test_get_refs_multiple_variants(self): + def test_get_refs_exclude_multiple(self): # Setup - mma_thallid = self.collection.id_to_printing[ - 'fc46a4b72d216117a352f59217a84d0baeaaacb7'] - + dark_rit = self.collection.name_to_card['Dark Ritual'] + lea = self.collection.code_to_card_set['LEA'] + ice = self.collection.code_to_card_set['ICE'] # Execute - print_refs = xlsx.get_other_print_references(mma_thallid) + print_refs = xlsx.get_references(dark_rit, exclude_sets={lea, ice}) + # Verify + expected = '=IF(\'HOP\'!A4>0,"HOP: "&\'HOP\'!A4&", ","")' + self.assertEqual(expected, print_refs) + def test_get_refs_multiple_variants(self): + # Setup + thallid = self.collection.name_to_card['Thallid'] + # Execute + print_refs = xlsx.get_references(thallid) # Verify expected = ( '=IF(\'FEM\'!A2+\'FEM\'!A3+\'FEM\'!A4+\'FEM\'!A5>0,' '"FEM: "&\'FEM\'!A2+\'FEM\'!A3+\'FEM\'!A4+\'FEM\'!A5&", ","")') self.assertEqual(expected, print_refs) + def test_create_all_cards_sheet(self): + # Setup + book = openpyxl.Workbook() + sheet = book.create_sheet() + + # Execute + xlsx.create_all_cards(sheet, self.collection) + + # Verify + rows = [[cell.value for cell in row] for row in sheet.rows] + expected = [ + # pylint: disable=line-too-long + ['name', 'have'], + ['Academy at Tolaria West', '=IF(\'HOP\'!A2>0,"HOP: "&\'HOP\'!A2&", ","")'], + ['Air Elemental', '=IF(\'LEA\'!A3>0,"LEA: "&\'LEA\'!A3&", ","")'], + ["Akroma's Vengeance", '=IF(\'HOP\'!A3>0,"HOP: "&\'HOP\'!A3&", ","")'], + ['Dark Ritual', '=IF(\'LEA\'!A2>0,"LEA: "&\'LEA\'!A2&", ","")&' + 'IF(\'ICE\'!A2>0,"ICE: "&\'ICE\'!A2&", ","")&' + 'IF(\'HOP\'!A4>0,"HOP: "&\'HOP\'!A4&", ","")'], + ['Forest', None], + ['Rhox', '=IF(\'S00\'!A2>0,"S00: "&\'S00\'!A2&", ","")'], + ['Snow-Covered Forest', '=IF(\'ICE\'!A6>0,"ICE: "&\'ICE\'!A6&", ","")'], + ['Thallid', '=IF(\'FEM\'!A2+\'FEM\'!A3+\'FEM\'!A4+\'FEM\'!A5>0,"FEM: "&\'FEM\'!A2+\'FEM\'!A3+\'FEM\'!A4+\'FEM\'!A5&", ","")'], + ] + self.assertEqual(expected, rows) + self.assertEqual('All Cards', sheet.title) + def test_create_set_sheet(self): # Setup forest1 = self.collection.id_to_printing[ @@ -164,24 +199,7 @@ def test_write_to_file(self): # Verify workbook = openpyxl.load_workbook(filename=xlsxfilename) expected_sheetnames = [ - 'All Sets', - 'LEA', - 'FEM', - 'pMEI', - 'ICE', - 'HML', - 'S00', - 'PLS', - 'CHK', - 'PLC', - 'pMGD', - 'HOP', - 'ARC', - 'ISD', - 'PC2', - 'MMA', - 'OGW', - ] + 'All Sets', 'All Cards', 'LEA', 'FEM', 'ICE', 'S00', 'HOP'] self.assertEqual(expected_sheetnames, workbook.sheetnames) s00_rows = [[cell.value for cell in row] for row in workbook['S00']]