25. تمرين التطبيق: الإصدار 8
25.1. مقدمة
سنقوم بكتابة تطبيق عميل/خادم جديد. الميزة الجديدة للخادم هي أنه سيدير جلسة عمل. بدلاً من وضع بيانات إدارة الضرائب في كائن نطاق [تطبيق]، سنضعها في كائن نطاق [جلسة عمل]. سيؤدي ذلك إلى انخفاض أداء الكود. عندما يمكن مشاركة كائن في وضع القراءة فقط من قبل جميع المستخدمين، يُفضل جعله كائنًا في نطاق [التطبيق] بدلاً من كائن في نطاق [الجلسة]. نكسب على الأقل بعض النطاق الترددي لأن هذا يقلل من حجم ملف تعريف ارتباط الجلسة. لكننا نريد عرض تطبيق عميل/خادم حيث يتبادل العميل والخادم ملف تعريف ارتباط الجلسة.
تظل بنية التطبيق دون تغيير:

25.2. خادم الويب
هيكل دليل نصوص الخادم هو كما يلي:

يتم إنشاء المجلد [http-servers/03] مبدئيًا عن طريق نسخ المجلد [http-servers/02]. ثم يتم إجراء التعديلات.
25.2.1. التكوين
هي نفسها كما في |الإصدار السابق| مع بعض التغييرات في البرنامج النصي [config]:
# dépendances absolues
absolute_dependencies = [
# dossiers du projet
# BaseEntity, MyException
f"{root_dir}/classes/02/entities",
# InterfaceImpôtsDao, InterfaceImpôtsMétier, InterfaceImpôtsUi
f"{root_dir}/impots/v04/interfaces",
# AbstractImpôtsdao, ImpôtsConsole, ImpôtsMétier
f"{root_dir}/impots/v04/services",
# ImpotsDaoWithAdminDataInDatabase
f"{root_dir}/impots/v05/services",
# AdminData, ImpôtsError, TaxPayer
f"{root_dir}/impots/v04/entities",
# Constantes, Tranches
f"{root_dir}/impots/v05/entities",
# index_controller
f"{script_dir}/../controllers",
# scripts [config_database, config_layers]
script_dir,
# Logger, SendAdminMail
f"{root_dir}/impots/http-servers/02/utilities",
]
- السطر 17: سنعيد كتابة وحدة تحكم لوظيفة [index] التي تتعامل مع عنوان URL /؛
- السطر 21: نستخدم الأدوات المساعدة من |الإصدار السابق|؛
25.2.2. البرنامج النصي الرئيسي [main]
يقدم البرنامج النصي [main] الجديد بعض التغييرات على البرنامج النصي [main] الرئيسي من الإصدار السابق:
# l'application Flask peut démarrer
app = Flask(__name__)
# clé secrète de la session
app.secret_key = os.urandom(12).hex()
- السطر 4: نقوم بإنشاء مفتاح سري للتطبيق. نحن نعلم أن هذا ضروري لإدارة الجلسة؛
بعد ذلك، لم يعد يتم طلب البيانات الضريبية في كود [main]. تمت إزالة الأسطر التالية:
بالإضافة إلى ذلك، تقبل وحدة التحكم [index_controller] معلمة إضافية، وهي جلسة Flask:
25.2.3. وحدة التحكم [index_controller]
تدير وحدة التحكم [index_controller] الآن جلسة عمل:
- السطر 14: يتلقى وحدة التحكم الجلسة الحالية من عميل الويب؛
- الأسطر 35–38: إذا كان لدى العميل جلسة عمل، فإنها تحتوي على مفتاحين:
- [client_id]: معرف العميل (السطر 37)؛
- [admindata]: بيانات الإدارة الضريبية في شكل قاموس (السطر 38)؛
- السطر 35: نتحقق مما إذا كانت الجلسة تحتوي على أحد المفتاحين المتوقعين؛
- الأسطر 42-51: الحالة التي لم يتم فيها تهيئة جلسة عمل العميل بعد؛
- السطر 43: استرداد بيانات سلطة الضرائب من طبقة [dao]؛
- السطر 45: يتم وضع هذه البيانات في الجلسة في شكل قاموس؛
- السطر 48: يتم تعيين رقم عشوائي للعميل. سيختلف هذا الرقم من عميل لآخر؛
- السطر 49: يتم تخزين هذا الرقم في الجلسة؛
- السطر 51: نقوم بتسجيل حقيقة أن بيانات سلطة الضرائب تم استردادها بواسطة طبقة [dao]. الوصول إلى طبقة [dao] مكلف بشكل عام. ولهذا السبب يجب أن يكون محدودًا. الفكرة هنا هي استرداد بيانات الضرائب من طبقة [dao] مرة واحدة، وتخزينها في الجلسة، واسترجاعها من هناك خلال الطلبات اللاحقة من نفس العميل. لاحظ أن هذا ليس الحل الأفضل. نظرًا لأن بيانات الضرائب من الإدارة هي نفسها لجميع العملاء، فإنها تنتمي إلى كائن في نطاق التطبيق؛
- الأسطر 35-40: الحالة التي تم فيها تهيئة جلسة عمل العميل خلال طلب سابق؛
- السطر 37: استرداد معرف العميل من الجلسة؛
- السطر 38: نسترد بيانات الضرائب الخاصة بالإدارة من الجلسة؛
- السطر 40: نسجل حقيقة أن العميل قد حصل على بيانات الضرائب الخاصة بالإدارة من الجلسة؛
25.3. عميل الويب

