srappli-flask Webアプリ
SRアプリという名のWebアプリをPython3 + Flask3 + SQLite3
で開発しています。これはその個人的な開発メモです。SRが何の略なのかは諸事情で明記できません(汗;)。
これからしばらくの間ちょくちょく更新していくと思いますので、よろしくお願いします。
フォルダパス構成

srappli-flask/instanceフォルダ
SQLiteデータベースのインスタンスを格納するフォルダです。
create_installdb.py
import sqlite3
DROP_INSTALLS = "DROP TABLE IF EXISTS installs"
CREATE_INSTALLS = '''
CREATE TABLE installs
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
applidate DATE,
applicantsid TEXT,
applicantsdiv TEXT,
applicantsname TEXT,
ledgerno TEXT,
hopedate DATE,
enddate DATE,
stragebin TEXT
)
'''
conn = sqlite3.connect('installdb.sqlite3')
cur = conn.cursor()
cur.execute(DROP_INSTALLS)
cur.execute(CREATE_INSTALLS)
conn.commit()
conn.close
insert_installdb.py
# coding=utf-8
import sqlite3
INSERT_INSTALL = '''
INSERT INTO installs (
applidate,
applicantsid,
applicantsdiv,
applicantsname,
ledgerno,
hopedate,
enddate,
stragebin
)
VALUES (
date('2020-06-01'),
'Z1234567',
'ホゲホゲ開発部',
'青木 孝',
'678934',
date('2020-07-01'),
date('2020-09-30'),
'1-2-3'
)
'''
SELECT_INSTALL = "SELECT * FROM installs"
conn = sqlite3.connect('installdb.sqlite3')
cur = conn.cursor()
cur.execute(INSERT_INSTALL)
conn.commit()
cur.execute(SELECT_INSTALL)
result = cur.fetchall()
print(result)
conn.close()
insert_stragebintable.py
# coding=utf-8
import sqlite3
SELECT_INSTALL = "SELECT * FROM stragebins"
conn = sqlite3.connect('installdb.sqlite3')
cur = conn.cursor()
for i in range(1,5):
for j in range(1,3):
for k in range(1,5):
insertbin = '''
INSERT INTO stragebins
(cabinetno,binno,seqno)
VALUES (%d,%d,%d)
''' %(i,j,k)
cur.execute(insertbin)
conn.commit()
cur.execute(SELECT_INSTALL)
result = cur.fetchall()
print(result)
conn.close()
srappli-flask/webフォルダ
srappli-flask/web/employees.py
employees.py
from flask import(
Blueprint, render_template,
request, redirect, url_for
)
from web.installdb import get_db
from web.files import save_csv, read_employees_csv
bp = Blueprint('employees', __name__)
@bp.route('/employees', methods = ['GET'])
def all():
installdb = get_db()
SELECT_EMPLOYEES = '''
SELECT
employees.id,
employees.name,
employees.afficode,
departments.abbreviation
FROM employees JOIN departments
ON employees.afficode = departments.id
'''
alldata = installdb.execute(SELECT_EMPLOYEES).fetchall()
return render_template('employees/all.html', employees = alldata)
@bp.route('/employees/upload', methods = ['GET', 'POST'])
def upload():
if request.method == 'POST':
if 'file' in request.files:
file = request.files['file']
save_csv(file)
datadict = read_employees_csv(file.filename)
installdb = get_db()
installdb.execute('DELETE FROM employees')
for data in datadict:
installdb.execute(
"INSERT INTO employees (id, name, afficode) VALUES (?, ?, ?)",
( data['id'], data['name'], data['afficode'] )
)
installdb.commit()
return redirect(url_for('employees.all'))
return render_template('employees/upload.html')
srappli-flask/web/files.py
files.py
from flask import current_app
import os
import csv
def save_img(file):
file.save(os.path.join(current_app.config['UPLOAD_FOLDER'],'img',file.filename))
def save_csv(file):
file.save(os.path.join(current_app.config['UPLOAD_FOLDER'],'csv',file.filename))
def read_departments_csv(filename):
datalist=[]
with open(os.path.join(current_app.config['UPLOAD_FOLDER'], 'csv',
filename),encoding="utf-8") as datafile:
datareader=csv.reader(datafile)
for row in datareader:
datadict={}
datadict['id']=row[0]
datadict['abbreviation']=row[1]
datalist.append(datadict)
return datalist
def read_employees_csv(filename):
datalist=[]
with open(os.path.join(current_app.config['UPLOAD_FOLDER'], 'csv',
filename),encoding="utf-8") as datafile:
datareader=csv.reader(datafile)
for row in datareader:
datadict={}
datadict['name']=row[0]
datadict['id']=row[1]
datadict['afficode']=row[2]
datalist.append(datadict)
return datalist
def write_file(filename, str):
with open(os.path.join(current_app.config['UPLOAD_FOLDER'], 'csv',
filename), 'w') as datafile:
datafile.write(str)
srappli-flask/web/installdb.py
installdb.py
import sqlite3
from flask import current_app, g
def get_db():
if 'installdb' not in g:
g.installdb = sqlite3.connect(
current_app.config['INSTALLDB'],
detect_types = sqlite3.PARSE_DECLTYPES
)
g.installdb.row_factory = sqlite3.Row
return g.installdb
def close_db(e = None):
installdb = g.pop('installdb', None)
if installdb is not None:
installdb.close()
def init_app(app):
app.teardown_appcontext(close_db)
srappli/web/installs.py
installs.py
import sys
from tkinter import messagebox
from flask import(
Blueprint, render_template,request, redirect, url_for
)
from web.installdb import get_db
from web.files import save_img, write_file
from web.utility import ValideteUtil #, EmailUtil
bp = Blueprint('installs', __name__)
@bp.route('/', methods = ['GET'])
def index():
return render_template('index.html')
@bp.route('/installs', methods = ['GET'])
def all():
installdb = get_db()
alldata = installdb.execute('SELECT * FROM installs').fetchall()
return render_template('installs/all.html', installs = alldata)
@bp.route('/installs/new', methods = ['GET', 'POST'])
def new():
installdb = get_db()
stragebins = installdb.execute('SELECT * FROM stragebins').fetchall()
departments = installdb.execute('SELECT * FROM departments').fetchall()
if request.method == 'POST':
if ValideteUtil.isDecimal(request.form['applicantsid']) == False:
applicantsid_message = '※社員ID:全て数値で入力してください',
return render_template('installs/new.html',
stragebins = stragebins, departments = departments,
applicantsid_message = applicantsid_message
)
if ValideteUtil.isDecimal(request.form['ledgerno']) == False:
ledgerno_message = '※台帳No.:全て数値で入力してください'
return render_template('installs/new.html',
stragebins = stragebins, departments = departments,
ledgerno_message = ledgerno_message
)
install_data = (
request.form['applidate'],
request.form['applicantsid'],
request.form['applicantsdiv'],
request.form['applicantsname'],
request.form['ledgerno'],
request.form['hopedate'],
request.form['enddate'],
request.form['stragebin']
)
installdb.execute(
"INSERT INTO installs (applidate, applicantsid, applicantsdiv, applicantsname, ledgerno, hopedate, enddate, stragebin) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
install_data
)
installdb.commit()
#EmailUtil.sendEmail()
return redirect(url_for('installs.all'))
return render_template(
'installs/new.html',
stragebins = stragebins, departments = departments
)
@bp.route('/installs/show/<install_id>', methods = ['GET', 'POST'])
def show(install_id):
if request.method == 'GET':
installdb = get_db()
install = installdb.execute('SELECT * FROM installs WHERE id = ?', install_id).fetchone()
print(install)
return render_template('installs/show.html', install = install)
@bp.route('/installs/edit/<install_id>', methods = ['GET', 'POST'])
def edit(install_id):
installdb = get_db()
if request.method == 'POST':
install_data = (
request.form['applidate'],
request.form['applicantsid'],
request.form['applicantsdiv'],
request.form['applicantsname'],
request.form['ledgerno'],
request.form['hopedate'],
request.form['enddate'],
request.form['stragebin'],
install_id
)
installdb.execute(
"UPDATE installs SET applidate = ?, applicantsid = ?, applicantsdiv = ?, applicantsname = ?, ledgerno = ?, hopedate = ?, enddate = ?, stragebin = ? where id = ?",
install_data
)
installdb.commit()
return redirect(url_for('installs.all'))
install = installdb.execute(
"SELECT * FROM installs where id = ?", (install_id,)
).fetchone()
stragebins = installdb.execute('SELECT * FROM stragebins').fetchall()
departments = installdb.execute('SELECT * FROM departments').fetchall()
return render_template(
'installs/edit.html',
install = install, stragebins = stragebins, departments = departments
)
@bp.route('/installs/delete/<install_id>', methods = ['GET'])
def delete(install_id):
installdb = get_db()
installdb.execute(
"DELETE from installs where id = ?", (install_id,)
)
installdb.commit()
return redirect(url_for('installs.all'))
@bp.route('/installs/upload/<install_id>', methods = [ 'GET', 'POST' ])
def upload(install_id):
db = get_db()
if request.method == 'POST':
if 'file' in request.files:
file = request.files['file']
save_img(file)
db.execute(
"UPDATE installs SET stragebin = ? WHERE id = ?",
(file.filename, install_id)
)
db.commit()
return redirect(url_for('installs.show', install_id = install_id))
install = db.execute('SELECT * FROM installs WHERE id = ?', install_id).fetchone()
return render_template('installs/upload.html', install = install)
@bp.route('/installs/write', methods = [ 'GET' ])
def write():
csv_str = ""
db = get_db()
alldata = db.execute('SELECT * FROM installs').fetchall()
for data in alldata:
csv_str += ",".join( [ data['ledgerdno'], data['applicantsname'], data['hopedate'] ] )
csv_str += "\n"
write_file("installs.csv", csv_str)
return render_template('installs/write.html', str = csv_str)
srappli/web/stragebins.py
stragebins.py
import sys
from tkinter import messagebox
from flask import(
Blueprint, render_template,request, redirect, url_for
)
from web.installdb import get_db
from web.files import save_img, write_file
from web.utility import ValideteUtil #, EmailUtil
bp = Blueprint('installs', __name__)
@bp.route('/', methods = ['GET'])
def index():
return render_template('index.html')
@bp.route('/installs', methods = ['GET'])
def all():
installdb = get_db()
alldata = installdb.execute('SELECT * FROM installs').fetchall()
return render_template('installs/all.html', installs = alldata)
@bp.route('/installs/new', methods = ['GET', 'POST'])
def new():
installdb = get_db()
stragebins = installdb.execute('SELECT * FROM stragebins').fetchall()
departments = installdb.execute('SELECT * FROM departments').fetchall()
if request.method == 'POST':
if ValideteUtil.isDecimal(request.form['applicantsid']) == False:
applicantsid_message = '※社員ID:全て数値で入力してください',
return render_template('installs/new.html',
stragebins = stragebins, departments = departments,
applicantsid_message = applicantsid_message
)
if ValideteUtil.isDecimal(request.form['ledgerno']) == False:
ledgerno_message = '※台帳No.:全て数値で入力してください'
return render_template('installs/new.html',
stragebins = stragebins, departments = departments,
ledgerno_message = ledgerno_message
)
install_data = (
request.form['applidate'],
request.form['applicantsid'],
request.form['applicantsdiv'],
request.form['applicantsname'],
request.form['ledgerno'],
request.form['hopedate'],
request.form['enddate'],
request.form['stragebin']
)
installdb.execute(
"INSERT INTO installs (applidate, applicantsid, applicantsdiv, applicantsname, ledgerno, hopedate, enddate, stragebin) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
install_data
)
installdb.commit()
#EmailUtil.sendEmail()
return redirect(url_for('installs.all'))
return render_template(
'installs/new.html',
stragebins = stragebins, departments = departments
)
@bp.route('/installs/show/<install_id>', methods = ['GET', 'POST'])
def show(install_id):
if request.method == 'GET':
installdb = get_db()
install = installdb.execute('SELECT * FROM installs WHERE id = ?', install_id).fetchone()
print(install)
return render_template('installs/show.html', install = install)
@bp.route('/installs/edit/<install_id>', methods = ['GET', 'POST'])
def edit(install_id):
installdb = get_db()
if request.method == 'POST':
install_data = (
request.form['applidate'],
request.form['applicantsid'],
request.form['applicantsdiv'],
request.form['applicantsname'],
request.form['ledgerno'],
request.form['hopedate'],
request.form['enddate'],
request.form['stragebin'],
install_id
)
installdb.execute(
"UPDATE installs SET applidate = ?, applicantsid = ?, applicantsdiv = ?, applicantsname = ?, ledgerno = ?, hopedate = ?, enddate = ?, stragebin = ? where id = ?",
install_data
)
installdb.commit()
return redirect(url_for('installs.all'))
install = installdb.execute(
"SELECT * FROM installs where id = ?", (install_id,)
).fetchone()
stragebins = installdb.execute('SELECT * FROM stragebins').fetchall()
departments = installdb.execute('SELECT * FROM departments').fetchall()
return render_template(
'installs/edit.html',
install = install, stragebins = stragebins, departments = departments
)
@bp.route('/installs/delete/<install_id>', methods = ['GET'])
def delete(install_id):
installdb = get_db()
installdb.execute(
"DELETE from installs where id = ?", (install_id,)
)
installdb.commit()
return redirect(url_for('installs.all'))
@bp.route('/installs/upload/<install_id>', methods = [ 'GET', 'POST' ])
def upload(install_id):
db = get_db()
if request.method == 'POST':
if 'file' in request.files:
file = request.files['file']
save_img(file)
db.execute(
"UPDATE installs SET stragebin = ? WHERE id = ?",
(file.filename, install_id)
)
db.commit()
return redirect(url_for('installs.show', install_id = install_id))
install = db.execute('SELECT * FROM installs WHERE id = ?', install_id).fetchone()
return render_template('installs/upload.html', install = install)
@bp.route('/installs/write', methods = [ 'GET' ])
def write():
csv_str = ""
db = get_db()
alldata = db.execute('SELECT * FROM installs').fetchall()
for data in alldata:
csv_str += ",".join( [ data['ledgerdno'], data['applicantsname'], data['hopedate'] ] )
csv_str += "\n"
write_file("installs.csv", csv_str)
return render_template('installs/write.html', str = csv_str)
srappli/web/utility.py
utility.py
import re
import smtplib
# from flask_mail import Mail, Message
'''
class EmailUtil:
@staticmethod
def sendEmail():
app.config.update(dict(
MAIL_SERVER = 'smtp.ogis-ri.co.jp',
MAIL_PORT = 465,
MAIL_USE_TLS = False,
MAIL_USE_SSL = True,
MAIL_USERNAME = 'UserName',
MAIL_PASSWORD = 'password'
))
mail = Mail(app)
msg = Message('Test', sender='Mail-Addrss@hogehoge.co.jp', recipients=['foo_bakako@hogehoge.co.jp'])
msg.body = 'This is a test email'
mail.send(msg)
return 'done'
'''
class ValideteUtil:
@staticmethod
def isInteger(value):
"""
整数チェック
:param value: チェック対象の文字列
:rtype: チェック対象文字列が、全て数値の場合 True
"""
return re.match(r"^\d+$", value) is not None
@staticmethod
def isDecimal(value):
'''
小数チェック
:param value: チェック対象の文字列
:rtype: チェック対象文字列が、整数または小数の場合 True True
'''
return re.match(r"^[+-]?[0-9]*[.]?[0-9]+$", value) is not None
@staticmethod
def isAlpha(value):
"""
半角英字チェック
:param value: チェック対象の文字列
:rtype: チェック対象文字列が、全て半角英字の場合 True
"""
return re.match(r"^[a-z]+$", value) is not None
@staticmethod
def isAlphaNumeric(value):
"""
半角英数字チェック
:param value: チェック対象の文字列
:rtype: チェック対象文字列が、全て半角英数字の場合 True
"""
return re.match(r"^\w+$", value) is not None
@staticmethod
def isHarf(value):
"""
半角文字チェック
:param value: チェック対象の文字列
:rtype: チェック対象文字列が、全て半角文字の場合 True (半角カナは含まない)
"""
return re.match(r"^[\x20-\x7E]+$", value) is not None
@staticmethod
def isHarfKana(value):
"""
半角カナチェック
:param value: チェック対象の文字列
:rtype: チェック対象文字列が、全て半角カナの場合 True
"""
return re.match(r"^[ヲ-゚]+$", value) is not None
@staticmethod
def isFull(value):
"""
全角文字チェック
:param value: チェック対象の文字列
:rtype: チェック対象文字列が、全て全角文字の場合 True
"""
return re.match(r"^[^\x01-\x7E]+$", value) is not None
srappli/web/departments.py
departments.py
from flask import(
Blueprint, render_template,
request, redirect, url_for
)
from web.installdb import get_db
from web.files import save_csv, read_departments_csv
bp = Blueprint('departments', __name__)
@bp.route('/departments', methods = ['GET'])
def all():
installdb = get_db()
alldata = installdb.execute('SELECT * FROM departments').fetchall()
return render_template('departments/all.html', departments = alldata)
@bp.route('/departments/upload', methods = ['GET', 'POST'])
def upload():
if request.method == 'POST':
if 'file' in request.files:
file = request.files['file']
save_csv(file)
datadict = read_departments_csv(file.filename)
installdb = get_db()
installdb.execute('DELETE FROM departments')
for data in datadict:
installdb.execute(
"INSERT INTO departments (id, abbreviation) VALUES (?, ?)",
(data['id'], data['abbreviation'])
)
installdb.commit()
return redirect(url_for('departments.all'))
return render_template('departments/upload.html')
srappli/web/init.py
__init__.py
from flask import Flask
import os
def create_app():
app = Flask(__name__)
from . import installs
app.register_blueprint(installs.bp)
from . import stragebins
app.register_blueprint(stragebins.bp)
from . import departments
app.register_blueprint(departments.bp)
from . import employees
app.register_blueprint(employees.bp)
app.config.from_mapping(
SELECT_KEY = 'temp',
INSTALLDB = os.path.join(app.instance_path, 'installdb.sqlite3'),
)
from . import installdb
installdb.init_app(app)
UPLOAD_FOLDER = 'C:/Dev/flask/srappli-flask/web/static'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
return app
srappli-flask/web/staticフォルダ
calendar.js を利用させていただいたサイトさまはこちら
calendar.js
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 【 カレンダー選択入力 】 http://www.cman.jp/
// 商用,改変,再配布はすべて自由ですですが、動作保証はありません
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// maintenance history
// Ver Date contents
// 0.9 2016/6/20 New
// 0.91 2016/6/21 カレンダ表示位置修理
// 0.92 2017/4/28 ユーザ様ご指摘による不具合修正
// 0.93 2018/9/28 ユーザ様ご指摘による不具合修正
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//使用方法
// https://web-designer.cman.jp/javascript_ref/keybord/calendar/
//【注意】
//引数やユーザ設定内容についてはノーチェックです
//解析しやすいようにコメントを多く入れています。
//JavaScriptのファイルサイズを削減する場合は、コメントやスペースを消してください。
//再配布する場合は、当サイトのJavaScriptが元であることを記載ください。
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var cmanCLD_VAR = {};
// ----- 初期イベントの登録 ------------------------------------------------
if ( window.addEventListener ){window.addEventListener("load", cmanCLD_JS_init, false);}
else if( window.attachEvent ) {window.attachEvent( 'onload', cmanCLD_JS_init );}
//======================================================================
// 初期処理(オンロード時)
//======================================================================
function cmanCLD_JS_init(){
var wID = '';
var wObjAt;
// ----- 対象タグの一覧を配列に格納 ----------------------------------
var wTagList = document.getElementsByTagName('input');
// ----- input type="date","text" を対象にイベント登録する -----------
for(var i = 0; i < wTagList.length; i++){
// ----- "date"と"text"だけを対象とする ---------------------
if((wTagList[i].type.toLowerCase() != "date")&&(wTagList[i].type.toLowerCase() != "text")){
continue; // 次の要素へ
}
// ----- 属性"cmanCLDat"を取得 ---------------------------
wObjAt = wTagList[i].getAttribute("cmanCLDat");
if(wObjAt === null){
wObjAt = '';
}
// ----- "text"の場合は属性"cmanCLDat"が実在しない場合は対象外
if(wObjAt ==''){
continue; // 次の要素へ
}
// ----- 属性で「OFF」(未使用)が定義されている場合は対象外 --------
wObjAt = wObjAt.replace(/\s+/g, "");
if(wObjAt.match(/USE:OFF/i)){
continue; // 次の要素へ
}
// ----- "date"でブラウザが"date"に対応している場合は動作させない -
// (二重にパレットが出る)
if(wTagList[i].type.toLowerCase() == "date"){
// ブラウザがdateに対応しているかチェック
var wObjTest = document.createElement("input");
wObjTest.setAttribute("type", "date");
if(wObjTest.type.toLowerCase() == "date"){
continue; // 次の要素へ
}
}
// ----- 対象の要素にカラーパレットを開くイベント(KeyDown時)を登録 --
wTagList[i].onkeydown = cmanCLD_JS_open;
wTagList[i].onclick = cmanCLD_JS_open;
}
// ----- カレンダーを閉じるためのイベント登録 ----------------------------
document.body.onclick = cmanCLD_JS_bodyClick;
window.onscroll = cmanCLD_JS_cl;
}
//======================================================================
// カレンダー以外がクリックされた場合は、カレンダを閉じる
//======================================================================
function cmanCLD_JS_bodyClick(e){
var wObjClick = e.target;
// ----- カレンダーが開いていない場合は対象外 --------------------------
if(document.getElementById(cmanCLD_VAR["popId"])){
}else{
return;
}
// ----- クリックされた要素の親要素に「cmanCLD」が実在する場合は対象外 ------
var wObjAt = wObjClick.getAttribute("cmanCLDat");
if((wObjAt === null)||(wObjAt == '')){
}else{
return;
}
// ----- クリックされた要素の親要素に「cmanCLD」が実在する場合は対象外 -------
if(wObjClick.id.match(/^cmanCLD/)){
return;
}
// ----- クリックされた要素の親要素に「cmanCLD」が実在する場合は対象外 -------
var wOyaObj = wObjClick;
for(var j = 0; j < 10; j++){
wOyaObj = wOyaObj.parentNode;
if((typeof wOyaObj === 'object')&&(wOyaObj.tagName.toLowerCase() != 'html')){
if(wOyaObj.id.match(/^cmanCLD/)){
return;
}
}else{
break;
}
}
// ----- カレンダーが開いている場合は閉じる --------------------------
cmanCLD_JS_cl();
}
//======================================================================
// カレンダーを開く
//======================================================================
function cmanCLD_JS_open(e){
// ----- カレンダーが開いている場合は閉じる -----------------------------
cmanCLD_JS_cl();
// ----- 対象外キーは処理を抜ける -----------------------------------
switch (e.keyCode){
case 9 : // tab
case 16 : // shift
return;
break;
}
// ----- 日付入力エリア --------------------------------------------
cmanCLD_VAR["objValue"] = e.target;
// ----- 選択済み日付をハッシュ変数に保存 -----------------------------
cmanCLD_VAR["selectedDate"] = '';
if(cmanCLD_JS_DateChk(cmanCLD_VAR["objValue"].value)){
var wSplit = cmanCLD_VAR["objValue"].value.split("-");
if(wSplit.length == 3){
cmanCLD_VAR["selectedDate"] = cmanCLD_JS_dateEdit(wSplit[0], wSplit[1], wSplit[2]);
}
}
// ----- カレンダ編集&表示 -----------------------------------------
if(cmanCLD_VAR["selectedDate"] == ''){
cmanCLD_JS_create('','');
}
else{
cmanCLD_JS_create( new Date(cmanCLD_VAR["selectedDate"]).getFullYear(), new Date(cmanCLD_VAR["selectedDate"]).getMonth() + 1);
}
// ----- 押されたキーはクリアする -------------------------------------
e.keyCode = 0;
return false;
}
//======================================================================
// 表示年月変更
//======================================================================
function cmanCLD_JS_res(argYM, argUD){
var wNextOutYMD;
wNextOutYMD = new Date( cmanCLD_VAR["outYYYY"], parseInt(cmanCLD_VAR["outMM"]) - 1, 1);
// ----- 次に表示する年月を求める ------------------------------------
if(('outYYYY' in cmanCLD_VAR)&&('outMM' in cmanCLD_VAR)){
if(argYM == 'y'){
if(argUD == 'u'){ wNextOutYMD.setYear(wNextOutYMD.getFullYear() + 1);
}else{ wNextOutYMD.setYear(wNextOutYMD.getFullYear() - 1);
}
}else{
if(argUD == 'u'){ wNextOutYMD.setMonth(wNextOutYMD.getMonth() + 1);
}else{
wNextOutYMD.setMonth(wNextOutYMD.getMonth() - 1);
}
}
}else{
wNextOutYMD = new Date();
}
// ----- カレンダ編集&表示 -----------------------------------------
cmanCLD_JS_edit( wNextOutYMD.getFullYear(), wNextOutYMD.getMonth() + 1);
}
//======================================================================
// カレンダーの編集
//======================================================================
function cmanCLD_JS_edit(argYYYY, argMM){
var wObjDate_FirstDay; // 月初
var wObjDate_LastDay; // 月末
var wStr_FirstDay_Week = 0; // 月初の曜日
var wStr_LastDay_Week = 0; // 月末の曜日
var wObjDate_LastMonth; // 前月
var wObjDate_NextMonth; // 翌月
var wObjDate_JpFirstDay; // 元号判定用の元号別月初
var wOutY_JpName = ''; // 元号
var wOutY_JpYY = ''; // 和暦年
var wDayList = []; // カレンダーに表示する日付
var wDayListIdx = 0;
var wDayListTargetIdxF = 0;
var wDayListTargetIdxT = 0;
var wStrNowDate = 0; // 現在日付(表示のカレンダの月と違う場合は0)
var wLang = "";
// ----- 曜日表示テーブル -----------------------------------------
var wWeekJA = [ '日', '月', '火', '水', '木', '金', '土' ];
var wWeekEN = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ];
// ----- 和暦の表示テーブル(元号変更時は追加が必要) --------------------
var wG_YMS = [ '1868-01-25', '1912-07-30', '1926-12-25', '1989-01-08' , '2019-05-01' ];
var wG_STR = [ '明治', '大正', '昭和', '平成', '令和' ];
// ----- カレンダー枠のID設定 --------------------------------------
cmanCLD_VAR["popId"] = 'cmanCLD_POP'; // POP枠のID
// ----- 属性"cmanCLDat"より言語を取得 -----------------------------
var wObjAt = cmanCLD_VAR["objValue"].getAttribute("cmanCLDat").replace(/\s+/g, "");
if(wObjAt.match(/LANG:EN/i)) {wLang = 'EN';}
else {wLang = 'JA';}
// ----- 表示する日付を設定 ----------------------------------------
if((argYYYY == '')||(argMM=='')){
wObjDate_FirstDay = new Date(new Date().getFullYear(), new Date().getMonth() , 1); // 当日の月初
}else{
wObjDate_FirstDay = new Date(argYYYY, argMM - 1, 1);
}
// 明治以前は表示しない
if(wObjDate_FirstDay.getTime() < new Date(wG_YMS[0]).getTime()){
wObjDate_FirstDay = new Date(wG_YMS[0]);
}
// ----- 月末を調べる ---------------------------------------------
wObjDate_LastDay = new Date(wObjDate_FirstDay.getFullYear(), wObjDate_FirstDay.getMonth() + 1, 0);
// ----- 表示する和暦年・元号を設定 ----------------------------------
for (var i = wG_YMS.length - 1; i >=0; i--){
var wSplit = wG_YMS[i].split("-");
wObjDate_JpFirstDay = new Date(wSplit[0], wSplit[1] - 1, 1); // Ver0.92 Insert
if(wObjDate_FirstDay.getTime() >= wObjDate_JpFirstDay.getTime()){
wOutY_JpName = wG_STR[i];
wOutY_JpYY = wObjDate_FirstDay.getFullYear() - wObjDate_JpFirstDay.getFullYear() + 1;
if(wOutY_JpYY == 1){wOutY_JpYY = '元';}
break;
}
}
// ----- 当日がカレンダに実在するか確認する ----------------------------
wStrNowDate = cmanCLD_JS_dateEdit( new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate());
// ----- 表示カレンダー設定(上部の年月) ------------------------------
document.getElementById('cmanCLD_ID_outy').innerHTML = wObjDate_FirstDay.getFullYear() + '年';
if(wLang == 'JA'){
document.getElementById('cmanCLD_ID_outy').innerHTML += '<div style="font-size:80%;font-weight: normal;padding-top:1px;text-align: center;">' + wOutY_JpName + wOutY_JpYY + '年</div>';
}
document.getElementById('cmanCLD_ID_outm').innerText = (wObjDate_FirstDay.getMonth() + 1) + '月';
// ----- 月初・月末の曜日を調べる ------------------------------------
wStr_FirstDay_Week = wObjDate_FirstDay.getDay(); // 0:日曜
wStr_LastDay_Week = wObjDate_LastDay.getDay(); // 0:日曜
// ----- 前月末日を調べる ------------------------------------------
wObjDate_LastMonth = new Date(wObjDate_FirstDay.getFullYear(), wObjDate_FirstDay.getMonth(), 0);
wObjDate_NextMonth = new Date(wObjDate_FirstDay.getFullYear(), wObjDate_FirstDay.getMonth() + 1, 1); // Ver0.93 Update
// ----- 1行目の前月最終分を配列に格納 -------------------------------
var wLastMonthDay = wObjDate_LastMonth.getDate() - wStr_FirstDay_Week + 1;
if(wStr_FirstDay_Week != 0){
for ( var j = 0; j < wStr_FirstDay_Week; j++){
wDayList[wDayListIdx++] = cmanCLD_JS_dateEdit(wObjDate_LastMonth.getFullYear(), wObjDate_LastMonth.getMonth() + 1, wLastMonthDay++);
}
}
// ----- 当月分を配列に格納 ----------------------------------------
wDayListTargetIdxF = wDayListIdx;
for ( var j = 1; j <= wObjDate_LastDay.getDate(); j++){
wDayList[wDayListIdx++] = cmanCLD_JS_dateEdit(wObjDate_FirstDay.getFullYear(), wObjDate_FirstDay.getMonth() + 1, j);
}
wDayListTargetIdxT = wDayListIdx - 1;
// ----- 最終行目の翌月最終分を配列に格納 ----------------------------
var wNextMonthDay = 1;
if(wStr_LastDay_Week != 6){
for ( var j = wStr_LastDay_Week; j < 6; j++){
wDayList[wDayListIdx++] = cmanCLD_JS_dateEdit(wObjDate_NextMonth.getFullYear(), wObjDate_NextMonth.getMonth() + 1, wNextMonthDay++); // Ver0.93 Update
}
}
// ----- カレンダー部分の編集 ---------------------------------------
var wCldTbl = '';
wCldTbl += '<table id="cmanCLD_ID_tbl1">';
// ----- カレンダー部分(曜日)の編集 ----------------------------------
var wWeekTbl;
if(wLang == 'JA'){wWeekTbl = wWeekJA.concat();}
else {wWeekTbl = wWeekEN.concat();}
wCldTbl += '<tr><th style="color: #ff3333;">'+wWeekTbl[0]+'< h><th>'+wWeekTbl[1]+'< h><th>'+wWeekTbl[2]+'< h><th>'+wWeekTbl[3]+'< h><th>'+wWeekTbl[4]+'< h><th>'+wWeekTbl[5]+'< h><th style="color: #0033ff;">'+wWeekTbl[6]+'< h>< r>';
// ----- カレンダー部分(日付)の編集 ----------------------------------
for (var i = 0; i < wDayList.length; i++){
if((i % 7) == 0){
wCldTbl += '<tr>';
}
if (i < wDayListTargetIdxF){
wCldTbl += '<td class="cmanCLD_CSS_StrOff" id="cmanCLD_ID_col' + i + '" onclick="cmanCLD_JS_sel(\'' + wDayList[i] + '\')">' + new Date(wDayList[i]).getDate() + '< d>';
}
else if (i > wDayListTargetIdxT){
wCldTbl += '<td class="cmanCLD_CSS_StrOff" id="cmanCLD_ID_col' + i + '" onclick="cmanCLD_JS_sel(\'' + wDayList[i] + '\')">' + new Date(wDayList[i]).getDate() + '< d>';
}
else{
wCldTbl += '<td class="cmanCLD_CSS_StrOn" id="cmanCLD_ID_col' + i + '" onclick="cmanCLD_JS_sel(\'' + wDayList[i] + '\')"';
var wStyle = '';
if(cmanCLD_VAR["selectedDate"] == wDayList[i]){
wStyle += 'background-color:#ffcccc;';
}
if(wStrNowDate == wDayList[i]){
wStyle += 'border:1px solid #0099ff;';
}
if(wStyle !=''){
wCldTbl += ' style="'+wStyle+'"';
}
wCldTbl += '>' + new Date(wDayList[i]).getDate() + '< d>';
}
if((i % 7) == 6){
wCldTbl += '< r>';
}
}
wCldTbl += '< able>';
document.getElementById('cmanCLD_ID_dateList').innerHTML = wCldTbl;
// ----- カレンダの表示 --------------------------------------------
cmanCLD_VAR["objPop"].style.display = '';
cmanCLD_VAR["outYYYY"] = wObjDate_FirstDay.getFullYear();
cmanCLD_VAR["outMM"] = wObjDate_FirstDay.getMonth() + 1;
}
//======================================================================
// カレンダーの雛形枠作成
//======================================================================
function cmanCLD_JS_create(argYYYY, argMM){
cmanCLD_VAR["popId"] = 'cmanCLD_POP'; // POP枠のID
if(document.getElementById(cmanCLD_VAR["popId"])){return;}
// ----- CSS定義 ------------------------------------------------
var wCss = '<style type="text/css">';
wCss += '#cmanCLD_ID_area {' +
'width: 100%;' +
'max-width:260px;' +
'border: 1px solid #000;' +
'font-size: 9pt;' +
'background-color: #fff;' +
'line-height: 1.1em;' +
'letter-spacing: 1px;' +
'}';
wCss += '#cmanCLD_ID_tbl1 {' +
'width: 100%;' +
'text-align: center;' +
'border-collapse:collapse;' +
'border: 1px solid #fff;' +
'font-size: 10pt;' +
'color: #333;' +
'}';
wCss += '#cmanCLD_ID_tbl1 th {' +
'background-color: #eee;' +
'padding: 3px;' +
'border-top: 1px solid #666;' +
'border-bottom: 1px solid #ccc;' +
'font-weight: normal;' +
'}';
wCss += '#cmanCLD_ID_tbl1 td {' +
'padding: 3px;' +
'cursor: pointer;' +
'font-weight: normal;' +
'}';
wCss += '#cmanCLD_ID_tbl1 td:hover {' +
'background-color: #ddeeff;' +
'}';
wCss += '#cmanCLD_ID_ym {' +
'width: 100%;' +
'text-align: center;' +
'border-collapse:collapse;' +
'border: 1px solid #fff;' +
'font-size: 10pt;' +
'color: #333;' +
'}';
wCss += '.cmanCLD_CSS_btm {' +
'margin: 5px;' +
'width: 22px;' +
'height: 22px;' +
'cursor: pointer;' +
'border: 1px solid #fff;' +
'border-radius: 15%;' +
'background-color: #fff;' +
'}';
wCss += '.cmanCLD_CSS_btm:hover {' +
'border: 1px solid #ccc;' +
'background-color: #eee;' +
'box-shadow: none;' +
'}';
wCss += '.cmanCLD_CSS_bty {' +
'margin: 2px auto 2px 0;' +
'width: 22px;' +
'height: 11px;' +
'cursor: pointer;' +
'border: 1px solid #fff;' +
'border-radius: 15%;' +
'background-color: #fff;' +
'}';
wCss += '.cmanCLD_CSS_bty:hover {' +
'border: 1px solid #ccc;' +
'background-color: #eee;' +
'box-shadow: none;' +
'}';
wCss += '.cmanCLD_CSS_mstr{' +
'font-size: 120%;' +
'font-weight :bold;' +
'}';
wCss += '.cmanCLD_CSS_StrOn{' +
'color: #333;' +
'}';
wCss += '.cmanCLD_CSS_StrOff{' +
'color: #999;' +
'}';
wCss += '</style>';
// ----- HTML定義 -----------------------------------------------
var wHtml = '';
// ----- カレンダー枠 ---------------------------------------------
wHtml +='<div id="cmanCLD_ID_area">';
// ----- 上部年月 ------------------------------------------------
wHtml += '<div>' +
'<table id="cmanCLD_ID_ym">' +
'<colgroup span="1" style="width:10%">' +
'<colgroup span="1" style="width:30%">' +
'<colgroup span="2" style="width:10%">' +
'<colgroup span="1" style="width:20%">' +
'<colgroup span="2" style="width:10%">' +
'<tr>' +
'<td>' +
'<div class="cmanCLD_CSS_btm" onclick="cmanCLD_JS_res(\'y\',\'d\')">' +
'<svg width="20px" height="20px" viewBox="0 0 22 22">' +
'<polygon points="14,4 14,18 7,11" style="fill:#999;stroke:none;stroke-width:1">' +
'</svg>' +
'</div>' +
'< d>' +
'<td>' +
'<div class="cmanCLD_CSS_mstr" style="text-align: center;" id="cmanCLD_ID_outy">YYYY年</div>' +
'< d>' +
'<td>' +
'<div class="cmanCLD_CSS_btm" onclick="cmanCLD_JS_res(\'y\',\'u\')">' +
'<svg width="20px" height="20px" viewBox="0 0 22 22">' +
'<polygon points="9,4 9,18 16,11" style="fill:#999;stroke:none;stroke-width:1">' +
'</svg>' +
'</div>' +
'< d>' +
'<td>' +
'<div class="cmanCLD_CSS_btm" style="margin-left:auto;margin-right:2px;" onclick="cmanCLD_JS_res(\'m\',\'d\')">' +
'<svg width="20px" height="20px" viewBox="0 0 22 22">' +
'<polygon points="14,4 14,18 7,11" style="fill:#999;stroke:none;stroke-width:1">' +
'</svg>' +
'</div>' +
'< d>' +
'<td>' +
'<div class="cmanCLD_CSS_mstr" style="text-align: center;" id="cmanCLD_ID_outm">MM月</div>' +
'< d>' +
'<td>' +
'<div class="cmanCLD_CSS_btm" style="margin-left:2px;margin-right:auto;" onclick="cmanCLD_JS_res(\'m\',\'u\')">' +
'<svg width="20px" height="20px" viewBox="0 0 22 22">' +
'<polygon points="9,4 9,18 16,11" style="fill:#999;stroke:none;stroke-width:1">' +
'</svg>' +
'</div>' +
'< d>' +
'<td>' +
'<div class="cmanCLD_CSS_btm" style="margin-left:auto;margin-right:2px;" onclick="cmanCLD_JS_cl()">' +
'<svg width="20px" height="20px" viewBox="0 0 22 22">' +
'<path stroke="#999" stroke-width="2" fill="none" d="M 7 7 L 16 16 M 7 16 L 16 7">' +
'</svg>' +
'</div>' +
'< d>' +
'< r>' +
'< able>' +
'</div>';
// ----- 日付 ---------------------------------------------------
wHtml += '<div id="cmanCLD_ID_dateList">' +
'</div>';
wHtml += '<div style="margin:3px 0 0 auto;text-align: center;font-size: 8pt;padding: 2px 5px;color:#999;background-color:#f3f3f3">' +
'calendar : ' +
'<a href="http://web-designer.cman.jp/javascript_ref/" target="_blank" style="color:#999;font-weight:normal;">web-designer.cman.jp</a>' +
'</div>';
wHtml +='</div>';
// ----- 表示枠の作成&割り当て(非表示で割り当て) -----------------------
var wEle = document.createElement("div"); // 新規に要素(タグ)を生成
wEle.id = cmanCLD_VAR["popId"];
wEle.style.display = "none";
wEle.style.position = 'fixed';
document.body.appendChild(wEle); // このページ (document.body) の最後に生成した要素を追加
cmanCLD_VAR["objPop"] = wEle;
cmanCLD_VAR["objPop"].innerHTML = wCss + "\n" + wHtml;
// ----- カレンダー内容を編集 ---------------------------------------
cmanCLD_JS_edit(argYYYY, argMM);
// ----- 入力要素の位置・サイズ取得 ----------------------------------
var wObjValue = cmanCLD_VAR["objValue"].getBoundingClientRect();
var wValueX = wObjValue.left;
var wValueY = wObjValue.top;
var wValueH = wObjValue.height;
// ----- カレンダーの位置を一旦入力エリアの下に割り当て -------------------
cmanCLD_VAR["objPop"].style.left = wValueX + 'px';
cmanCLD_VAR["objPop"].style.top = ( wValueY + wValueH ) + 'px';
// ----- カレンダーを100%透過(見えない状態で)非表示解除 -----------------
cmanCLD_VAR["objPop"].style.opacity = 0;
cmanCLD_VAR["objPop"].style.display = '';
// ----- カレンダーの縦サイズを取得 ----------------------------------
var wObjPop = cmanCLD_VAR["objPop"].getBoundingClientRect();
var wPopH = wObjPop.height;
// ----- カレンダーの表示位置設定 -----------------------------------
// 表示可能域を比較し、優先順位:下>上で設定
// 両方とも満たさない場合は下に表示
var wH = document.documentElement.clientHeight - (wValueY + wValueH);
if(wH > wPopH){
cmanCLD_VAR["objPop"].style.top = (wValueY + wValueH)+'px';
}
else if (wValueY > wPopH){
cmanCLD_VAR["objPop"].style.top = (wValueY - wPopH )+'px';
}
else{
cmanCLD_VAR["objPop"].style.top = (wValueY + wValueH)+'px';
}
// ----- カレンダーの透過を解除して表示 -------------------------------
cmanCLD_VAR["objPop"].style.opacity = 1;
}
// =========================================================================================
// 日付が選択されたら
// =========================================================================================
function cmanCLD_JS_sel(argYMD){
// ----- 選択した日付を設定する -------------------------------------
cmanCLD_VAR["objValue"].value = argYMD;
cmanCLD_VAR["selDate"] = argYMD;
// ----- カレンダーを閉じる -----------------------------------------
cmanCLD_JS_cl();
}
//======================================================================
// 閉じるボタンが押されたら
//======================================================================
function cmanCLD_JS_cl(){
// ----- カレンダーが開いていない場合はreturn -------------------------
if(document.getElementById(cmanCLD_VAR["popId"])){
}else{
return;
}
// ----- 指定の関数を実行 -----------------------------------------
var wObjAt = cmanCLD_VAR["objValue"].getAttribute("cmanCLDat").replace(/\s+/g, "").split(",");
for( var i=0; i<wObjAt.length; i++){
if(wObjAt[i].substr(0,5).toUpperCase() == "FUNC:"){
if('selDate' in cmanCLD_VAR){
}else{
cmanCLD_VAR["selDate"] = '';
}
try{
eval(wObjAt[i].substr(5)+'("'+cmanCLD_VAR["selDate"]+'")');
}
catch(e){
alert('関数('+wObjAt[i].substr(5)+')を実行できません');
}
}
}
// ----- 登録したカレンダー要素を消す ---------------------------------
var wDelElm = document.getElementById(cmanCLD_VAR["popId"]);
document.body.removeChild(wDelElm);
// ----- WKハッシュクリア -------------------------------------------
for(var key in cmanCLD_VAR){
delete cmanCLD_VAR[key];
}
}
//======================================================================
// 日付チェック
//======================================================================
function cmanCLD_JS_DateChk(argDate) {
var wY = 0;
var wM = 0;
var wD = 0;
if(argDate==''){return true;}
var wYMD = argDate.split("-");
if(wYMD.length != 3){
return false;
}
if(wYMD[0].toString().match(/^[0-9]+$/)){wY = parseInt(wYMD[0]);}
if(wYMD[1].toString().match(/^[0-9]+$/)){wM = parseInt(wYMD[1]);}
if(wYMD[2].toString().match(/^[0-9]+$/)){wD = parseInt(wYMD[2]);}
if((wY < 1900)||(wM < 1)||(wM > 12)||(wD < 1)||(wD > 31)){
return false;
}
var wDate = new Date(wY, wM - 1, wD, 0, 0, 0, 0);
if((wDate.getFullYear() != wY)||(wDate.getMonth() != wM - 1)||(wDate.getDate() != wD)){
return false;
}
return true;
}
//======================================================================
// 日付編集
//======================================================================
function cmanCLD_JS_dateEdit(argYear, argMonth, argDate){
if(argYear == ''){
return '';
}
var wYYYY = argYear;
var wMM = ("0" + argMonth).slice(-2);
var wDD = ("0" + argDate).slice(-2);
var wObjAt = cmanCLD_VAR["objValue"].getAttribute("cmanCLDat").replace(/\s+/g, "");
if (wObjAt.match(/FORM:2/i)){return wYYYY + '' + wMM + '' + wDD;}
else if (wObjAt.match(/FORM:3/i)){return wYYYY + '/' + wMM + '/' + wDD;}
else {return wYYYY + '-' + wMM + '-' + wDD;}
}
utility.js
function confirm_delete_appli( id ){
if( window.confirm("申請を削除しますがよろしいですか?")){
location.href = "/delete/" + id;
}
}
function getToday(){
let today = new Date();
return(
today.getFullYear() + '-'
+ ('0' + (today.getMonth() + 1 ) ).slice( -2 ) + '-'
+ ('0' + today.getDate() ).slice( -2 )
);
}
function numeric_check(itemobject){
var flag = 0;
if(itemobject.value.match(/[^0-9]+/)){
flag = 1;
}
if(flag){
window.alert('数字以外が入力されています');
return false;
}
else{
return true;
}
}
style.css
body { margin: 0px; }
h1 { width:800px; background: slategray; color: snow; padding: 2px; }
h2 { margin-left:0px; padding-left:5px; width:780px; background: rgb(219, 219, 219); border-bottom: 1px solid dimgray; }
h3 { margin-left:20px; padding-left:5px; width:680px; background: rgb(239, 236, 239); border-top: 1px solid dimgray; }
a { color: #000; text-decoration: none; background: #ccc; }
th, td { border-collapse: collapse; border-bottom:1px solid dimgray; padding:2px; }
input.submit { width: 180px; font-weight: bold;}
button.menu { width: 250px; height:40px; background: seashell; font-size: 20px; }
.required { background: lemonchiffon; }
div.footer { width: 800px; margin-top: 50px; border-top: dimgray 1px solid; border-bottom: dimgray 1px solid;font-size: 12px; background: rgb(187, 186, 186); color: white; }
.caution { color: red;}
.required { background: lemonchiffon; color:black;}
.new_author { position: relative; left: 200px; }
.author,span { background:dimgray;color: white; }
.backgainsboro { background: gainsboro; }
.backsilver { background: silver; }
.backgray { background: gray; }
.rightalign { text-align: right; }
.centeralign { text-align: center; }
.leftalign { text-align: left; }
.bottomnone { border-bottom:none; }
.width200{ width: 200px;}.width300{width: 300px; }.width500{width: 500px; }
.padding5{padding: 5px;}
.marginleft200 { margin-left: 200px; }
# applicantsdiv { width: 300px; }
srappli-flask/web/templatesフォルダ
srappli-flask/web/templates/base.html
base.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta charset="utf-8" />
<title>{% block title %}{% endblock %} - 設置申請< itle>
<link rel="stylesheet" href="{{ url_for('static', filename = 'style.css') }}" />
<script src="{{ url_for('static', filename = 'calendar.js') }}" charset="utf-8"></script>
<script src="{{ url_for('static', filename = 'utility.js') }}" charset="utf-8"></script>
<script>
window.onload = function() {
if( document.getElementById('applidate') ){
document.getElementById('applidate').value = getToday();
}
}
</script>
</head>
<body>
{% block content %}{% endblock %}
<div class="footer centeralign">ホゲホゲ本部ファーファー部</div>
</body>
</html>
srappli-flask/web/templates/index.html
index.html
{% extends 'base.html' %}
{% block title %}サーバールーム設置申請システム{% endblock %}
{% block content %}
<h1>サーバールーム設置申請システム</h1>
<h2>申請メニュー</h2>
<div>
<h3>設置</h3>
<a href="{{ url_for('installs.new') }}"><button class="menu">新規設置申請</button></a>
<a href="{{ url_for('installs.all') }}"><button class="menu">設置申請一覧</button></a>
</div>
<h2>管理者メニュー</h2>
<div>
<h3>棚</h3>
<a href="{{ url_for('stragebins.all') }}"><button class="menu">棚番号一覧</button></a>
<a href="{{ url_for('stragebins.new') }}"><button class="menu">新規棚番号</button></a>
</div>
<div>
<h3>所属部署</h3>
<a href="{{ url_for('departments.all') }}"><button class="menu">所属部署一覧</button></a>
<a href="{{ url_for('departments.upload') }}"><button class="menu">所属部署アップロード</button></a>
</div>
<div>
<h3>社員</h3>
<a href="{{ url_for('employees.all') }}"><button class="menu">社員一覧</button></a>
<a href="{{ url_for('employees.upload') }}"><button class="menu">社員アップロード</button></a>
</div>
{% endblock %}
web/templates/installs
web/templates/installs/all.html
all.html
{% extends 'base.html' %}
{% block title %}サーバー{% endblock %}
{% block content %}
<h1>設置申請一覧</h1>
<a href="/"><button>メニューに戻る</button></a>
<a href="{{ url_for('installs.new') }}"><button>新規設置申請</button></a>
<a href="{{ url_for('stragebins.all') }}"><button>棚番一覧</button></a>
<a href="{{ url_for('installs.write') }}"><button>データの書き出し</button></a>
<table>
<thead class="backgainsboro">
<th colspan="2">申請</th><th colspan="3">申請者</th><th colspan="3">コンピュータ</th><th>棚</th>
</thead>
<thead>
<th>ID</th><th>年月日</th><th>社員ID</th><th>所属</th><th>氏名</th><th>台帳No.</th><th>設置希望日</th><th>設置終了予定日</th><th>希望棚番</th>
</thead>
<tbody>
{% for install in installs %}
<tr>
<td>
<a href="{{ url_for( 'installs.show', install_id = install['id'] ) }}"><button>{{ install['id'] }}</button></a>
</td>
<td>{{ install['applidate'] }}</td>
<td>{{ install['applicantsid'] }}</td>
<td>{{ install['applicantsdiv'] }}</td>
<td>{{ install['applicantsname'] }}</td>
<td>{{ install['ledgerno'] }}</td>
<td>{{ install['hopedate'] }}</td>
<td>{{ install['enddate'] }}</td>
<td>{{ install['stragebin'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
web/templates/installs/edit.html
edit.html
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}申請Ωを編集{% endblock %}</h1>
{% endblock %}
{% block content %}
<h2>申請を編集します</h2>
<div>
<a href="/"><button>メニューに戻る</button></a>
<a href="{{ url_for( 'installs.show', install_id = install['id'] ) }}"><button>詳細に戻る</button></a>
<a href="{{ url_for( 'installs.all' ) }}"><button>申請一覧に戻る</button></a>
</div>
<div class="caution">{{ applicantsid_message }}</div>
<div class="caution">{{ ledgerno_message }}</div>
<form method="POST">
<table>
<thead>
<th class="rightalign">分類</th><th class="rightalign">申請項目</th><th class="width300 leftalign">申請入力</th>
</thead>
<tbody>
<tr>
<td class="rightalign backgainsboro">申請</td>
<td class="rightalign">申請日</td>
<td><input name="applidate" id="applidate" size="10" class="backsilver" value="{{ install['applidate'] }}" /></td>
</tr>
<tr>
<td rowspan="3" class="rightalign backgainsboro">申請者</td>
<td class="rightalign bottomnone">社員ID(先頭P無し7桁)</td>
<td class="bottomnone"><input name="applicantsid" id="applicantsid" size="7" class="required" value="{{ install['applicantsid'] }}" /></td>
</tr>
<tr>
<td class="rightalign bottomnone">所属部署</td>
<td class="bottomnone">
<select name="applicantsdiv" id="applicantsdiv">
<option value=""></option>
{% for department in departments %}
{% if (department['id']+'-'+department['abbreviation']) == install['applicantsdiv'] %}
<option value="{{ department['id'] }}-{{ department['abbreviation'] }}" selected>
{{ department['id'] }}-{{ department['abbreviation'] }}
</option>
{% else %}
<option value="{{ department['id'] }}-{{ department['abbreviation'] }}">
{{ department['id'] }}-{{ department['abbreviation'] }}
</option>
{% endif %}
{% endfor %}
</select>
</td>
</tr>
<tr>
<td class="rightalign">氏名</td>
<td><input name="applicantsname" id="applicantsname" value="{{ install['applicantsname'] }}" /></td>
</tr>
<tr>
<td rowspan="3" class="rightalign backgainsboro">コンピューター</td>
<td class="rightalign bottomnone">台帳No.</td>
<td class="bottomnone"><input name="ledgerno" id="ledgerno" size="7" class="required" value="{{ install['ledgerno'] }}" /></td>
</tr>
<tr>
<td class="rightalign bottomnone">設置希望日</td>
<td class="bottomnone"><input name="hopedate" id="hopedate" type="date" cmanCLDat="USE:ON" class="required" value="{{ install['hopedate'] }}" /></td>
</tr>
<tr>
<td class="rightalign">設置終了予定日</td>
<td><input name="enddate" id="enddate" type="date" cmanCLDat="USE:ON" value="{{ install['enddate'] }}" /></td>
</tr>
<tr>
<td class="rightalign backgainsboro">棚</td>
<td class="rightalign">希望棚番</td>
<td>
<select name="stragebin" id="stragebin">
<option value=""></option>
<option value="{{ install['stragebin'] }}" selected>{{ install['stragebin'] }}</option>
{% for stragebin in stragebins %}
<option value="{{ stragebin['cabinetno'] }}-{{ stragebin['columnno'] }}-{{ stragebin['stageno'] }}">
{{ stragebin['cabinetno'] }}-{{ stragebin['columnno'] }}-{{ stragebin['stageno'] }}
</option>
{% endfor %}
</select>
(キャビネットNO.-列No.-段No.)
</td>
</tr>
</tbody>
</table>
<p><input class="submit marginleft200" type="submit" value="編集確定"></p>
</form>
<h3>棚番マップ</h3>
<img src="./../../static/img/staragebins_map.png" style="width:700px;" />
{% endblock %}
web/templates/installs/new.html
new.html
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}新規書籍{% endblock %}</h1>
{% endblock %}
{% block content %}
<h2>サーバールームに新規サーバーの設置を申請します</h2>
<div>
<a href="/"><button>メニューに戻る</button></a>
<a href="{{ url_for( 'installs.all' ) }}"><button>設置申請一覧に戻る</button></a>
</div>
<div class="caution">{{ applicantsid_message }}</div>
<div class="caution">{{ ledgerno_message }}</div>
<form method="POST">
<table>
<thead>
<th class="rightalign">分類</th><th class="rightalign">申請項目</th><th class="width300 leftalign">申請入力</th>
</thead>
<tbody>
<tr>
<td class="rightalign backgainsboro">申請</td>
<td class="rightalign">申請日</td>
<td><input name="applidate" id="applidate" size="10" type="date" cmanCLDat="USE:ON" class="backsilver" /></td>
</tr>
<tr>
<td rowspan="3" class="rightalign backgainsboro">申請者</td>
<td class="rightalign bottomnone">社員ID(先頭P無し7桁)</td>
<td class="bottomnone"><input name="applicantsid" id="applicantsid" size="7" class="required" /></td>
</tr>
<tr>
<td class="rightalign bottomnone">所属部署</td>
<td class="bottomnone">
<select name="applicantsdiv" id="applicantsdiv">
<option value=""></option>
{% for department in departments %}
<option value="{{ department['id'] }}-{{ department['abbreviation'] }}">
{{ department['id'] }}-{{ department['abbreviation'] }}
</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td class="rightalign">氏名</td>
<td><input name="applicantsname" id="applicantsname" /></td>
</tr>
<tr>
<td rowspan="3" class="rightalign backgainsboro">コンピューター</td>
<td class="rightalign bottomnone">台帳No.</td>
<td class="bottomnone"><input name="ledgerno" id="ledgerno" size="7" class="required" /></td>
</tr>
<tr>
<td class="rightalign bottomnone">設置希望日</td>
<td class="bottomnone"><input name="hopedate" id="hopedate" type="date" cmanCLDat="USE:ON" class="required" /></td>
</tr>
<tr>
<td class="rightalign">設置終了予定日</td>
<td><input name="enddate" id="enddate" type="date" cmanCLDat="USE:ON" /></td>
</tr>
<tr>
<td class="rightalign backgainsboro">棚</td>
<td class="rightalign">希望棚番</td>
<td>
<select name="stragebin" id="stragebin" class="required">
<option value=""></option>
{% for stragebin in stragebins %}
<option value="{{ stragebin['cabinetno'] }}-{{ stragebin['columnno'] }}-{{ stragebin['stageno'] }}">
{{ stragebin['cabinetno'] }}-{{ stragebin['columnno'] }}-{{ stragebin['stageno'] }}
</option>
{% endfor %}
</select>
(キャビネットNO.-列No.-段No.)
</td>
</tr>
</tbody>
</table>
※<span class="required">黄色背景色の項目</span>は必須入力です
<p><input class="submit marginleft200" type="submit" value="申請"></p>
</form>
<h3>棚番マップ</h3>
<img src="../static/img/staragebins_map.png" style="width:700px;" />
{% endblock %}
show.html
{% extends 'base.html' %}
{% block title %}設置申請の詳細{% endblock %}
{% block content %}
<h2>申請ID:{{ install['id'] }} 設置申請の詳細</h2>
<div>
<a href="/"><button>メニューに戻る</button></a>
<a href="{{ url_for( 'installs.all' ) }}"><button>設置申請一覧に戻る</button></a>
<a href="{{ url_for( 'installs.edit', install_id = install['id'] ) }}"><button>申請を編集</button></a>
<a href="{{ url_for( 'installs.delete', install_id = install['id'] ) }}"><button>申請を削除</button></a>
</div>
<table>
<thead>
<th class="rightalign">分類< h><th class="rightalign">申請項目< h><th class="width300 leftalign">申請内容< h>
< head>
<tbody>
<tr>
<td class="rightalign backgainsboro">申請< d>
<td class="rightalign">申請日< d>
<td>{{ install['applidate'] }}< d>
< r>
<tr>
<td rowspan="3" class="rightalign backgainsboro">申請者< d>
<td class="rightalign bottomnone">社員ID< d>
<td class="bottomnone">{{ install['applicantsid'] }}< d>
< r>
<tr>
<td class="rightalign bottomnone">所属< d>
<td class="bottomnone">{{ install['applicantsdiv'] }}< d>
< r>
<tr>
<td class="rightalign">氏名< d>
<td>{{ install['applicantsname'] }}< d>
< r>
<tr>
<td rowspan="3" class="rightalign backgainsboro">コンピューター< d>
<td class="rightalign bottomnone">台帳No.< d>
<td class="bottomnone">{{ install['ledgerno'] }}< d>
< r>
<tr>
<td class="rightalign bottomnone">設置希望日< d>
<td class="bottomnone">{{ install['hopedate'] }}< d>
< r>
<tr>
<td class="rightalign">設置終了予定日< d>
<td>{{ install['enddate'] }}< d>
< r>
<tr>
<td class="rightalign backgainsboro">棚< d>
<td class="rightalign">希望棚番< d>
<td>{{ install['stragebin'] }}< d>
< r>
< body>
< able>
{% endblock %}
upload.html
{% extends 'base.html' %}
{% block title %}画像をアップロード{% endblock %}
{% block content %}
<h2> 「{{ install['title'] }}」の画像を指定</h2>
<form method="POST" enctype="multipart/form-data">
<div>
ファイルをアップロードしてください<br/>
<input type="file" name="file" />
</div>
<input type="submit" value="アップロード" />
</form>
<div>
<a href="{{ url_for('installs.show', install_id=install['id']) }}">
アップロードをやめる
</a>
</div>
{% endblock %}
write.html
{% extends 'base.html' %}
{% block title %}データの書き出し{% endblock %}
{% block content %}
<h1>一覧のCSV形式</h1>
<div>
<a href="{{ url_for( 'static' , filename='csv/installs.csv') }}" download="installs.csv">
ダウンロード
</a> |
<a href="{{ url_for( 'installs.all' ) }}">一覧に戻る</a>
</div>
<textarea rows="10" cols="50">{{ str }}< extarea>
{% endblock %}
srappli-flask/web emplates/stragebinsフォルダ
all.html
{% extends 'base.html' %}
{% block title %}棚番号{% endblock %}
{% block content %}
<h1>棚番一覧</h1>
<a href="/"><button>メニューに戻る</button></a>
<a href="{{ url_for('stragebins.new') }}"><button>新規棚番号</button></a>
<a href="{{ url_for('installs.all') }}"><button>設置申請一覧</button></a>
<a href="{{ url_for('stragebins.upload') }}"><button>CSVファイルから棚番号データを追加</button></a>
<table>
<thead>
<th>ID< h><th>棚番号< h><th>キャビネットNo.< h><th>棚段No.< h><th>列No.< h>
< head>
<tbody>
{% for stragebin in stragebins %}
<tr>
<td>{{ stragebin['id'] }}<td>
<a href="{{ url_for( 'stragebins.show', stragebin_id = stragebin['id'] ) }}">
<button>{{ stragebin['cabinetno'] }} - {{ stragebin['binno'] }} - {{ stragebin['seqno'] }}</button>
</a>
< d>
<td class="centeralign">{{ stragebin['cabinetno'] }}< d>
<td class="centeralign">{{ stragebin['binno'] }}< d>
<td class="centeralign">{{ stragebin['seqno'] }}< d>
< r>
{% endfor %}
< body>
< able>
{% endblock %}
edit.html
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}棚番号を編集{% endblock %}</h1>
{% endblock %}
{% block content %}
<h2>ID:{{ stragebin['id'] }} の棚番号を編集します</h2>
<div>
<a href="/"><button>メニューに戻る</button></a>
<a href="{{ url_for( 'stragebins.show', stragebin_id = stragebin['id'] ) }}"><button>詳細に戻る</button></a>
<a href="{{ url_for( 'stragebins.all' ) }}"><button>棚番一覧に戻る</button></a>
</div>
<form method="POST">
<table>
<thead>
<th>項目< h><th>番号< h>
< head>
<tbody>
<tr>
<td class="backgainsboro">キャビネット番号< d>
<td class="centeralign">
<select name="cabinetno" id="cabinetno">
{% for cabinetno in range(1,7) %}
{% if cabinetno == stragebin['cabinetno'] %}
<option value="{{ cabinetno }}" selected>{{ cabinetno }}</option>
{% else %}
<option value="{{ cabinetno }}">{{ cabinetno }}</option>
{% endif %}
{% endfor%}
</select>
< d>
< r>
<tr>
<td class="backgainsboro">段番号< d>
<td class="centeralign">
<select name="binno" id="binno">
{% for binno in range(1,4) %}
{% if binno == stragebin['binno'] %}
<option value="{{ binno }}" selected>{{ binno }}</option>
{% else %}
<option value="{{ binno }}">{{ binno }}</option>
{% endif %}
{% endfor%}
</select>
< d>
< r>
<tr>
<td class="backgainsboro">位置番号< d>
<td class="centeralign">
<select name="seqno" id="seqno">
{% for seqno in range(1,4) %}
{% if seqno == stragebin['seqno'] %}
<option value="{{ seqno }}" selected>{{ seqno }}</option>
{% else %}
<option value="{{ seqno }}">{{ seqno }}</option>
{% endif %}
{% endfor %}
</select>
< d>
< r>
< body>
< able>
<p><input type="submit" value="登録" class="submit"/></p>
</form>
{% endblock %}
new.html
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}新規棚番号{% endblock %}</h1>
{% endblock %}
{% block content %}
<h2>棚番号を追加します</h2>
<div>
<a href="/"><button>メニューに戻る</button></a>
<a href="{{ url_for( 'stragebins.all' ) }}"><button>棚番号一覧に戻る</button></a>
</div>
<form method="POST">
<table>
<thead>
<th>項目< h><th>番号< h>
< head>
<tbody>
<tr>
<td class="backgainsboro">キャビネット番号< d>
<td class="centeralign">
<select name="cabinetno" id="cabinetno">
<option value=""></option>
{% for cabinetno in range(1,7) %}
<option value="{{ cabinetno }}">{{ cabinetno }}</option>
{% endfor%}
</select>
< d>
< r>
<tr>
<td class="backgainsboro">段番号< d>
<td class="centeralign">
<select name="binno" id="binno">
<option value=""></option>
{% for binno in range(1,4) %}
<option value="{{ binno }}">{{ binno }}</option>
{% endfor%}
</select>
< d>
< r>
<tr>
<td class="backgainsboro">位置番号< d>
<td class="centeralign">
<select name="seqno" id="seqno">
<option value=""></option>
{% for seqno in range(1,4) %}
<option value="{{ seqno }}">{{ seqno }}</option>
{% endfor %}
</select>
< d>
< r>
< body>
< able>
<p><input type="submit" value="登録" class="submit" /></p>
</form>
{% endblock %}
show.html
{% extends 'base.html' %}
{% block title %}棚番号詳細{% endblock %}
{% block content %}
<h2>
棚ID:{{ stragebin['id'] }}
( 棚番:
{{ stragebin['cabinetno'] }} - {{ stragebin['binno'] }} - {{ stragebin['seqno'] }}
) 詳細
</h2>
<div>
s <a href="{{ url_for( 'stragebins.edit', stragebin_id = stragebin['id']) }}"><button>編集</button></a>
<a href="{{ url_for( 'stragebins.delete', stragebin_id = stragebin['id'] ) }}"><button>データを削除</button></a>
</div>
<table>
<thead>
<th>項目< h><th>番号< h>
< head>
<tbody>
<tr>
<td class="backgainsboro">ID番号< d>
<td class="centeralign">{{ stragebin['id'] }}< d>
< r>
<tr>
<td class="backgainsboro">キャビネット番号< d>
<td class="centeralign">{{ stragebin['cabinetno'] }}< d>
< r>
<tr>
<td class="backgainsboro">段番号< d>
<td class="centeralign">{{ stragebin['binno'] }}< d>
< r>
<tr>
<td class="backgainsboro">位置番号< d>
<td class="centeralign">{{ stragebin['seqno'] }}< d>
< r>
< body>
< able>
{% endblock %}
upload.html
{% extends 'base.html' %}
{% block title %}棚番号データをアップロード{% endblock %}
{% block content %}
<h2>棚番号データをCSVで読みこませる</h2>
<form method="POST", enctype="multipart/form-data">
<div>ファイルをアップロードしてください</div>
<input type="file" name="file" />
<input type="submit" value="アップロード" />
</form>
<div>
<a href="{{ url_for('stragebins.all') }}">アップロードをやめる</a>
</div>
{% endblock %}
DB作成
bash(macOS,Linux)
$ cd instance
$ python create_installdb.py
$ python insert_installdb.py
$ insert_stragebintable.py
$ cd ..
Webアプリ起動
bash(macOS,Linux)
$ export FLASK_ENV="development"
$ export FLASK_APP="web"
$ flask run --host='0.0.0.0' --port=5000
powershell(WindowsServer2016)
> cd c:\Dev\srappli-flask\
> $env:FLASK_ENV="development"
> $env:FLASK_APP="web"
> flask run --host='0.0.0.0' --port=5000
Webアプリ実行
URL http://SaverName:5000/ にWebブラウザでアクセスする