動機

今まで,何かの予定を決める際,Googleカレンダーに入っている予定とバイトのシフトをそれぞれ見ながらどこが空いているか確認する必要がありました.バイトのシフトをカレンダーに追加しようにも,月に15回程シフトが入っているため,結構手間がかかります.

そこで,シフトの画像を読み取ることで自動でカレンダーにシフト予定を追加するアプリケーションを作りたいと考えました.

処理の流れ

graph
	Slack -- 1.シフト画像 --> アプリケーション
	アプリケーション -- 5.シフトデータ --> Slack
	アプリケーション -- 2.シフト画像 --> Google_Cloud_Vision
	Google_Cloud_Vision -- 3.画像解析結果 --> アプリケーション
	アプリケーション -- 4.シフトデータ --> Google_Calender

設計

以下のサイトを参考にしながらやりました.

正直,知識不足で7割くらいGeminiに設計してもらいました.

 classDiagram
    direction LR

    class Controller {
        <<Controller>>
        - _slack_client: SlackClient
        - _image_processor: ImageProcessor
        - _calendar_client: CalendarClient
        + result_dir: str
        + run() None
        + handle_file_share_event(file: File) None
    }

    class SlackClient {
        <<Client>>
        - _bot_token: str
        - _socket_token: str
        - _app: Slack_bolt.app.app.App
        - _controller: Controller
        + listen_for_events() None
	      - _regist_handlers() None
		      - _handle_event(event: dict) None
		    - _process_file_share_event(event: dict) None
        + download_image(private_url; str, result_dir: str) None
        + post_result(file: File, shifts: list~Shift~) None
    }

    class ImageProcessor {
        <<Service>>
        - _vision_api_client: VisionClient
        - _shift_parser: ShiftParser
        + process_image(result_dir: str) list~Shift~
    }

    class VisionClient {
        <<Client>>
        - _api_key: str
        - _creds: google.oauth2.service_account.Credentials
        - _client: google.cloud.vision_v1.ImageAnnotatorClient
        + extract_data_from_image(result_dir: str) None
    }

    class ShiftParser {
        <<Parser>>
        + parse_data_to_shifts(result_dir: str) list~Shift~
    }

    class CalendarClient {
        <<Client>>
        - _api_key: str
        - _calendar_id: str
        - _creds: google.oauth2.service_account.Credentials
        - _service: googleapiclient.discovery.Resource
        + create_events_from_shifts(shifts: list~Shift~) None
    }
    
    class Shift {
        <<Data>>
        + summary: str
        + start_datetime: str
        + end_datetime: str
        + timezone: str
        + to_dict() dict
    }

    class File {
        <<Data>>
        + id: str
        + name: str
        + private_url: str
        + channel_id: str
        + timestamp: datetime
    }

    Controller o-- SlackClient
    Controller o-- ImageProcessor
    Controller o-- CalendarClient
    ImageProcessor o-- VisionClient
    ImageProcessor o-- ShiftParser

    Controller ..> File : uses
    Controller ..> Shift : uses
    SlackClient ..> File : creates
    ImageProcessor ..> Shift : creates
    VisionClient ..> ShiftParser : provides text for
    ShiftParser ..> Shift : creates
    CalendarClient ..> Shift : uses

シフト画像は次の通りです.

左から順に日付,曜日,開始時刻,終了時刻を表しています.

shift.jpg