自作アプリケーションに関する説明

こちらの記事では、私が作ったWebアプリケーションの紹介をします。
アプリケーションのリポジトリ(データ)は以下URLをご参照願います。
https://github.com/machi6/imanani

内容
1)本アプリケーションについて
 1-1) 概要
 1-2) 開発環境
 1-3) 使用技術
 1-4) 推奨ブラウザ
2) 使用画面
3) 何ができるのか
4) こんな問題を解決します
5) 実装と処理についての解説
 5-1) 構築環境
 5-2) データベース設計
 5-3) 要素の動的配置処理
 5-4) 要素のスケジュールシフト機能処理

1)本アプリケーションについて

1-1)概要

本アプリケーションは、「製品設計者向け課題潰し込みスケジュールツール」です。
業務効率を向上し、流動的な状況下でも働きやすい環境を実現することを目的に作られました。
ブラウザ上で動作するWebアプリケーションです。
名前を「imanani(イマナニ)」といいます。

具体的には週間業務計画をデジタル化したもので、
開発中の製品に対し課題及びやる事を管理できます。
表示される要素は時系列に沿って最適化され動的に配置されます。

またチーム内の自分以外の方のスケジュール共有と編集が可能です。
日程の見える化により、上長や同僚との業務進行に関わるコミュニケーションを支援します。

1-2)対象のユーザー

ものづくり業界で活躍する設計者。
担当部品を1〜複数個持ち、中長期(半年〜2年程度)の製品開発プロジェクトに携わる方。

1-3)開発環境

ハードウェア...mac book air (メモリ8GB), ソフトウェア...Visual Studio Code

1-4)使用技術

HTML, CSS, Ruby on rails, Javascript, MySQL, Devise, Active Record, AWS S3及びEC2

1-5)推奨ブラウザ

Google Chrome

2)使用画面

ログイン画面 f:id:machi11038004:20201016121909j:plain スケジュール画面 f:id:machi11038004:20201016122133j:plain 課題編集画面 f:id:machi11038004:20201016122305j:plain チームメンバー 一覧画面 f:id:machi11038004:20201016122320j:plain

3)何ができるのか

  • 担当開発部品の登録・編集・削除
  • 担当開発部品に属する課題の登録・編集・削除
  • 担当開発部品に属する課題に対するやる事の登録・編集・削除
  • チームメンバーのスケジュール共有

4)こんな問題を解決します

  • 課題登録機能により、開発中製品の課題の抜け・漏れを防ぐ
  • スケジュール共有機能により、チーム各人の進捗状況確認の手間を排除する
  • やる事の動的なシフト機能により、突発業務発生時、全ての予定していた業務が何分後ろ倒しになるか即座に確認可能になる
  • 課題とやる事の見える化により、上長や有識者へ業務の進め方の相談をする際に、工数と優先順位の認識の相違を最小限にする

5)実装と処理についての解説

5-1) 構築環境

構築環境の図を以下に記します。 f:id:machi11038004:20201016151348j:plain
本アプリケーションのデプロイ先は、AWSAmazon Web Services)で利用できるEC2仮想サーバです。
WebサーバではNginx, アプリケーションサーバではUnicorn, データベースサーバではMaria DBをそれぞれ使用しています。

本アプリケーションは、Ruby on railsによるWebアプリケーションフレームワークを利用し作られています。

5-2) データベース設計

ユーザーは0以上の製品を持ち、製品は0以上の課題を持ち、課題は0以上のやる事を持ちます。
ER図を以下に示します。
f:id:machi11038004:20201017135853j:plain

5-3) 要素の動的配置処理

このアプリケーションの特徴の一つは、登録した製品、課題、やる事を動的に時系列順に配置する事です。

