Debug- Official Website -

犬土偶日記

海の近くに住みたい

Debug日記の使い方

話題:Webプログラミングとか

2016年09月28日

勉強時間グラフ作成プログラム大改造

最終更新日: 2016年12月23日 15時23分58秒

晝に起きた。明後日が試驗なので早朝に起きる練習をすべきだつたんだが、特に何の對策もせず普通に寢て普通に起きた。夜型でないだけマシだが、明日は早朝に起きた方が良いだらう。今日は雨。

最近よく腕や肩や背筋が攣る。免許更新の歸りに電車内で激しく攣つて苦しんだのが始まり。あの直前に飲んだ紅茶に何か入つてゐたのではないかと適當に考へて流してゐたが、そこから何度か攣つてゐる。寢てゐる時にも激しく攣つて目が覺めたりした。さういふのは足が攣る場合が多いやうだ。ググつても足のことばかり出て來る。腕や肩が攣るのは普通とは違ふかもしれない。少しだが毎日筋トレをしてゐるので運動不足といふことは無いだらう。ミネラル不足か老化。どちらもあり得る。たぶんミネラル不足だらう。といふか、全面的に榮養失調だと思ふ。最近旅行時以外ほとんど飯を食つてゐない氣がする。米も最近炊いた記憶が無い。GENKYに食料を買ひに行つても買ふのは大半が菓子類や酒類だ。旅行で大量に食つた分が吸收されてそろそろ攣らなくなるかと思つたが、今日も腕を激しく攣つた。視界が狹くなり、呼吸もできなくなり、氣分が惡くなり、ゲロ吐きさうになり、激烈な痛みで命乞ひしさうになる。まともに飯を食はねばならんな。拒食症とかでは全然ないんだが、腹が減らないから油斷すると何も食はない日もある。さうだ、來月は可兒市の健康診斷で3日以内の2日分のウンコを提出せねばならんのだつた。3日以内に2囘もウンコを出すためには相當食はねばならんぞ。同じ日の2囘ではダメで、2日分必要なのだとか。そのくせ前日は食ふなとか。難しい。確か健康診斷は19日だつたかな。それは後で考へよう。攣るのがヤバい。次GENKYに行つた時にマルチミネラルのサプリを買はう。

腦トレとして資格試驗を受けるに當り、勉強時間を記録するのが通例になつてゐる。試驗を受けることではなく勉強をすることが腦トレであり、いつからか申し込んだ日から試驗日まで必ず毎日1日も休まずに勉強するといふルールになつてゐる。そしてそのルールを破らないために毎日勉強時間を記録するといふ習慣もできてゐる。そして試驗日の日記にそのデータから作成した勉強時間のグラフを載せることになる。表計算ソフトで記録してゐて、データはCSV形式。「日付,時間」を行として記録する。A列が日付でB列が勉強時間と言へば分かりやすいだらうか。グラフ化してHTMLで表示するため、いろいろ整形するためのプログラムを作つてある。Perlで適當に。これを今囘はそのまま流用できない。自宅での勉強と、パチ屋での勉強を分けて記録してゐるので、「日付,時間(家),時間(パチ)」といふ3列になつてゐる。データ形式も最終的な出力も變るのでプログラムに修正が必要だ。今年から日記がHTML5になつたので、去年のままでは出力するHTMLの文法に不都合が生じる箇所もあるかもしれない。そんなわけで、プログラムを修正する。行政書士試驗の時だつたか、日記にプログラムソースを晒したことがある。その後も細かい修正が施されてゐるはずなので、現時點での最新版は日記には載せてゐない。自分が使ふだけのやつなので物凄く雜に作つてあるが十分だらう。企業で膨大な量のデータを扱ふといふやうな用途では全然ダメな設計だ。データ量が大きくなる見込みの場合、日付のデータ列は無い方が良い。記録開始日のデータだけで良い。後からプログラムで自動附加できるからだ。月によつて日數が違ふし閏年も考慮する必要があるし、面倒なのでかういふ全體のデータ量が小さいことが分かつてゐて用途もどうでもいいものである場合はさういふ面倒なことはしなくて良い。表計算ソフトに入力する段階でも何月何日の行か分からないのはつらいしな。日付はセルの右下をドラッグすれば自動的に追加できるし、データ作成段階では日付があつた方が便利。毎日勉強してゐてもファイルに記録し忘れて翌日に記入漏れに氣附かず何日も經つてから1行足りないことに氣附いたなんてことになつたら絶望的だ。さういふ入力ミスを防ぐためにも日付はあつた方が良い。あまりプログラムの效率とかは考へなくて良い。ちやんとしたものを作るなら、データ入力の時點から表計算ソフトではなく同じプログラムで扱ふやうに專用アプリを作るべきかもしれない。そこまでする意味は無いだらう。そんなこんなで、修正が必要なのだが、既にあるプログラムがどうなつてゐるのか忘れてゐるのでまづはそれを見直すところから。

