単語カードアプリの作成

Flashcard App

フラッシュカードアプリを作成しました。英単語などを覚えるツールとしてレンタルサーバで動作するWebアプリです。私はiPhoneからアクセスして勉強しています。単語登録はMySQLへ直接登録する必要があります。

動作サンプル

Screen shots

利用方法は橙色の面に表示された問題を見て回答をイメージします。そしてタップすると裏側の緑色の面が表示されるので答え合わせをします。さらにタップすると次の問題が橙色の面に表示されます。設定は左上のメニューをタップして出題の種類を選択します。

screen shot

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;
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です