Flashcard WebAPP
フラッシュカードアプリを作成しました。英単語などを覚えるツールとしてレンタルサーバで動作するWebアプリです。私はiPhoneからアクセスして勉強しています。単語登録はMySQLへ直接登録する必要があります。
動作サンプル
Screen shots
利用方法は橙色の面に表示された問題を見て回答をイメージします。そしてタップすると裏側の緑色の面が表示されるので答え合わせをします。さらにタップすると次の問題が橙色の面に表示されます。設定は左上のメニューをタップして出題の種類を選択します。
Condition
動作環境
項目 | 内容 |
---|---|
サーバ | さくらインターネット |
契約プラン | レンタルサーバ スタンダード |
ライブラリ | jquery |
データベース | MySQL |
インターフェース | cgi |
cgi処理 | python3.7 |
ファイル構成
Filename | 内容 |
---|---|
flashcard.html | メインページ |
flashcard.js | jqueryでユーザーインターフェース処理 |
flashcard.cgi | pythonでデータベース処理 |
flashcard.css | 装飾 |
データベース
データベースのレコード定義はシンプルに下記としました。
CREATE TABLE IF NOT EXISTS `FlashCard` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`side_a` TEXT,
`side_b` TEXT,
`notice` TEXT,
PRIMARY KEY (`id`)
) ;
Source code Sample
下記ソースコードは実際に運用しているものを少々改変しています。
flashcard.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Flashcard</title>
<link rel="stylesheet" href="../js/jquery.mobile-1.4.5.min.css" />
<link rel="stylesheet" href="flashcard.css" />
<script src="../js/jquery-1.12.4.min.js"></script>
<script src="../js/jquery.mobile-1.4.5.min.js"></script>
<script src="flashcard.js"></script>
</head>
<body>
<div data-role="page" role="main" data-title="Flashcard">
<div data-role="panel" id="menu-left" data-theme="b">
<form data-mini="true">
<fieldset data-role="controlgroup">
<input type="checkbox" data-mini="true" name="levels" value="L1" id="L1">
<label for="L1">Level 1</label>
<input type="checkbox" data-mini="true" name="levels" value="L2" id="L2">
<label for="L2">Level 2</label>
</fieldset>
<select data-mini="true" id="select_question">
<option data-mini="true" value="side-a">Question</option>
<option data-mini="true" value="side-b">Answer</option>
</select>
</form>
</div>
<div data-role="header" id="header">
<a href="#menu-left" data-role="button" data-icon="bars" data-iconpos="notext"> </a>
<h1 id="title">Flashcard</h1>
</div>
<div class="ui-content" role="main" id="card">
<div class="side-a" id="question"></div>
<div class="side-b" id="answer"></div>
<div id="temp"></div>
<div id="env"><input type="hidden" name="range" value="L1L2" /></div>
<div id="q_mode"><input type="hidden" name="mode" value="side-a" /></div>
</div>
<div data-role="footer" id="footer">
<h3><small>yam.ktm@gmail.com</small></h3>
</div>
</div>
</body>
</html>
flashcard.js
var QMode;
var RangeCode;
var CardIndex;
var Question;
var Answer;
var SideA;
var SideB;
var Notice;
var QText;
var AText;
var MyStorage = function(app) {
this.app = app;
this.storage = localStorage;
this.data = JSON.parse(this.storage[this.app] || '{}');
};
MyStorage.prototype = {
getItem: function(key) {
return this.data[key];
},
setItem: function(key, value) {
this.data[key] = value;
},
save: function() {
this.storage[this.app] = JSON.stringify(this.data);
}
};
function make_range_code(levels) {
var rcode = "";
if(levels.length == 0) {
rcode += "L1";
} else {
for(var i = 0; i < levels.length; i++) {
rcode += levels[i];
}
}
return rcode;
}
function register_data(data) {
CardIndex = data['index'];
SideA = data['side-a'];
SideB = data['side-b'];
Notice = data['notice'];
}
var Storage = new MyStorage('Flashcard');
$(function() {
$(document).ready(function(){
QMode = Storage.getItem('qmode');
RangeCode = Storage.getItem('rangecode');
if(QMode == null) {
QMode = "side-a";
}
$("select option").prop("selected", false);
$("#select_question").val(QMode).selectmenu("refresh");
if(RangeCode == null) {
RangeCode = "L1";
}
var regex;
regex = /L1/;
if(regex.test(RangeCode)) {
$('input[value="L1"]').prop("checked", true).checkboxradio("refresh");
}
regex = /L2/;
if(regex.test(RangeCode)) {
$('input[value="L2"]').prop("checked", true).checkboxradio("refresh");
}
$.get("flashcard.py", {"range":RangeCode}, function(data) {
register_data(JSON.parse(data));
$("#answer").html("<p class='answer'>Click to start!</p>\n");
});
Storage.setItem('qmode', QMode);
Storage.setItem('rangecode', RangeCode);
Storage.save();
var hsize = $(window).height();
hsize -= 150;
$("#card").css("height", hsize + "px");
$("#question").css("height", "15em");
$("#answer").css("height", "15em");
$("#question").hide();
});
$('input[name="levels"]').change(function() {
var levels = [];
$('input[name="levels"]:checked').each(function() {
levels.push($(this).val());
});
RangeCode = make_range_code(levels);
$.get("flashcard.py", {"range":RangeCode}, function(data) {
register_data(JSON.parse(data));
});
Storage.setItem('qmode', QMode);
Storage.setItem('rangecode', RangeCode);
Storage.save();
});
$("#select_question").change(function() {
QMode = $("option:selected").val();
Storage.setItem('qmode', QMode);
Storage.setItem('rangecode', RangeCode);
Storage.save();
});
$("#answer").click(function() {
switch(QMode) {
case "side-a":
QText = SideA;
AText = SideB;
break;
case "side-b":
QText = SideB;
AText = SideA;
break;
default:
QText = SideA;
AText = SideB;
break;
}
var qes = "<p>" + QText + "</p>\n";
$("#question").html(qes);
$("#answer").hide();
$("#question").show();
});
$("#question").click(function() {
var ans = "\n"
ans += "<p>" + QText + "</p>\n";
ans += "<p class='answer'>" + AText + "</p>\n";
ans += "<p class='phrase'>" + Notice + "</p>\n";
$("#answer").html(ans);
$("#question").hide();
$("#answer").show();
$.get("flashcard.py", {"range":RangeCode}, function(data) {
register_data(JSON.parse(data));
});
});
$(window).resize(function () {
hsize = $(window).height();
$("section").css("height", hsize + "px");
});
});
flashcard.py
#!/usr/bin/env -S LD_LIBRARY_PATH=/home/yourid/.pyenv/shims /home/yourid/.pyenv/shims/python
#-*- coding:utf-8 -*-
"""
CREATE TABLE IF NOT EXISTS `FlashCard` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`side_a` TEXT,
`side_b` TEXT,
`notice` TEXT,
PRIMARY KEY (`id`)
) ;
"""
import os, sys, io
import re
import random
import cgi
import json
#import cgitb
import mysql.connector
from urllib.parse import urlparse
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
#cgitb.enable()
url = urlparse('mysql://yourid:yourpassword@mysqlserver.domain/databasename')
# setting of database
connector = mysql.connector.connect(
host = url.hostname or 'localhost',
port = url.port or 3306,
user = url.username or 'root',
password = url.password or '',
database = url.path[1:],
)
REC_NAME = "FlashCard"
#Create instance of FieldStorage
form = cgi.FieldStorage()
if "range" in form:
rcode = form["range"].value
else:
rcode = "L1"
ids = []
if rcode.find("L1") >= 0:
ids += list(range(1, 7)) # declare data range
if rcode.find("L2") >= 0:
ids += list(range(7, 14)) # declare data range
idx = random.choice(ids)
# Get data from fields
cursor = connector.cursor()
sql = "select * from %s where id=%d" % (REC_NAME, idx)
cursor.execute(sql)
result = cursor.fetchall()
for row in result:
index = int(row[0])
SideA = row[1]
SideB = row[2]
Notice = row[3]
cursor.close()
connector.close()
# SideA = SideA.replace("someword", "somenotice")
data = {
"index":index,
"side-a":SideA,
"side-b":SideB,
"notice":Notice
}
print("Content-Type: text/html")
print()
print(json.dumps(data))
flashcard.css
p {
line-height: 1em;
margin: 0.5em 0.5em;
padding: 0.2em 0.2em;
font-size: medium;
}
p.answer {
font-size: large;
}
.side-a {
background-color: #c85d05;
color: #fff;
margin: 0.5em 1em;
padding: 0.5em 1em;
position: relative;
}
.side-a::after {
content: '';
border-color: #3e8504 #fff #fff #3e8504;
border-style: solid;
border-width: 0 0 24px 24px;
bottom: 0;
position: absolute;
right: 0;
}
.side-b {
background-color: #469704;
color: #fff;
margin: 0.5em 1em;
padding: 0.5em 1em;
position: relative;
}
.side-b::after {
content: '';
border-color: #853e04 #fff #fff #853e04;
border-style: solid;
border-width: 0 0 24px 24px;
bottom: 0;
position: absolute;
right: 0;
}