【Python】FullCalendarの週、日表示から操作できるようにする
おはようございます。
前回に引き続き、FullCalendarで色々とやってみたいと思います。
今回は、週表示、日表示から直接イベントの登録、修正、削除をできるようにしてみました。
ついでにプログラムの整理とおかしい部分の修正も。
プログラムは前回のものを流用します。
スポンサーリンク
プログラムの修正
画面側ロジック
script.js
/**
* ページ初期処理.
*/
function initializePage() {
// カレンダーの設定
$('#calendar').fullCalendar({
height: 550,
lang: "ja",
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
navLinks: true,
timeFormat: 'HH:mm',
selectable: true,
selectHelper: true,
eventSources: [{
url: 'http://localhost:8080/getCalendar',
dataType: 'json',
async: false,
type : 'GET',
error: function() {
$('#script-warning').show();
}
}],
select: function(start, end, resource) {
// 日付選択された際のイベント
// ダイアログタイトル設定
$("#dialogTitle").text("スケジュール登録");
// タイトル初期化
$("#inputTitle").val("");
// 備考初期化
$("#inputDescription").val("");
// ボタン制御
$("#registButton").show();
$("#updateButton").hide();
$("#deleteButton").hide();
// ダイアログ表示
$('#inputScheduleForm').on('show.bs.modal', function (event) {
setTimeout(function(){
$('#inputTitle').focus();
}, 500);
}).modal("show");
// 日付ピッカーの設定
$('#inputYmdFrom').datetimepicker({locale: 'ja', format : 'YYYY年MM月DD日', useCurrent: false });
$('#inputYmdTo').datetimepicker({locale: 'ja', format : 'YYYY年MM月DD日', useCurrent: false });
$('.ymdHm').datetimepicker({
locale: 'ja',
format : 'YYYY年MM月DD日 HH時mm分'
});
// 開始終了が逆転しないように制御
$("#inputYmdFrom").on("dp.change", function (e) {
$('#inputYmdTo').data("DateTimePicker").minDate(e.date);
});
$("#inputYmdTo").on("dp.change", function (e) {
$('#inputYmdFrom').data("DateTimePicker").maxDate(e.date);
});
if (this.name == "month") {
$('.ymdHm').hide()
$('.ymd').show()
// 終日チェックボックス
$('#allDayCheck').prop("checked", true);
// 選択された日付をフォームにセット
// FullCalendar の仕様で、終了が翌日の00:00になるため小細工
var startYmd = moment(start);
var endYmd = moment(end);
if (endYmd.diff(startYmd, 'days') > 1) {
endYmd = endYmd.add(-1, "days");
} else {
endYmd = startYmd;
}
$('#inputYmdFrom').val(startYmd.format("YYYY年MM月DD日"));
$('#inputYmdFrom').data("DateTimePicker").date(startYmd.format("YYYY年MM月DD日"));
$('#inputYmdTo').val(endYmd.format("YYYY年MM月DD日"));
$('#inputYmdTo').data("DateTimePicker").date(endYmd.format("YYYY年MM月DD日"));
} else {
$('.ymdHm').show();
$('.ymd').hide();
// 終日チェックボックス
$('#allDayCheck').prop("checked", false);
var startYmd = moment(start);
var endYmd = moment(end);
$('#inputYmdHmFrom').val(startYmd.format("YYYY年MM月DD日 HH時mm分"));
$('#inputYmdHmFrom').data("DateTimePicker").date(startYmd.format("YYYY年MM月DD日 HH時mm分"));
$('#inputYmdHmTo').val(endYmd.format("YYYY年MM月DD日 HH時mm分"));
$('#inputYmdHmTo').data("DateTimePicker").date(endYmd.format("YYYY年MM月DD日 HH時mm分"));
}
},
eventClick: function(event) {
// 予定クリック時のイベント
$("#dialogTitle").text("スケジュール詳細");
// ユーザーCD設定
$("#userCd").val(event.user_cd);
// スケジュールID設定
$("#scheduleId").val(event.id);
// タイトル設定
$("#inputTitle").val(event.title);
// 備考設定
$("#inputDescription").val(event.description);
// ボタン制御
$("#registButton").hide();
$("#updateButton").show();
$("#deleteButton").show();
// ダイアログ表示
$('#inputScheduleForm').on('show.bs.modal', function (event) {
setTimeout(function(){
$('#inputTitle').focus();
}, 500);
}).modal("show");
// 日付ピッカーの設定
$('#inputYmdFrom').datetimepicker({locale: 'ja', format : 'YYYY年MM月DD日', useCurrent: false });
$('#inputYmdTo').datetimepicker({locale: 'ja', format : 'YYYY年MM月DD日', useCurrent: false });
$('.ymdHm').datetimepicker({
locale: 'ja',
format : 'YYYY年MM月DD日 HH時mm分'
});
// 開始終了が逆転しないように制御
$("#inputYmdFrom").on("dp.change", function (e) {
$('#inputYmdTo').data("DateTimePicker").minDate(e.date);
});
$("#inputYmdTo").on("dp.change", function (e) {
$('#inputYmdFrom').data("DateTimePicker").maxDate(e.date);
});
// 終日チェックボックス
$('#allDayCheck').prop("checked", true);
// 選択された日付をフォームにセット
if (this.name == "month") {
$('.ymdHm').hide()
$('.ymd').show()
// 終日チェックボックス
$('#allDayCheck').prop("checked", true);
// 選択された日付をフォームにセット
// FullCalendar の仕様で、終了が翌日の00:00になるため小細工
var startYmd = moment(start);
var endYmd = moment(end);
if (endYmd.diff(startYmd, 'days') > 1) {
endYmd = endYmd.add(-1, "days");
} else {
endYmd = startYmd;
}
$('#inputYmdFrom').val(startYmd.format("YYYY年MM月DD日"));
$('#inputYmdFrom').data("DateTimePicker").date(startYmd.format("YYYY年MM月DD日"));
$('#inputYmdTo').val(endYmd.format("YYYY年MM月DD日"));
$('#inputYmdTo').data("DateTimePicker").date(endYmd.format("YYYY年MM月DD日"));
} else {
$('.ymdHm').show();
$('.ymd').hide();
// 終日チェックボックス
$('#allDayCheck').prop("checked", false);
var startYmd = moment(event.start);
var endYmd = moment(event.end);
$('#inputYmdHmFrom').val(startYmd.format("YYYY年MM月DD日 HH時mm分"));
$('#inputYmdHmFrom').data("DateTimePicker").date(startYmd.format("YYYY年MM月DD日 HH時mm分"));
$('#inputYmdHmTo').val(endYmd.format("YYYY年MM月DD日 HH時mm分"));
$('#inputYmdHmTo').data("DateTimePicker").date(endYmd.format("YYYY年MM月DD日 HH時mm分"));
}
},
editable: true,
eventLimit: true
});
}
/**
* 予定入力フォームの登録ボタンクリックイベント.
*/
function registSchedule() {
// 開始終了日付の調整
var startYmd = moment(formatNengappi($('#inputYmdFrom').val() + "00時00分00", 1));
var endYmd = moment(formatNengappi($('#inputYmdTo').val() + "00時00分00", 1));
var allDayCheck = $('#allDayCheck').prop("checked");
if (!allDayCheck) {
startYmd = moment(formatNengappi($('#inputYmdHmFrom').val() + "00", 1));
endYmd = moment(formatNengappi($('#inputYmdHmTo').val() + "00", 1));
}
if (endYmd.diff(startYmd, 'days') > 0) {
endYmd = endYmd.add(+1, "days");
}
// 非同期でサーバーにリクエストを送信
var eventData = {
title: $('#inputTitle').val(),
start: startYmd.format("YYYY-MM-DDTHH:mm:ss"),
end: endYmd.format("YYYY-MM-DDTHH:mm:ss"),
allDay: allDayCheck,
description: $('#inputDescription').val()
};
sendAjaxRequest("regist", eventData);
}
/**
* 予定入力フォームの更新ボタンクリックイベント.
*/
function updateSchedule() {
// 開始終了日付の調整
var startYmd = moment(formatNengappi($('#inputYmdFrom').val() + "00時00分00", 1));
var endYmd = moment(formatNengappi($('#inputYmdTo').val() + "00時00分00", 1));
var allDayCheck = $('#allDayCheck').prop("checked");
if (!allDayCheck) {
startYmd = moment(formatNengappi($('#inputYmdHmFrom').val(), 1));
endYmd = moment(formatNengappi($('#inputYmdHmTo').val(), 1));
}
if (endYmd.diff(startYmd, 'days') > 0) {
endYmd = endYmd.add(+1, "days");
}
// 非同期でサーバーにリクエストを送信
var eventData = {
user_cd: $("#userCd").val(),
id: $("#scheduleId").val(),
title: $('#inputTitle').val(),
start: startYmd.format("YYYY-MM-DDTHH:mm:ss"),
end: endYmd.format("YYYY-MM-DDTHH:mm:ss"),
allDay: allDayCheck,
description: $('#inputDescription').val()
};
sendAjaxRequest("update", eventData);
}
/**
* 予定入力フォームの削除ボタンクリックイベント.
*/
function deleteSchedule() {
// リクエストパラメータの設定
var eventData = {
user_cd: $("#userCd").val(),
id: $("#scheduleId").val(),
};
sendAjaxRequest("delete", eventData);
}
/**
* リクエスト送信.
*/
function sendAjaxRequest(method, eventData) {
var cal = $("#calendar").fullCalendar("getView");
eventData.searchStart = cal.start;
eventData.searchEnd = cal.end;
// 処理名を設定
var methodName = "登録";
if (method == "update") {
methodName = "更新"
} else if (method == "delete") {
methodName = "削除"
}
$.ajax({
url: "http://localhost:8080/" + method,
type: "POST",
data: JSON.stringify(eventData),
success: function(jsonResponse) {
// カレンダー再描画
$('#calendar').fullCalendar('removeEvents');
$('#calendar').fullCalendar('renderEvents', $.parseJSON(jsonResponse) )
$('#inputScheduleForm').modal('hide');
alert("予定を" + methodName + "しました。");
},
error: function() {
alert("予定の" + methodName + "に失敗しました。");
}
});
$('#calendar').fullCalendar('unselect');
}
/**
* 終日チェックボックスクリックイベント.
* 入力フィールドの表示切替と設定された日時の同期を実施.
*
*/
function allDayCheckClick(element) {
if (element && element.checked) {
// 日付に変換して設定
var startYmdHm = formatNengappi($("#inputYmdHmFrom").val() + "00", 1);
var endYmdHm = formatNengappi($("#inputYmdHmTo").val() + "00", 1);
var startYmd = moment(startYmdHm);
var endYmd = moment(endYmdHm);
$("#inputYmdFrom").val(startYmd.format("YYYY年MM月DD日"));
$("#inputYmdTo").val(endYmd.format("YYYY年MM月DD日"));
// 表示切替
$('.ymdHm').hide();
$('.ymd').show();
} else {
// 日時に変換して設定
var startYmd = formatNengappi($("#inputYmdFrom").val(), 0);
var endYmd = formatNengappi($("#inputYmdTo").val(), 0);
var startYmdHm = moment(startYmd + "T" + moment().format("HH") + ":00:00");
var endYmdHm = moment(startYmd + "T" + moment().format("HH") + ":00:00").add(1, "hours");
$("#inputYmdHmFrom").val(startYmdHm.format("YYYY年MM月DD日 HH時mm分"));
$("#inputYmdHmTo").val(endYmdHm.format("YYYY年MM月DD日 HH時mm分"));
// 表示切替
$('.ymdHm').show();
$('.ymd').hide();
}
}
/**
* 年月日の形式を変換する.
*/
function formatNengappi(nengappi, flg) {
var ret = nengappi.replace("年", "-").replace("月", "-").replace("日", "");
if (flg == 1){
ret = nengappi.replace("年", "-").replace("月", "-").replace("日", "T").replace("時",":").replace("分",":").replace(" ","");
}
return ret;
}サーバー側ロジック
MySQLUtil.py(抜粋)
def get_schedule(self, start, end, user_cd=""):
"""
条件に合致するデータを返します
:return:
"""
result = []
with closing(mysql.connector.connect(**self.config)) as conn:
c = conn.cursor(dictionary=True)
sql = "SELECT * FROM TBL_SCHEDULE"
sql += " WHERE DATE_FORMAT(START, '%Y-%m-%d') >= '" + start + "'"
sql += " AND DATE_FORMAT(END, '%Y-%m-%d') <= '" + end + "'"
if user_cd != "":
sql += " AND USER = '" + user_cd + "'"
sql += " ORDER BY USER_CD, ID"
c.execute(sql)
for r in c.fetchall():
result.append({
"user_cd": r['USER_CD'],
"id": r['ID'],
"title": r['TITLE'],
"start": r['START'],
"end": r['END'],
"textColor": r['TEXTCOLOR'],
"color": r['COLOR'],
"url": r['URL'],
"allDay": (r['ALLDAY'] == 'TRUE'),
"description": r['DESCRIPTION']
})
return result
class GetCalendar(RequestHandler):
"""
カレンダー取得
"""
def initialize(self):
logging.info("GetCalendar [initialize]")
def get(self):
logging.info("GetCalendar [get]")
mysql = MySQLUtil()
arg = self.request.arguments
start = arg["start"][0].decode("UTF-8")
end = arg["end"][0].decode("UTF-8")
data = mysql.get_schedule(start, end, "")
self.write(json.dumps(data, default=support_datetime_default))
class RegistSchedule(RequestHandler):
"""
スケジュール登録
"""
def initialize(self):
logging.info("RegistSchedule [initialize]")
def post(self):
logging.info("RegistSchedule [post]")
mysql = MySQLUtil()
param = json.loads(self.request.body)
allday = "TRUE" if param["allDay"] else "FALSE"
id = mysql.get_next_id('001')
data = [
"001",
id,
param["title"],
param["start"],
param["end"],
"",
"",
"",
allday,
param["description"]
]
mysql.insert_data(data)
# 再検索
data = mysql.get_schedule(param["searchStart"], param["searchEnd"], "")
self.write(json.dumps(data, default=support_datetime_default))
class UpdateSchedule(RequestHandler):
"""
スケジュール更新
"""
def initialize(self):
logging.info("UpdateSchedule [initialize]")
def post(self):
logging.info("UpdateSchedule [post]")
mysql = MySQLUtil()
param = json.loads(self.request.body)
allday = "TRUE" if param["allDay"] else "FALSE"
id = mysql.get_next_id('001')
data = [
param["title"],
param["start"],
param["end"],
"",
"",
"",
allday,
param["description"],
param["user_cd"],
param["id"]
]
mysql.update_data(data)
# 再検索
data = mysql.get_schedule(param["searchStart"], param["searchEnd"], "")
self.write(json.dumps(data, default=support_datetime_default))
class DeleteSchedule(RequestHandler):
"""
スケジュール削除
"""
def initialize(self):
logging.info("DeleteSchedule [initialize]")
def post(self):
logging.info("DeleteSchedule [post]")
mysql = MySQLUtil()
param = json.loads(self.request.body)
data = [
param["user_cd"],
param["id"]
]
mysql.delete_data(data)
# 再検索
data = mysql.get_schedule(param["searchStart"], param["searchEnd"], "")
self.write(json.dumps(data, default=support_datetime_default))
起動してみる
無事に表示、登録・修正・削除ができるようになりました。
まとめ
細かいエラー処理や入力チェックなんかは入れていないですが、
結構スケジューラ-っぽくなってきましたね。
あとはユーザ認証機能なんかをつけてちょっと弄れば複数人で使えそうです。
ということで次回はユーザー認証なんかをやってみたいと思います。
ではでは。
ディスカッション
コメント一覧
まだ、コメントがありません