【Pytnon】FullCalendarで登録済みの予定を更新、削除してみる
おはようございます。
祝日のない6月もそろそろ終わり、夏休みの時期が近づいてきましたね。
といっても会社員の皆さんは多くても1週間ちょっとくらいしか休みが取れないでしょうけど、
まあ楽しみですよね。
私もお盆の時期に休みを取る予定で
8月のカレンダーを眺めていたんですが、山の日が土曜日と被ってることに気づいて驚愕。
出来たばっかなのに酷いですよねぇ。
話が逸れましたが、
前回に引き続きカレンダーページを弄っていきます。
今回は、既に登録されているイベントを更新したり削除したりできるようにします。
プログラムは前回のものを流用しまっす。
https://www.doraxdora.com/blog/2018/06/24/post-5027/
スポンサーリンク
画面の修正
登録フォームに、隠しタグ、ボタンを追加。
Main.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | <!DOCTYPE html> <html> <head> <title>カレンダーサンプル</title> <link rel="stylesheet"href="{{ static_url('css/fullcalendar.min.css') }}"/> <link rel="stylesheet"href="https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <link rel="stylesheet"href="{{ static_url('css/style.css') }}"/> <link rel="stylesheet"href="http://cdn.rawgit.com/Eonasdan/bootstrap-datetimepicker/v4.0.0/build/css/bootstrap-datetimepicker.css"> <script type="text/javascript"src="http://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> <script type="text/javascript"src="https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> <script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment-with-locales.js"></script> <script type="text/javascript"src="{{ static_url('js/moment.min.js') }}"></script> <script type="text/javascript"src="{{ static_url('js/fullcalendar.min.js') }}"></script> <script type="text/javascript"src="{{ static_url('lang/ja.js') }}"></script> <script src="http://cdn.rawgit.com/Eonasdan/bootstrap-datetimepicker/v4.0.0/src/js/bootstrap-datetimepicker.js"></script> <script type="text/javascript"src="{{ static_url('js/script.js') }}"></script> <script> // ページ読み込み時の処理 $(document).ready(function(){ initializePage(); }); </script> </head> <body> <div id='calendar'></div> <div id="inputScheduleForm"class="modal fade"tabindex="-1"> <div class="modal-dialog modal-nm"> <div class="modal-content"> <div class="modal-header"> <button type="button"class="close"data-dismiss="modal"aria-hidden="true">×</button> <h4 id="dialogTitle"class="modal-title">スケジュール登録</h4> </div> <div class="modal-body"> <div class="container"> <div class="row"> <div class="col-md-2"> <label for="">タイトル</label> <input id="userCd"type="hidden"value="" /> <input id="scheduleId"type="hidden"value="" /> </div> <div class="col-md-5"> <input id="inputTitle"type="text"class="form-control input-sm ime-active"placeholder="タイトル"value="" > </div> </div> <div class="row"> <div class="col-md-2 required"> <label for="">日時</label> </div> <div class="col-md-5"> <div class="input-group"> <div class="checkbox"style="margin-top: 0px;"> <input type="checkbox"id="allDayCheck"onclick="allDayCheckClick(this);"checked/><label for="allDayCheck">終日</label> </div> </div> <div class="form-inline"> <div class="form-group"style="position:relative;"> <input id="inputYmdFrom"type="text"class="form-control input-sm ymd"value=""/> <input id="inputYmdHmFrom"type="text"class="form-control input-sm ymdHm"/> ~ <input id="inputYmdTo"type="text"class="form-control input-sm ymd" /> <input id="inputYmdHmTo"type="text"class="form-control input-sm ymdHm" /> </div> </div> </div> </div> <div class="row"> <div class="col-md-2"> <label for="">詳細</label> </div> <div class="col-md-4"> <textarea id="inputDescription"class="form-control ime-active"rows="5"placeholder="詳細"></textarea> </div> </div> </div> </div> <div class="modal-footer"> <div id="inputError"class="pull-left"style="color:red; padding:5px;"></div> <button id="registButton"type="button"class="btn btn-primary"onclick="registSchedule();">登録</button> <button id="updateButton"type="button"class="btn btn-primary"onclick="updateSchedule();">更新</button> <button id="deleteButton"type="button"class="btn btn-primary"onclick="deleteSchedule();">削除</button> <button type="button"class="btn btn-default"data-dismiss="modal">閉じる</button> </div> </div> </div> </div> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | /** * ページ初期処理. */ functioninitializePage(){ // カレンダーの設定 $('#calendar').fullCalendar({ height:550, lang:"ja", header:{ left:'prev,next today', center:'title', right:'month,basicWeek,basicDay' }, timeFormat:'HH:mm', selectable:true, selectHelper:true, navLinks: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"); // 日付ピッカーの設定 $('.ymdHm').hide() $('#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); // 選択された日付をフォームにセット // FullCalendar の仕様で、終了が翌日の00:00になるため小細工 varstartYmd=moment(start); varendYmd=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日")); }, eventClick:function(event){ // 予定クリック時のイベント console.log(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"); // 日付ピッカーの設定 $('.ymdHm').hide() $('#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); // 選択された日付をフォームにセット // FullCalendar の仕様で、終了が翌日の00:00になるため小細工 varstartYmd=moment(event.start); varendYmd=moment(event.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日")); }, editable:true, eventLimit:true }); } /** * 予定入力フォームの登録ボタンクリックイベント. */ functionregistSchedule(){ varstartYmd=moment(formatNengappi($('#inputYmdFrom').val()+"00時00分00",1)); varendYmd=moment(formatNengappi($('#inputYmdTo').val()+"00時00分00",1)); varallDayCheck=$('#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"); } vareventData; if($('#inputTitle').val()){ 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() }; $.ajax({ url:"http://localhost:8080/regist", type:"POST", data:JSON.stringify(eventData), success:function(jsonResponse){ $('#calendar').fullCalendar('renderEvent',jsonResponse,true); alert("予定を登録しました。"); }, error:function(){ } }); } $('#calendar').fullCalendar('unselect'); } /** * 予定入力フォームの更新ボタンクリックイベント. */ functionupdateSchedule(){ varstartYmd=moment(formatNengappi($('#inputYmdFrom').val()+"00時00分00",1)); varendYmd=moment(formatNengappi($('#inputYmdTo').val()+"00時00分00",1)); varallDayCheck=$('#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"); } vareventData; if($('#inputTitle').val()){ 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() }; filter={ user_cd:$("#userCd").val(), id:$("#scheduleId").val() } $.ajax({ url:"http://localhost:8080/update", type:"POST", data:JSON.stringify(eventData), success:function(jsonResponse){ // 再描画 $('#calendar').fullCalendar('removeEvents'); $('#calendar').fullCalendar('renderEvents',$.parseJSON(jsonResponse)) alert("予定を更新しました。"); }, error:function(){ } }); } $('#calendar').fullCalendar('unselect'); } /** * 予定入力フォームの削除ボタンクリックイベント. */ functiondeleteSchedule(){ // ユーザCD varuserCd=$("#userCd").val(); // スケジュールID varid=$("#scheduleId").val(); varstartYmd=moment(formatNengappi($('#inputYmdFrom').val()+"00時00分00",1)); varendYmd=moment(formatNengappi($('#inputYmdTo').val()+"00時00分00",1)); varallDayCheck=$('#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"); } vareventData; if($('#inputTitle').val()){ eventData={ user_cd:$("#userCd").val(), id:$("#scheduleId").val(), }; $.ajax({ url:"http://localhost:8080/delete", type:"POST", data:JSON.stringify(eventData), success:function(jsonResponse){ // 再描画 $('#calendar').fullCalendar('removeEvents'); $('#calendar').fullCalendar('renderEvents',$.parseJSON(jsonResponse)) alert("予定を削除しました。"); }, error:function(){ } }); } $('#calendar').fullCalendar('unselect'); } /** * 終日チェックボックスクリックイベント. * */ functionallDayCheckClick(element){ if(element&& element.checked) { $('.ymdHm').hide(); $('.ymd').show(); } else{ varstartYmd=moment(formatNengappi($("#inputYmdFrom").val(),0)); varendYmd=moment(formatNengappi($("#inputYmdTo").val(),0)); varstartYmdHm=moment(startYmd.format("YYYY-MM-DD")+"T"+moment().format("HH")+":00:00"); varendYmdHm=moment(startYmd.format("YYYY-MM-DD")+"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分")); $('.ymd').hide(); $('.ymdHm').show(); } } /** * 年月日の形式を変換する. */ functionformatNengappi(nengappi,flg){ varret=nengappi.replace("年","-").replace("月","-").replace("日",""); if(flg==1){ ret=nengappi.replace("年","-").replace("月","-").replace("日","T").replace("時",":").replace("分",":").replace(" ",""); } returnret; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | importjson importlogging importos importtornado.ioloop fromtornado.web importRequestHandler fromtornado.options importoptions fromUtils.MySQLUtil importMySQLUtil fromdatetimeimportdatetime classMainHandler(tornado.web.RequestHandler): defget(self): self.render("Main.html") classGetCalendar(RequestHandler): """ カレンダー取得 """ definitialize(self): logging.info("GetCalendar [initialize]") defget(self): logging.info("GetCalendar [get]") mysql=MySQLUtil() data=mysql.get_schedule("201806","") self.write(json.dumps(data,default=support_datetime_default)) classRegistSchedule(RequestHandler): """ スケジュール登録 """ definitialize(self): logging.info("RegistSchedule [initialize]") defpost(self): logging.info("RegistSchedule [post]") mysql=MySQLUtil() param=json.loads(self.request.body) allday="TRUE"ifparam["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) # ユーザCD、IDを設定して返す param["userCd"]="001" param["id"]=id self.write(param) classUpdateSchedule(RequestHandler): """ スケジュール更新 """ definitialize(self): logging.info("UpdateSchedule [initialize]") defpost(self): logging.info("UpdateSchedule [post]") mysql=MySQLUtil() param=json.loads(self.request.body) allday="TRUE"ifparam["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("201806","") self.write(json.dumps(data,default=support_datetime_default)) classDeleteSchedule(RequestHandler): """ スケジュール削除 """ definitialize(self): logging.info("DeleteSchedule [initialize]") defpost(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("201806","") self.write(json.dumps(data,default=support_datetime_default)) defsupport_datetime_default(o): ifisinstance(o,datetime): returno.isoformat() raiseTypeError(repr(o)+" is not JSON serializable") app=tornado.web.Application([ (r"/",MainHandler), (r"/getCalendar",GetCalendar), (r"/regist",RegistSchedule), (r"/update",UpdateSchedule), (r"/delete",DeleteSchedule), ], template_path=os.path.join(os.getcwd(),"templates"), static_path=os.path.join(os.getcwd(),"static"), ) if__name__=="__main__": options.parse_command_line() app.listen(8080) logging.info("server started") tornado.ioloop.IOLoop.instance().start() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | importmysql.connector importlogging fromcontextlibimportclosing classMySQLUtil: """ MySQL 操作用クラス """ def__init__(self,host="localhost",port="3306",user="USER01",password="USER01",database="DB01"): self.config={ "host":host, "port":port, "user":user, "password":password, "database":database } self.create_db() defcreate_db(self): """ データベース、及び必要なテーブルを作成します. :return: """ withclosing(mysql.connector.connect(**self.config))asconn: c=conn.cursor() # スケジュールテーブル sql="CREATE TABLE IF NOT EXISTS TBL_SCHEDULE (" sql+=" USER_CD VARCHAR(10)" sql+=", ID INT(10)" sql+=", TITLE VARCHAR(100)" sql+=", START DATETIME" sql+=", END DATETIME" sql+=", TEXTCOLOR VARCHAR(20)" sql+=", COLOR VARCHAR(20)" sql+=", URL VARCHAR(100)" sql+=", ALLDAY VARCHAR(10)" sql+=", DESCRIPTION VARCHAR(1000)" sql+=", PRIMARY KEY(USER_CD, ID)" sql+=")" c.execute(sql) c.close() conn.commit() definsert_data(self,data): """ データを登録します :param data: :return: """ withclosing(mysql.connector.connect(**self.config))asconn: c=conn.cursor() # データ登録 sql="INSERT INTO TBL_SCHEDULE VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)" c.execute(sql,data) c.close() conn.commit() defupdate_data(self,data): """ データを更新します :param data: :return: """ withclosing(mysql.connector.connect(**self.config))asconn: c=conn.cursor() # データ登録 sql="UPDATE TBL_SCHEDULE SET TITLE = %s, "\ " START = %s, "\ " END = %s, "\ " TEXTCOLOR = %s, "\ " COLOR = %s, "\ " URL = %s, "\ " ALLDAY = %s, "\ " DESCRIPTION = %s "\ "WHERE "\ " USER_CD = %s AND "\ " ID = %s" c.execute(sql,data) c.close() conn.commit() defdelete_data(self,data): """ データを削除します :return: """ logging.info("delete_data") withclosing(mysql.connector.connect(**self.config))asconn: c=conn.cursor() # データクリア sql="DELETE FROM TBL_SCHEDULE WHERE USER_CD = %s AND ID = %s" c.execute(sql,data) c.close() conn.commit() defget_schedule(self,year_month,user_cd=""): """ データ(テーブル)をデータフレームに変換してかえします :return: """ result=[] withclosing(mysql.connector.connect(**self.config))asconn: c=conn.cursor(dictionary=True) sql="SELECT * FROM TBL_SCHEDULE" sql+=" WHERE (DATE_FORMAT(START, '%Y%m') = '"+year_month+"'" sql+=" OR DATE_FORMAT(END, '%Y%m') = '"+year_month+"')" ifuser_cd!="": sql+=" AND USER = '"+user_cd+"'" sql+=" ORDER BY USER_CD, ID" c.execute(sql) forrinc.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'] }) returnresult defget_next_id(self,user_cd=""): """ データ(テーブル)をデータフレームに変換してかえします :return: """ result=0 withclosing(mysql.connector.connect(**self.config))asconn: c=conn.cursor(dictionary=True) sql="SELECT MAX(ID) + 1 AS ID FROM TBL_SCHEDULE WHERE USER_CD = '"+user_cd+"'" c.execute(sql) result=c.fetchone() returnresult[r"ID"] |
起動してみる
登録済みのイベントをクリックします。
登録されている予定の内容がダイアログに表示され、更新、削除ボタンが表示されます。
とりあえず削除してみましょうか。
無事に予定が削除されました。
まとめ
これでとりあえず、予定の登録、修正、削除まで出来るようになりました。
次回は月表示以外の部分もみていきたいと思います。
ではでは。
ディスカッション
コメント一覧
まだ、コメントがありません