HTML カレンダーを出力する 【Python】

Python

Python でカレンダーを自作する際のサンプルスクリプトです。ローカル PC のブラウザで動作します。
最初に、予定を日付つきでテキストファイルに書き出しておきます。Python のスクリプトを実行すると、予定を読み込んで、HTML のカレンダーを生成します。
ローカル PC /ローカルネットワーク内/サーバー上のいずれでも、タスク管理が可能になります。

以下の環境で動作確認をしています。
環境: Windows 10、Python 3.X

背景: JavaScript がローカルで動かなくなった!!

過去数年間、オープンソースの JavaScript のカレンダーを、ローカル PC にダウンロードして使ってきました。

ところが、最近のブラウザの仕様変更により、ローカルでは JavaScript が動かなくなるケースが多発しています(jQuery など。。)Microsoft Edge などのセキュリティが強化され、ブラウザが自動でアップデートされていくので、やむを得ないですね。JavaScript などの過去の便利なツールが使えなくなっていく傾向は今後も続くと考えられます。

そこで、ローカル PC で、各月の HTML のカレンダーを生成する Python のスクリプトを自作しました。JavaScript は使わず、完全にローカルですぐに使えるようにすることを意図しています。
また、HTML のカレンダーには予定入力が可能です。予定を年月日つきでテキストファイル(schedule1.txt)に書き出しておきます。Python のスクリプトを実行すると、カレンダーに反映します。
なお、CSS ファイルについて、別ファイルとすると問題の原因となりえます。そこで、CSS は HTML と同一ファイルに記載するようにしました。必要により修正は容易だと思います。

設定方法

① PC 上で、カレンダーを入れるフォルダを作成してください。
② ①のフォルダに、html_calendar1.pyschedule1.txt の名前でテキストファイルを作成してください。
さらに、①のフォルダ内に、“calendar” という名前でフォルダを作成してください。この中に html のカレンダーが生成されます。
③ html_calendar1.py を編集して、下記のスクリプトをコピー&ペーストして保存してください。

使い方:Python から HTML カレンダー出力

④ 下記の記載例を参考に、テキスト schedule1.txt に予定を記載してください。
年月日を指定すると、年月日が完全に一致したカレンダーの欄に文字列を表示します。
年月日とスラッシュは半角としてください。また、年月日と年月日に続く文字列との間には、かならず、半角スペース ” ” を入れてください。半角スペースをセパレータとして使っていますので半角スペースが必要です。日付部分には余計なスペースを入れないようにしてください。
年、または、月の部分に “*” を入れると、毎年、または、毎月、その指定日となるカレンダーの欄に文字列を表示します。
⑤ ③の 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 のカレンダーを出力できるようになりました。
Google 検索しても、手軽に使えるカレンダーが見つかりませんでしたので自作しました。
これでタスク管理やカレンダーの扱いも自由自在になりました。

最近は、Windows やブラウザが自動更新されるようになり、ネットがつながらなくなったり、突然、JavaScript が動かなくなったりしています。
ローカルで実行できるようにすることで、ネット環境に依存せず、カレンダーを扱えるようになりました。クラウド化、サービス化が進む一方で、以前に回帰したいという需要もあると思います。

なお、HTML やブラウザを使うのではなく、Python の GUI のアプリ(tkinter)としてカレンダーを生成する場合については、下記のリンクにまとめています。もしも、関心のある場合は、参考にしてみてください。

関連リンク
・ Python カレンダー 【tkinter】
・ Python で HTML ファイルを出力する
・ HTML のグラフを出力する 【Python】
・ バッチファイルで Anaconda から Python を実行する方法 【Windows 10】

スケジュール schedule1.txt の記載例


2021/04/01 Amazonで注文する
2021/04/10 POINTチャージする

*/*/24 給料日
*/*/25 残高を確認する
*/*/26 クレジットカード引落日
*/4/21 優待送付日(A社)

サンプルコード

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 ) 

タイトルとURLをコピーしました