🏆

アプレンティスチーム開発でOpenAI搭載の日報タスク管理アプリを開発 - Best Award 授与

2023/12/15に公開

index1

導入文

はじめまして、アプレンティス 2 期生の Nao です。
アプレンティスチーム開発において、私たちのチームはメンターから最も高い評価を受け、Best Award に選ばれました 🏆
『自分たちの役に立つものを開発せよ』というプロダクトテーマに基づき、チーム全体でのプロダクトの決定から実装、発表、そしてその結果まで、私たちの開発プロセスや課題の解決策をまとめました。この情報が皆さんの参考になれば嬉しいです。

https://apprentice.jp/

開発プロダクトの選定

私達チームは、以下 3 つを満たすプロダクトを各自で考えて持ち寄りました。

  1. 自分たちの役に立つものとは?
  2. どこに新しさ・驚きがあるか?
  3. 誰にどのような価値を、どのような形で提供するのか?

私たちアプレンティス生は、毎日 Discord と X にて「日報」を提出しています。驚くべきことに、私を含め「日報」に関する課題を解決したいという気持ちがチーム全体にありました。毎日投稿する日報に成長できる仕組みを提供して、自己学習を促進する。そのため、開発プロダクトは「日報の PDCA の促進」に着目しました。また、プロダクトの決定からチームメンバーそれぞれが「クライアント」で「ユーザー」でもあったので、要件の聞き取りの経験も開発と同時に積むことができました。

課題の洗い出し

課題解決のためには、システム化の方法は二の次です。とにかく現状の課題を洗い出しました。

  1. X の文字数制限のせいで、Discord の日報を再利用できない。
  2. 提出する日報のフォーマットが決まってない為、見づらい。
  3. タスクの進捗を図なので可視化したい。
  4. 積み上げた日報の可視化をしたい。
  5. 日報に対するフィードバックが欲しい。
  6. 提出だけで終わらず日々の学習を改善する仕組みが欲しい。
  7. 日報提出忘れ防止の機能が無い。
  8. 毎回チャット欄でテキスト入力するので作成補助があれば嬉しい。

これらの課題に対し、具体的な解決策を検討しました。

課題の解決策

web 技術を使って自分達の課題をどう解決するか。アプローチを模索しました。

課題解決の調査

ユーザーが私たちのアプリを利用する価値を見出すため、他のアプリとの差別化を確認しました。

  1. 最新技術の採用
    AI の活用が注目を集めており、他アプリとの違いを生み出す新規性を確認
  2. 類似アプリとの差異
    他の同様のアプリには見られないフィードバック機能や、画像生成機能の欠如が確認されました。

AI の使用や画像生成といった特徴は、他のアプリには見られない要素であり、ユーザーに新しい価値を提供できる事が分かりました。

具体的な課題の解決策

  1. 日報をテキストから画像化
    X の文字数制限やフォーマットの統一化を解消します。日報を ping にして提供します。
  2. X と Discord へのシェア機能の導入
    X ではテキストまたは画像形式での日報提出を簡単に行えるフォームを提供します。
    Discord ではフォーマットされた日報テキストのコピペ機能を導入します。
  3. ChatGPT による振りかえり機能
    AI が学習の振り返りを支援し、本日の成果の振り返りをしてくれます。
  4. 積み上げをグラフ化
    学習の進捗を分かりやすく可視化します。
  5. Line 通知による日報提出のリマインド機能
    提出を忘れるリスクを減らします。

これらの解決策により、日報作成の手間が削減され、学習の進捗がより明確に把握できるようになります。ChatGPT によるフィードバックにより日々の質が向上し、日報の提出管理も改善されます。また、私は web 開発の経験があったので、実装方法のイメージを持っています。解決策の提案はもちろん実装方法の提案も率先してさせて頂きました。

ユーザーの利点と価値提供

アプレンティス生は、これらの解決策によって次のような利点を得られます。

  1. 効率的な日報作成と整形
    画像化やフォーマットの統一化により、日報作成の手間が軽減されます。
  2. 学習進捗の可視化
    積み上げのグラフ化によって、自身の学習進捗が視覚的に理解しやすくなります。
  3. AI によるフィードバック
    ChatGPT による学習の振り返りや日報への提案を通じて、質の高いフィードバックが提供されます。
  4. 提出管理の改善
    Line 通知によるリマインダーが提供されるため、提出を忘れるリスクが低減されます。

プロダクトの新規性と特長