その前に文字エンコーディングについて注意すべきことがある。BRとDebugの日記は記録データと出力がShift_JISになつてゐる。しかしプログラムはEUC-JP。統一感が無い。これには事情がある。BRの初期に無料ホスティングサービスのiswebでWebサイトを作り始めたんだが、その時に日記のプログラムは自作ではなかつた。ネットで拾つて來た無料配布のプログラムを使つてゐた。自分で作るのが面倒だつたからだ。今では文字エンコーディングはUTF-8を使ふのが主流だが、大昔はShift_JISかEUC-JPの2擇だつた。UTF-8を使ふなんて聞いたことも無かつたし、そもそもUTFつて何?といふやうな時代があつた。俺が知らなかつただけかもしれんが。Shift_JISでは文字化けが起こりやすい。エスケープ文字の\記號と同じバイトを後ろ半分に持つ2バイト文字があり、その次の文字で勝手に1バイトエスケープされてそれ以降1バイト分(0.5文字分)ズレて文字が崩れてグチャグチャになつてしまふといふことがあつた。普通にテキストを扱ふ状況では問題無いのだが、多くのプログラミング言語では\がエスケープ文字として使はれてゐるので、プログラム内で\と同じバイトを持つ文字列を扱ふと文字化けが發生してしまふ。それを囘避するためには\と同じバイトを含む文字の後に\を入れて\をエスケープするといふやうなことが必要になつたりする。それが死ぬほど厄介だから、CGIプログラムは通常はEUC-JPで作るべきといふことになつてゐた。で、當時のWindows附屬標準テキストエディタのメモ帳ではEUC-JPが扱へないといふ問題があり、初心者がEUC-JPに對應するテキストエディタを用意するのは大變だらうといふことで、無料CGI配布者達はメモ帳で設定事項を編集できるやうに敢へてShift_JISでCGIプログラムを組んでゐた。さういふ背景があつたので、俺が使はせて貰つたプログラムもShift_JISで書かれてゐた。データファイルも最終的な出力も當然全てShift_JISだつた。

BRが解散し、Debugを結成し、有料のホスティングサービスを契約し、新しくWebサイトを構築する時になつて、全て自作しようと思ひ立つた。日記のCGIプログラムも自作のものに差し替へることになつた。いろいろと不滿があつたからだ。ただし過去のデータは流用する。それまでに既に結構な量の日記データが蓄積されてゐた。これをどうするか惱んだ。プログラムと記録データと入出力の文字コードは一致させた方が良いに決まつてゐる。しかしShift_JISでプログラムを組みたくない。ではデータを全てEUCに變換し直すか。變換するプログラムはすぐに組める。だが、この變換が信用できなかつたのだ。何故か部分的に化けてゐたりして、完全に變換する方法は無いのではないかと思へた。變換してみて全て無事に變換できたかチェックするのは既に量的に無理な状況。データそのものを弄つてしまふと何かあつた時に戻せなくなる。バックアップを取つておけば良いかもしれないが、數年後に氣附いたなんてことになつたら厄介だ。データファイルは弄らずにそのまま使ふといふ決斷を下した。しかしプログラムをShift_JISで書くつもりは無かつた。データファイルと畫面の出力はShift_JISで、プログラムはEUC-JP。内部でイチイチ變換しながら操作する。表示時に文字化けが發生したとしてもデータが壞れてゐるわけではない安心感がある。といふことで文字エンコーディングが統一されてゐない。日記に投稿すべきデータはEUC-JP。プログラムがEUC-JPなので、いろいろ處理する前段階からEUC-JPの方が都合が良い。入力畫面もプログラムに渡すデータもEUC-JPだ。處理して記録する段階になつてからShift_JISに變換してデータを記録する。日記はさういふ仕組みなので、勉強時間のグラフを表示するためのHTMLはEUC-JPで欲しい。グラフ用のHTMLを作成する今囘修正が必要になつたプログラムでは最後にファイルと畫面に出力する。コピペするだけなら畫面に出力するだけで足るが、後で使ひ囘すかもしれないのでファイルにも書き出しておく。日記CGIと違ひ、これは最初から最後までEUC-JPに統一すれば全く問題が無いといふことになる。しかし鬱陶しいことに勉強時間を記録してゐるCSVファイルの文字コードはShift_JISらしい。數字は何でも同じだが、日付はマルチバイト文字だ。表計算ソフトの設定で變更できるのかもしれないが、設定項目を輕く探してみた感じでは見つからなかつた。どうせ數字以外で使はれてゐる文字は「月」「日」の2文字だけなのでどうでもいいかとも思ふ。プログラム内でShift_JISからEUC-JPに變換する手間がかかるが處理時間の差なんか人間に體感できるほどではないしどうでもいい。

で、現時點での最新版。念のため過去の日記を調べてみた。プログラムソースを公開したのはやはり2013年の行政書士試驗の時。そのソースと手持ちのソースは違ふ。前の段落で長々と書いた文字エンコーディングの件だが、行政書士試驗の時に晒したプログラムでは何と文字コード變換を行つてゐなかつた。Shift_JISのデータをEUC-JPでそのまま扱つても問題は起きなかつたんだな。「月」と「日」だけならたまたま大丈夫といふことだらう。手持ちの最新版では變換してゐる。最新版は去年の海事代理士口述試驗の時に使つたやうだ。


