Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
新人の私が_君_修正早いね_と言ってもらえた話.pdf
Search
mizzsugar
June 12, 2019
1.4k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
新人の私が_君_修正早いね_と言ってもらえた話.pdf
mizzsugar
June 12, 2019
More Decks by mizzsugar
See All by mizzsugar
自分のPCにPython環境を用意して_VSCodeでStreamlitアプリを動かすまでの8ステップ.pdf
mizzsugar
0
130
Colabのみで初心者向けPythonハンズオン.pdf
mizzsugar
0
93
厳しさとゆるさの間で迷う人に捧げる個人開発記
mizzsugar
0
76
SQLModel入門〜クエリと型〜
mizzsugar
3
1.6k
フルリモート向いてないと思っていた私が、なんだかんだ健やかに 1年半フルリモート出来ている話
mizzsugar
1
170
Djangoでのプロジェクトだって型ヒントを運用出来る!
mizzsugar
4
9.2k
「動くものは作れる」の一歩先へ 〜「自走プログラマー」の紹介〜
mizzsugar
0
660
pytestの第一歩 〜「テスト駆動Python」の紹介〜
mizzsugar
3
490
データ分析ツール開発でpoetryを使う選択肢
mizzsugar
1
1.2k
Featured
See All Featured
Amusing Abliteration
ianozsvald
1
320
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
3
3.8k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
Paper Plane
katiecoart
PRO
4
53k
The Pragmatic Product Professional
lauravandoore
37
7.5k
Fashionably flexible responsive web design (full day workshop)
malarkey
409
67k
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2.2k
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
730
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
420
Paper Plane (Part 1)
katiecoart
PRO
2
11k
Transcript
職業Pythonista歴半年の私が「君、 修正早いね」 先輩を唸らせた話 2019/06/12 @Stapy mizzsugar0425
お前誰よ ・みずきと申します ・2019/01~ 職業Pythonista(Djangoで受託開発してます。案件 募集中!!!) ・好き→Python/Django/Pyramid/React/PostgreSQL/コーヒー/ ロードバイク/一眼レフ
ある日のこと・・・
上司に仕様変更を伝えられる
それを聞いた先輩「これはエグい変更ですね」
しかし30分程で変更を実装した私
先輩「ぎゃふん」
YOU LOSE!! 俺の勝ち! 戦ってないけど
速攻ポイント ①テストコード書いた ②疎結合な実装を試みた
仕事のコード見せるわけにもいかないので FEの問題を参考にしました 仕様書 https://www.fe-siken.com/kakomon/30_haru/pm05.html 今回書いた詳しいコード(まだ途中) https://github.com/mizzsugar/medical-checkup
健診管理システムでこんな仕様があったとして https://www.fe-siken.com/kakomon/30_haru/pm05.html より 1. 当月が誕生月である従業員を抽出する。 2. ①で抽出した従業員を受診対象者とし,健康診断コースを決定する。 3. 受診日を決定し,受診対象者の健康診断レコードを健康管理システ ムに登録する。
突如打ち込まれる新たな手順 https://www.fe-siken.com/kakomon/30_haru/pm05.html より 1. 当月が誕生月である従業員を抽出する。 2. 前月の定期健康診断において,再検査が必要と判定された従業員を抽出 する。 3. ①と②で抽出した従業員を受診対象者とし,健康診断コースを決定する。 4.
受診日を決定し,受診対象者の健康診断レコードを健康管理システムに登 録する。
突如打ち込まれる新たな手順 https://www.fe-siken.com/kakomon/30_haru/pm05.html より 1. 当月が誕生月である従業員を抽出する。 2. 前月の定期健康診断において,再検査が必要と判定された従業員を抽出 する。 3. ①と②で抽出した従業員を受診対象者とし,健康診断コースを決定する。 4.
受診日を決定し,受診対象者の健康診断レコードを健康管理システムに登 録する。 さあ、 どうする!?!?
①テストコード書いた
テストコード書かないと プロダクトコードを修正 →ブラウザ開いて画面ぽちぽちして動作確認 →エラーになったらエラーの該当箇所を確認 →プロダクトコードを修正 →ブラウザ開いて画面ぽちぽちして動作確認 ・・・
テストコード書かないと プロダクトコードを修正 →ブラウザ開いて画面ぽちぽちして動作確認 →エラーになったらエラーの該当箇所を確認 →プロダクトコードを修正 →ブラウザ開いて画面ぽちぽちして動作確認 ・・・ 大変!!!!(>_<)
テストコード書くと ①「当月が誕生月である従業員を抽出する。」のテストケースのテストのみ書いたテスト がある(すでに通っている) ー当月誕生日である従業員が抽出されている ー当月誕生日でない従業員は抽出されていない ②「前月の定期健康診断において,再検査が必要と判定された従業員を抽出する」のテ ストケースのテストを追加 ー前月の定期健康診断を受けて再検査必要な従業員が抽出されている ー前月の定期健康診断を受けて再検査不要な従業員が抽出されていない ー前月以前に定期健身を受けた従業員は抽出されない
③テスト回す→落ちる→プロダクトコード修正→通る→リファクタリング
①「当月が誕生月である従業員を抽出する。」のテストケースのテストのみ書いたテスト がある(すでに通っている) ー当月誕生日である従業員が抽出されている ー当月誕生日でない従業員は抽出されていない
class TestExtractExamees(TestCase): @classmethod def setUpTestData(cls): """テスト用のデータを作成 長いので省略 """ ... def
test_iter_birthday_month_employees(self): actual = medical_checkup.core.extract_examinee.iter_birthday_month_employees(today=datetime.date(2019, 5, 1)) actual_list = list(actual) with self.subTest('5月が誕生日である社員がリストに入ってる'): self.assertTrue( employee.types.Employee( id=self.emp_1.id, birthday=datetime.date(1990, 5, 10), gender=employee.types.Gender(1), is_manager=False ) and employee.types.Employee( id=self.emp_2.id, birthday=datetime.date(1980, 5, 11), gender=employee.types.Gender(0), is_manager=True ) in actual_list )
) with self.subTest('5月が誕生日でない社員がリストに入っている'): self.assertFalse( employee.types.Employee( id=self.emp_3.id, birthday=datetime.date(1980, 6, 11), gender=employee.types.Gender(0),
is_manager=True ) in actual_list ) self.assertFalse( employee.types.Employee( id=self.emp_4.id, birthday=datetime.date(1980, 6, 12), gender=employee.types.Gender(1), is_manager=True ) in actual_list ) @classmethod def tearDownClass(cls): pass
②「前月の定期健康診断において,再検査が必要と判定された従業員を抽出する」のテ ストケースのテストを追加 ー前月の定期健康診断を受けて再検査必要な従業員が抽出されている ー前月の定期健康診断を受けて再検査不要な従業員が抽出されていない ー前月以前に定期健身を受けた従業員は抽出されない
def test_iter_reexamine_employees(self): # 前月(このテストでは2019年5月)の定期健康診断において,再検査が必要と判定された従業員のみがリストに入っている actual = medical_checkup.core.extract_examinee.iter_reexamine_employees( conducted_year=2019, conducted_month=5 )
actual_list = list(actual) with self.subTest('前月の定期健康診断を受けて再検査必要な従業員が抽出されている'): self.assertTrue( employee.types.Employee( id=self.emp_5.id, birthday=datetime.date(1980,5,11), gender=employee.types.Gender(0), is_manager=True ) in actual_list ) with self.subTest('前月の定期健康診断を受けて再検査不要な従業員が抽出されていない'): self.assertTrue( employee.types.Employee( id=self.emp_6.id, birthday=datetime.date(1980,5,11), gender=employee.types.Gender(0), is_manager=True ) in actual_list
gender=employee.types.Gender(0), is_manager=True ) in actual_list ) with self.subTest('前月の定期健康診断を受けて再検査不要な従業員が抽出されていない'): self.assertFalse( employee.types.Employee(
id=self.emp_6.id, birthday=datetime.date(1980,5,11), gender=employee.types.Gender(0), is_manager=True ) in actual_list ) with self.subTest('前月以前に定期健身を受けた従業員は抽出されない'): self.assertFalse( employee.types.Employee( id=self.emp_7.id, birthday=datetime.date(1980,5,11), gender=employee.types.Gender(0), is_manager=True ) in actual_list )
テスト用コマンド打つだけで 結果確認できて楽!
②疎結合な実装を試みた
ここを実装するとします https://www.fe-siken.com/kakomon/30_haru/pm05.html より 1. 当月が誕生月である従業員を抽出する。 2. ①で抽出した従業員を受診対象者とし,健康診断コースを決定する。 3. 受診日を決定し,受診対象者の健康診断レコードを健康管理システ ムに登録する。
Viewに全てをまとめるとこうなります。 import datetime from django.http import HttpRespons import employee.models.employee def
show_examiees_fat_controller(request): examinees = [ { 'id': emp.id, 'birthday': emp.birthday } for emp in employee.models.employee.Employee.objects.all() if emp.birthday.month==datetime.date.today().month ] return HttpResponse( { 'examinees': examinees } status=200, content_type='application/json' )
追加されます https://www.fe-siken.com/kakomon/30_haru/pm05.html より 1. 当月が誕生月である従業員を抽出する。 2. 前月の定期健康診断において,再検査が必要と判定された従業員を抽出 する。 3. ①と②で抽出した従業員を受診対象者とし,健康診断コースを決定する。 4.
受診日を決定し,受診対象者の健康診断レコードを健康管理システムに登 録する。
def show_examiees_fat_controller(request): # 当月誕生日の人 employees = [ { 'id': emp.id,
'birthday': emp.birthday } for emp in employee.models.employee.Employee.objects.all() if emp.birthday.month==datetime.date.today().month ] # 前月を定義 last_month = datetime.date.today() - relativedelta(month=1) # 前月の検査で再検査が必要と言われた人 re_exam_employees = [ { 'id': mc.employee.id, 'birthday': mc.employee.birthday } for mc in medical_checkup.models.checkup.MedicalCheckUp.objecsts.all() if mc.conducted_year==last_month.year and mc.conducted_month==last_month.month and mc.need_reexamination ] # 当月誕生日の人と前月の検査で再検査が必要と言われた人の配列を結合 employees += re_exam_employees return HttpResponse(
{ 'employees': employees } status=200, content_type='application/json' ) 動きはするけれども、ひとつの場所で いろんなことやってて大変だなあ・・・
viewと先ほどの計算部分のモジュールを分割すると
# (1)1 当月が誕生月である従業員を抽出する。 def iter_birthday_month_employees(today: datetime.date)->Iterator[employee.types.Employee]: """ today: 基本的にはdatetime.date.today()が入ります """
return ( emp for emp in employee.models.employee.Manager.iter_all() if emp.birthday.month==today.month ) # (1)2 前月の定期健康診断において,再検査が必要と判定された従業員を抽出する。 def iter_reexamine_employees(conducted_year: int, conducted_month: int) -> Iterator[employee.types.Employee]: return ( mc.employee for mc in medical_checkup.models.checkup.Manager.iter_all() if mc.conducted_year==conducted_year and mc.conducted_month==conducted_month and mc.need_reexamination ) # (1)と(2)を合わせたメソッド。View関数内にはこちらをいれる def extract_examiee(today: datetime.date) -> Iterator[employee.types.Employee]: last_month = today - relativedelta(month=1) conducted_year = last_month.year conducted_month = last_month.month return itertools.chain( iter_birthday_month_employees((today)), iter_reexamine_employees(conducted_year, conducted_year) ) service.py
views.py def show_examiees_fat_controller(request): # 対象者を抽出するメソッド employees = [ asdict(emp) for
emp in extract_examiee(datetime.date.today()) ] return HttpResponse( { 'employees': employees } status=200, content_type='application/json' )
Viewで表示する項目が変わった訳ではないので Viewの確認はほとんどしなくて良くなった
まとめ ・テストコードで楽々動作確認 ・疎結合で修正箇所を小さく抑える
ありがとうございました!!!