Python で HTML のカレンダーを生成するスクリプトについてまとめておきます。ローカルパソコンのブラウザで動作します。
最初にテキストファイルに、日付つきで予定を書き出しておきます。Python のスクリプトを実行すると予定を読み込んで、HTML のカレンダーを生成します。
ローカルパソコン・ネットワーク/公開サーバーなど、いずれの環境でも、タスク管理が容易になります。
以下の環境で動作確認をしています。
環境: Windows パソコン、Python 3.X
背景: JavaScript がローカルで動かなくなった!!
過去数年間、オープンソースの JavaScript のカレンダーを、ローカルパソコンにダウンロードして使ってきました。
ところが、最近のブラウザやライブラリの仕様変更、バージョンの相性等により、ローカル環境で JavaScript が動かなくなるケースが多発しています(jQuery など。。)。
Microsoft Edge などのブラウザが自動でアップデートされ、セキュリティも年々強化されていくので仕方がありません。
突然の仕様変更やバージョンアップで、JavaScript などの過去の便利なライブラリが使えなくなっていく傾向は今後も続くと考えられます。
そこで、ローカルのパソコンでも動くよう、HTML のカレンダーを生成する Python のスクリプトをまとめ、公開しておくことにします。
ネット上のライブラリを参照するようにすると、ネットワークがたまたまつながらない、いずれかのライブラリがバージョンアップした程度の理由で、自分の予定すら参照できなくなってしまいます。カレンダーの仕様としては論外です。そこで、余計なライブラリは使わずに、ローカル環境でも確実に動くことを優先します。
HTML のカレンダーには予定入力が可能です。予定を年月日つきでテキストファイル(schedule1.txt)で書き出しておき、Python のスクリプトを実行すると、カレンダーに反映するようにします。
なお、CSS ファイルについても別ファイルとすると複雑度が上がり、組み合わせの問題が生じる遠因となります。そこで、CSS も HTML と同一ファイルに入れます。
うまく動いたら、予定入力や CSS の部分など、必要によりスクリプトやデザインを修正し、自由にカスタマイズしてください。
設定方法
① パソコン上で、カレンダーを入れるフォルダを作成してください。
例: C:/user/
② ①のフォルダに、html_calendar1.py、schedule1.txt の名前でテキストファイルを作成してください。
さらに、①のフォルダ内に、“calendar” という名前でフォルダを作成してください。この中に html のカレンダーが生成されます。
③ html_calendar1.py を編集して、下記のスクリプトをコピー&ペーストして保存してください。
使い方:Python から HTML カレンダー出力
④ 下記の記載例を参考に、テキスト schedule1.txt に予定を記載してください。
年月日を指定すると、年月日が完全に一致したカレンダーの欄に文字列を表示します。
年月日とスラッシュは半角としてください。また、年月日と年月日に続く文字列との間には、半角スペース ” ” を入れてください。
Python のスクリプト内でセパレータとして使っていますので、半角スペースが必要です。日付部分には余計なスペースや文字を入れないようにしてください。
年、または、月の部分に “*” を入れると、毎年、または、毎月、その指定日となるカレンダーの欄に文字列を表示します。
⑤ ③の html_calendar1.py を実行してください。”calendar” フォルダに html カレンダーが生成されます。バッチファイルから実行(下記の関連リンク参照)できるようにしておくと楽です。
“calendar” フォルダ内に生成される “calendar1.html” がメインとなる html ファイルです。ブラウザで開いて、”prev”、”next” をクリックすると、前の月、つぎの月のカレンダーが表示されます。
“calendar1.html” は、Python スクリプトを実行した年月のカレンダーとなります。更新するには、Python のスクリプトを再度、実行してください。また、schedule1.txt を更新してデータを更新したいときも、Python スクリプトを再度、実行してください。
“calendar1.html” ファイルをブラウザのお気に入り等に保存しておけば、ローカルでカレンダーをすぐに参照できます。
ウェブにアクセスができなくても、JavaScript やブラウザのバージョンアップ等があっても、環境の影響を最小化できます。
また、各月のカレンダーも同時に生成し、リンクを張っています。カレンダーを毎月作成すると、前月、前々月等、過去に作成したカレンダーも参照できるようになります。
スクリプトの説明
・ 最初に os, datetime, calendar をインポートしています。
・ def としたところで関数を定義しています。read1()、write1() 等は、このサイトの他のページで説明していますので、詳細は省略します。
・ generate_calendar1() は、年月を指定すると、7列×6段のカレンダーの枠に対応するよう、日を埋めて、出力します。実際は1次元の配列(リスト)です。
・ get_schedule1() は、schedule1.txt のテキストを入力すると、年月日を判別して、カレンダーの枠にその月の予定を割りつけます。
・ generate_html1, 0 で、HTML ファイルを生成しています。html ファイルの文字列をベタ書きで generate_html0() 内に記載しており、出力したいカレンダーの年月等に応じて、文字列を差し込んで、出力しています。
・ 上記の関数を def で定義したのち、path1 とした行以降でスクリプトを実行します。
・ まず、テキストファイル schedule1.txt のパスを設定し文字列 str0 を読み込みます。
・ つぎに、現在の年月日 y1, m1, d1 を取得し、年月と文字列 str0 から、html ファイルを生成します。
・ 生成した html ファイルを calendar1.html として書き出します。
・ つぎに数か月分進めたカレンダーについても for ループで生成しています。生成するカレンダーの数(月数)は 12 か月としていますが、必要に応じて調整してください。
・ 最新の月以降のカレンダーを生成するようになっていますが、<prev> をクリックすると、過去に生成したカレンダーについても参照ができるようにしています。
・ また、コメントアウトしている行は、過去のカレンダーを生成する場合に使用するためのものです。スクリプトでは prev_next() の中で -12 か月としています。過去(1年前から現在まで等)のカレンダーを生成したい場合は、一時的にそのままコメントアウトを外して実行してみてください。
まとめ
Python から HTML のカレンダーを出力するスクリプトについてまとめました。
ネット検索をしたのですが、手軽で確実に動く実用的なカレンダーが見つかりませんでした。
そこでスクリプトをまとめ公開しておくことにします。
私の場合は、Windows のタスクスケジューラを使って、予定が更新されていたら指定時間に自動でその月のカレンダーが更新されるようにしています。過去のカレンダーも参照できるようになっています。
これでタスク管理やカレンダーのカスタマイズ等も自由自在です。
最近は、Windows やブラウザが自動更新されるようになり、ネットがつながらなくなったり、突然、JavaScript が動かなくなったりしています。
ローカルで実行できるようにすることで、ネット環境やライブラリの不具合等に依存せず、確実に使えるようになります。
なお、パソコン上で実行形式で動くカレンダーアプリ(tkinter)についても、以下の関連リンクにまとめています。もし関心があるようでしたら、参考にしてみてください。
関連リンク
・ Python カレンダー 【tkinter】
・ タスク管理ツールを作ってみる 【JavaScript】
・ Python で HTML ファイルを出力する
・ HTML の棒グラフを出力する 【Python】
・ HTML の折れ線グラフを出力する 【Python】
・ ファイルが更新されていたらスクリプトを実行する 【Python】
・ バッチファイルで Anaconda から Python を実行する方法 【Windows】
スケジュール schedule1.txt の記載例
2021/04/01 Amazonで注文する
2021/04/10 POINTチャージする
*/*/24 給料日
*/*/25 残高を確認する
*/*/26 クレジットカード引落日
*/4/21 優待送付日(A社)
サンプルコード html_calendar1.py
import os
import datetime as dt1
import calendar as cl1
def read1( file1 ):
with open( file1, 'r', encoding='utf-8' ) as f1:
str1 = f1.read()
return str1
def write1( file1, str1 ):
with open( file1, 'w', encoding='utf-8' ) as f1:
f1.write( str1 )
return 0
def generate_calendar1( y1, m1 ):
cal1 = [""]*42
date1 = dt1.date( y1, m1, 1 )
wd1 = date1.weekday() # weekday 0-6 Mon-Sun
if wd1 > 5:
wd1 = wd1 - 7
wd1 = wd1 + 1
cal_max1 = cl1.monthrange( y1, m1 )[1]
for i1 in range( cal_max1 ):
str1 = str( i1+1 )
i2 = i1 + wd1
cal1[i2] = str1
return wd1, cal1
def get_schedule1( y1, m1, cal1, wd1, str0 ):
cal2 = [""]*len( cal1 )
a1 = str0.split( "\n" )
for i1 in range( len( a1 ) ):
a2 = a1[i1].strip().split( " " )
a3 = a2[0].split( "/" )
if len( a3 ) == 3:
y2 = a3[0]
m2 = a3[1]
if "*" in y2 or int( y2 ) == y1:
if "*" in m2 or int( m2 ) == m1:
d1 = int( a3[2] )
a4 = a2
del a4[0]
str1 = str( " ".join( a4 ) ).strip()
cal2[ d1-1 + wd1 ] = cal2[ d1-1 + wd1 ] + str1 + "
"
cal3 = []
for i1 in range( len( cal1 ) ):
cal3.append( cal1[i1] )
cal3.append( cal2[i1] )
return cal3
def generate_ymd1():
now1 = dt1.datetime.now()
y1 = now1.year
m1 = now1.month
d1 = now1.day
return y1, m1, d1
def prev_next1( n1, y1, m1 ):
m2 = m1 + n1
y2 = y1 + m2//12
m2 = m2%12
if m2 == 0:
y2 = y2 - 1
m2 = 12
return y2, m2
def generate_html1( y1, m1, str0 ):
wd1, cal1 = generate_calendar1( y1, m1 )
cal2 = get_schedule1( y1, m1, cal1, wd1, str0 )
str1 = generate_html0( y1, m1, cal2 )
return str1
def generate_html0( y1, m1, cal1 ):
m0 = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]
y2, m2 = prev_next1( -1, y1, m1 )
prev1 = str( y2 ) + f'{m2:02}'
y2, m2 = prev_next1( 1, y1, m1 )
next1 = str( y2 ) + f'{m2:02}'
str1 = '''
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>calendar</title>
<style media="screen">
.header0 {{
height: 30px;
line-height: 30px;
text-align: left;
font-size: 40px;
padding: 10px;
margin: 0;
display: inline-block;
_display: inline;
font-weight: bold;
}}
table {{
table-layout: fixed;
width: 100%;
}}
th {{
text-align: center;
padding: 0px;
}}
td {{
text-align: left;
vertical-align: top;
padding: 5px;
height: 60px;
}}
.calendar0 {{
background: #EEEEE8;
}}
.header1 {{
font-size: 13px;
padding: 5px;
}}
.calendar_table1 {{
height: 60%;
padding: 5px;
}}
.days1 {{
background: #FFFFFF;
}}
.day1 {{
font-weight: bold;
font-size: 14px;
}}
.content1 {{
border-radius: 5px;
background: #F0FFF0;
font-size: 14px;
font-family: 'Meiryo UI';
color: #000000;
}}
.w1 {{
color: #FF0000;
background: #FFF0F0;
}}
.w7 {{
color: #0000A0;
background: #F6F0FF;
}}
</style>
</head>
<body class="calendar0">
<div class="calendar1">
<table>
<tr>
<td>
<div class="header0">{_str01} </div>{_str02} {_str03}
</td>
<td></td>
<td></td>
<td>
<a href = "./calendar_{_str04}.html">prev</a>
<a href = "./calendar_{_str05}.html">next</a><br>
</td>
</tr>
</table>
'''.format( _str01 = m1, _str02 = y1, _str03 = m0[m1-1], _str04 = prev1, _str05 = next1 ).strip()
str2 = '''
<table class="header1">
<tr>
<th>Sunday</th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
<th>Saturday</th>
</tr>
</table>
<table class="calendar_table1">
<tr class="days1">
<td class = w1><div class=day1>{0[0]}</div><cr><div class=content1>{0[1]}</div></td>
<td class = w2><div class=day1>{0[2]}</div><cr><div class=content1>{0[3]}</div></td>
<td class = w3><div class=day1>{0[4]}</div><cr><div class=content1>{0[5]}</div></td>
<td class = w4><div class=day1>{0[6]}</div><cr><div class=content1>{0[7]}</div></td>
<td class = w5><div class=day1>{0[8]}</div><cr><div class=content1>{0[9]}</div></td>
<td class = w6><div class=day1>{0[10]}</div><cr><div class=content1>{0[11]}</div></td>
<td class = w7><div class=day1>{0[12]}</div><cr><div class=content1>{0[13]}</div></td>
</tr>
<tr class="days1">
<td class = w1><div class=day1>{0[14]}</div><cr><div class=content1>{0[15]}</div></td>
<td class = w2><div class=day1>{0[16]}</div><cr><div class=content1>{0[17]}</div></td>
<td class = w3><div class=day1>{0[18]}</div><cr><div class=content1>{0[19]}</div></td>
<td class = w4><div class=day1>{0[20]}</div><cr><div class=content1>{0[21]}</div></td>
<td class = w5><div class=day1>{0[22]}</div><cr><div class=content1>{0[23]}</div></td>
<td class = w6><div class=day1>{0[24]}</div><cr><div class=content1>{0[25]}</div></td>
<td class = w7><div class=day1>{0[26]}</div><cr><div class=content1>{0[27]}</div></td>
</tr>
<tr class="days1">
<td class = w1><div class=day1>{0[28]}</div><cr><div class=content1>{0[29]}</div></td>
<td class = w2><div class=day1>{0[30]}</div><cr><div class=content1>{0[31]}</div></td>
<td class = w3><div class=day1>{0[32]}</div><cr><div class=content1>{0[33]}</div></td>
<td class = w4><div class=day1>{0[34]}</div><cr><div class=content1>{0[35]}</div></td>
<td class = w5><div class=day1>{0[36]}</div><cr><div class=content1>{0[37]}</div></td>
<td class = w6><div class=day1>{0[38]}</div><cr><div class=content1>{0[39]}</div></td>
<td class = w7><div class=day1>{0[40]}</div><cr><div class=content1>{0[41]}</div></td>
</tr>
<tr class="days1">
<td class = w1><div class=day1>{0[42]}</div><cr><div class=content1>{0[43]}</div></td>
<td class = w2><div class=day1>{0[44]}</div><cr><div class=content1>{0[45]}</div></td>
<td class = w3><div class=day1>{0[46]}</div><cr><div class=content1>{0[47]}</div></td>
<td class = w4><div class=day1>{0[48]}</div><cr><div class=content1>{0[49]}</div></td>
<td class = w5><div class=day1>{0[50]}</div><cr><div class=content1>{0[51]}</div></td>
<td class = w6><div class=day1>{0[52]}</div><cr><div class=content1>{0[53]}</div></td>
<td class = w7><div class=day1>{0[54]}</div><cr><div class=content1>{0[55]}</div></td>
</tr>
<tr class="days1">
<td class = w1><div class=day1>{0[56]}</div><cr><div class=content1>{0[57]}</div></td>
<td class = w2><div class=day1>{0[58]}</div><cr><div class=content1>{0[59]}</div></td>
<td class = w3><div class=day1>{0[60]}</div><cr><div class=content1>{0[61]}</div></td>
<td class = w4><div class=day1>{0[62]}</div><cr><div class=content1>{0[63]}</div></td>
<td class = w5><div class=day1>{0[64]}</div><cr><div class=content1>{0[65]}</div></td>
<td class = w6><div class=day1>{0[66]}</div><cr><div class=content1>{0[67]}</div></td>
<td class = w7><div class=day1>{0[68]}</div><cr><div class=content1>{0[69]}</div></td>
</tr>
<tr class="days1">
<td class = w1><div class=day1>{0[70]}</div><cr><div class=content1>{0[71]}</div></td>
<td class = w2><div class=day1>{0[72]}</div><cr><div class=content1>{0[73]}</div></td>
<td class = w3><div class=day1>{0[74]}</div><cr><div class=content1>{0[75]}</div></td>
<td class = w4><div class=day1>{0[76]}</div><cr><div class=content1>{0[77]}</div></td>
<td class = w5><div class=day1>{0[78]}</div><cr><div class=content1>{0[79]}</div></td>
<td class = w6><div class=day1>{0[80]}</div><cr><div class=content1>{0[81]}</div></td>
<td class = w7><div class=day1>{0[82]}</div><cr><div class=content1>{0[83]}</div></td>
</tr>
</table>
</div>
</body>
</html>
'''.format( cal1 ).strip()
return str1 + str2
path1 = os.path.dirname(__file__) + "/"
str0 = read1( path1 + "schedule1.txt" )
y1, m1, d1 = generate_ymd1()
str1 = generate_html1( y1, m1, str0 )
file1 = path1 + "calendar/calendar1.html"
write1( file1, str1 )
# y1, m1 = prev_next1( -12, y1, m1 )
for i1 in range( 12 ):
str1 = generate_html1( y1, m1, str0 )
file1 = path1 + "calendar/calendar_" + str(y1) + f'{m1:02}' + ".html"
write1( file1, str1 )
y1, m1 = prev_next1( 1, y1, m1 )