use Encode;

$title = "海事代理士口述試驗";
$in_file = "kaiji-k-151124.csv";
$out_file = "kaiji-k-151124html.txt";

$html = "";
$total_time = 0;

$html .= "<table summary=\"$title 勉強時間の表\" class=\"tid\">\n";
$html .= " <caption>$title 勉強時間</caption>\n";
$html .= " <tr><th>日付</th><th>時間(分)</th></tr>\n";

open (FILE, "./$in_file");

while (<FILE>){
    chomp;
    ($date, $time) = split(/,/);
    Encode::from_to ($date, 'Shift_JIS', 'EUC-JP');
    $total_time += $time;
    $width = int ($time * 1.5 + 0.5);
    $html .= " <tr><td>$date</td><td><img src=\"../../data/dogoo/img/graph.png\" alt=\"\" height=\"10\" width=\"$width\"> $time</td></tr>\n";
}

close (FILE);

$hour = int ($total_time / 60);
$minute = $total_time % 60;

$html .= " <tr><td colspan=\"2\"> </td></tr>\n";
$html .= " <tr><td>合計</td><td>$total_time分 ($hour時間$minute分)</td></tr>\n";
$html .= "</table>\n";

open (FILE, ">./$out_file");
print FILE $html;
close (FILE);

print "content-type: text/plain;charset=euc-jp\n\n";
print $html;

exit;
海事代理士口述試驗 勉強時間
日付時間(分)
10月26日 21
10月27日 10
10月28日 12
10月29日 13
10月30日 18
10月31日 7
11月1日 10
11月2日 9
11月3日 12
11月4日 14
11月5日 11
11月6日 20
11月7日 10
11月8日 18
11月9日 18
11月10日 13
11月11日 25
11月12日 13
11月13日 17
11月14日 33
11月15日 10
11月16日 21
11月17日 10
11月18日 19
11月19日 50
11月20日 32
11月21日 26
11月22日 10
11月23日 30
11月24日 154
合計666分 (11時間6分)

まづ最初にプログラム内で變數の初期化や入出力ファイル名の指定をしてゐる。プログラムを實行してからユーザーにファイルを選擇させるのではなく、使用する都度、實行前にプログラムを書き換へる仕組みになつてゐる。面倒だが、使ふのは俺自身だけだし精々年に2囘か3囘しか使はないからどうでもいい。$titleといふ變數は文字通りタイトルだが、TABLE要素のSUMMARY屬性とCAPTION要素で使はれてゐる。SUMMARY屬性はHTML5では廢止されたのでこの部分は消さねばなるまい。グラフのWIDTH屬性とHEIGHT屬性もスタイルシートで指定するやうに變更する。$htmlは最終的に出力するHTMLを格納する變數で、最初にTABLEのヘッダ部分を用意してからデータ處理のループに入る。データファイルを開き、1行づつ順に處理して行く。1行を日付と時間に分割し、日付は文字コード變換。時間のデータを使つてグラフを整形する。データファイルの全ての行を處理するまで$htmlに日付や時間やグラフを當て嵌めたTABLE要素の行を追加して行く。グラフの畫像は幅1ピクセルの線だが、勉強時間(分)を1.5倍して四捨五入した幅のWIDTH屬性を指定して引き伸ばしてゐる。1.5倍の部分は試驗によつて變へるべきだらう。難しい試驗は勉強時間が多いはずだから幅が廣くなつて畫面に收まり切らなくなるかもしれない。逆に少ないやつはもう少し引き伸ばしても良いかもしれない。調整できるやうにこの倍率は變數にしてプログラムの頭で設定するやうにした方が良いかもな。$total_timeは勉強時間の合計を計算するためにループのたびに勉強時間を加算してゐる。TABLEの最後の行で使ふことになる。データファイルを全行處理し終へると、$htmlにはグラフ部分のTR要素が全て追加され、トータル勉強時間が$total_timeに保持された状態になつてゐる。次に合計時間を表示する行の處理。$total_timeは分單位になつてゐるが、これを「○時間○分」といふ形式でも表示するために時間($hour)と分($minute)に分ける。そして合計時間を表示する行を追加し、TABLE要素を閉ぢる。これで變數$htmlに出力すべき全ての要素が入つてゐる状態になる。最後にこれをファイルに書き出し、畫面にも表示する。畫面への表示はHTMLではなくプレーンテキストで行ふ。目的は完成したグラフを見ることではなく日記に投稿する時にグラフのHTMLをコピペすることだから、コピペするためのテキストが欲しいのだ。と、こんな仕組み。超單純。前囘使つた去年の海事代理士口述試驗の時のデータを利用してもう一度プログラムを實行してみる。かういふ感じのグラフが生成される。ただし今囘はSUMMARY要素は削除した。TABLE要素のCLASS屬性は日記での表示用スタイルシートの指定。tidといふ値はtable in diaryの頭文字だつたと思ふ。それにしても1ヶ月で勉強時間が11時間て凄いな。ここまでやる氣が出なかつたことがかつてあつただらうか。しかも最終日だけ激烈に多いからそれ以外の日の平均だととんでもなく少ないぞ。これは落ちても仕方が無い。運の要素も確かにあつただらうが、根本的に足りな過ぎる。今年は50時間はやるべきかな。半引き籠りで普段聲を出すことが無いからベロや口の筋肉と聲帶を鍛へる物理的なトレーニングも必要になるよな。月に1囘のバンド練習と月に1囘あるか無いかの親との會話。それ以外で聲を出すことが無い。たまに喋ると自分でもビックリするほど聲が出ない。普通の人は知らないだらうけど、聲つて本當に短期間で出なくなるよ。