このプロダクトの新規性と特長には以下が挙げられます。

  1. 独自の解決策の提供
    類似アプリとは異なる、画像化や AI による振り返り機能などの新しい解決策が提案されています。
  2. 多様な機能の組み合わせ
    X や Discord へのシェア機能、ChatGPT による振り返り、積み上げのグラフ化など、様々な機能が組み込まれています。

インセプションデッキ

インセプションデッキは、アジャイル開発で使用される全体の方向性を共有するための 10 の質問になります。アジャイル開発では、柔軟性や変化への対応力が求められるため、プロジェクト開始時に全体的な方向性を共有することが特に重要です。インセプションデッキは、プロジェクトの初期段階で関係者が一致した理解を深め、共通の目標を確立するのに役立ちます。このプロジェクトでは必須ではありませんでしたが、プロジェクトの方向性を明確にするために作成することにしました。

1.我われはなぜここにいるのか?

対象顧客はアプレンティス生であり、日々の日報作成のプロセスを改善するためにここにいます。
プロジェクト発足の理由は、アプレンティス生が日報作成に関する課題を解決し、自己学習を支援するためです。

2.エレベーターピッチ

有効なタスク管理をしたいアプレンティス生向けのプロダクトです。タスクの進捗管理、AI による振りかえり、日報の画像生成、日報の各種 SNS へのシェアをすることができ、一般のタスク管理アプリや日報アプリとは違ってこれらの作業をまとめて管理し、日々の日報を可視化することができます。

3.パッケージデザイン

デザインはシンプルかつ使いやすいものを目指し、ロゴやアプリ外観を Canva と Figma で設計します。色彩は 2〜3 色のシンプルなデザインに統一します。

4.やらないことリスト

実装する機能の優先度を以下に決めました。

やること やらないこと 後で決めること
1. カレンダーの生成 1. タスクのカテゴリー分け機能 1. レスポンシブ対応
2. タスクの登録/変更/削除 2. 通知機能
3. タスクの実績登録/変更 3. タスクのドラッグ入れ替え
4. 達成率のグラフ化 4. ログイン機能
5. OpenAI 振りかえり
6. 日報画像生成処理
7. X ,Discord シェア機能

5.ご近所さんを探せ

  1. チーム開発メンバー 3 人
  2. アプレンティス生
  3. メンター

6.技術的な解決策を描く

フロントエンド: HTML, CSS, JavaScript(フレームワークなし)
バックエンド: PHP(フレームワークなし)
データベース: MySQL
ライブラリ: php_codesniffer, phpunit, phpdotenv, twitteroauth, Ajax,
外部 API: OpenAI API , Twitter API v2
パッケージ管理: Composer, npm
バージョン管理: Git/GitHub
コンテナ: Docker
環境: WSL2/Ubuntu

7.夜も眠れない問題

期限内に終わるか不安。。。
自分達で実装できるのか不安。。。

困ったらチームに相談し一人で悩まないように!!

8.期間を見極める

発表 2 日前までに実装完了を目指す。その後は、プレゼンテーション用のスライド作成やプロダクトの魅せ方に集中し、発表に向けた練習や準備に時間を割きます。

9.トレードオフスライダー

優先度 1 2 3
期日を死守する(納期遵守)
バグを出さない(品質遵守)
機能を全部揃える(スコープ遵守)

コア機能が正常に動くプロダクトを作り、発表できる状態を目指します。

10.何がどれだけ必要か

実装期間 :1 週間
開発チーム:3 人

実装の期間が短い為、発表のデモで見せる範囲を最高クオリティで仕上げる。
発表のデモで見せないものは作らない。

機能要件

  1. タスクの登録/変更/削除機能
  2. タスクの実績登録/変更機能
  3. 達成率のグラフ化機能
  4. OpenAI による本日の日報の振りかえり機能
  5. 日報画像生成処理
  6. X ,Discord シェア機能
  7. Line 通知機能
  8. ログイン機能

非機能要件

  1. 使いやすさとデザイン
    ページ数とボタンを減らして、ユーザビリティを向上させ直感的な操作性を実現する。
    2〜3 色のシンプルなデザインを統一し、色彩のバランスを考慮する。
    可能な範囲でレスポンシブ対応を検討する。
  2. 安全性と信頼性
    バリデーション処理を実装して、データの正当性を確保する。
    エラーハンドリングを強化して、システムの安定性を向上させる。

基本設計: 業務フロー

