【SQLAlchemy】計算問題を解いてダイエット 間違えた問題をデータベースに保存する

はじめに

以前計算問題をただひたすら読み上げてくれるプログラムを書きました。
touch-sp.hatenablog.com
今回は間違えた問題をデータベースに保存することにチャレンジします。


Pythonのリスト型に格納してpickleで保存する方法が最も簡易な方法と思います。


しかし今回はデータベースの学習も兼ねて「SQLAlchemy」を使いました。

「SQLAlchemy」の導入

pipで問題なくインストールできました。

pip install sqlalchemy

「SQLAlchemy」の使い方

データベースの作成

以下のスクリプトを「make_database.py」という名前で保存して実行すると「formula」というテーブルを含む「try_again.db」というデータベースが作成されます。

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, Column, Integer

engine = create_engine('sqlite:///try_again.db', echo = False)

Base = declarative_base()

class Multiplication(Base):

    __tablename__ = 'formula'

    id = Column('account_id', Integer, primary_key=True)
    q1 = Column('first_number', Integer)
    q2 = Column('second_number', Integer)

Base.metadata.create_all(bind=engine)

データの挿入

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from make_database import Multiplication

engine = create_engine('sqlite:///try_again.db')

session = sessionmaker(bind=engine)()

session.add(
    Multiplication(
        q1 = 99,
        q2 = 99
    )
)

session.commit()
session.close()

データの一覧表示

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from make_database import Multiplication

engine = create_engine('sqlite:///try_again.db')

session = sessionmaker(bind=engine)()

query_result = session.query(Multiplication)
for formula in query_result:
    print('%d X %d'%(formula.q1, formula.q2))
session.close()

データの削除

テーブル内のデータを全削除するスクリプトです。テーブル自体は削除されません。

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from make_database import Multiplication

engine = create_engine('sqlite:///try_again.db')

session = sessionmaker(bind=engine)()

session.query(Multiplication).delete()
session.commit()
session.close()

計算アプリのスクリプト

キーボードの「M」を押すとデータベースに現在の問題が保存されるようにしました。
間違える度に「M」を押せばどんどんデータが蓄積されます。

import sys
import random

import win32com.client

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QMainWindow, QApplication, QLabel

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from constructGUI import construct
from make_database import Multiplication

min_q = 11
max_q = 22

engine = create_engine('sqlite:///try_again.db')

class Window(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()
        self.speaker = win32com.client.Dispatch('SAPI.SpVoice')
        self.q1 = 0
        self.q2 = 0
        self.answer = 0
        self.playing = False
        self.session = sessionmaker(bind = engine)()
        
    def initUI(self):
        self.setWindowTitle("かけ算")

        self.num_label = construct(QLabel(), "settings.yaml", "label_1")

        self.setCentralWidget(self.num_label)

    def keyPressEvent(self, e):

        if e.key() == Qt.Key.Key_N:
            self.calc_exe()
        
        if e.key() == Qt.Key.Key_Q:
            self.close_Event()

        if e.key() == Qt.Key.Key_M:
            self.session.add(
                Multiplication(q1 = self.q1, q2 = self.q2)
            )
            self.session.commit()

    def calc_exe(self):
        if self.playing == False:
            self.q1 = random.randint(min_q,max_q)
            self.q2 = random.randint(min_q,max_q)
            self.num_label.setText('%d x %d'%(self.q1, self.q2))
            self.answer = self.q1 * self.q2
            self.speaker.Speak('%dかける%d'%(self.q1, self.q2))
            self.playing = not self.playing
        else:
            self.speaker.Speak('%d'%self.answer)
            self.playing = not self.playing
    
    def close_Event(self):
        self.session.close()
        sys.exit()

if __name__ == "__main__":
    app = QApplication([])
    ex =Window()
    ex.show()
    app.exec()

動作環境

Windows 11
Python 3.9.12
greenlet==1.1.2
PyQt6==6.2.3
PyQt6-Qt6==6.2.4
PyQt6-sip==13.2.1
pywin32==303
PyYAML==6.0
SQLAlchemy==1.4.32

さいごに

保存された問題から出題するスクリプトを書けば間違えた問題を何度も繰り返すことができます。

こちらのスクリプトになります。

import sys
import random

import win32com.client

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QMainWindow, QApplication, QLabel

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from constructGUI import construct
from make_database import Multiplication

engine = create_engine('sqlite:///try_again.db')
session = sessionmaker(bind = engine)()
question_list = [(x.q1, x.q2) for x in session.query(Multiplication)]
session.close()

class Window(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()
        self.speaker = win32com.client.Dispatch('SAPI.SpVoice')
        self.q1 = 0
        self.q2 = 0
        self.answer = 0
        self.playing = False
        
    def initUI(self):
        self.setWindowTitle("かけ算")

        self.num_label = construct(QLabel(), "settings.yaml", "label_1")

        self.setCentralWidget(self.num_label)

    def keyPressEvent(self, e):

        if e.key() == Qt.Key.Key_N:
            self.calc_exe()
        
        if e.key() == Qt.Key.Key_Q:
            self.close_Event()

    def calc_exe(self):
        if self.playing == False:
            self.q1, self.q2 = random.choice(question_list)
            self.num_label.setText('%d x %d'%(self.q1, self.q2))
            self.answer = self.q1 * self.q2
            self.speaker.Speak('%dかける%d'%(self.q1, self.q2))
            self.playing = not self.playing
        else:
            self.speaker.Speak('%d'%self.answer)
            self.playing = not self.playing
    
    def close_Event(self):
        sys.exit()

if __name__ == "__main__":
    app = QApplication([])
    ex =Window()
    ex.show()
    app.exec()

反復練習にはうってつけですね(笑)。


記憶を目的としたアプリであれば記憶の定着にかなり効果がありそうです。
touch-sp.hatenablog.com
touch-sp.hatenablog.com