さて、修正だ。データ項目が増えたのをどうするか。家での勉強とパチ屋での勉強を別々のグラフにする。グラフ全體を2つに分けてTABLEを2つ表示するのはスマートぢやない。1行に2本の棒グラフを表示すれば良いだらう。グラフ畫像は氣紛れで使ひ分けるために青つぽいやつと濃いピンクが用意されてゐるので新たに畫像を用意する必要は無い。今囘は家とパチ屋で使ひ分けることになるが、今後は例へば宅建と行政書士みたいに同時期に複數の試驗の勉強をしてゐる時などにも使へるだらう。必ず毎日勉強するルールだから兩方とも0といふ日はあり得ないことになつてゐるが、交通事故に遭つてどうしてもできない日があつたなんていふ事態も起こらないとは言へない。兩方0といふのも存在し得る前提で作るべき。片方だけ0といふ日は普通にあり得る。0が存在する場合にどうするか。0のグラフは表示しないことにするか。0でも必ず表示する(グラフ幅は0だが、0といふ數字は表示される)仕組みにするか。0を表示しないとするとTABLEの行の縱幅が日によつて變ることになつて見榮えが惡くなるから必ず兩方表示すべきだらうか。しかしそもそもこれまでのやうに項目が1つしか無い場合もある。その場合は0を必ず表示するのは都合が惡い。0の時に表示するかどうかのスイッチを最初の設定項目に作るべきだらう。グラフ1は0でも表示するがグラフ2は0だと表示しない、又はその逆、などいろいろ對應できるやうにした方が後々應用がきくだらう。兩方0の場合若しくは日付しかデータが存在しない場合はどうしようか。兩方とも0は表示しない設定で兩方0だと困つたことになるな。兩方0の場合、0は表示しない設定でも必ず0と表示すべきだらう。いろんなパターンを考へねばならんから面倒だが、それが面白いとも言へる。プログラミングをやらない人からするとそんなことまで考へてゐるのかと驚くかもしれないが、これが普通。

Sample Data
日付時間1時間2
9月1日21015
9月2日1320
9月3日0200
9月4日00
9月5日
9月6日 150
9月7日20
9月8日0
9月9日 0
9月10日250170

まづはテスト用データを作つてみよう。「日付,時間1,時間2」のデータが10行。0、1以上、NULL(何も無い)の組み合はせは9パターンか。それなら9行で良いが意味も無く10行にした。適當に10日分の日付セルを生成したからそのまま。時間1と時間2が1以上の數値で存在するパターン。兩方0のパターン。時間1は1以上の數値で存在するが時間2が存在しないパターン。その逆。時間1が0で時間2が1以上のパターン。その逆。時間1が0で時間2が存在しないパターン。その逆。兩方存在しないパターン。これで全パターン網羅してゐるだらう。NULLと0の區別をどうすべきか。區別が必要かどうか。Perlはコンテキストで變數の型を自在に變換するので型の宣言や明示的な變換を必要としない。數値の0として扱ふか、文字の0として扱ふか、僞として扱ふか、どうにでもできる。「NULL」「0」を區別しないならプログラム的には兩方ともそのままでBoolean型として使へば僞となる。區別するなら0は數値型として扱へば良い。どういふ仕組みにしようか。やはり先に書いたやうに0のグラフでも表示するかどうかのスイッチフラグを設定項目にしようか。データが存在しないパターンとはどういふパターンだらうか。時間2の列が全て無い場合はこれまでのやうに單一のデータしか記録してゐないパターンだらう。2項目記録してゐるにも拘らず部分的にどちらか若しくは兩方が0ではなく存在してゐない場合はどういふ状況だらうか。それは記録漏れといふことだらう。記録しないまま放置されてゐるといふことは0とみなしても良いのではないか。0とNULLの區別は必要無いかもしれない。0とNULLを同等に扱ふことにすれば記録の段階で0を入力する手間が省けるメリットもある。NULLは0として扱ふことにしよう。さうするとグラフ2で0の場合に表示しない設定にすれば單一項目のデータを扱ふ時にも不都合が生じない。2列目は全てNULLで0と同等に扱つて、0ならば表示しないといふことで、これまでと同じグラフを生成できる。本當に大丈夫か?論理的に漏れがあるとバグに繋がる。漏れなくダブり無く網羅する必要がある。