人物 業務フロー
アプレンティス生 ログインまたはアカウント作成 → メインダッシュボードへ
アプレンティス生 タスクの登録 → 登録したタスクを表示
アプレンティス生 タスクの実績登録 → タスクの完了・未完了を記録
アプレンティス生 本日の振りかえりを登録 → 学習の進捗をグラフで表示
アプレンティス生 本日の振りかえりを登録 → ChatGPT からのフィードバックを表示
アプレンティス生 日報の提出忘れ → リマインダーによる通知
アプレンティス生 日報画像生成ボタン押下 → png 画像でダウンロードが可能
アプレンティス生 コピーボタン押下 → Discord 用フォーマットされた日報を投稿
アプレンティス生 Discord 用フォーマットされた日報を投稿 → Line による完了通知
アプレンティス生 X シェアボタン押下 → アプリ内フォームにて X にツイート投稿
アプレンティス生 アプリ内フォームにて X にツイート投稿 → Line による完了通知

基本設計: 画面遷移図 と ワイヤーフレーム

wireframe_transition_diagram

ツール: Adobe XD

チームメンバーが作成したワイヤーフレームと画面遷移図です。
シンプルなレイアウトでページ数を最小限に抑え使いやすさを重視しました。ファーストビューで全機能を提示するデザインになっています。

以下のリンクから、詳細を確認できます。
https://xd.adobe.com/view/e3aeb51c-bea7-46f9-aa28-353943c2dbe1-b6fd/

基本設計: DB 設計

各自の DB 設計を出し合い、私の提案が採用されました。改めて見直すとカラム名が冗長だったり、テーブル名が複数形になっていない箇所が見つかりました。今後の DB 設計では改善に努めます。

ER 図

er_diagram

ツール: MySQL Workbench

テーブル定義

users テーブル

Field Type Null Key Default Extra
user_id bigint NO PRI NULL auto_increment
user_name varchar(255) NO UNI NULL
password varchar(30) NO NULL
twitter_api_key varchar(255) YES NULL

tasks テーブル

Field Type Null Key Default Extra
task_id bigint NO PRI NULL auto_increment
user_id bigint NO MUL NULL
task_name varchar(255) NO NULL
execution_date date NO NULL
completion_status tinyint YES NULL
display_order int NO NULL

reports テーブル

Field Type Null Key Default Extra
report_id bigint NO PRI NULL auto_increment
user_id bigint NO MUL NULL
reflection_comment text NO NULL
ai_comment text NO NULL
submitted_date date NO NULL
study_hours time NO NULL

task_history テーブル

Field Type Null Key Default Extra
task_history_id bigint NO PRI NULL auto_increment
report_id bigint NO MUL NULL
task_name varchar(255) NO NULL
completion_status tinyint NO NULL

詳細設計: 技術アーキテクチャ

tech_architecture_diagram

ツール: draw.io

こちらの図は私が作成させて頂いたものになります。

技術選定のポイント

  1. Docker 環境導入:チーム全体の開発環境の統一を実現
  2. OpenAI API:AI からの振り返り機能の実装を実現
  3. Twitter API v2:日報のシェア機能の実装を実現
  4. Ajax 非同期処理:UX の向上とシームレスな操作を実現
  5. DBMS:効率的なデータ管理と操作のための導入

詳細設計: デザインカンプ

design_comp_login_page

design_comp_main_page

ツール: Figma

チームメンバーが作成したデザインカンプです。
各要素にはメインカラー、ベースカラー、アクセントカラーなど色彩テーマが取り入れられています。

以下のリンクから、詳細を確認できます。
https://www.figma.com/file/sRAsZrJGoqSqxSVdqdv3hG/デイリポ?type=design&node-id=2-7&mode=design&t=68LrgnkQO5KilkUW-0

タスクばらし

task_breakdown

メンバーがデザインカンプ作成中に、私はタスクの分割作業を進めました。

我々は、成長や経験求めてフロントエンドとバックエンドでは分けず、機能ごとに分割する事にしました。またコア機能の実装を必須にする為、合わせて優先度の設定もここで行いました。

Docker 環境構築

タスクの分割作業が早く終わったため、Docker 環境の構築も行いました。これにより docker-compose.yml や Dockerfile の記述にも慣れることができました。

特に努力した点は、コンテナの起動時に SQL を実行してデータベースの構築とテストデータの導入を自動化することです。これにより、開発環境の構築がより効率的に行えるようになりました。また、チーム内でのレビューを通じて、パスワードなどの重要情報を.env ファイル化することで、よりセキュアな Docker 環境の構築が可能になりました。

以下のリンクから、コードの詳細を確認できます。
https://github.com/OBookBook/APPRENTICE-dev1-team-dev

実装編: Notion を用いたプロジェクト管理

進捗管理とタスク割り当て

私達チームは Notion を使って進捗管理を行いました。Gantt チャートを利用してプロジェクトのスケジュールを視覚的に管理し、タスクの割り当てを行いました。