25.3.1. طبقة [dao]
25.3.1.1. فئة [ImpôtsDaoWithHttpSession]
يتم تنفيذ طبقة [dao] بواسطة فئة [ImpôtsDaoWithHttpSession] التالية:
- السطر 30: ستدير طبقة [dao] قاموسًا لملفات تعريف الارتباط؛
- السطر 58: الخاصية [response.cookies] هي قاموس يحتوي على ملفات تعريف الارتباط المرسلة من الخادم في رؤوس HTTP [Set-Cookie]؛
- السطر 59: يتم تخزين ملفات تعريف الارتباط هذه في طبقة [dao]. وسيتم إرسالها مرة أخرى إلى الخادم خلال الطلبات اللاحقة من نفس العميل؛
- الأسطر 60–68: على الرغم من أن ذلك ليس ضروريًا بالضرورة، فإننا نسترد ملف تعريف الارتباط الخاص بالجلسة. في قائمة ملفات تعريف الارتباط التي يرسلها الخادم، يرتبط ملف تعريف الارتباط الخاص بالجلسة بالمفتاح [session]؛
- الأسطر 62–68: نقوم بفك تشفير ملف تعريف الارتباط الخاص بالجلسة لتسجيل دخول المستخدم؛
- السطر 68: سنعود لاحقًا إلى الدالة [decode_flask_session]، التي تقوم بفك تشفير ملف تعريف ارتباط الجلسة؛
- السطران 52 و57: مع كل طلب من نفس العميل، يتم إرجاع ملفات تعريف الارتباط المرسلة من الخادم إليه. هكذا يتم الحفاظ على جلسة Flask بين العميل والخادم؛
يجب أن نتذكر الآن أن طبقة [dao] سيتم تنفيذها في وقت واحد بواسطة خيوط متعددة. لذلك يجب أن نفحص جميع خصائص مثيل الفئة ونرى ما إذا كان الوصول المتزامن إلى هذه الخصائص يمثل مشكلة. هنا أضفنا خاصية [self.__cookies] في السطر 30. يتم تعديل هذه الخاصية في السطر 59. لذلك لدينا حق الوصول للكتابة إلى البيانات المشتركة بين جميع الخيوط. ومع ذلك، فإن هذا الوصول يشكل مشكلة: كل خيط يمثل عميلًا معينًا له ملف تعريف ارتباط الجلسة الخاص به. في الواقع، يحتوي على معرف عميل فريد (=خيط) لكل عميل. إذا لم نفعل شيئًا، يمكن للخيط T2 الكتابة فوق ملفات تعريف الارتباط الخاصة بالخيط T1.
لقد رأينا بالفعل طريقة للتعامل مع هذه المشكلة: يمكننا إنشاء مفاتيح مختلفة لكل مؤشر ترابط في ملف [config] الذي يتم تمريره كمعلمة إلى المنشئ (السطر 17). على سبيل المثال، يمكننا استخدام اسم مؤشر الترابط كمفتاح:
- السطر 59، يمكننا كتابة:
config[thread_name][‘cookies’]=cookies
- في السطر 52، يمكننا بعد ذلك كتابة:
cookies=config[thread_name][‘cookies’]
هنا، سنستخدم تقنية مختلفة: سيكون لكل مؤشر ترابط (=عميل) طبقة [dao] خاصة به. بهذه الطريقة، لن يكون السطر 59 مشكلة بعد الآن لأن ملفات تعريف الارتباط المستخدمة هي تلك الخاصة بعميل واحد.
للقيام بذلك، سننشئ فئة جديدة [ImpôtsDaoWithHttpSessionFactory].
25.3.1.2. وظيفة فك تشفير جلسة Flask
يتم تعريف وظيفة [decode_flask_session] في البرنامج النصي [myutils]:

