スケジュール表

#実行した環境
Ubuntu Stdio 18.04LTS
Python 3.6.9
Firefox 71.0(64ビット)

#参考にしたサイト
Pythonで自分だけのTODOアプリを作ろう
https://news.mynavi.jp/article/zeropython-57/

JavaScript を使ってみる   例題11： カレンダーの作成
http://home.e02.itscom.net/shouji/pc/js/p-11.html

その他

![schedule.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238800/51ef3e21-0b9b-5f3b-029a-9e5c668f9542.png)

追加ボタンのサイズを変更する方法を探しているのですが見つかりません。
#作るきっかけ
「Pythonで自分だけのTODOアプリを作ろう」の記事を読んでいて、そのままスケジュール表に使えるやんと思って使い始めました。
正味、ド素人がネット上にあるサンプルコードをつなぎ合わせたものです。
年がいって勉強しても1週間もしない内に記憶から抹消されてしまうので万年初心者です。

やったことは「Pythonで自分だけのTODOアプリを作ろう」のサンプルプログラムにド素人のソート機能（半角・全角スペースがあるとバラバラの行になってしまうので、アンダースコアで繋ぐ必要があります。）
時計そしてカレンダーもあったほうが便利ということで追加しました。
ほとんどがネット上にあるサンプルそのままです。（ありがとうございます）

「Pythonで自分だけのTODOアプリを作ろう」のサイトにコードは記載されていませんが、ダウンロードできるようにリンクが貼られていますので、そこから「こちらにある57回目のプログラム」をダウンロードします。

そのままでは動きませんでしたので、ちょっと修正が必要でした。
１.data.txtが入っていません。
　　詳しい人ならコードを見ればすぐにわかると思いますが、私はわからないので適当に文字を羅列したdata.txtファイルを作成しましたがちゃんと動作しました。

２．OSにより異なると思いますが、todo.py の1行目　#!/usr/bin/python3　に変更する。

３．python2では動作しなかった（多分ライブラリ不足）ので、start.sh の2行目をpython ・・・からpython3 ・・・に変更する。
　　Windowsの場合は、start.batだと思います。

４．start.sh に実行権限を与える。
　　todo.py に実行権限を与える。

これで、start.shを実行すると簡易Webサーバーが起動しました。
そして、ブラウザで「http://localhost:8000/cgi-bin/todo.py」にアクセスするとサンプルプログラムが起動し「褒めるTODO」が表示されます。

pythonスクリプト（プログラム）でHPが表示できることを始めて知った。（でも面倒くさい）

これに、ソート・時計・カレンダーを加えました。