ここまで適當に考へたことをまづプログラムに反映させよう。グラフ幅の倍率を變數にして冒頭で設定する。合計時間等、グラフが2つになることにより當然に變數も2つづつになる。例へば$total_timeは$total_time1と$total_time2にする。$width、$time、$hour、$minuteも同樣。$timeが0orNULLの場合に グラフを表示するかどうかを決める變數を導入する。變數名はどうしよう。適當で良いんだがいつも無駄に迷ふ。$render_graph1_when0orNULLとか長いのは嫌ひだ。他人が保守する可能性がある場合は分りやすさを優先する必要があるが、自分專用なので自分さへ分かれば良い。ならば$aとかでも良いのだが、何故か惱んでしまふ。$render_0graph1、$render_0graph2でいいか。適當適當。表示するかどうかは基本的にその2つのフラグに從ふ。ただし兩方表示しない状況になつた場合には強制的に0を表示する。グラフを2本表示する場合には2本の間に改行のBRタグを入れる。これくらゐで十分かな。if文の條件式で漏れ無くダブり無くしつかり考へる必要がある。ここで漏れがあつたり判定されない無意味な條件があつたりするとアホな結果になる。頭の惡い奴はかういふ論理の部分で失敗する。本當に起こり得る全てのパターンを網羅できてゐるか。いつも不安を拭ひ切れない。

棒グラフ2本表示するとして、條件式を具體的にどうするか。時間1のグラフ、改行タグ、時間2のグラフといふ3つを順に考へる。まづグラフ1を表示するかしないかの判定。$time1が眞(1以上)なら必ず書き出す。$time1が僞(0又はNULL)で且つ$render_0graph1が1(時間1が0やNULLでも表示)には幅0の棒グラフではなく數値の0を書き出す。といふことで良いだらうか。その2つの條件式にすると、それ以外の場合がどうなるか。それ以外とはどの状態か。まづ$time1については眞と僞の兩方を判定してゐるので良いだらう。眞は必ず表示。殘るは$time1が僞で且つ$render_0graph1が0の場合だ。これだと何も處理されないことになる。0やNULLでは表示しない設定で0やNULLの場合に書き出さないといふことだから問題は無い。OK。大丈夫なはず。つまり、if($time1){グラフを書き出す}elsif(!$time1 && $render_0graph1){0を書き出す}、それ以外は何もしなくて良いからelseは省略。グラフ2についても同じ判定で良い。

2本のグラフの間に入れる改行タグが問題だ。HTMLでは通常の改行は改行として扱はれない。だからグラフ2本をそのまま表示するとグラフ1の横にそのまま續けてグラフ2が表示されることになる。だから2本表示する場合には間に改行タグ<br>を入れる必要がある。何が厄介かといふと、改行が必要になる場合と必要無い場合を判定しなければならず、それが面倒臭いのだ。グラフ1の後に必ず改行するといふことにしてしまふと、グラフ1が表示されない場合にいきなり改行から始まつて上に變な隙間ができてしまふ。グラフ1が表示される状況でもグラフ2が表示されない状況で改行が入ると今度は下に變な隙間ができることになる。グラフが1本の場合には改行は必要無い。といふか入れてはいけない。どう判定すれば良いだらうか。$time1と$time2の兩方が眞の場合に改行すれば良いなどと單純に考へる奴はアホ。どちらか又は兩方とも僞だとしても、0を表示する場合がある。2本のグラフについて、兩方ともグラフ又は0を表示するといふ場合に改行が必要になる。改行が最後なら簡單なんだ。實際にグラフか0を表示した時に新しい變數でフラグを立て、兩方とも書き出したことをその變數で確認して判定すれば良い。だがグラフ2の處理をする前に改行をするかどうかの判定が必要になるから面倒臭い。「時間1についてグラフ又は0を書き出し、且つ、時間2についてグラフ又は0を書き出す場合」といふ條件になるだらうか。どうすればいい?グラフを書き出す時の條件判定を思ひ出してみよう。$time1が眞なら書き出す。$time1が僞で且つ0を表示する設定の場合に0を書く。他はスルー。つまり、既に上の段落で書いた條件1又は條件2を滿たす場合にグラフ又は0が書き出される。條件1も條件2も滿たさない場合に何も書き出さない。何だ、簡單ぢやないか。條件式1が眞、又は條件式2が眞、といふ條件でグラフか0が書き出される。グラフ2についても同じだ。グラフ1と2の條件を「且つ」で結合すればOK。((グラフ1の條件1が眞又はグラフ1の條件2が眞)且つ(グラフ2の條件1が眞又はグラフ2の條件2が眞))といふこと。ややこしい。論理式は慣れないと鬱陶しいが、避けて通ることはできない。實際のプログラムではかうなる。(($time1 || (!$time1 && $render_0graph1)) && ($time2 || (!$time2 && $render_0graph2)))。これを滿たす場合に改行タグを書き出せば良い。本當か?間違つてないか?かういふ論理部分でミスると後が大變だ。

