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 -*-

# وارد کردن ماژول کلاس 'Impots'
from impots import *
import re

# --------------------------------------------------------------------------
def copyToMysql(limites,coeffR,coeffN,HOTE,USER,PWD,BASE,TABLE):
    #سه جدول محدودیت عددی را کپی می‌کند، 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 موجود در فهرست درخواست‌ها روی این اتصال را اجرا می‌کند
    #اگر tracking=True باشد، پس از هر اجرای یک فرمان SQL پیامی نمایش داده می‌شود که موفقیت یا شکست آن را نشان می‌دهد
    # اگر stop=False باشد، تابع در اولین خطای برخورد شده متوقف می‌شود؛ در غیر این صورت، تمام دستورات SQL را اجرا می‌کند
    # این تابع یک لیست (تعداد خطاها، خطا۱، خطا۲، ...) را بازمی‌گرداند

    #اتصال
    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=""
# ماشین میزبان DBMS
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()

یادداشت‌ها:

  • خطوط ۱۰۶–۱۱۰: ما کلاس [ImpotsFile] را که در بخش ۸.۱ ارائه شده است، نمونه سازی می‌کنیم؛
  • خط ۱۱۳: آرایه‌های limites، coeffR و coeffN به پایگاه داده‌ای به نام MySQL منتقل می‌شوند؛
  • خط ۸: تابع copyToMysql این انتقال را انجام می‌دهد. تابع copyToMysql فهرستی از پرس‌وجوها را ایجاد می‌کند و آن‌ها را توسط تابع executerCommandes اجرا می‌نماید، خط ۲۵؛
  • رده‌های ۲۸–۸۹: تابع executerCommandes همان تابع توصیف‌شده در بند ۹.۷ است، با یک تفاوت: به جای قرار داشتن در یک فایل متنی، پرس‌وجوها در یک لیست قرار دارند؛

نتایج

فایل متنی 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 است و توییپ (tuple) (limits, coeffR, coeffN) را برمی‌گرداند. بنابراین، لایه [metier] نسبت به نسخه قبلی بدون تغییر باقی خواهد ماند.

10.3. کلاس [ImpotsMySQL]

لایه [dao] اکنون توسط کلاس زیر [ImpotsMySQL] (فایل impots.py) پیاده‌سازی شده است:


class ImpotsMySQL:

    # سازنده
    def __init__(self,HOTE,USER,PWD,BASE,TABLE):
        # ویژگی‌های limit را مقداردهی اولیه می‌کند، coeffR, coeffN
        #داده‌های مورد نیاز برای محاسبه مالیات در جدول mysqL TABLE قرار داده شده‌اند.
        #متعلق به پایگاه داده BASE است. جدول دارای ساختار زیر است
        #limits 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)

یادداشت‌ها:

  • خط ۱۸: پرس‌وجوی 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 *

# ------------------------------------------------ اصلی
#شناسهٔ کاربر
USER="root"
PWD=""
# ماشین میزبان DBMS
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)
    # استخراج سه فیلد «married:children:salary» که خط را تشکیل می‌دهند
    (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()

یادداشت‌ها:

  • خط ۲۳: نمونه‌سازی لایه‌های [dao] و [metier
  • بقیه کد مشخص است.

نتایج

همانند نسخه‌های قبلی تمرین.