```python:
#!/usr/bin/python3
# coding: UTF-8

FILE_DATA = "./data.txt"

with open(FILE_DATA) as f:
    s = f.read()
    s1=s.split() # strをListに変換
    s1.sort() # ソート
    s = '\n'.join(s1) # Listをstrに変換

with open(FILE_DATA, mode='w') as f: # 書き込み
    f.write(s)

# 日本語を扱うために必要な設定
import os, sys, io, cgi, re
sys.stdin, sys.stdout, sys.etderr =  [
    open(sys.stdin.fileno(),  'r', encoding='UTF-8'),
    open(sys.stdout.fileno(), 'w', encoding='UTF-8'),
    open(sys.stderr.fileno(), 'w', encoding='UTF-8')]
out = lambda s: print(s, end="\n")

form = cgi.FieldStorage()

# パラメータを確認して処理を分岐
def check_param():
    m = get_form('m', 'show')
    if m == "show":
        mode_show()
    elif m == "add":
        mode_add()
    elif m == "rm":
        mode_remove()
    else:
        out_html("パラメータの間違い")

# TODOの一覧を表示する
def mode_show():
    items = load_items()
    t = "<h1>私の予定表</h1>"

    t += '<div class="div_1">'
    t += '<div class="div_2">'
    t += "<div id='items'>"

    t += '<h2 id="time"></h2>' # ここから
    t += "<script>"
    t += "time();"
    t += "function time(){"
    t += "    var now = new Date();"
    t += '    document.getElementById("time").innerHTML = now.toLocaleString();'
    t += "}"
    t += "setInterval('time()',1000);"
    t += "</script>"  # ここまで時計

    t += "<form><p class='frm'>予定: "  # ここから
    t += input_tag('text', 'todo', '')
    t += input_tag('hidden', 'm', 'add')
    t += input_tag('submit', '', '追加')
    t += "</p></form></div>"  # ここまで予定追加部

    for no, it in enumerate(items):  # 予定表
        k = "todo.py?m=rm&no="+str(no)
        a = "<a href='" + k + "'>❎️</a> "
        t += "<p>" + a + it + "</p>"

    t += "</div>"
    t += '<div class="div_2">'   # カレンダー
    t += ' <script type="text/javascript"><!--\n'
    t += 'var now = new Date();'
    t += 'var year = now.getFullYear();'
    t += 'var month = now.getMonth() + 1;'
    t += 'var today = now.getDate();'
    t += 'now.setDate(1);'
    t += 'var startDay = now.getDay();'
    t += 'var monthdays = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);'
    t += 'var dateMax = monthdays[month - 1];'
    t += 'if (month == 2 && ((year%4 == 0 && year%100 != 0) || year%400 == 0)) dateMax = 29;'
    t += 'var holidays = new Array();'
    t += 'for (var i = 0; i <= dateMax; i++) holidays[i] = 0;'
    t += 'var Holidays1 = new Array(1,1, 2,11, 2,23, 3,21, 4,29, 5,3, 5,4, 5,5, 9,23, 11,3, 11,23);'
    t += 'var firstSunday = (startDay == 0) ? 1: 8 - startDay;'
    t += 'for (i = 0; i < Holidays1.length; i += 2) {'
    t += 'if (Holidays1[i] == month) {'
    t += 'holidays[Holidays1[i+1]] = 1;'
    t += 'for (var j = firstSunday; j < dateMax; j += 7)'
    t += 'if (Holidays1[i+1] == j ) { holidays[j+1] = 1; break; } '
    t += '}'
    t += '}'
    t += 'var Holidays2 = new Array(1,2, 7,3, 9,3, 10,2);'
    t += 'var firstMonday = (startDay < 2) ? 2 - startDay: 9 - startDay;'
    t += 'for (i = 0; i < Holidays2.length; i += 2)'
    t += 'if (Holidays2[i] == month) holidays[(Holidays2[i+1] - 1) * 7 + firstMonday] = 1;'
    t += 'var days = new Array("日", "月", "火", "水", "木", "金", "土");'
    t += '''document.write("<table class='calen'>\\n<tr class='bg1'><th colspan=7>" + year + "年 " + month + "月</th></tr>\\n");'''
    t += '''document.write("<tr class='bg2'><th class='sun'>" + days[0] + "</th>");'''
    t += 'for (i = 1; i < 6; i++) document.write("<th>" + days[i] + "</th>");'
    t += '''document.write("<th class='sat'>" + days[6] + "</th></tr>\\n");'''
    t += 'var col = 0, s1;'
    t += 'if (startDay > 0) {'
    t += 'document.write("<tr>");'
    t += 'for ( ; col < startDay; col++) document.write("<td>&nbsp;</td>");'
    t += '}'
    t += 'for (i = 1; i <= dateMax; i++) {'
    t += 'if (col == 0) document.write("<tr>");'
    t += 'if (i == today) {'
    t += '''if (holidays[i] == 1 || col == 0) s1 = "<td class='today sun'>";'''
    t += '''else if (col == 6) s1 = "<td class='today sat'>";'''
    t += '''else s1 = "<td class='today'>";'''
    t += '}'
    t += '''else if (holidays[i] == 1 || col == 0) s1 = "<td class='sun'>";'''
    t += '''else if (col == 6) s1 = "<td class='sat'>";'''
    t += 'else s1 = "<td>";'
    t += 'document.write(s1 + i + "</td>");'
    t += 'if (col == 6) { document.write("</tr>\\n"); col = 0; } else col++;'
    t += '}'
    t += 'if (col != 0) {'
    t += 'for ( ; col < 7; col++) document.write("<td>&nbsp;</td>");'
    t += 'document.write("</tr>");'
    t += '}'
    t += 'document.write("</table>");'
    t += '//-->'
    t += '</script>'
    t += '</div>'
    t += '</div>'

    out_html(t)

# 新規TODOを追加する
def mode_add():
    todo = get_form('todo', '')
    if todo == '':
        out_html('追加するテキストがありません')
        return
    items = load_items()
    items.append(todo)
    save_items(items)
    s = "<div id='items'><p>書き込ました。<br/>"
    s += "素晴らしい！いつも頑張ってますね！</p>"
    s += "<a href='todo.py'>戻る</a></div>"
    out_html(s)

# アイテムを削除
def mode_remove():
    no = int(get_form('no', '-1'))
    if no < 0:
        out_html('番号の間違い')
        return
    items = load_items()
    del items[no]
    save_items(items)
    out_html('削除しました。<a href="todo.py">戻る</a>')

# アイテムを読み込む
def load_items():
    text = "test1\ntest2\ntest3\n"
    if os.path.exists(FILE_DATA):
        with open(FILE_DATA, 'rt', encoding="utf-8") as f:
            text = f.read()
    items = text.strip().split("\n")
    return items

# アイテムを保存
def save_items(items):
    text = "\n".join(items)
    with open(FILE_DATA, 'wt', encoding="utf-8") as f:
        f.write(text)

# HTMLを出力する
def out_html(html):
    out("Content-type: text/html; charset=utf-8")
    out("")
    out("<html><head>")
    out("<title>私の予定表</title>")
    out('<link href="/style.css" rel="stylesheet">')
    out("</head><body>" + html)
    out("</body></html>")

# <input>タグを返す
def input_tag(type, name, value):
    s = '<input type="{}" name="{}" value="{}">'
    return s.format(type, name, value)

# フォームの値を取得して返す
def get_form(name, defvalue):
    if name in form:
        return form[name].value
    return defvalue

if __name__ == '__main__':
    check_param()
```

