BETA

【Python】FullCalendarの週、日表示から操作できるようにする

投稿日:2019-01-15
最終更新:2019-01-15

前回に引き続き、FullCalendarで色々とやってみたいと思います。

今回は、週表示、日表示から直接イベントの登録、修正、削除をできるようにしてみました。
ついでにプログラムの整理とおかしい部分の修正も。

プログラムは前回のものを流用します。

【Pytnon】FullCalendarで登録済みの予定を更新、削除してみる

プログラムの修正

画面側ロジック


script.js

変更点

  1. 週表示、日表示を詳細(時刻あり)に変更

  2. リクエスト送信処理の共通化

  3. その他細かいブラッシュアップや不備修正

/**  
 * ページ初期処理.  
 */  
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-DDTHHss"),  
            end: endYmd.format("YYYY-MM-DDTHHss"),  
            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-DDTHHss"),  
            end: endYmd.format("YYYY-MM-DDTHHss"),  
            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(抜粋)

変更点

  1. 検索用メソッドの引数変更対応

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

Main.py(抜粋)

変更点


  1. 登録、更新、削除後にデータを再検索して返却するように

  2. 検索用メソッドの引数変更対応

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))

起動してみる


無事に表示、登録・修正・削除ができるようになりました。

まとめ


細かいエラー処理や入力チェックなんかは入れていないですが、
結構スケジューラ-っぽくなってきましたね。

あとはユーザ認証機能なんかをつけてちょっと弄れば複数人で使えそうです。

ということで次回はユーザー認証なんかをやってみたいと思います。

ではでは。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@doraxdoraの技術ブログ 主に Java, C#, Python, Javascript の記事を載せていく予定。

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう