Skip to content

10. Вправа [IMPOTS] з MySQL

10.1. Перенесення текстового файлу в таблицю MySQL

Наступний скрипт перенесе дані з такого текстового файлу:

12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:272.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062

у таблицю [impots] бази даних MySQL [dbimpots]:

 

Підключення до бази даних [dbimpots] відбуватиметься з використанням облікових даних (root,"").


Програма (impotstxt2mysql)

Ми будемо використовувати таку архітектуру:

Скрипт [console], який ми будемо писати, використовуватиме клас [ImpotsFile] для доступу до даних текстового файлу. Доступ для запису до бази даних здійснюватиметься за допомогою методів, розглянутих раніше.

Код скрипта такий:


# -*- coding=utf-8 -*-

# імпорт модуля класів «Податки»
from impots import *
import re

# --------------------------------------------------------------------------
def copyToMysql(limites,coeffR,coeffN,HOTE,USER,PWD,BASE,TABLE):
    # копіює 3 таблиці числових обмежень: coeffR, coeffN
    # у таблицю TABLE бази даних MySQL BASE
    #; база даних MySQL розташована на комп’ютері HOTE
    # користувач ідентифікується за допомогою USER та PWD

    # запити SQL додаються до списку
    # видалення таблиці
    requetes=["drop table %s" % (TABLE)]
    # створення таблиці
    requete="create table %s (limites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2))" % (TABLE)
    requetes.append(requete)
    # заповнення
    for i in range(len(limites)):
        # запит на вставку
        requetes.append("insert into %s (limites,coeffR,coeffN) values (%s,%s,%s)" % (TABLE,limites[i],coeffR[i],coeffN[i]))
    # виконуються команди SQL
    return executerCommandes(HOTE,USER,PWD,BASE,requetes,False,False)
    

def executerCommandes(HOTE,ID,PWD,BASE,requetes,suivi=False,arret=True):
    # використовує з'єднання (HOTE,ID,PWD,BASE)
    # виконує через це з'єднання команди SQL, що містяться у списку запитів
    # якщо «супровід»=True, то після кожного виконання команди SQL відображається повідомлення про її успіх або невдачу
    # якщо «зупинка» = False, функція зупиняється при першій виявленій помилці, інакше вона виконує всі команди SQL
    # функція повертає список (кількість помилок, помилка1, помилка2, ...)

    # підключення
    try:
        connexion=MySQLdb.connect(host=HOTE,user=ID,passwd=PWD,db=BASE)
    except MySQLdb.OperationalError,erreur:
        return [1,"Erreur lors de la connexion a MySQL sous l'identite (%s,%s,%s,%s) : %s" % (HOTE, ID, PWD, BASE, erreur)]
      
    # запитується курсор
    curseur=connexion.cursor()
    # виконання запитів SQL, що містяться у списку запитів
    # вони виконуються — спочатку помилок немає
    erreurs=[0]
    for i in range(len(requetes)):
        # запит зберігається у локальній змінній
        requete=requetes[i]
        # чи запит порожній?
        if re.match(r"^\s*$",requete):
            continue
        # виконання запиту i
        erreur=""
        try:
            curseur.execute(requete)
        except Exception, erreur:
            pass
        # чи сталася помилка?
        if erreur:
            # ще одна помилка
            erreurs[0]+=1
            # повідомлення про помилку
            msg="%s : Erreur (%s)" % (requete[0:len(requete)-1],erreur)
            erreurs.append(msg)
            # відстежувати на екрані чи ні?
            if suivi:
                print msg
            # зупиняємося?
            if arret:
                return erreurs
        else:
            if suivi: 
                # відобразити запит без символу кінця рядка
                print "%s : Execution reussie" % (cutNewLineChar(requete))
                # інформація про результат виконаного запиту
                afficherInfos(curseur)
    # закриття з'єднання
    try:
        connexion.commit()
        connexion.close()
    except MySQLdb.OperationalError,erreur:
        # ще одна помилка
        erreurs[0]+=1
        # повідомлення про помилку
        msg="%s : Erreur (%s)" % (requete,erreur)
        erreurs.append(msg)

    # повернення
    return erreurs


# ------------------------------------------------ головна
# ідентифікатор користувача
USER="root"
PWD=""
# хост-машина СГБД
HOTE="localhost"
# ідентифікатор бази даних
BASE="dbimpots"
# ідентифікатор таблиці даних
TABLE="impots"
# файл даних
IMPOTS="impots.txt"

# інстанціювання шару [dao]
try:
    dao=ImpotsFile(IMPOTS)
except (IOError, ImpotsError) as infos:
    print ("Une erreur s'est produite : {0}".format(infos))
    sys.exit()

# передача отриманих даних у таблицю MySQL
erreurs=copyToMysql(dao.limites,dao.coeffR,dao.coeffN,HOTE,USER,PWD,BASE,TABLE)
if erreurs[0]:
    for i in range(1,len(erreurs)):
        print erreurs[i]
else:
    print "Transfert opere"
# кінець
sys.exit()

Примітки:

  • рядки 106–110: створюється екземпляр класу [ImpotsFile], представленого в розділі 8.1;
  • рядок 113: масиви limites, coeffR, coeffN переносяться до бази MySQL;
  • рядок 8: функція copyToMysql виконує це перенесення. Функція copyToMysql створює масив запитів, що підлягають виконанню, і передає їх на виконання функції executerCommandes, рядок 25;
  • рядки 28–89: функція executerCommandes — це та сама функція, що вже була представлена раніше в розділі 9.7, з однією відмінністю: замість текстового файлу запити містяться у списку;

Результати

Текстовий файл impots.txt:

12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:272.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062

Результати на екрані:

Transfert opéré

Перевірка за допомогою phpMyAdmin:

 

10.2. Програма для розрахунку податку

Тепер, коли дані, необхідні для розрахунку податку, знаходяться в базі даних, ми можемо написати скрипт для розрахунку податку. Ми знову використовуємо трирівневу архітектуру:

Новий рівень [dao] буде підключений до SGBD та MySQL і буде реалізований класом [ImpotsMySQL]. Вона надасть шару [metier] той самий інтерфейс, що й раніше, який складається з єдиного методу getData, що повертає кортеж (межі, coeffR, coeffN). Таким чином, рівень [metier] не зміниться порівняно з попередньою версією.

10.3. Клас [ImpotsMySQL]

Рівень [dao] тепер реалізовано за допомогою наступного класу [ImpotsMySQL] (файл impots.py):


class ImpotsMySQL:

    # конструктор
    def __init__(self,HOTE,USER,PWD,BASE,TABLE):
        # ініціалізує атрибути меж, coeffR, coeffN
        # дані, необхідні для розрахунку податку, були розміщені в таблиці mysqL TABLE
        #, що належить до бази даних BASE. Таблиця має таку структуру
        # limites decimal(10,2), coeffR decimal(10,2), coeffN decimal(10,2)
        # підключення до бази даних MySQL на сервері HOTE здійснюється під ідентифікатором (USER,PWD)
        # генерує виняток у разі можливої помилки
        
        # підключення до бази даних MySQL
        connexion=MySQLdb.connect(host=HOTE,user=USER,passwd=PWD,db=BASE)
        # запит курсора
        curseur=connexion.cursor()
        
        # блокове читання таблиці TABLE
        requete="select limites,coeffR,coeffN from %s" % (TABLE)
        # виконує запит [requete] до бази даних [base] за з’єднанням [connexion]
        curseur.execute(requete)
        # обробка результату запиту
        ligne=curseur.fetchone()
        self.limites=[]
        self.coeffR=[]
        self.coeffN=[]
        while(ligne):
            # поточний рядок
            self.limites.append(ligne[0])
            self.coeffR.append(ligne[1])
            self.coeffN.append(ligne[2])
            # наступний рядок
            ligne=curseur.fetchone()
        # відключення
        connexion.close()

    def getData(self):
        return (self.limites, self.coeffR, self.coeffN)

Примітки:

  • рядок 18: запит SQL SELECT, який запитує дані з бази даних MySQL. Потім рядки результатів запиту SELECT по черзі обробляються запитом [curseur.fetchone] (рядки 22 та 32) для створення таблиць limites, coeffR, coeffN (рядки 28–30);
  • метод getData інтерфейсу шару [dao].

10.4. Консольний скрипт

Код консольного скрипта (impots_04) має такий вигляд:


# -*- coding=utf-8 -*-

# імпорт модуля класів Impots*
from impots import *

# ------------------------------------------------ main
# ідентифікатор користувача
USER="root"
PWD=""
# хост-машина СГБД
HOTE="localhost"
# ідентифікатор бази даних
BASE="dbimpots"
# ідентифікатор таблиці даних
TABLE="impots"
# файл вхідних даних
DATA="data.txt"
# файл вихідних даних
RESULTATS="resultats.txt"

# інстанція рівня [metier]
try:
    metier=ImpotsMetier(ImpotsMySQL(HOTE,USER,PWD,BASE,TABLE))
except (IOError, ImpotsError) as infos:
    print ("Une erreur s'est produite : {0}".format(infos))
    sys.exit()

# дані, необхідні для розрахунку податку, були розміщені у файлі IMPOTS
# по одному рядку на таблицю у вигляді
# val1:val2:val3,...

# читання даних
try:
    data=open(DATA,"r")
except:
    print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
    sys.exit()

# відкриття файлу результатів
try:
    resultats=open(RESULTATS,"w")
except:  
    print "Impossible de creer le fichier des résultats [RESULTATS]"
    sys.exit()

# допоміжні функції
u=Utilitaires()

# обробка поточного рядка файлу даних
ligne=data.readline()
while(ligne != ''):
    # видалення можливого символу кінця рядка
    ligne=u.cutNewLineChar(ligne)
    # витягуються 3 поля «одружений:діти:зарплата», що утворюють рядок
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # розраховується податок
    impot=metier.calculer(marie,enfants,salaire)
    # записуємо результат
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # зчитується новий рядок
    ligne=data.readline()
# закриваємо файли
data.close()
resultats.close()

Примітки:

  • рядок 23: створення екземплярів шарів [dao] та [metier];
  • решта коду відома.

Результати

Такі самі, як і в попередніх версіях вправи.