これでTABLEの行の處理はOK。本當か?常に疑へ。全てを盡くしたか。まだだね。グラフ1もグラフ2も0かNULLで、兩方とも0かNULLの場合には表示しない設定だつたらどうなるか。何も表示されない行が出現してしまふ。これは困る。何も書き出されない行には強制的に0を1個表示する方が良いだらう。((!$time1 && !$render_0graph1) && (!$time2 && !$render_0graph2))の時、何も表示されてゐないはずだから、最後に0を書き出すことにする。これで今度こそOKだらう。

さて、これで終りなら良いんだが、まだあるぞ。合計時間を表示する最後の作業だ。今までは單一項目だつたから單純に合計時間を表示するだけだつた。今囘も1と2で別々に合計を集計してゐる。數値を扱ふ部分については問題無い。どう表示するかが問題だ。別々にグラフを表示して別々に合計値を計算してゐるのに兩方の合計だけを表示するのはをかしい。かと言つてそれぞれの合計だけを表示するのもをかしい。グラフ1の合計、グラフ2の合計、2つの合計と、3つ表示する必要がある。さらにそれ以前の問題として、グラフ1が何を示してゐてグラフ2が何を示してゐるのかといふ情報もどこかに表示せねばならんだらう。今までは單一項目だつたから、列の一番上にTH要素を置いて「勉強時間(分)」と入れておくだけで良かつた。複數項目になるとさういふわけにはいかない。そして、複數項目を扱へるやうに改造しつつも以前と同じ單一項目のグラフも作成できるやうにしておかねばならん。單一項目データを使用した場合には以前と同じTABLEが書き出されるやうに。$total_time2が0の場合は單一項目のグラフだといふことにしようか。さうでない場合には3種類の合計値を表示することになる。それと各グラフの説明も。それらはどこにどうやつて表示するか。今まで單一の合計時間を表示してゐた場所に全て縱に竝べるか。見榮え的にどうかな。いや、待て。$total_time2が0でも省略するわけにはいかない場合もあるかもしれない。今囘の海事代理士の勉強では家とパチ屋を別々に記録するといふことになつた。もしさう決めたにも關らず一度もパチ屋へ行かなかつた場合、パチ屋での勉強時間が0であることを明示する必要があると考へることもできる。グラフ1が何のグラフでグラフ2が何のグラフであるか説明するための變數を用意しよう。$title1と$title2で良いだらう。單一項目の場合は強制的にグラフ1を使用するといふルールにしよう。1を使はず2を使ふことは許さないといふルール。プログラム上では面倒だから禁止しないが、自分内のルールとして禁止しよう。さうすれば、$title2の中身が空文字列の場合にはグラフ2が使用されてゐないとみなせる。單一項目であるといふことだから、グラフ1とグラフ2の合計を別々に表示する部分はキャンセルする。つまり$title2をフラグにすれば良い。$title2が無い場合、$title1も使ふ必要が無い。識別する必要が無いから、これまで通りTH要素と$titleを使つたCAPTION要素だけで足る。さて、仕樣は固まつた。サクッとコードに反映させよう。その後が怖い。動かしてみたら全然思つたのと違ふ結果を出したりとか、エラーが出まくつたりとか。一撃で上手く行く可能性は低いだらうなと思ふ。

で、適當にプログラムを修正して實行してみる。上の方に擧げた10日分のサンプルデータを使ふ。0やNULLで表示するかどうかのフラグの組み合はせを全てチェックする。グラフが2種類しか無い前提だから4通りで簡單にチェックできる。TABLEのCAPTION(表の上部に表示される表のタイトル)の部分に「(1, 1)」のやうにフラグの値を書き出すことにする。0やNULLでも表示する場合が1、表示しない場合は0。左側がグラフ1、右側がグラフ2。4種類の實行結果のHTMLを貼り付けて完成した表を表示してみよう。これまでの内容を踏まへて意圖した通りの表示になつてゐるか。

Sample (0, 0) 勉強時間
日付時間(分)
9月1日 210
15
9月2日 132
9月3日 200
9月4日 0
9月5日 0
9月6日 150
9月7日 20
9月8日 0
9月9日 0
9月10日 250
170
合計
  • 612分 (10時間12分) : グラフ1
  • 535分 (8時間55分) : グラフ2
1147分 (19時間7分)
Sample (0, 1) 勉強時間
日付時間(分)
9月1日 210
15
9月2日 132
0
9月3日 200
9月4日 0
9月5日 0
9月6日 150
9月7日 20
0
9月8日 0
9月9日 0
9月10日 250
170
合計
  • 612分 (10時間12分) : グラフ1
  • 535分 (8時間55分) : グラフ2
1147分 (19時間7分)
Sample (1, 0) 勉強時間
日付時間(分)
9月1日 210
15
9月2日 132
9月3日 0
200
9月4日 0
9月5日 0
9月6日 0
150
9月7日 20
9月8日 0
9月9日 0
9月10日 250
170
合計
  • 612分 (10時間12分) : グラフ1
  • 535分 (8時間55分) : グラフ2
1147分 (19時間7分)
Sample (1, 1) 勉強時間
日付時間(分)
9月1日 210
15
9月2日 132
0
9月3日 0
200
9月4日 0
0
9月5日 0
0
9月6日 0
150
9月7日 20
0
9月8日 0
0
9月9日 0
0
9月10日 250
170
合計
  • 612分 (10時間12分) : グラフ1
  • 535分 (8時間55分) : グラフ2