例として、登録した「やる事」について示します。
やる事を登録すると、以下の図のように、設定した時間の位置に予定した工数の幅を確保し配置されます。
f:id:machi11038004:20201017142530j:plain
要素の配置は、Javascriptを用いて実現しています。
登録した各情報はHTML要素として生成される際、自身の情報をid及びdata属性に確保します。
以下はやる事(task)のHTML要素を生成する時の記述です。

<div class = "task" data-start=<%= "#{l task.start, format: :time_only}" %> 
                    data-time=<%= "#{l task.time, format: :time_only}" %> 
                    data-wday=<%= "#{task.start.wday}" %> 
                    id = <%= "task_#{task.issue.product.id}_#{task.issue.id}|#{task.id}"  %>>
    (中略)

data属性に、自身の開始時間、予定工数、曜日を設定しています。

Javascriptではページが読み込まれるとイベントが発火し、taskを適切な位置に配置します。
本アプリケーションでは1分を1ピクセルと決めています。
各task要素が持つdata属性から開始時間を取得し時間をピクセルに変換して位置を変更します。

let tasks = document.getElementsByClassName("task");
    let parent_issue_id_stack = [];
    for (i = 0; i<tasks.length; i++){
      (中略)
      //横位置の調整
      start = tasks[i].dataset.start.split(':');
      let start_time_H = Number(start[0]);//15:30開始なら15
      let start_time_M = Number(start[1]);//15:30開始なら30
      let start_time_wday = Number(tasks[i].dataset.wday);
      let start_pos = (start_time_H * 60) + (start_time_M) + 
      (start_time_wday * 1440);
     document.getElementById(tasks[i].id).setAttribute
     ("style", `top: ${task_top}px; left: ${start_pos}px; visibility: visible;`); 
       (中略)
    }

他にも読み込み時の要素配置について動的な処理は存在しますが(Y位置の調整など)、基本的な実装方法は同様です。
それぞれの要素は生成時に自分と先祖までの親のidを情報として持つので、互いに要素の位置や幅を適切に取得し設定できます。

5-4) 要素のスケジュールシフト機能処理

このアプリケーションの特徴の一つは、やる事が新たに割り込む形で挿入された際、既存のやる事を自動的に未来へシフトすることです。
この時、やる事の開始順序は崩さず、隙間の時間は埋められ、かつ勤務時間外には作業開始時間が重複しません。
これは、やる事がシフトした先に既に別のやる事が存在した場合、それも未来へシフトさせることを再起的に行うことで実現しています。

例として、以下のような新たなやる事「task N」を登録する場合を示します。 f:id:machi11038004:20201016205344p:plain
この場合、目指す姿は以下のようなものです。
f:id:machi11038004:20201016205428p:plain

これは、shift_taskと名付けたメソッドにより実装されています。
流れは以下のようになります。
f:id:machi11038004:20201016205425p:plain
f:id:machi11038004:20201016205421p:plain

既存のやる事(task)は重複が続く限り連鎖的に未来へシフトします。

この処理のコードは以下です。

def shift_task(ids)
    #開始時間が重複したタスクは時間をシフトする
    wrapped_task_ids = get_ids_in_time_range(ids)
    if wrapped_task_ids.length > 0
      #どれだけ重複したか
      difference = get_latest_end_time(ids) 
                   - get_fastest_start_time(wrapped_task_ids)
      wrapped_task_ids.each do |id|
        task = Task.find(id)
        task.update(start: task.start + difference)
      end
      #移動した先で開始時間が重複したタスクはそれも時間をシフトする
      shift_task(wrapped_task_ids)
    end
  end


この節の冒頭で、やる事は勤務時間外には作業開始時間が重複しませんと書きました。
この機能もshift_taskメソッドを利用し実現しています。

具体的には、まず勤務時間外に作業開始時間が重複したやる事をチェックします。
そして、該当したやる事を翌日の勤務開始時間に移動します。
その後でこのshift_taskメソッドを呼び出し、重複したやる事を未来へシフトさせます。


本アプリケーションの紹介は以上です。