def test_header_skipper_multiple_line_header(path_sf_card_viewer): """should""" expected = [[ "2019/01/27", "", "", "", "", "", "", "195", "2896", "バス/路面等" ]] god_slayer = GodSlayerFactory( header=InstanceResource.HEADER_SF_CARD_VIEWER, encoding="shift_jis_2004").create(path_sf_card_viewer) GodSlayerChecker.assert_god_slayer(god_slayer, 1, expected)
def test_record_reader(path_gold_point_card_plus): """should""" expected = [ [ "2018/7/3", "東京電力 電気料金等", "ご本人", "1回払い", "", "18/8", "11402", "11402", "", "", "", "", "" ], [ "2018/7/4", "AMAZON.CO.JP", "ご本人", "1回払い", "", "18/8", "3456", "3456", "", "", "", "", "" ], ] god_slayer = GodSlayerFactory( encoding="shift_jis_2004").create(path_gold_point_card_plus) GodSlayerChecker.assert_god_slayer(god_slayer, 0, expected)
def test_header_skipper(path_gold_point_card_plus_201912): """should""" expected = [ ["2020/7/3", "東京電力 電気料金等", "11402", "1", "1", "11402", ""], [ "2020/7/3", "AMAZON WEB SERVICES (AWS.AMAZON.CO)", "66", "1", "1", "66", "0.60 USD 110.712 07 03" ], ["2020/7/4", "AMAZON.CO.JP", "3456", "1", "1", "3456", ""], ] god_slayer = GodSlayerFactory( header=InstanceResource.REGEX_HEADER_GOLD_POINT_CARD_PLUS, footer=InstanceResource.REGEX_FOOTER_GOLD_POINT_CARD_PLUS, encoding="shift_jis_2004", ).create(path_gold_point_card_plus_201912) GodSlayerChecker.assert_god_slayer(god_slayer, 0, expected)
def test_partition_skip_record_reader(path_view_card): """should""" expected = [ [ "2020/03/21", "板橋駅 オートチャージ", "3,000", "", "3,000", "1回払", "", "3,000", "", " ", "" ], [ "2020/03/31", "カード年会費", "524", "", "524", "1回払", "", "524", "", " ", "" ], ] god_slayer = GodSlayerFactory( header=InstanceResource.HEADER_VIEW_CARD, partition=[r"^\*{4}-\*{4}-\*{4}-[0-9]{4}\s.*$"], encoding="shift_jis_2004", ).create(path_view_card) GodSlayerChecker.assert_god_slayer(god_slayer, 6, expected)
def test_convert_string_to_int_or_none(): """ Argument should set into properties. Default encode should be UTF-8. Default csv herder should be None. """ regex_csv_file_name = r".*waon.*\.csv" god_slayer_factory = GodSlayerFactory() input_row_data_class = WaonRowData input_row_factory = WaonRowFactory() zaim_row_factory_selector = WaonZaimRowConverterFactory() account_context = AccountContext( regex_csv_file_name, god_slayer_factory, input_row_data_class, input_row_factory, zaim_row_factory_selector, ) assert account_context.regex_csv_file_name == regex_csv_file_name assert account_context.god_slayer_factory == god_slayer_factory assert account_context.input_row_data_class == input_row_data_class assert account_context.input_row_factory == input_row_factory assert account_context.zaim_row_converter_selector == zaim_row_factory_selector
class AccountContext(Generic[TypeVarInputRowData, TypeVarInputRow]): """This class implements recipe for converting steps for WAON CSV.""" # pylint: disable=invalid-name GOD_SLAYER_FACTORY_SF_CARD_VIEWER: GodSlayerFactory = field( default=GodSlayerFactory( header=[ "利用年月日", "定期", "鉄道会社名", "入場駅/事業者名", "定期", "鉄道会社名", "出場駅/降車場所", "利用額(円)", "残額(円)", "メモ" ], encoding="shift_jis_2004", ), init=False, ) # pylint: disable=invalid-name GOD_SLAYER_FACTORY_AMAZON: GodSlayerFactory = field( default=GodSlayerFactory( header=[ "注文日", "注文番号", "商品名", "付帯情報", "価格", "個数", "商品小計", "注文合計", "お届け先", "状態", "請求先", "請求額", "クレカ請求日", "クレカ請求額", "クレカ種類", "注文概要URL", "領収書URL", "商品URL", ], partition=[ r"\d{4}/\d{1,2}/\d{1,2}", r".*", "(注文全体)", "", "", "", "", r"\d*", "", "", r".*", r"\d*", "", "", r".*", r".*", r".*", "", ], encoding="utf-8-sig", ), init=False, ) regex_csv_file_name: str god_slayer_factory: GodSlayerFactory input_row_data_class: Type[TypeVarInputRowData] input_row_factory: InputRowFactory[TypeVarInputRowData, TypeVarInputRow] zaim_row_converter_selector: ZaimRowConverterFactory[TypeVarInputRow] def create_input_row_data_instance( self, list_input_row_standard_type_value: List[str]) -> InputRowData: """This method creates input row data instance by list data of input row.""" # noinspection PyArgumentList return self.input_row_data_class( *list_input_row_standard_type_value) # type: ignore def create_input_row_instance( self, input_row_data: TypeVarInputRowData) -> TypeVarInputRow: """This method creates input row instance by input row data instance.""" return self.input_row_factory.create(input_row_data) def convert_input_row_to_zaim_row(self, input_row: TypeVarInputRow) -> ZaimRow: """This method converts input row into zaim row.""" converter = self.zaim_row_converter_selector.create(input_row) return ZaimRowFactory.create(converter)
class Account(Enum): """This class implements constant of account in Zaim.""" WAON = AccountContext( r".*waon.*\.csv", GodSlayerFactory( header=["取引年月日", "利用店舗", "利用金額(税込)", "利用区分", "チャージ区分"]), WaonRowData, WaonRowFactory(), WaonZaimRowConverterFactory(), ) GOLD_POINT_CARD_PLUS = AccountContext( r".*gold_point_card_plus.*\.csv", GodSlayerFactory(encoding="shift_jis_2004"), GoldPointCardPlusRowData, GoldPointCardPlusRowFactory(), GoldPointCardPlusZaimRowConverterFactory(), ) GOLD_POINT_CARD_PLUS_201912 = AccountContext( r".*gold_point_card_plus_201912.*\.csv", GodSlayerFactory( header=[ r".* 様", r"[0-9\*]{4}-[0-9\*]{4}-[0-9\*]{4}-[0-9\*]{4}", "ゴールドポイントカードプラス" ], footer=["^$", "^$", "^$", "^$", "^$", r"^\d*$", "^$"], encoding="shift_jis_2004", ), GoldPointCardPlus201912RowData, GoldPointCardPlus201912RowFactory(), GoldPointCardPlus201912ZaimRowConverterFactory(), ) GOLD_POINT_CARD_PLUS_202009 = AccountContext( r".*gold_point_card_plus_202009.*\.csv", GodSlayerFactory( header=[ r".* 様", r"[0-9\*]{4}-[0-9\*]{4}-[0-9\*]{4}-[0-9\*]{4}", "ゴールドポイントカードプラス", "", "", "", "" ], footer=["^$", "^$", "^$", "^$", "^$", r"^\d*$", "^$"], encoding="shift_jis_2004", ), GoldPointCardPlus201912RowData, GoldPointCardPlus201912RowFactory(), GoldPointCardPlus201912ZaimRowConverterFactory(), ) MUFG = AccountContext( r".*mufg.*\.csv", GodSlayerFactory( header=[ "日付", "摘要", "摘要内容", "支払い金額", "預かり金額", "差引残高", "メモ", "未資金化区分", "入払区分" ], encoding="shift_jis_2004", ), MufgRowData, MufgRowFactory(), MufgZaimRowConverterFactory(), ) PASMO = AccountContext( r".*pasmo.*\.csv", AccountContext.GOD_SLAYER_FACTORY_SF_CARD_VIEWER, SFCardViewerRowData, # On this timing, CONFIG is not loaded. So we wrap CONFIG by lambda. SFCardViewerRowFactory(lambda: CONFIG.pasmo), SFCardViewerZaimRowConverterFactory(lambda: CONFIG.pasmo), ) AMAZON = AccountContext( r".*amazon.*\.csv", AccountContext.GOD_SLAYER_FACTORY_AMAZON, AmazonRowData, AmazonRowFactory(), AmazonZaimRowConverterFactory(), ) AMAZON_201911 = AccountContext( r".*amazon_201911.*\.csv", AccountContext.GOD_SLAYER_FACTORY_AMAZON, Amazon201911RowData, Amazon201911RowFactory(), Amazon201911ZaimRowConverterFactory(), ) VIEW_CARD = AccountContext( r".*view_card.*\.csv", GodSlayerFactory( header=[ "ご利用年月日", "ご利用箇所", "ご利用額", "払戻額", "ご請求額(うち手数料・利息)", "支払区分(回数)", "今回回数", "今回ご請求額・弁済金(うち手数料・利息)", "現地通貨額", "通貨略称", "換算レート", ], partition=[r"^\*{4}-\*{4}-\*{4}-[0-9]{4}\s.*$"], encoding="shift_jis_2004", ), ViewCardRowData, ViewCardRowFactory(), ViewCardZaimRowConverterFactory(), ) SUICA = AccountContext( r".*suica.*\.csv", AccountContext.GOD_SLAYER_FACTORY_SF_CARD_VIEWER, SFCardViewerRowData, # On this timing, CONFIG is not loaded. So we wrap CONFIG by lambda. SFCardViewerRowFactory(lambda: CONFIG.suica), SFCardViewerZaimRowConverterFactory(lambda: CONFIG.suica), ) PAY_PAL = AccountContext( r".*pay_pal.*\.csv", GodSlayerFactory(header=PayPalRowData.HEADER), PayPalRowData, PayPalRowFactory(), PayPalZaimRowConverterFactory(), ) @DynamicClassAttribute def value(self) -> AccountContext: """This method overwrite super method for type hint.""" return super().value @staticmethod def create_by_path_csv_input(path: Path) -> Account: """This function create correct setting instance by argument.""" # noinspection PyUnusedLocal account: Account matches = [ account for account in Account if re.search(account.value.regex_csv_file_name, path.name) ] if not matches: raise ValueError( "can't detect account type by csv file name. Please confirm csv file name." ) return max(matches, key=lambda matched_account: len(matched_account.value. regex_csv_file_name)) def create_input_row_data_instance( self, list_input_row_standard_type_value: List[str]) -> InputRowData: """This method creates input row data instance by list data of input row.""" return self.value.create_input_row_data_instance( list_input_row_standard_type_value) def create_input_row_instance(self, input_row_data: InputRowData) -> InputRow: """This method creates input row instance by input row data instance.""" return self.value.create_input_row_instance(input_row_data) def convert_input_row_to_zaim_row(self, input_row: InputRow) -> ZaimRow: """This method converts input row into zaim row.""" return self.value.convert_input_row_to_zaim_row(input_row)
def test_partition_skip_record_before_footer_reader( path_itabashiku_population): """should""" expected = [ ["0", "4062", "2069", "1993"], ["1", "4279", "2171", "2108"], ["2", "4285", "2152", "2133"], ["3", "4434", "2268", "2166"], ["4", "4243", "2207", "2036"], ["5", "4369", "2283", "2086"], ["6", "4345", "2213", "2132"], ["7", "4117", "2163", "1954"], ["8", "4155", "2146", "2009"], ["9", "4031", "2151", "1880"], ["10", "3933", "1988", "1945"], ["11", "4137", "2161", "1976"], ["12", "3842", "2018", "1824"], ["13", "3898", "1925", "1973"], ["14", "3820", "1914", "1906"], ["15", "3869", "1986", "1883"], ["16", "3902", "1996", "1906"], ["17", "4139", "2133", "2006"], ["18", "4474", "2242", "2232"], ["19", "5382", "2632", "2750"], ["20", "5674", "2854", "2820"], ["21", "6331", "3090", "3241"], ["22", "7218", "3385", "3833"], ["23", "8700", "4042", "4658"], ["24", "8773", "4132", "4641"], ["25", "9247", "4411", "4836"], ["26", "8757", "4205", "4552"], ["27", "9015", "4302", "4713"], ["28", "8814", "4289", "4525"], ["29", "8446", "4125", "4321"], ["30", "8173", "4050", "4123"], ["31", "8334", "4157", "4177"], ["32", "8145", "4179", "3966"], ["33", "7986", "3956", "4030"], ["34", "8126", "4170", "3956"], ["35", "8342", "4293", "4049"], ["36", "8472", "4320", "4152"], ["37", "8375", "4288", "4087"], ["38", "8213", "4253", "3960"], ["39", "8392", "4343", "4049"], ["40", "8402", "4344", "4058"], ["41", "8555", "4461", "4094"], ["42", "8545", "4400", "4145"], ["43", "8839", "4605", "4234"], ["44", "8908", "4620", "4288"], ["45", "9319", "4751", "4568"], ["46", "9611", "4927", "4684"], ["47", "9448", "4842", "4606"], ["48", "9291", "4857", "4434"], ["49", "8962", "4640", "4322"], ["50", "8714", "4400", "4314"], ["51", "8620", "4485", "4135"], ["52", "8455", "4466", "3989"], ["53", "6323", "3295", "3028"], ["54", "7974", "4093", "3881"], ["55", "7523", "3942", "3581"], ["56", "6935", "3606", "3329"], ["57", "6750", "3433", "3317"], ["58", "6449", "3358", "3091"], ["59", "6197", "3254", "2943"], ["60", "5945", "3065", "2880"], ["61", "6113", "3162", "2951"], ["62", "5380", "2755", "2625"], ["63", "5527", "2771", "2756"], ["64", "5626", "2825", "2801"], ["65", "5717", "2885", "2832"], ["66", "5913", "2994", "2919"], ["67", "6100", "3099", "3001"], ["68", "6266", "3121", "3145"], ["69", "6845", "3443", "3402"], ["70", "7587", "3805", "3782"], ["71", "7696", "3725", "3971"], ["72", "7861", "3761", "4100"], ["73", "5344", "2533", "2811"], ["74", "4419", "1971", "2448"], ["75", "5497", "2546", "2951"], ["76", "5814", "2611", "3203"], ["77", "5589", "2446", "3143"], ["78", "5471", "2371", "3100"], ["79", "4757", "2059", "2698"], ["80", "4120", "1673", "2447"], ["81", "3882", "1586", "2296"], ["82", "4202", "1584", "2618"], ["83", "3957", "1471", "2486"], ["84", "3882", "1390", "2492"], ["85", "3163", "1137", "2026"], ["86", "3031", "1035", "1996"], ["87", "2681", "916", "1765"], ["88", "2220", "680", "1540"], ["89", "1980", "603", "1377"], ["90", "1653", "508", "1145"], ["91", "1442", "374", "1068"], ["92", "1141", "291", "850"], ["93", "919", "240", "679"], ["94", "782", "179", "603"], ["95", "521", "119", "402"], ["96", "375", "59", "316"], ["97", "308", "51", "257"], ["98", "187", "32", "155"], ["99", "131", "23", "108"], ["100", "91", "7", "84"], ["101", "57", "5", "52"], ["102", "43", "7", "36"], ] god_slayer = GodSlayerFactory( header=InstanceResource.HEADER_ITABASHIKU_POPULATION, partition=[r"^(総数)|(\d*〜\d*歳?)$", r"^\d*$", r"^\d*$", r"^\d*$"], footer=InstanceResource.FOOTER_ITABASHIKU_POPULATION, encoding="shift_jis_2004", ).create(path_itabashiku_population) GodSlayerChecker.assert_god_slayer_partition(god_slayer, expected)