1147分 (19時間7分)

うむ。大丈夫さうだな。では最後に、この修正したプログラムに去年の海事代理士口述試驗のデータを指定して單一項目データの表を出力してみる。$in_fileは遙か上の方で擧げた「現時點での最新版」といふ、今では最新版でないプログラムに記述されてゐる「kaiji-k-151124.csv」を指定する。合計時間のところにありもしないグラフ2の合計が表示されると困るので$title2の値を空文字列にしておく。そしてグラフ2が0やNULLの場合にグラフを生成しないやうに$render_0graph2を0にしておく。問題無いはずだがどうだらうか。ドッキドキだね。さあ、實行だ!

海事代理士口述2015年 勉強時間
日付時間(分)
10月26日 21
10月27日 10
10月28日 12
10月29日 13
10月30日 18
10月31日 7
11月1日 10
11月2日 9
11月3日 12
11月4日 14
11月5日 11
11月6日 20
11月7日 10
11月8日 18
11月9日 18
11月10日 13
11月11日 25
11月12日 13
11月13日 17
11月14日 33
11月15日 10
11月16日 21
11月17日 10
11月18日 19
11月19日 50
11月20日 32
11月21日 26
11月22日 10
11月23日 30
11月24日 154
合計 666分 (11時間6分)

よし!完璧。30分もあれば修正できるぜ!なんて思つてやり始めたのだが、やつてみたら恐ろしく時間が掛かつた。思考過程や途中經過を日記に書きながらやつたからだ。大部分の時間を日記執筆に費やした。アホだな。でもエラーも出ず意圖した通りに動いて良かつたな。本當は色々と問題があるんだよ。單一項目のグラフを生成する場合、グラフ2のタイトルが空かどうかで判定してゐる。これはさういふルールを自分で勝手に決めて自分がそれを守る前提で成り立つてゐる。自分だけが使ふプログラムだから許される。もし他人が使ふプログラムだつた場合、データの中身も調べて單一項目のデータであることを確定させなければならない。作り手の意圖した通りに使用して貰へるとは限らないからだ。想定してゐる部分は漏れ無く完全に作り込み、それ以外の操作がされた場合には餘計な部分の處理を囘避して動かさないやうにしておく。フールプルーフといふ言葉がある。防水のことを英語でウォータープルーフと言ふが、それと同じ使ひ方で、バカ對策のことだ。バカが想定外の操作をしても大丈夫なやうに作つて對策しておくこと。これ、マジでIT用語として存在してゐるから。嘘だと思つたらググつてみろ。基本情報技術者試驗のテキストにも普通に出て來たよ。似たやうなところでフェイルセーフといふ言葉もある。ついでに調べておくと良いかもね。他にも文句の付け所は色々あるんだよ。もつとかうした方が良いのにとか、もつとかうするべきだとか、考へてみるのも勉強になるだらう。グラフの種類を2種類に限定してしまつてゐるのが一番大きい問題かな。もし同一時期に3つの試驗を受けることになつたらどうするんだ?またプログラムを大きく改變するのか?バカだよな。項目數はいくつでもOKといふ仕樣にしておくべきなんだよ。項目ごとに變數を用意してゐるが、スカラーではなく配列でやれば良い。例へば$render_0graph1、$render_0graph2、$render_0graph3、$render_0graph4・・・とするのではなく、@render_0graphといふ配列で設定して$render_0graph[0]、$render_0graph[1]、$render_0graph[2]・・・のやうに配列の要素を使へば數をいくらでも増やせる。添字に變數を使つて操作することもできるやうになる。ただ、項目數の制限を無くすと今度はグラフ畫像をどうするかといふ問題が出て來る。棒グラフ部分は畫像なんだよな。これが拙い。マークアップ言語であるHTMLのタグを、例へばSPAN要素を、ブロック化して幅を持たせてスタイルシートでbackground-colorを設定するといふやうなやり方でグラフを實現するのは邪道だからやるべきではないが、畫像を使はずにこれと同じやうなことを實現するにはどうすれば良いだらうか。またさうやつて新たな問題にぶつかつて、それを乘り越へるたびに知識や技術や經驗が積み上がつて行く。それが面白いんだよ。

最後に修正後の完成版プログラムソースを載せておかう。最初とだいぶ變つたな。超適當。こんなことしてる暇があつたら勉強しろ!試驗2日前だぞ!!と自分を責めたくなるが、こつちの方が面白いんだから仕方が無い。どうせ試驗日の日記を更新するまでにはやらねばならんことなんだ。

use Encode;

$title = "Sample";         # CAPTION要素
$title1 = "グラフ1";       # グラフ1のタイトル
$title2 = "グラフ2";       # グラフ2のタイトル
$in_file = "sample.csv";   # 入力ファイル
$out_file = "sample.txt";  # 出力ファイル
$scale_factor = "1.5";     # グラフ横幅の倍率
$render_0graph1 = 1;       # グラフ1の表示フラグ (1: 0やNULLでも表示、0: 0やNULLでは表示しない)
$render_0graph2 = 1;       # グラフ2 同上