style.css

```shell-session
h1 {
    font-size: 24px;
    background-color: #f05090;
    padding: 8px;
    color: white;
}

#items {
	margin: 12px;
}

#items p {
	font-size: 16px;
	border-bottom: 4px solid #f09090;
	border-right: 6px solid #f09090;
	padding: 12px;
	margin: 12px;
	background-color: #fff0f0;
}
#items a {
	padding: 8px;
	text-decoration: none;
}
#items input[type=text] {
	width: 300px;
	font-size: 16px;
	padding: 8px;
}
#items input[type=submit] {
	font-size: 24px;
	padding: 8px;
	width: 60px;
	margin: 8px;
}

#items p.frm {
	background-color: #fff0ff;
}

.div_1 {
    padding:  10px;             /* 余白指定 */
    display: flex;              /* フレックスボックスにする */
}
 
.div_2 {
    padding: 10px;
    margin:  10px;              /* 外側の余白 */
    border-radius:  5px;        /* 角丸指定 */
    font-size: 30px;            /* 文字サイズ指定 */

}
 
.div_2:nth-child(1) {
    flex-basis:  1100px;         /* 幅指定 */

}
 
.div_2:nth-child(2) {
    flex-basis:  200px;         /* 幅指定 */
    text-align:  center;        /* 文字中央揃え */
}

/*カレンダー*/
table.calen { border-collapse:collapse; font-size:11pt; background-color:#fffafa; color:black; }
table.calen th { border:1px solid #a57d6b; height:30px; }
table.calen td { border:1px solid #a57d6b; text-align:center; height:30px; width:30px; }
table.calen .sat { color:blue; }
table.calen .sun { color:red; }
table.calen .today { background-color:#ffe4ce; font-weight:bold; color:black; }
table.calen tr.bg1 { background-color:#f5f5dc; color:black; }
table.calen tr.bg2 { background-color:#eeeeee; color:black; }
```