notion_gantt_chart

私は以下のコア機能の実装を担当しました。

  1. 振り返りコメント、作業時間の登録/変更 (Ajax)
  2. OpenAI を使用した本日の日報の振り返り機能
  3. 日報画像生成機能
  4. Discord シェア機能(日報テキストのコピペ)
  5. X シェア機能,

結論: チームの皆様に助けてもらいながら何とか実装する事ができました!!

notion_assigned_tasks

実装編: Web 開発

スクリーンショット 2023-12-08 051930

Ajax による登録/変更処理

ReportFormHandler.js
/**
 * ReportFormHandlerはレポートデータのフォーム送信を処理します。
 * @class
 */
class ReportFormHandler {
    /**
     * フォームの送信を非同期で処理します。
     * @param {Event} event - 送信イベント
     * @returns {Promise<void>} 処理が完了した時に解決されるPromise
     */
    handleSubmit = async (event) => {
        event.preventDefault();
        const requestData = this.createRequestData();
        await this.sendRequest(requestData);
    };

    /**
     * フォームの入力からリクエストデータを作成します。
     * @returns {Object} リクエストデータのオブジェクト
     */
    createRequestData = () => {
        const formatter = new ReportDateFormatter('year-month', 'task-management');
        return {
            userId: 1,
            reflectionComment: document.getElementById("reflectionComment").value,
            studyHours: document.getElementById("studyHours").value,
            submittedDate: formatter.getFormattedDate()
        };
    };

    /**
     * 提供されたデータでリクエストを送信します。
     * @param {Object} requestData - 送信するデータ
     * @returns {Promise<void>} リクエストが完了した時に解決されるPromise
     */
    sendRequest = async (requestData) => {
        try {
            const response = await fetch("../../php/functions/post_report.php", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify(requestData)
            });

            if (response.ok) {
                console.log("データが正常に送信されました。");
            } else {
                throw new Error("データの送信中にエラーが発生しました。");
            }
        } catch (error) {
            console.error("エラーが発生しました:", error);
        }
    };
}

OpenAI API による振り返り

aicoment

ChatGPT.php
<?php
require './../../../vendor/autoload.php';

/**
 * OpenAIを使用してテキスト生成を行うクラスです。
 */
class ChatGPT
{
    /** @var string OpenAI APIキー */
    private string $apiKey;

    /** @var string OpenAIのエンドポイント */
    private string $apiEndpoint;

    public function __construct()
    {
        $dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . "/../../../");
        $dotenv->load();

        $this->apiKey = $_ENV['OPEN_AI_API_KEY'];
        $this->apiEndpoint = $_ENV['OPEN_AI_API_ENDPOINT'];
    }

    /**
     * テキスト生成
     *
     * @param string $prompt テキスト生成のためのプロンプト
     * @return string 生成されたテキスト
     */
    public function generateText($prompt): string
    {
        $data = array(
            'prompt' => $prompt,
            'max_tokens' => 30
        );

        $postData = json_encode($data);

        $ch = curl_init($this->apiEndpoint);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/json',
            'Authorization: Bearer ' . $this->apiKey
        ));

        $response = curl_exec($ch);
        curl_close($ch);

        echo ($response);

        $responseData = json_decode($response, true);
        return $responseData['choices'][0]['text'];
    }
}

日報画像生成機能

screenshot

生成された日報画像(ping)

日報_20231215

Screenshot.js
/**
 * 画面のスクリーンショットをキャプチャおよび取得するクラスです。
 * @class
 */
class Screenshot {
  get(id1, id2) {
    let elementToCapture = document.getElementById(id1);
    let elementToCapture2 = document.getElementById(id2);
    elementToCapture.appendChild(elementToCapture2);

    elementToCapture.classList.add('js-capture-style');

    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, '0');
    const day = String(today.getDate()).padStart(2, '0');
    const dateFormatted = `${year}${month}${day}`;

    html2canvas(elementToCapture).then(function (canvas) {
      const img = canvas.toDataURL('image/png');
      const link = document.createElement('a');
      link.href = img;
      link.download = `日報_${dateFormatted}.png`;
      link.click();

      elementToCapture.classList.remove('js-capture-style');
    });
  }
}

Discord シェア機能(日報テキストのコピペ)

capture

クリップボードに保存された本日の日報

本日の実績
学習時間 12 時間

【学習内容】
Git リポジトリ作成
リーダブルコード
SOLID の原則
ドメイン駆動設計