$html = "";
$total_time = 0;
$total_time1 = 0;
$total_time2 = 0;

$html .= "<table class=\"tid\">\n";
$html .= " <caption>$title 勉強時間</caption>\n";
$html .= " <tr><th>日付</th><th>時間(分)</th></tr>\n";

open (FILE, "./$in_file");

while (<FILE>){
    $time1 = 0;
    $time2 = 0;
    chomp;
    ($date, $time1, $time2) = split(/,/);
    Encode::from_to($date, 'Shift_JIS', 'EUC-JP');
    $total_time1 += $time1;
    $total_time2 += $time2;
    $width1 = int($time1 * $scale_factor + 0.5);
    $width2 = int($time2 * $scale_factor + 0.5);
    $html .= " <tr>\n";
    $html .= "  <td>$date</td>\n";
    $html .= "  <td>\n";

    if($time1){
        $html .= "   <img src=\"../../data/dogoo/img/graph.png\" alt=\"\" style=\"width:${width1}px; height:10px;\"> $time1\n";
    }elsif(!$time1 && $render_0graph1){
        $html .= "   0\n";
    }

    $html .= "   <br>\n" if(($time1 || (!$time1 && $render_0graph1)) && ($time2 || (!$time2 && $render_0graph2)));

    if($time2){
        $html .= "   <img src=\"../../data/dogoo/img/graph2.png\" alt=\"\" style=\"width:${width2}px; height:10px;\"> $time2\n";
    }elsif(!$time2 && $render_0graph2){
        $html .= "   0\n";
    }

    $html .= "   0\n" if((!$time1 && !$render_0graph1) && (!$time2 && !$render_0graph2));
    $html .= "  </td>\n </tr>\n";
}
close (FILE);

$hour1 = int($total_time1 / 60);
$minute1 = $total_time1 % 60;
$hour2 = int($total_time2 / 60);
$minute2 = $total_time2 % 60;
$total_time = $total_time1 + $total_time2;
$hour = int($total_time / 60);
$minute = $total_time % 60;

$html .= " <tr><td colspan=\"2\"> </td></tr>\n";
$html .= " <tr>\n";
$html .= "  <td>合計</td>\n";
if($title2){
    $html .= "  <td>\n";
    $html .= "   <ul class=\"ls-n\">\n";
    $html .= "    <li>\n";
    $html .= "     <img src=\"../../data/dogoo/img/graph.png\" alt=\"\" style=\"width:10px; height:10px;\">\n";
    $html .= "      $total_time1分 ($hour1時間$minute1分) : $title1\n";
    $html .= "    </li>\n";
    $html .= "    <li>\n";
    $html .= "     <img src=\"../../data/dogoo/img/graph2.png\" alt=\"\" style=\"width:10px; height:10px;\">\n";
    $html .= "      $total_time2分 ($hour2時間$minute2分) : $title2\n";
    $html .= "    </li>\n";
    $html .= "   </ul>\n";
    $html .= "   $total_time分 ($hour時間$minute分)\n";
    $html .= "  </td>\n";
}else{
    $html .= "  <td>$total_time分 ($hour時間$minute分)</td>\n";
}
$html .= " </tr>\n";
$html .= "</table>\n";

open (FILE, ">./$out_file");
print FILE $html;
close (FILE);

print "content-type: text/plain;charset=euc-jp\n\n";
print $html;

exit;

(1.0)Anniversary song、fake、MIRAI、turning、トウダイモトクラシー、pain、Pleasure'98 〜人生の快楽〜、GO FURTHER、煌めく人、今夜月の見える丘に、いつかのメリークリスマス、LOVE PHANTOM、ultra soul [Alternative Guitar Solo ver.]、STAY GREEN 〜未熟な旅はとまらない〜、Raging River。

ギター練習65分、腹筋200囘、背筋100囘、讀書122ページ。

話題:Webプログラミングとか

Info.
公開日時2016年10月06日 01時23分41秒
最終更新日2016年12月23日 15時23分58秒 (更新回数: 2)
本文文字数34782文字 (タグ込み)
URLhttps://orca.xii.jp/debug/diary/diary.cgi?id=dogoo;date=20160928
RSS1.0https://orca.xii.jp/info/diary-dogoo.rdf
Comments

コメントはありません。

コメント投稿フォーム
文字色              
  • 名前を省略すると「名無しDebugger」になります。
  • メールアドレスの入力は任意です。
  • 海外からのコメントスパム対策のため、表示された漢字の読みを必ず入力してください。
  • 本文は必須項目です。投稿する場合は必ず記入してください。タグは使えません。
  • 改行が1つ入力された場合は強制改行として処理されますが、2つ以上連続する改行は段落の終了として処理されます。
  • 本文の行頭に「>」のある文は引用としてマークします。引用でない部分の冒頭に「>」は付けないでください。
  • コメントの削除は管理者若しくは日記執筆者しかできません。書き込む時は注意しましょう。
  • 全部記入が済んだら投稿ボタンを押す前に一度読み直して推敲しましょう。