لقد درسنا بالفعل البرنامج النصي |myutils|. هذا البرنامج النصي هو وحدة نمطية على نطاق الآلة يمكن للبرامج النصية المختلفة في هذه الدورة استيرادها باستخدام العبارة:
فيه، نُعرّف الدالة [decode_flask_session] على النحو التالي:
- السطر 2: عنوان URL الذي وجدت فيه هذه الوظيفة؛
- السطر 1: المعلمة [cookie] هي السلسلة المرتبطة بمفتاح [session] في قاموس ملفات تعريف الارتباط التي يتلقاها عميل الويب؛
- الأسطر 3–16: لن أعلق على هذا الكود، لأنني لا أفهمه تمامًا؛
نضيف استيرادًا جديدًا إلى ملف [__init__.py]:
from .myutils import set_syspath, json_response, decode_flask_session
يتم تثبيت الإصدار الجديد من [myutils] ضمن الوحدات النمطية على مستوى الجهاز باستخدام الأمر [pip install .] في محطة PyCharm:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install .
Processing c:\data\st-2020\dev\python\cours-2020\python3-flask-2020\packages
Using legacy setup.py install for myutils, since package 'wheel' is not installed.
Installing collected packages: myutils
Attempting uninstall: myutils
Found existing installation: myutils 0.1
Uninstalling myutils-0.1:
Successfully uninstalled myutils-0.1
Running setup.py install for myutils ... done
Successfully installed myutils-0.1
- السطر 1: يجب أن تكون في مجلد [packages] لإدخال هذا الأمر؛
25.3.1.3. فئة [ImpôtsDaoWithHttpSessionFactory]
فئة [ImpôtsDaoWithHttpSessionFactory] هي كما يلي:
- تسمح لنا فئة [ImpôtsDaoWithHttpSessionFactory] بإنشاء تطبيق جديد لطبقة [dao] باستخدام طريقة [new_instance] في الأسطر 10–12؛
25.3.2. التكوين
يتم تعديل البرنامج النصي [config_layers]، الذي يقوم بتكوين طبقات عميل الويب، على النحو التالي:
- السطران 5-6: بدلاً من إنشاء مثيل لطبقة [DAO] واحدة كما كان يحدث سابقًا، نقوم بإنشاء مثيل لـ "مصنع" لهذه الطبقة (المصنع = مصنع إنتاج الكائنات، وفي هذه الحالة طبقة [DAO])؛
- السطور 9-11: نُرجع تكوين الطبقة؛
25.3.3. النص البرمجي الرئيسي للعميل
تغير البرنامج النصي [الرئيسي] على النحو التالي مقارنة بالإصدار السابق:
- السطران 29-30: كل مؤشر ترابط له طبقة [dao] خاصة به؛
25.3.4. تنفيذ العميل
يتم تشغيل خادم الويب، ويتم تشغيل نظام إدارة قواعد البيانات (DBMS)، ويتم تشغيل خادم البريد [hMailServer]. ثم نقوم بتشغيل البرنامج النصي [main] لعميل الويب. وتكون سجلات التنفيذ في [data/logs/logs.txt] كما يلي:
2020-07-25 10:21:46.478511, Thread-1 : début du thread [Thread-1] avec 1 contribuable(s)
2020-07-25 10:21:46.479111, Thread-1 : début du calcul de l'impôt de {"id": 1, "marié": "oui", "enfants": 2, "salaire": 55555}
2020-07-25 10:21:46.479111, Thread-2 : début du thread [Thread-2] avec 1 contribuable(s)
2020-07-25 10:21:46.480195, Thread-3 : début du thread [Thread-3] avec 2 contribuable(s)
2020-07-25 10:21:46.480195, Thread-2 : début du calcul de l'impôt de {"id": 2, "marié": "oui", "enfants": 2, "salaire": 50000}
2020-07-25 10:21:46.481137, Thread-4 : début du thread [Thread-4] avec 3 contribuable(s)
2020-07-25 10:21:46.481137, Thread-3 : début du calcul de l'impôt de {"id": 3, "marié": "oui", "enfants": 3, "salaire": 50000}
2020-07-25 10:21:46.482279, Thread-5 : début du thread [Thread-5] avec 3 contribuable(s)
2020-07-25 10:21:46.482622, Thread-6 : début du thread [Thread-6] avec 1 contribuable(s)
2020-07-25 10:21:46.482622, Thread-4 : début du calcul de l'impôt de {"id": 5, "marié": "non", "enfants": 3, "salaire": 100000}
2020-07-25 10:21:46.485923, Thread-5 : début du calcul de l'impôt de {"id": 8, "marié": "non", "enfants": 0, "salaire": 100000}
2020-07-25 10:21:46.485923, Thread-6 : début du calcul de l'impôt de {"id": 11, "marié": "oui", "enfants": 3, "salaire": 200000}
2020-07-25 10:21:46.725910, Thread-4 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,90000.0],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"fa3c83b82761c83e13217967"}
2020-07-25 10:21:46.725910, Thread-4 : {"réponse": {"result": {"marié": "non", "enfants": 3, "salaire": 100000, "impôt": 16782, "surcôte": 7176, "taux": 0.41, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:46.725910, Thread-4 : fin du calcul de l'impôt de {"id": 5, "marié": "non", "enfants": 3, "salaire": 100000, "impôt": 16782, "surcôte": 7176, "taux": 0.41, "décôte": 0, "réduction": 0}
2020-07-25 10:21:46.726960, Thread-4 : début du calcul de l'impôt de {"id": 6, "marié": "oui", "enfants": 3, "salaire": 100000}
2020-07-25 10:21:47.514108, Thread-3 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,24999.5],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"700e3f5dc808c7c48f0c9007"}
2020-07-25 10:21:47.514610, Thread-3 : {"réponse": {"result": {"marié": "oui", "enfants": 3, "salaire": 50000, "impôt": 0, "surcôte": 0, "taux": 0.14, "décôte": 720, "réduction": 0}}}
2020-07-25 10:21:47.514939, Thread-3 : fin du calcul de l'impôt de {"id": 3, "marié": "oui", "enfants": 3, "salaire": 50000, "impôt": 0, "surcôte": 0, "taux": 0.14, "décôte": 720, "réduction": 0}
2020-07-25 10:21:47.514939, Thread-3 : début du calcul de l'impôt de {"id": 4, "marié": "non", "enfants": 2, "salaire": 100000}
2020-07-25 10:21:47.527211, Thread-5 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,90000.0],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"9e14a5d4a3057f69ab95ab2d"}
2020-07-25 10:21:47.527211, Thread-2 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,22500.0],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"a06e8fd70a44c9e311f4dce0"}
2020-07-25 10:21:47.527211, Thread-5 : {"réponse": {"result": {"marié": "non", "enfants": 0, "salaire": 100000, "impôt": 22986, "surcôte": 0, "taux": 0.41, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.527211, Thread-1 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,90000.0],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"28c38df998f67685b3a482b8"}
2020-07-25 10:21:47.527211, Thread-2 : {"réponse": {"result": {"marié": "oui", "enfants": 2, "salaire": 50000, "impôt": 1384, "surcôte": 0, "taux": 0.14, "décôte": 384, "réduction": 347}}}
2020-07-25 10:21:47.528341, Thread-5 : fin du calcul de l'impôt de {"id": 8, "marié": "non", "enfants": 0, "salaire": 100000, "impôt": 22986, "surcôte": 0, "taux": 0.41, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.528341, Thread-1 : {"réponse": {"result": {"marié": "oui", "enfants": 2, "salaire": 55555, "impôt": 2814, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.528842, Thread-2 : fin du calcul de l'impôt de {"id": 2, "marié": "oui", "enfants": 2, "salaire": 50000, "impôt": 1384, "surcôte": 0, "taux": 0.14, "décôte": 384, "réduction": 347}
2020-07-25 10:21:47.529349, Thread-5 : début du calcul de l'impôt de {"id": 9, "marié": "oui", "enfants": 2, "salaire": 30000}
2020-07-25 10:21:47.529699, Thread-1 : fin du calcul de l'impôt de {"id": 1, "marié": "oui", "enfants": 2, "salaire": 55555, "impôt": 2814, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.529699, Thread-2 : fin du thread [Thread-2]
2020-07-25 10:21:47.531905, Thread-1 : fin du thread [Thread-1]
2020-07-25 10:21:47.536121, Thread-6 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,93749.0],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"38499b63076516c02f2770ec"}
2020-07-25 10:21:47.537161, Thread-3 : {"réponse": {"result": {"marié": "non", "enfants": 2, "salaire": 100000, "impôt": 19884, "surcôte": 4480, "taux": 0.41, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.537161, Thread-6 : {"réponse": {"result": {"marié": "oui", "enfants": 3, "salaire": 200000, "impôt": 42842, "surcôte": 17283, "taux": 0.41, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.538156, Thread-3 : fin du calcul de l'impôt de {"id": 4, "marié": "non", "enfants": 2, "salaire": 100000, "impôt": 19884, "surcôte": 4480, "taux": 0.41, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.538557, Thread-6 : fin du calcul de l'impôt de {"id": 11, "marié": "oui", "enfants": 3, "salaire": 200000, "impôt": 42842, "surcôte": 17283, "taux": 0.41, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.538828, Thread-3 : fin du thread [Thread-3]
2020-07-25 10:21:47.538828, Thread-6 : fin du thread [Thread-6]
2020-07-25 10:21:47.546198, Thread-5 : {"réponse": {"result": {"marié": "oui", "enfants": 2, "salaire": 30000, "impôt": 0, "surcôte": 0, "taux": 0.0, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.546198, Thread-5 : fin du calcul de l'impôt de {"id": 9, "marié": "oui", "enfants": 2, "salaire": 30000, "impôt": 0, "surcôte": 0, "taux": 0.0, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.546198, Thread-5 : début du calcul de l'impôt de {"id": 10, "marié": "non", "enfants": 0, "salaire": 200000}
2020-07-25 10:21:47.739643, Thread-4 : {"réponse": {"result": {"marié": "oui", "enfants": 3, "salaire": 100000, "impôt": 9200, "surcôte": 2180, "taux": 0.3, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.739643, Thread-4 : fin du calcul de l'impôt de {"id": 6, "marié": "oui", "enfants": 3, "salaire": 100000, "impôt": 9200, "surcôte": 2180, "taux": 0.3, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.740668, Thread-4 : début du calcul de l'impôt de {"id": 7, "marié": "oui", "enfants": 5, "salaire": 100000}
2020-07-25 10:21:48.557469, Thread-5 : {"réponse": {"result": {"marié": "non", "enfants": 0, "salaire": 200000, "impôt": 64210, "surcôte": 7498, "taux": 0.45, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:48.558715, Thread-5 : fin du calcul de l'impôt de {"id": 10, "marié": "non", "enfants": 0, "salaire": 200000, "impôt": 64210, "surcôte": 7498, "taux": 0.45, "décôte": 0, "réduction": 0}
2020-07-25 10:21:48.558715, Thread-5 : fin du thread [Thread-5]
2020-07-25 10:21:48.753025, Thread-4 : {"réponse": {"result": {"marié": "oui", "enfants": 5, "salaire": 100000, "impôt": 4230, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:48.753318, Thread-4 : fin du calcul de l'impôt de {"id": 7, "marié": "oui", "enfants": 5, "salaire": 100000, "impôt": 4230, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}
2020-07-25 10:21:48.753540, Thread-4 : fin du thread [Thread-4]
- يوجد إجمالي 6 سلاسل، مما يعني 6 عملاء (الأسطر 1، 3، 4، 6، 8، 9) يستعلمون في وقت واحد عن خادم حساب الضرائب؛
- سنتابع الخيط [Thread-4]، الذي يتعامل مع 3 دافعي ضرائب (السطر 6). سيقوم بإرسال ثلاثة طلبات متتالية إلى خادم حساب الضرائب؛
- السطر 10: الطلب الأول لـ [Thread-4]؛
- السطر 13: تلقى [Thread-4] الرد على طلبه الأول. وفيه، يجد ملف تعريف ارتباط جلسة يحتوي على الرقم [fa3c83b82761c83e13217967] الذي خصصه له الخادم؛
- السطر 14: الضريبة الخاصة بالمكلف الأول؛
- السطر 16: [Thread-4] يقدم طلبًا للمكلف الثاني؛
- السطر 43: [Thread-4] يتلقى مبلغ الضريبة للمكلف الثاني؛
- السطر 45: [Thread-4] يقدم طلبًا للمكلف الثالث؛
- السطر 49: [Thread-4] يتلقى مبلغ الضريبة للمكلف الثالث؛
- السطر 51: [Thread-4] قد أنهى عمله؛
الآن، دعونا نلقي نظرة على كيفية معالجة الطلبات الثلاثة الواردة من [Thread-4] على جانب الخادم. يمكننا تتبعها في سجلات الخادم باستخدام معرف العميل الخاص بها [fa3c83b82761c83e13217967].
سجلات جانب الخادم [data/logs/logs.txt] هي كما يلي:
2020-07-25 10:21:39.187366, MainThread : [serveur] démarrage du serveur
2020-07-25 10:21:40.439093, MainThread : [serveur] démarrage du serveur
2020-07-25 10:21:46.502011, Thread-2 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=3&salaire=50000' [GET]>
2020-07-25 10:21:46.504049, Thread-2 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:46.505452, Thread-3 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=2&salaire=55555' [GET]>
2020-07-25 10:21:46.506257, Thread-3 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:46.507292, Thread-4 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=non&enfants=0&salaire=100000' [GET]>
2020-07-25 10:21:46.507292, Thread-4 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:46.508301, Thread-5 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=2&salaire=50000' [GET]>
2020-07-25 10:21:46.509293, Thread-5 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:46.511808, Thread-6 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=non&enfants=3&salaire=100000' [GET]>
2020-07-25 10:21:46.517604, Thread-7 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=3&salaire=200000' [GET]>
2020-07-25 10:21:46.517604, Thread-7 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:46.719504, Thread-6 : [index_controller] client [fa3c83b82761c83e13217967], données fiscales prises dans la couche dao
2020-07-25 10:21:46.720003, Thread-6 : [index] {'réponse': {'result': {'marié': 'non', 'enfants': 3, 'salaire': 100000, 'impôt': 16782, 'surcôte': 7176, 'taux': 0.41, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:46.736108, Thread-8 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=3&salaire=100000' [GET]>
2020-07-25 10:21:46.736108, Thread-8 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:47.506709, Thread-2 : [index_controller] client [700e3f5dc808c7c48f0c9007], données fiscales prises dans la couche dao
2020-07-25 10:21:47.507216, Thread-2 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 3, 'salaire': 50000, 'impôt': 0, 'surcôte': 0, 'taux': 0.14, 'décôte': 720, 'réduction': 0}}}
2020-07-25 10:21:47.507216, Thread-3 : [index_controller] client [28c38df998f67685b3a482b8], données fiscales prises dans la couche dao
2020-07-25 10:21:47.508442, Thread-4 : [index_controller] client [9e14a5d4a3057f69ab95ab2d], données fiscales prises dans la couche dao
2020-07-25 10:21:47.508940, Thread-3 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 2, 'salaire': 55555, 'impôt': 2814, 'surcôte': 0, 'taux': 0.14, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.510506, Thread-4 : [index] {'réponse': {'result': {'marié': 'non', 'enfants': 0, 'salaire': 100000, 'impôt': 22986, 'surcôte': 0, 'taux': 0.41, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.511513, Thread-5 : [index_controller] client [a06e8fd70a44c9e311f4dce0], données fiscales prises dans la couche dao
2020-07-25 10:21:47.514939, Thread-5 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 2, 'salaire': 50000, 'impôt': 1384, 'surcôte': 0, 'taux': 0.14, 'décôte': 384, 'réduction': 347}}}
2020-07-25 10:21:47.520727, Thread-7 : [index_controller] client [38499b63076516c02f2770ec], données fiscales prises dans la couche dao
2020-07-25 10:21:47.523162, Thread-7 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 3, 'salaire': 200000, 'impôt': 42842, 'surcôte': 17283, 'taux': 0.41, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.530835, Thread-9 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=non&enfants=2&salaire=100000' [GET]>
2020-07-25 10:21:47.531736, Thread-9 : [index_controller] client [700e3f5dc808c7c48f0c9007], données fiscales prises en session
2020-07-25 10:21:47.531905, Thread-9 : [index] {'réponse': {'result': {'marié': 'non', 'enfants': 2, 'salaire': 100000, 'impôt': 19884, 'surcôte': 4480, 'taux': 0.41, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.541899, Thread-10 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=2&salaire=30000' [GET]>
2020-07-25 10:21:47.542488, Thread-10 : [index_controller] client [9e14a5d4a3057f69ab95ab2d], données fiscales prises en session
2020-07-25 10:21:47.542488, Thread-10 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 2, 'salaire': 30000, 'impôt': 0, 'surcôte': 0, 'taux': 0.0, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.553628, Thread-11 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=non&enfants=0&salaire=200000' [GET]>
2020-07-25 10:21:47.553628, Thread-11 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:47.736910, Thread-8 : [index_controller] client [fa3c83b82761c83e13217967], données fiscales prises en session
2020-07-25 10:21:47.737191, Thread-8 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 3, 'salaire': 100000, 'impôt': 9200, 'surcôte': 2180, 'taux': 0.3, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.748226, Thread-12 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=5&salaire=100000' [GET]>
2020-07-25 10:21:47.748226, Thread-12 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:48.554695, Thread-11 : [index_controller] client [9e14a5d4a3057f69ab95ab2d], données fiscales prises en session
2020-07-25 10:21:48.555070, Thread-11 : [index] {'réponse': {'result': {'marié': 'non', 'enfants': 0, 'salaire': 200000, 'impôt': 64210, 'surcôte': 7498, 'taux': 0.45, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:48.748753, Thread-12 : [index_controller] client [fa3c83b82761c83e13217967], données fiscales prises en session
2020-07-25 10:21:48.748753, Thread-12 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 5, 'salaire': 100000, 'impôt': 4230, 'surcôte': 0, 'taux': 0.14, 'décôte': 0, 'réduction': 0}}}
- يظهر العميل [fa3c83b82761c83e13217967] لأول مرة في السطر 14: لحساب الضريبة، كان على الخادم استرداد البيانات من قاعدة بيانات مصلحة الضرائب؛
- ثم نرى العميل [fa3c83b82761c83e13217967] مرة أخرى في السطر 36. هذه المرة، يسترد الخادم بيانات سلطة الضرائب من الجلسة، مما يوفر عليه وصولاً قد يكون مكلفاً إلى طبقة [DAO]؛
- نلتقي بالعميل [fa3c83b82761c83e13217967] للمرة الثالثة في السطر 42، حيث يستخدم الخادم مرة أخرى جلسة عمل العميل؛
يوضح هذا المثال بوضوح قيمة الجلسة بالنسبة للعميل: فهي تخزن البيانات المشتركة بين جميع طلبات ذلك العميل، والتي يكون استرجاعها مكلفًا.
على جانب العميل، النتائج الموجودة في الملف [data/output/results.json] هي نفسها الموجودة في الإصدارات السابقة.
25.4. اختبار طبقة [DAO]
كما فعلنا في |الإصدارات السابقة|، نختبر طبقة [dao] الخاصة بالعميل:

سيتم تنفيذ فئة الاختبار في البيئة التالية:

- التكوين [2] مطابق للتكوين [1] الذي قمنا بفحصه للتو؛
فئة الاختبار [TestHttpClientDao] هي كما يلي:
- نقوم بإنشاء |تكوين تنفيذ| لهذا الاختبار؛
- نقوم بتشغيل خادم الويب مع بيئته بالكامل؛
- نقوم بتشغيل الاختبار؛
والنتائج هي كما يلي:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-clients/03/tests/TestHttpClientDao.py
tests en cours...
...........
----------------------------------------------------------------------
Ran 11 tests in 3.392s
OK
Process finished with exit code 0