Python中級 - Python中級のチャレンジ - そこそこ複雑な注文集計処理を作る演習問題 - 2問目¶
main.py
の2問目終了時の解答コードです。
1from datetime import date
2import os
3
4ITEM_TSV_PATH = 'items.tsv'
5ORDER_DIR = 'order/'
6FAILURE_DIR = 'failure/'
7DELIVERY_DIR = 'delivery/'
8
9
10class Item:
11 """ 1商品に対応するクラス
12 """
13 def __init__(self, item_id, name, price):
14 self.item_id = item_id
15 self.name = name
16 self.price = price
17
18
19class Items:
20 def __init__(self, items):
21 self.items = items
22
23 def has_id(self, item_id):
24 """ item_id をもつ商品が存在するかチェックする
25 """
26 for item in self.items:
27 if item.item_id == item_id:
28 return True
29 return False
30
31
32class Order:
33 def __init__(self, item_id, amount, shipping_address, tel_number,
34 fullname, shipping_date_str, order_file):
35 self.item_id = item_id
36 self.amount = amount
37 self.shipping_address = shipping_address
38 self.tel_number = tel_number
39 self.fullname = fullname
40 self.shipping_date_str = shipping_date_str
41 self.order_file = order_file
42
43 self.amount_int = None
44 self.shipping_date = None
45
46 def validate(self, items):
47 """ 各注文の値が正しいかバリデーションチェックする。OKの場合True、NGの場合False
48 """
49
50 def row_string(self):
51 return ','.join((
52 self.item_id,
53 self.amount,
54 self.shipping_address,
55 self.tel_number,
56 self.fullname,
57 self.shipping_date_str,
58 self.order_file,
59 ))
60
61
62def load_items():
63 """ ITEM_TSV_PATHのTSVからItemsを作る
64 """
65 items = []
66 with open(ITEM_TSV_PATH, encoding='utf-8') as f:
67 for row in f:
68 item_id, name, price = row.split('\t')
69 item = Item(item_id.strip(), name.strip(), price.strip())
70 items.append(item)
71 return Items(items)
72
73
74def load_orders(target_date):
75 """ ORDER_DIR のCSVからOrderのリストを作る
76
77 * 各値の前後から空白を除外する
78 """
79 date_str = target_date.strftime('%Y%m%d')
80 orders = []
81 for filename in os.listdir(ORDER_DIR):
82 if date_str not in filename:
83 # 対象日でないファイルは無視する
84 continue
85
86 filepath = os.path.join(ORDER_DIR, filename)
87 with open(filepath, encoding='utf-8') as f:
88 for row in f:
89 item_id, amount, address, tel, name, shipping_date = row.split(',')
90 order = Order(
91 item_id.strip(),
92 amount.strip(),
93 address.strip(),
94 tel.strip(),
95 name.strip(),
96 shipping_date.strip(),
97 filename,
98 )
99 orders.append(order)
100 return orders
101
102
103def write_deliver_orders(orders):
104 """ Orderのリストを受け取って日別注文ファイルに書き込み
105 """
106 # 宅配日毎に集計。ファイルをオープンする回数を減らすため事前にまとめる
107 date_orders = {}
108 for order in orders:
109 if order.shipping_date in date_orders:
110 date_orders[order.shipping_date].append(order)
111 else:
112 date_orders[order.shipping_date] = [order]
113
114 for d, day_orders in date_orders.items():
115 filename = 'delivery_{}.csv'.format(d.strftime('%Y%m%d'))
116 filepath = os.path.join(DELIVERY_DIR, filename)
117 with open(filepath, 'a', encoding='utf-8') as f:
118 for order in day_orders:
119 f.write(order.row_string() + '\n')
120
121
122def write_failure_orders(orders, order_date):
123 """ Orderのリストを受け取って注文受付失敗ファイルに書き込み
124 """
125
126
127def main(target_date=None):
128 """ 毎日の注文集計用スクリプト
129
130 1. 商品マスター読み込み
131 2. 当日分の注文受付ファイル読み込み
132 3. 注文をバリデーションチェック
133 4. 日別注文ファイル書き込み
134 5. 注文受付失敗ファイル書き込み
135 """
136 target_date = target_date or date.today()
137
138 items = load_items()
139 orders = load_orders(target_date)
140 validated_orders = []
141 failed_orders = []
142 for order in orders:
143 if order.validate(items):
144 validated_orders.append(order)
145 else:
146 failed_orders.append(order)
147
148 if validated_orders:
149 write_deliver_orders(validated_orders)
150 if failed_orders:
151 write_failure_orders(failed_orders, target_date)
152
153
154if __name__ == '__main__':
155 main(date(2016, 12, 14)) # あくまで動作確認用に日付を指定している