【メモ】
褒めてください!! アプレンティスチーム開発において、私たちのチームはメンターから最も高い評価を受け、Best Award に選ばれました 🛕 チームで協力して成し遂げることができて本当に嬉しかったです!!
Clipboard.js
/**
 * 日報テキストをクリップボードぶ保存するクラスです。
 * @class
 */
class Clipboard {
    /**
     * 指定された要素のテキストをクリップボードにコピーします。
     * @param {string} targetId - コピー先要素のID
     * @returns {Promise<void>} コピーが成功した場合はresolve、失敗した場合はrejectを返します。
     */
    copyToClipboard(targetId) {
        const copyTarget = document.getElementById(targetId);
        if (!copyTarget) {
            console.error(`IDが ${targetId} の要素は存在しません`);
            return Promise.reject(`IDが ${targetId} の要素は存在しません`);
        }

        const lines = copyTarget.innerText.split('\n');
        const copiedContent = lines
            .map(line => line.replace(/(alarm|priority|dangerous)/g, '').trim())
            .filter(line => line !== '');

        copiedContent.splice(2, 0, "");
        copiedContent.splice(3, 0, "【学習内容】");
        copiedContent.splice(copiedContent.length - 1, 0, "");
        copiedContent.splice(copiedContent.length - 1, 0, "【メモ】");
        const formattedContent = copiedContent.join('\n');

        return navigator.clipboard.writeText(formattedContent)
            .then(() => {
                alert("コピーできました! : " + formattedContent);
            })
            .catch(err => {
                console.error('コピーに失敗しました:', err);
                throw err;
            });
    }
}

X シェア機能

tweet

Twitter.php
<?php
require './../../../vendor/autoload.php';

use Abraham\TwitterOAuth\TwitterOAuth;
use Abraham\TwitterOAuth\TwitterOAuthException;

/**
 * Twitter APIとやり取りしてツイートを投稿するためのクラスです。
 */
class Twitter
{
    private string $api_key;
    private string $api_key_secret;
    private string $access_token;
    private string $access_token_secret;
    private TwitterOAuth $twitterClient;

    public function __construct()
    {
        $dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . "/../../../");
        $dotenv->load();

        $this->api_key = $_ENV['X_API_KEY'];
        $this->api_key_secret = $_ENV['API_KEY_SECRET'];
        $this->access_token = $_ENV['ACCESS_TOKEN'];
        $this->access_token_secret = $_ENV['ACCESS_TOKEN_SECRET'];

        $this->twitterClient = new TwitterOAuth($this->api_key, $this->api_key_secret, $this->access_token, $this->access_token_secret);
        $this->twitterClient->setApiVersion('2');
    }

    /**
     * ツイートを投稿します。
     *
     * @param string $tweetMessage ツイートするメッセージ。
     * @return void
     */
    public function tweet(string $tweetMessage): void
    {
        try {
            $this->twitterClient->post('tweets', ['text' => $tweetMessage], true);
        } catch (TwitterOAuthException $e) {
            echo 'エラー: ' . $e->getMessage();
            echo 'エラーコード: ' . $this->twitterClient->getLastHttpCode();
        }
    }
}

以下のリンクから、コードの詳細を確認できます。
https://github.com/OBookBook/APPRENTICE-dev1-team-dev

発表

presentation_slide

発表直前まで、デモの見せ方やプレゼンの練習をビデオ通話で行いました。発表はプロジェクトリーダーが担当し、私は技術に関する質問に備えていました。

講評

視点:身近な問題の解決
技術:Ajax や外部 API の活用
UI: デザインがまとまっていて見やすい
新規性:AI がコメントを返すのは面白い
実用性:アプレンティ生以外の人(勉強用途)にもアプローチできる

私たちのチームはメンターから最も高い評価を受け、Best Award に選ばれました 🏆
チームのアイディアからデザイン、実装まで、みんなで力を合わせて達成した喜びを感じました 😆

まとめ

チームの目標であった Best Award 授与を達成 🏆 チームで協力して成し遂げることができて本当に嬉しかったです!!
​Docker、Ajax、外部 API、Figma、Canva、Notion の積極的な挑戦と提案力は非常に刺激的で貴重な経験を得ることができました。私自身の反省点はたくさんありますが、チームの皆様のお陰て良い結果となりました。改めて本当に感謝です 🙇
発表を通じてプロダクトの着眼点や利用者の視点など、皆様からアイディアをたくさん頂けました。今後の開発に取り入れさせて頂きます。

長くなりましたが、お読みいただきありがとうございます。皆さんのお役に立てれば幸いです。ありがとうございました!!

GitHubで編集を提案

Discussion