لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 06/10/21 في كل الموقع
-
يمكننا استخدام دالتين مساعدتين zip و dict بالشكل التالي: keys = ['x', 'y', 'z'] values = ['abc', 123, True] zip_iterator = zip(keys, values) # تعيد pairs of elements dictionary = dict(zip_iterator) # تحويل لقاموس print(dictionary)2 نقاط
-
كيف يمكنني إخراج نص ملون إلى terminal في بايثون؟ رأيت في أحد السكريبتات أنه يتم ظهور الإختيارات والنتيجة .. إلخ في الـ terminal بألوان مختلفة، كيف يمكنني عمل ذلك أيضًا؟ وهل هذه الطريقة تعمل في جميع أنظمة التشغيل (Winodws و Linux و MacOS)؟1 نقطة
-
الإصدار 1.0.0
116578 تنزيل
سطع نجم لغة البرمجة بايثون في الآونة الأخيرة حتى بدأت تزاحم أقوى لغات البرمجة في الصدارة وذاك لمزايا هذه اللغة التي لا تنحصر أولها سهولة كتابة وقراءة شيفراتها حتى أصبحت الخيار الأول بين يدي المؤسسات الأكاديمية والتدريبية لتدريسها للطلاب الجدد الراغبين في الدخول إلى مجال علوم الحاسوب والبرمجة. أضف إلى ذلك أن بايثون لغةً متعدَّدة الأغراض والاستخدامات، لذا فهي دومًا الخيار الأول في شتى مجالات علوم الحاسوب الصاعدة مثل الذكاء الصنعي وتعلم الآلة وعلوم البيانات وغيرها، كما أنَّها مطلوبة بشدة في سوق العمل وتعتمدها كبرى الشركات التقنية. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن بني هذا العمل على كتاب «How to code in Python» لصاحبته ليزا تاغليفيري (Lisa Tagliaferri) وترجمه إلى العربية محمد بغات وعبد اللطيف ايمش، وحرره جميل بيلوني، ويأتي شارحًا المفاهيم البرمجية الأساسية بلغة بايثون، ونأمل في أكاديمية حسوب أن يكون إضافةً نافعةً للمكتبة العربيَّة وأن يفيد القارئ العربي في أن يكون منطلقًا للدخول إلى عالم البرمجة من أوسع أبوابه. رُبط هذا الكتاب مع توثيق لغة بايثون في موسوعة حسوب لتسهيل عملية الاطلاع على أي جزء من اللغة مباشرة وقراءة التفاصيل باللغة العربية. هذا الكتاب مرخص بموجب رخصة المشاع الإبداعي Creative Commons «نسب المُصنَّف - غير تجاري - الترخيص بالمثل 4.0». يمكنك قراءة فصول الكتاب على شكل مقالات من هذه الصفحة، «المرجع الشامل إلى تعلم لغة بايثون»، أو مباشرةً من الآتي: المقال الأول: دليل تعلم بايثون اعتبارات عملية للاختيار ما بين بايثون 2 و بايثون 3 المقال الثاني: تثبيت بايثون 3 وإعداد بيئتها البرمجية المقال الثالث: كيف تكتب أول برنامج لك المقال الرابع: كيفية استخدام سطر أوامر بايثون التفاعلي المقال الخامس: كيفية كتابة التعليقات المقال السادس: فهم أنواع البيانات المقال السابع: مدخل إلى التعامل مع السلاسل النصية المقال الثامن: كيفية تنسيق النصوص المقال التاسع: مقدمة إلى دوال التعامل مع السلاسل النصية المقال العاشر: آلية فهرسة السلاسل النصية وطريقة تقسيمها المقال الحادي عشر: كيفية التحويل بين أنواع البيانات المقال الثاني عشر: كيفية استخدام المتغيرات المقال الثالث عشر: كيفية استخدام آلية تنسيق السلاسل النصية المقال الرابع عشر: كيفية إجراء العمليات الحسابية المقال الخامس عشر: الدوال الرياضية المضمنة المقال السادس عشر: فهم العمليات المنطقية المقال السابع عشر: مدخل إلى القوائم المقال الثامن عشر: كيفية استخدام توابع القوائم المقال التاسع عشر: فهم كيفية استعمال List Comprehensions المقال العشرون: فهم نوع البيانات Tuples المقال الحادي والعشرين: فهم القواميس المقال الثاني والعشرين: كيفية استيراد الوحدات المقال الثالث والعشرين: كيفية كتابة الوحدات المقال الرابع والعشرين: كيفية كتابة التعليمات الشرطية المقال الخامس والعشرين: كيفية إنشاء حلقات تكرار while المقال السادس والعشرين: كيفية إنشاء حلقات تكرار for المقال السابع والعشرين: كيفية استخدام تعابير break وcontinue وpass عند التعامل مع حلقات التكرار المقال الثامن والعشرين: كيفية تعريف الدوال المقال التاسع والعشرين: كيفية استخدام *args و**kwargs المقال الثلاثين: كيفية إنشاء الأصناف وتعريف الكائنات المقال الحادي والثلاثين: فهم متغيرات الأصناف والنسخ المقال الثاني والثلاثين: وراثة الأصناف المقال الثالث والثلاثين: كيفية تطبيق التعددية الشكلية (Polymorphism) على الأصناف المقال الرابع والثلاثين: كيف تستخدم منقح بايثون المقال الخامس والثلاثين: كيفية تنقيح شيفرات بايثون من سطر الأوامر التفاعلي المقال السادس والثلاثين: كيف تستخدم التسجيل Logging المقال السابع والثلاثين: كيفية ترحيل شيفرة بايثون 2 إلى بايثون 31 نقطة -
قد تقضِ أيامًا وأسابيعًا أو حتى شهورًا تُخطط لحملة إعلانية، وتُجري العديد من التعديلات وتعتقد أنك نجحت في هذه المرة ولكن لا تحصل على النتائج المرجوة. تُرى ما هو الخطأ؟ في الحقيقة، لن تعرف إذا لم تختبر رسائلك الإلكترونية. يُعَد فشل الحملات الإعلانية أو أخطاء البريد الإلكتروني من أسوأ الأشياء التي يواجهها مسوّق البريد الإلكتروني، ولكن الخبر السار هنا أنه يمكن تجنبها. يمكنك النظر إلى اختبار البريد الإلكتروني على أنه وسيلة تأمينية للعمل الشاق الذي أنجزته وأيضًا لما سيحدث بعد ضغطك على زر الإرسال. لماذا يعد اختبار البريد الإلكتروني ضروريا؟ جميعنا نعلم أنه من الجيد مراجعة عملك مرةً أو اثنين، لكن هنا يأتي السؤال، أين يقع بند اختبار البريد الإلكتروني في قائمة أولوياتك؟ هل تعلم أن ٤٣٪ من المسوقين عبر البريد الإلكتروني لا يختبرون كل بريد يرسلونه؟ هذه حقًا مخاطرة كبيرة، فإذا كنت أحد أفراد فريق تسويق البريد الإلكتروني الذين لا يتحققون من الرسائل بانتظام، فنحن هنا لنعلمك كيف تفعل ذلك. اختبار البريد الإلكتروني يجنبنا الأخطاء يُعَد التسويق عبر البريد الإلكتروني بيئةً دائمة التغير، إذ تحتوي على العديد من المتغيرات، وتُحدَّث قائمة عملاء البريد الإلكتروني بما يُعادل كل يومين، وفي كل مرة تُحدِّث بيانات العميل، وهناك احتمالية أن تُعرض رسالتك بطريقة خاطئة (هذا إن وصلت إلى البريد الوارد من الأساس)، فحتى الأخطاء التي تبدو بسيطة قد تؤثِّر على عائدك الاستثماري. من الممكن لرابط لا يعمل أن يُفسد نتائج حملتك، فيقول ٥١٪ من المشتركين أنهم ألغوا اشتراكاتهم في خدمات البريد الإلكتروني لأنها لم تُعرَض أو لم تكن تعمل جيدًا. يساعدنا اختبار البريد الإلكتروني على تكوين رؤى مستقبلية تتخطى منافع اختبار رسالة البريد الإلكتروني وتحليلها مجرد عرضه لمعرفة كيف يبدو، حيث توفر لنا إجراء اختبارات A/B على أحد عناصر البريد الإلكتروني مثل سطر الموضوع، (المقدمة التي تحدد هدف رسائل البريد الإلكتروني) رؤيةً حول أداء أفضل مع الجمهور. ويمكنك كتابة رسائل بريد إلكتروني أكثر تأثيرًا في المستقبل من خلال إجراء اختبارات A/B أو تحليل الرسائل التي حظيت بأكبر قدر من التفاعل. أدوات وأنواع اختبار البريد الإلكتروني قد تعاني بصفتك مسوقًا عبر البريد الإلكتروني من الميزانية المحدودة أو وضيق الوقت، ولذلك ننصح باستخدام أدوات اختبار البريد الإلكتروني، إذا أردت إنجاز أكبر قدر ممكن من العمل في أقل وقت. اختبار ما قبل الإرسال من أجل عملية الإعداد Rendering ومعاينة تجربة العميل إذا أردت أن تبدو رسائلك رائعةً لجميع المشتركين، فأنت بحاجة إلى إجراء اختبار ما قبل الإرسال حتى تتحقق من عرضها وتُعاين تجربة العميل. اختبار عملية الإعداد Rendering عملية الإعداد Rendering هي عملية جمع البيانات وتحميل القوالب، ثم تطبيق البيانات المجمعة على القوالب، وإرسال الخرج النهائي إلى المستخدِم، وهي المسؤولة عن كيفية ظهور البريد الإلكتروني وعمله مع المشتركين. لن يُخرِّب رابط لا يعمل برنامجك التسويقي بأكمله، لكن ما يُزعج المشتركين هي التجربة الرديئة المستمرة، فقد يصل الأمر بهم إلى إلغاء الاشتراك. منصة Litmus تُقدم لك منصة Litmus المدفوعة، مجموعةً متنوعةً من أدوات الاختبار، وأحدها هو اختبار عملية الإعداد Rendering، حيث تتيح لك معاينات Litmus الحية رؤيةً حول كيف ستبدو رسالتك عبر أجهزة العملاء الموجودين في قائمة البريد التي أعددتها، كما وتسمح لك بمتابعة عمليات الإعداد وهي تحدث تلقائيًا، والتركيز على أي مشاكل في العملية لحلها بسرعة. منصة Email on Acid تحتوي منصة Email on Acid المدفوعة أيضًا أدوات لاختبار عملية الإعداد Rendering عبر أكثر من ٩٠ عميل بريد إلكتروني وجهاز، إذ تسمح لك المعاينات الحية على هذه المنصة برؤية ومشاركة عمليات الإعداد على شيفرتك البرمجية Code، حتى تنتبه إلى الأخطاء قبل أن تُرسل الرسائل. اختبار الروابط لأن كل رسالة بريد إلكتروني ترسلها لديها هدف ومهمة محددان في قُمع التسويق، فمن المحتمل أن تحتوي على روابط، ولأن الروابط ضرورية لجلب زيارات العملاء المهتمين، ومساعدتك على تتبع الأداء، فاحرص على أن تكون سليمة. تأكد أيضًا من أن الروابط المضمنة في الرسالة تشير إلى الصفحة الصحيحة، إذ من الشائع نسيان المسوقين لتحديث الروابط إلى عناوين URL الصحيحة أثناء إنشاء حملات جديدة. منصة Litmus إذا أردت مساعدةً في إنشاء قائمة تحتوي على جميع العناصر التي تحتاج التحقق منها قبل ارسال الرسالة، فقد تساعدك قائمة تحقق Litmus في التحقق من مراقبة العناصر المهمة في مرحلة نمو البريد الإلكتروني قبل الإرسال، مثل: الروابط، وحظر الصور. اختبار إمكانية الوصول Accessibility يستحق كل عميل من عملائك نفس تجربة البريد الإلكتروني الرائعة، ويضمن اختبار إمكانية الوصول حدوث ذلك. منصة Litmus ميزة أخرى من مزايا قائمة التحقق قبل الإرسال على هذه المنصة هي مدقق إمكانية الوصول، فعند تشغيل رسالة من خلال الأداة، فستوضع علامة تلقائيًا على كل مشكلة من مشكلات الوصول حتى يتسنى لك معالجتها، وتتضمن بعض نقاط التحقق علامات ALT، أو التسلسل الهرمي للمحتوى، أو قارئ الشاشة، أو توافق الأجهزة المساعدة. الاختبار اليدوي إذا كنت تعمل وِفق ميزانية، أو تريد أن تختبر وصول البريد الإلكتروني بدون استخدام أدوات خارجية، فهناك طرق مجانية لعمل هذا. يستغرق الأمر بعض الوقت، وتزداد احتمالية إغفالك لشيء ما، لكن هذا أفضل من عدم الاختبار على الإطلاق، وتُعد المهام العديدة/ مثل: استخدام التعليق الصوتي Voiceover على هاتف iPhone لقراءة الرسائل، أو عرض الرسالة بدون الصور، أو وضع عمى الألوان جميعها مفيدة. المحتوى و العنوان المسبق بالإضافة إلى التفاصيل الدقيقة أو عناصر البريد الإلكتروني التي يصعب اختبارها، فستحتاج أيضًا إلى التحقق من المحتوى، وسطر الموضوع، ونص العنوان المسبق (النص الملخص الذي يتبع سطر الموضوع عند عرض البريد الإلكتروني قبل فتحه). تغطي معظم برامج اختبار البريد الإلكتروني هذه النقاط، ويمكنك أيضًا مراجعتها يدويًا قبل الإرسال. منصة Email on Acid تمكنك حملة التحقق المسبق في منصة Email on Acid من قصر عملية اختبار البريد الإلكتروني على مراجعة عناصره، مثل: المحتوى، ونص العنوان المسبق، كما تحتوي الأداة أيضًا على خاصية التدقيق الإملائي لتجنب أخطاء الكتابة المحرجة، والكلمات المسيئة. الاختبار اليدوي إذا كنت ترغب في مراجعة البريد الإلكتروني يدويًا، فتأكد من وجود حيز أو مجال في سير العمل من أجل نسختك، إذ تتيح لك أدوات اختبار البريد الإلكتروني المجانية مثل Testsubject رؤية الشكل الذي سيبدو عليه سطر الموضوع، والعنوان المسبق باختلاف الأجهزة. كما يمكنك تصحيح الأخطاء الإملائية عن طريق استخدام الإصدار المجاني من Grammarly لدى الكتابة باللغة الإنجليزية. اختبار معدل التوصيل والتسليم بعد أن يغادر البريد الإلكتروني مزود خدمتك، فيجب أن يعبر بوابتان قبل أن يراه العميل، والبوابة الأولى هي أن يُقبل من قبل خادم المستلِم، وتكون النسبة المئوية لمرات حدوث هذا هي معدلك للتوصيل؛ أما الثانية فهي أن يدخل مجلد البريد الوارد وليس مجلد الرسائل غير المرغوبة، والنسبة المئوية لمرات حدوث هذا هي معدلك للتسليم. من المهم للغاية إجراء اختبار التأكد من عدم حظر رسالتك من قِبل عوامل تصفية الرسائل غير المرغوب فيها أو عناوين البريد الإلكتروني غير الصالحة. التأكد من صحة البريد الإلكتروني يستهلك إرسال بريد إلكتروني إلى عنوان بريد غير صالح من مواردك الثمينة دون داعٍ. صحيح أنه لن يضرك كثيرًا وجود بريد واحد أو اثنين خاطئين في قائمتك ولن يكون له آثار سلبية كبيرة عليك، لكن إذا كنت تدفع مقابلًا لخدمات البريد الإلكتروني بناءً على عدد الرسائل، فاحرص على التأكد من دقة أرقامك. منصة Mailgun تُعَدّ منصة Mailgun المدفوعة خيارًا رائعًا آخر إذا أردت التحقق من صحة قائمة بريدك، حيث تتحقق الخدمة من وجود عناوين البريد الإلكتروني الموجودة في قائمتك عبر عملاء البريد الإلكتروني الرئيسيين، مما يحسن معدلك للتوصيل. منصة Kickbox.io تتحقق أداة Kickbox.io المدفوعة من عناوين البريد الإلكتروني في قائمتك لتخلصك من الارتدادات (رسائل البريد الإلكتروني التي لا يمكن تسليمها إما بسبب فشل الخادم، أو بسبب مشكلات البريد العشوائي)، وتُحسن موضع الحملة. على الرغم من أن الخدمة ليست مجانية، إلا أنها تُعَد استثمارًا جيدًا، حيث تعتمد تكلفة هذه الخدمة على عدد عناوين البريد الإلكتروني في قائمتك، وتتحقق من ٥٠٠ حساب الكتروني مقابل ٥ دولارات فقط، كما أنك لن تحتاج التحقق من قائمتك في كل مرة تُرسل فيها بريدًا إلكترونيًا، فقط اختر عدد مرات التكرار التي تتوافق مع سرعة نمو قائمتك وادفع مقدمًا. تكوين البريد الإلكتروني Email configuration من العوامل الأخرى المؤثرة على معدلاتك للتوصيل والتسليم نجد خادم بريدك الإلكتروني، إذ يتحقق اختبار تكوين بريدك الإلكتروني من خادمك ويقيس زمن الاستجابة. ويمكنك تطبيق هذا الاختبار أيضًا يدويًا، لكنك لن تحتاج إلى عمله كل يوم. منصة Mail-Tester Mail-Tester هي أداة مجانية لتكوين البريد الإلكتروني، حيث تقيس الأداة نسبة احتمالية احتساب البريد الإلكتروني بريدًا غير مرغوب فيه Spammy، وكل ما عليك فعله هو أن ترسل بريدًا إلكترونيًا تجريبيًا إلى عنوان البريد العشوائي الذي يقدمونه، لتتلقى درجة البريد الإلكتروني Spammyness score. وبالإضافة إلى ذلك، تلفت الأداة الانتباه إلى التفاصيل التي تحتاج إلى معالجة لتجنب تصفية البريد الإلكتروني إلى بريد عشوائي Spam أو إلى القوائم السوداء. منصة MX Toolbox ستحتاج إلى معرفة عنوان خادم بريدك الإلكتروني من أجل تكوينه، ومن الأدوات المجانية التي تُمكِّنك من هذا هي أداة MX Lookup. تشتمل الاختبارات المضمّنة في عملية المصادقة Authentication على وقت المعاملة SMTP، والتحقق من صحة اسم المضيف SMTP، وفحص شعار SMTP. اختبار التسليم يمكن تتبع (الارتداد) أو البريد الإلكتروني الذي فشل تسليمه من قِبل معظم مزودي خدمات البريد الإلكتروني، ولكن نظرًا لأنهم لا يوفرون مقاييس مثل هذه، فستحتاج إلى أداة خارجية من أجل تتبع معدلك للتسليم. منصة Everest لقد دُمِجت أداة اختبار معدلات التسليم الرائدة 250ok مع أداة ReturnPath مؤخرًا لإنشاء أداة جديدة تسمى Everest. حيث تُمكِّنك أداة Everest من إجراء التحسينات قبل الإرسال، والتحقق من الأداء، ومراجعة التحليلات بعد الإرسال. وتشمل مراقبة معدل التسليم كلًا من درجات السمعة، وتحذيرات IP، وشكاوى البريد العشوائي، ودرجات المرسل الإجمالية. درجات البريد العشوائي Spam Scores تٌعَد عوامل تصفية البريد العشوائي لعنة مسوقي البريد الإلكتروني. فبينما يوضح معدَّل التوصيل أن كل شيءٍ على ما يرام إلا أن معدل التسليم يظهر في حالة يرثى لها بسبب مجلدات البريد العشوائي التي تدمره. تتحقق أدوات اختبار الرسائل غير المرغوب فيها من مواضعك عند عملاء البريد الإلكتروني لترى إذا كنت مدرجًا في القائمة السوداء، وتجمع درجاتك من الشركات المختلفة لتصفية الرسائل غير المرغوب فيها. منصة Litmus توفِّر منصة Litmus خدمة اختبار البريد العشوائي لعملائها، كما يمكنها الإندماج مباشرةً مع مزود خدمة بريدك الإلكتروني. حيث يتحقق اختبار Litmus من رسائلك عبر عوامل تصفية البريد العشوائي المختلفة، ويصنف كل عامل على أنه ممرر، أو ثانوي، أو أساسي. وإذا حددت المنصة مشكلة ما، فستعلم المشكلات الأكثر أهمية. وتوجد العديد من الأدوات لاختبار عوامل تصفية البريد العشوائي لكن أشهرها أدوات، مثل: Email on Acid، وReturn Path، وSpam Assassin. منصة Is Not Spam تتوفر بعض أدوات اختبار البريد العشوائي المجانية التي تجري الفحوصات لمرة واحدة. تسمح منصة Is Not Spam لك بإرسال بريد إلكتروني اختباري إلى عنوان تجريبي لتتلقى درجة البريد العشوائي Spam Score، وتشمل الاختبارات التي يجرونها كلًا من تحقق نظام التعرف على هوية المرسل SPF، وتحقق معرف المرسل، وتحقق مفاتيح النطاق، وفحص DKIM، وفحص SpamAssassin. سطر موضوع البريد الإلكتروني واختبار الأداء لا يقتصر اختبار البريد الإلكتروني على الجانب التقني فقط، فيُمكننا أيضًا استخدام البريد الإلكتروني للتعرف على الجمهور. ها أنت تستخدم بعض مقاييس البريد الإلكتروني الأولية المتاحة في مزود خدمتك، في ظل وجود أداة مثل Vero يمكنها أن تمدك برؤى أكبر من أجل زيادة تفاعل المشتركين. تحتوي أيضًا هذه الأداة على إرشادات اجراء اختبار A/B، وهذه بعض الاختبارات التي يمكنك مراقبتها باستخدام Vero. أنماط سطر الموضوع والتخصيص يوجد الكثير من الأشياء لاختبارها فيما يخص سطر الموضوع، ويمكنك التحقق لترى ما إذا كان جمهورك يتفاعل بصورة أفضل مع كتابته بـ "طريقة العنوان" (أي جميع الكلمات الرئيسية تكون بأحرف كبيرة، بينما تُكتب الكلمات الصغيرة بحروف صغيرة)، أو بـ "طريقة الجملة" (وهي الطريقة التقليدية لاستخدام الأحرف الكبيرة في الجملة، أو كتابة الكلمة الأولى فقط وأي أسماء علم بأحرف كبيرة)، وهذا بالطبع إن كنت تكتب باللغة الإنجليزية، ولترى أيضًا هل يُفضلون سطر الموضوع الطويل، أم السطر القصير. وما إذا كنت ستستخدم رمزًا تعبيريًا أم لا. يمكنك أيضًا التلاعب في التخصيص داخل سطر الموضوع من خلال تضمين اسم المستلم، أو الصناعة، أو معرف الطلب، أو الموقع، .. أو المزيد. توقيت الإرسال المثالي ما هو أفضل وقت لإرسال رسالتك الإخبارية؟ بناءً على مكان وتفضيلات جمهورك المستهدف، كما ويمكنك زيادة معدلات فتح رسائلك عن طريق تغيير أوقات الإرسال. ولاختبار ذلك قسّم قائمة بريدك الإلكتروني إلى قسمين وحدد وقتين مختلفين من اليوم لإجراء الاختبار، بعدها ابدأ بالاحتفاظ بجزء واحد على أساس عنصر ضابط مع وقت الإرسال العادي، ثم أرسل بعض الرسائل الإخبارية إلى كل عنصر في الأوقات التي حددتها له، وشاهد اختلاف معدلات الفتح ونسب النقر إلى الظهور. نسخة CTA للحث على اتخاذ إجراء تُعَد رسائل البريد الإلكتروني للمعاملات فرصة اختبار رائعة، كونها تذهب دوريًا إلى المشتركين المتفاعلين، ونظرًا لكثرة إرسالها، فيمكنك استخدامها وسيلةً لضبط للرسائل، كما يمكن أن تمنحك العناوين الرئيسية، وأزرار الحث على اتخاذ إجراء رؤى أكبر حول الأشياء الأكثر إرضاءً للمشتركين. أنواع القوالب هل تستحق قوالب HTML المنمقة جهدًا إضافيًا أم يُفضل جمهورك قراءة نصًا بسيطًا؟ يمكنك موازنة أداء أنماط البريد الإلكتروني من خلال الاطلاع على توقيتات التفاعل، ومعدلات التحويل Conversion، قد يفيد أيضًا إجراء هذا الاختبار مع أنواع بريد إلكتروني مختلفة، فقد تجد أن العملاء يفضلون أن تكون الرسائل الترويجية منمقة، ولكن الرسائل الإخبارية يُفضلوها نصية بسيطة مثل تلك التي قد تصلهم من صديق. فيما يلي أمثلة على رسائل البريد الإلكتروني المنمقة مقابل رسائل البريد الإلكتروني ذات النص العادي. عناوين المرسل أحد العناصر الأخرى التي يجب مراعاتها هي عنوان المرسل، إذ يمكن أن يأتي البريد الإلكتروني الترحيبي مثلًأ (هو أول بريد إلكتروني يُرسل من الشركة إلى العميل المحتمل) من عنوان عام للشركة، أو من الرئيس التنفيذي، أو أن يُقدم في صورة مندوب خدمة العملاء. أجرِ اختبارًا لمعرفة كيف تؤثر هذه الاختلافات على معدلات الفتح والتسليم ومواضع البريد الوارد، ونسب النقر لكل ظهور. يمكنك إجراء الاختبارات المسبقة لعملية الإعداد Rendering ولمعدلات التسليم يدويًا، أو باستخدام أدوات مجانية لمرة واحدة فقط، لكن استخدامك الأدوات الآلية سيجعل اختباراتك أكثر كفاءة. ترجمة وبتصرّف للمقال Email testing: What you need to know to get started لصاحبته Stephanie Knapp. اقرأ أيضًا كيفية إجراء اختبار A/B للبريد الإلكتروني بطريقة صحيحة. مجموعة نصائح حول التسويق بالعمولة. التسويق بالبريد الإلكتروني وأهم الخطوات اللازمة لتنفيذ حملة تسويقية ناجحة. لمحة عامة عن الإعلانات الإلكترونية.1 نقطة
-
لدي قائمتين بالشكل التالي: keys = ['x', 'y', 'z'] values = ['abc', 123, True] ما هي أسهل طريقة لإنتاج القاموس التالي من خلال القوائم السابقة؟ {'x': 'abc', 'y': 123, 'z': True}1 نقطة
-
أريد أن أقوم باستدعاء دالة ما وبدء عداد counter وإذا وصل العداد إلى 10 ثوان ولم تنتهي الدالة بعد يتم إيقاف الدالة على الفور (حتى وإن لم تقم بعمل return ) أي كيف أقوم بعمل TimeOut لدالة ما في بايثون؟1 نقطة
-
سلام عليكم و رحمة الله سؤال في مكتبة TCPDF ازاي اخلي الخلية تحتوي النص ما يطلعش براها بالشكل ده ، احطلها خاصية warp text زي الاكسيل كده الكود الخاص بي <?php include("includes/db.php"); include("includes/functions.php"); $title = "التقارير"; $order_id = $_GET['id']; $uSql = "SELECT * FROM orders WHERE id='$order_id'"; if ($result = $conn->query($uSql)){ $order = $result->fetch_assoc() ?? false; $result->free(); } if (!$order){ header("location: ./"); } $date = $order['date'].''; $date = explode(" ",$date); $date = $date[0]; $query = "SELECT username, logo, qr FROM clients WHERE id=".$order['client_id'].""; $user = array(); if ($result = $conn->query($query)){ $user = $result->fetch_assoc() ?? false; $result->free(); } $client = $user['username']; $logo = $user['logo']; $qr = $user['qr']; $query = "SELECT * FROM order_general_info WHERE id=".$order['general_info_id'].""; if ($result = $conn->query($query)){ $info = $result->fetch_assoc() ?? false; $result->free(); } $season = $info['season']; $gender = $info['gender']; $query = "SELECT name FROM models WHERE id=".$info['model_id'].""; $model = array(); if ($result = $conn->query($query)){ $model = $result->fetch_assoc() ?? false; $result->free(); } $model = $model['name']; $query = "SELECT name FROM brands WHERE id=".$info['brand_id'].""; $brand = array(); if ($result = $conn->query($query)){ $brand = $result->fetch_assoc() ?? false; $result->free(); } $brand = $brand['name']; $query = "SELECT m.image, d.name FROM order_model m JOIN design_sub_category d ON m.sub_design_id = d.id WHERE m.order_id=".$order['id'].""; $model_sub = array(); if ($result = $conn->query($query)){ $model_sub = $result->fetch_assoc() ?? false; $result->free(); } $model_image = $model_sub['image']; $model_design_name = $model_sub['name']; $query = "SELECT total_count, sample_size FROM patron WHERE id=".$order['patron_id'].""; $patron = array(); if ($result = $conn->query($query)){ $patron = $result->fetch_assoc() ?? false; $result->free(); } $total_count = $patron['total_count']; $sample_size = $patron['sample_size']; $query = "SELECT DISTINCT title FROM size_table_sizes WHERE size_table_id=".$order['size_table_id'].";"; $size_table = array(); if ($result = $conn->query($query)){ while ($row= $result->fetch_assoc()){ array_push($size_table, $row) ; } $result->free(); } $sizes = ''; foreach ($size_table as $k => $size){ $sizes .= $size['title'] . ' - '; if ($k == count($size_table)-1) $sizes .= $size['title']; } $query = "SELECT DISTINCT cloth_composition_id FROM order_cloths WHERE order_id=".$order['id']." AND usage_id=15 ;"; $cloth_composition_id = array(); if ($result = $conn->query($query)){ $cloth_composition_id = $result->fetch_assoc() ?? false; $result->free(); } $cloth_composition_id = $cloth_composition_id['cloth_composition_id']; $query = "SELECT c.mix, f.name FROM cloths_fabric c JOIN fibers f ON c.fiberT = f.id WHERE c.item=".$cloth_composition_id.";"; $clothes = array(); if ($result = $conn->query($query)){ while ($row= $result->fetch_assoc()){ array_push($clothes, $row) ; } $result->free(); } $clothes_description = ''; foreach ($clothes as $k => $clothe){ if ($k == count($clothes)-1) $clothes_description .= '('.$clothe['name'].': %'. $clothe['mix'] . ') '; else $clothes_description .= '('.$clothe['name'].': %'. $clothe['mix'] . ') - '; } $size_table = array(); $query = "SELECT * FROM size_table WHERE id=".$order['size_table_id'].""; if ($result = $conn->query($query)){ $size_table = $result->fetch_assoc() ?? false; $result->free(); $query = "SELECT * FROM size_table_factors WHERE size_table_id=".$order['size_table_id'].""; $size_table_factors = array(); if ($result = $conn->query($query)){ while ($row= $result->fetch_assoc()){ array_push($size_table_factors, $row) ; } $result->free(); foreach ($size_table_factors as $k => $factor){ $size_table_factors[$k]['sizes'] = array(); $query = "SELECT * FROM size_table_sizes WHERE size_table_factor_id=".$factor['id'].""; if ($result = $conn->query($query)){ while ($row= $result->fetch_assoc()){ array_push($size_table_factors[$k]['sizes'], $row) ; } $result->free(); } } } } //var_dump($size_table_factors); exit; include('includes/tcpdf/tcpdf.php'); class MYPDF extends TCPDF { //Page header public function Header() { } // Page footer public function Footer() { $this->SetY(-15); $this->SetFont('tahoma', 'I', 6); $this->Cell(0, 10, 'Powered By GTMS - الريادة لنظم المعلومات', 0, false, 'C', 0, '', 0, false, 'T', 'M'); } } $pdf = new MYPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); // set header and footer fonts $pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN)); $pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); // set default monospaced font $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED); // set margins $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP-22, PDF_MARGIN_RIGHT); $pdf->SetHeaderMargin(PDF_MARGIN_HEADER); $pdf->SetFooterMargin(PDF_MARGIN_FOOTER); // set auto page breaks $pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM-15); // set image scale factor $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); // set some language dependent data: $lg = Array(); $lg['a_meta_charset'] = 'UTF-8'; $lg['a_meta_dir'] = 'rtl'; $lg['a_meta_language'] = 'fa'; $lg['w_page'] = 'page'; // set some language-dependent strings (optional) $pdf->setLanguageArray($lg); // --------------------------------------------------------- // use the font $pdf->SetFont('tahoma', '', 11, '', false); // Restore RTL direction $pdf->setRTL(true); // add a page $pdf->AddPage(); $pdf->SetLineStyle( array( 'width' => 1, 'color' => array(0,0,0))); //$pdf->Line(0,0,$pdf->getPageWidth()-15,0); $pdf->Line($pdf->getPageWidth()-15,5,$pdf->getPageWidth()-15,$pdf->getPageHeight()-27); $pdf->Line(5,5,5,$pdf->getPageHeight()-27); /* QR Line */ $pdf->SetFillColor(249, 219, 193); $pdf->Cell(26,10,"رقم العقد",1,0,'C',1); $pdf->Cell(24,10,$order_id,1,0,'C'); $pdf->Cell(26,10,"العميل",1,0,'C',1); $pdf->Cell(32,10,$client,1,0,'C'); $pdf->Cell(26,10,"البراند",1,0,'C',1); $pdf->Cell(26,10,$brand,1,1,'C'); $pdf->SetFillColor(193, 223, 249); $pdf->Cell(26,10,"الموسم",1,0,'C',1); $pdf->Cell(24,10,$season,1,0,'C'); $pdf->Cell(26,10,"التاريخ",1,0,'C',1); $pdf->Cell(32,10,$date,1,0,'C'); $pdf->Cell(26,10,"مقاس العينة",1,0,'C',1); $pdf->Cell(26,10,$sample_size,1,1,'C'); $pdf->SetFillColor(252, 229, 134); $pdf->Cell(26,10,"النوع",1,0,'C',1); $pdf->Cell(24,10,$gender,1,0,'C'); $pdf->Cell(26,10,"وصف المنتج",1,0,'C',1); $pdf->Cell(84,10,$model_design_name,1,1,'C'); $pdf->Image('./assets/images/clients/'.$logo, 5, 5, 30, 30, '', '', 'J', false, 300, 'J', false, false, 1, true, false, false); /* LOGO Line */ $pdf->Cell(26,10,"نوع القماش",1,0,'C',1); $pdf->Cell(134,10,$clothes_description,1,1,'C'); $pdf->Cell(26,10,"المقاسات",1,0,'C',1); $pdf->Cell(134,10,$sizes,1,1,'C'); $pdf->Cell(26,10,"عدد القطع",1,0,'C',1); $pdf->Cell(50,10,$total_count,1,0,'C'); $pdf->Cell(30,10,"الموديل",1,0,'C',1); $pdf->Cell(54,10,$model,1,1,'C'); $pdf->Image($qr, 5, 35, 30, 30, '', '', 'J', false, 300, 'J', false, false, 1, true, false, false); $pdf->Ln(15); // Header Ends $pdf->SetFillColor(193, 223, 249); $pdf->Cell(190, 12, 'جدول المقاسات', 1, 1, 'C', 1, '', 0, false, 'M', 'M'); $pdf->Ln(10); //$this->Image($image_file, 'C', 6, '', '', 'JPG', false, 'C', false, 300, 'C', false, false, 0, false, false, false); $pdf->Image('./assets/images/size_table/'.$size_table['image'], '', 100, $pdf->getPageWidth()/1.2, $pdf->getPageHeight()/2, '', '', 'C', false, 300, 'C', false, false, 1, true, false, false); /* سطر الملاحظات */ $pdf->Ln(); $pdf->SetXY(15, $pdf->getPageHeight()-28); $pdf->SetFont('tahoma', '', 12, '', false); $pdf->Cell(55, 0, 'مسؤول التشغيل الخارجي', 1, $ln=0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(40, 0, '', 1, $ln=0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(55, 0, 'مسؤول التشغيل الفني', 1, $ln=0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(40, 0, '', 1, $ln=1, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(55, 0, 'التوقيع', 1, $ln=0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(40, 0, '', 1, $ln=0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(55, 0, 'التوقيع', 1, $ln=0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(40, 0, '', 1, $ln=1, 'R', 0, '', 0, false, 'J', 'B'); // The Second Page // Header Ends $pdf->SetLineStyle( array( 'width' => 0.5, 'color' => array(0,0,0))); $pdf->SetFont('tahoma', '', 10, '', false); $pdf->SetFillColor(193, 223, 249); $col_width = intval(143 / (count($size_table_factors[0]['sizes'])+1)) ; foreach ($size_table_factors as $f => $factor){ /* The Header */ if ($f%13 == 0 || $f==0){ $pdf->AddPage(); $pdf->SetLineStyle( array( 'width' => 1, 'color' => array(0,0,0))); $pdf->Line($pdf->getPageWidth()-15,5,$pdf->getPageWidth()-15,$pdf->getPageHeight()-27); $pdf->Line(5,5,5,$pdf->getPageHeight()-27); // Header Starts /* QR Line */ $pdf->SetFillColor(249, 219, 193); $pdf->Cell(26,10,"رقم العقد",1,0,'C',1); $pdf->Cell(24,10,$order_id,1,0,'C'); $pdf->Cell(26,10,"العميل",1,0,'C',1); $pdf->Cell(32,10,$client,1,0,'C'); $pdf->Cell(26,10,"البراند",1,0,'C',1); $pdf->Cell(26,10,$brand,1,1,'C'); $pdf->SetFillColor(193, 223, 249); $pdf->Cell(26,10,"الموسم",1,0,'C',1); $pdf->Cell(24,10,$season,1,0,'C'); $pdf->Cell(26,10,"التاريخ",1,0,'C',1); $pdf->Cell(32,10,$date,1,0,'C'); $pdf->Cell(26,10,"مقاس العينة",1,0,'C',1); $pdf->Cell(26,10,$sample_size,1,1,'C'); $pdf->SetFillColor(252, 229, 134); $pdf->Cell(26,10,"النوع",1,0,'C',1); $pdf->Cell(24,10,$gender,1,0,'C'); $pdf->Cell(26,10,"وصف المنتج",1,0,'C',1); $pdf->Cell(84,10,$model_design_name,1,1,'C'); $pdf->Image('./assets/images/clients/'.$logo, 5, 5, 30, 30, '', '', 'J', false, 300, 'J', false, false, 1, true, false, false); /* LOGO Line */ $pdf->Cell(26,10,"نوع القماش",1,0,'C',1); $pdf->Cell(134,10,$clothes_description,1,1,'C'); $pdf->Cell(26,10,"المقاسات",1,0,'C',1); $pdf->Cell(134,10,$sizes,1,1,'C'); $pdf->Cell(26,10,"عدد القطع",1,0,'C',1); $pdf->Cell(50,10,$total_count,1,0,'C'); $pdf->Cell(30,10,"الموديل",1,0,'C',1); $pdf->Cell(54,10,$model,1,1,'C'); $pdf->Image($qr, 5, 35, 30, 30, '', '', 'J', false, 300, 'J', false, false, 1, true, false, false); $pdf->Ln(10); // Header Ends $pdf->SetFillColor(193, 223, 249); $pdf->Cell(47, 10, "عامل القياس", 1, 0, 'C', 1); $pdf->Cell($col_width, 10, "الرمز", 1, 0, 'C', 1); foreach ($size_table_factors[0]['sizes'] as $i => $size){ if ($i == count($size_table_factors[0]['sizes'])-1){ $pdf->Cell($col_width, 10, $size['title'], 1, 1, 'C', 1); } else { $pdf->Cell($col_width, 10, $size['title'], 1, 0, 'C', 1); } } } $pdf->Cell(47, 10, $factor['factor'], 1, 0, 'C', 1); $pdf->Cell($col_width, 10, $factor['code'], 1, 0, 'C', 1); foreach ($factor['sizes'] as $s => $size) { if ($s == count($factor['sizes'])-1){ $pdf->Cell($col_width, 10, $size['value'], 1, 1, 'C', 0); } else { $pdf->Cell($col_width, 10, $size['value'], 1, 0, 'C', 0); } } /* The Footer*/ if ($f%13 == 12 || $f== sizeof($size_table_factors)-1 ) { // Footer $pdf->SetXY(15, $pdf->getPageHeight() - 28); $pdf->SetFont('tahoma', '', 12, '', false); $pdf->Cell(55, 0, 'مسؤول التشغيل الخارجي', 1, $ln = 0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(40, 0, '', 1, $ln = 0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(55, 0, 'مسؤول التشغيل الفني', 1, $ln = 0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(40, 0, '', 1, $ln = 1, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(55, 0, 'التوقيع', 1, $ln = 0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(40, 0, '', 1, $ln = 0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(55, 0, 'التوقيع', 1, $ln = 0, 'R', 0, '', 0, false, 'J', 'B'); $pdf->Cell(40, 0, '', 1, $ln = 1, 'R', 0, '', 0, false, 'J', 'B'); } } //Close and output PDF document $pdf->Output('size_table_report.pdf', 'I'); //============================================================+ // END OF FILE size_table_report.pdf1 نقطة
-
هناك listTile معينة، تظهر للمستخدمين، لكن عندما يقوم المستخدم بإضافة شيء من بياناته وإرسالها إلى قاعدة البيانات، أريد أن تختفي هذه ال listTile1 نقطة
-
يمكنك الاستفادة من الطريقتين في مشروعك، فايربيس لتخزين البيانات التي تريد تحميلها بسرعة و تتطلب عمليات بحث.. مايتعلق بالمستخدمين.. كما يمكن الاستفادة من توثيق المستخدمين و تسجيل الدخول عن طريق مواقع ال social media وغيرها وكما التحقق من الحسابات للمستخدمين، لكن firebase تصبح غالية/مكلفة عند زيادة حجم البيانات فعليه الاستضافة المدفوعة تكون أرخص و تخزن عليها الصور مثلا.. روابط مفيدة: Firebase Database REST API مقالة: Integrate Firebase With PHP and Optimize Your Real Time Communication1 نقطة
-
كلا، لأنك ستسجل حساب ربما Whatsapp bussiniss أو ماشابه مثل حساب مطور.. طبعا ليس أنت من ستقوم بإرسال الرسائل, إنما تجلب الرقم من المستخدم، ثم ترسل طلبية POST كما في المثال للرقم المطلوب ومن ثم تنتظر من المستخدم إدخال الكود الذي أرسلته لتتم عملية التحقق.1 نقطة
-
مرحبا اخي الكريم شاكر لك ردك اخوي ولكن لدي استفسار لو تكرمت هذا الخدمه لا تسبب حجب لرقم المستعمل من التطبيق لنه سيكون ثابت بطبيعة الحال ؟ ايضا كيف نقدر نخلي التطبيق يتفاعل بشكل تلقائي في ارسال الرساله يعني مثلا ما معقوله اكون انا شغال 24 ساعه ارسل اكواد التفعيل اكيد في طريقة لفعل ذلك بشكل تلقائي1 نقطة
-
يوفر Whatsapp خدمة api يمكنك من خلالها إرسال أي رسالة عن طريق الانترنت بتحديد رابط مكون من رقم المستخدم مع بادئة خاصةب Whatsapp domain: مثلا من متصفح بشكل بسيط.. https://api.whatsapp.com/send?phone=XXXXXXXXXXXX الآن برمجيا باتباع التوثيق: POST /v1/messages { "preview_url": false | true, "recipient_type": "individual", "to": "whatsapp-id", "type": "text", "text": { "body": "your-text-message-content" //////// OTP } } رابط التوثيق: developers.facebook/docs/whatsapp/overview يوجد SDK كامل يمكنك الاطلاع عليه وتضمينه في مشروعك إن وجدت طريقة ناجحة يمكنك مشاركتها ليستفيد الآخرون، شكرا جزيلا1 نقطة
-
يوجد بعض الاخطاء البسيطة if (nationalId.value[0] != "0" || nationalId.value[0] != "1") { alert('يجب أن يبدأ الرقم الوطني بـ 0 أو 1'); } || بدل && وايضا if (clinicNumber.value < 0 || clinicNumber.value > 99) { alert('يجب أن يكون رقم العيادة أكبر من 0 وأقل من 100'); } شكرا1 نقطة
-
بالفعل، فمن الخطأ أن تقوم بإستخدام المتغيرات بداخل جملة SQL مباشرة خصوصًا إذا كان المتغير يحتوي على مدخلات من المستخدم، والحل هو إستخدام Bindings أي تمرير المتغيرات أثناء تنفيذ جملة SQL وهذا اللأمر يحمي البرنماج من ثغرة SQL injection التي قد تؤدي إلى تسريب أو حذف البيانات من قاعدة البيانات. يمكنك إستخدام هذه الطريقة من خلال تمرير مدخل ثاني يكون عبارة عن قائمة أو Tuple من المتغيرات المستخدمه في جملة SQL بنفس الترتيب، كالتالي: cursor.execute("SELECT * FROM settings WHERE id = ?", (setting,)) لاحظ تم إضافة فاصلة , بعد كلمة setting عند إستخدام متغير واحد فقط. كما يمكنك أن تستخدم متغيرات تحمل مسميات لتسهل هذا الأمر كالتالي: cursor.execute("SELECT * FROM settings WHERE id = :id AND name = :name", {'id': 15, 'name': 24})1 نقطة
-
قد تكون نسخة البرنامج (الاصدار) على الجهاز الذي تم عمل التصميم عليه تختلف عن نسخته على الجهاز الاخر بحيث ان النسخة التي صممتي عليها أحدث من النسخة على الجهاز الاخر تأكدي من أن النسختين متماثلتين ونفس الاصدار1 نقطة
-
مرحبًا @Meezo ML، يجب عليك قبل إستدعاء الحزمة إلى مشروعك تنصيب الحزمة. ويمكنك تنصيب الحزمة من خلال مدير الحزم pip الخاص بللغة بايثون من خلال الأمر: pip install -U scikit-learn scipy matplotlib بعد تنزيل الحزمة يجب أن تعمل المكتبة دون مشاكل. أما بالنسبة لبيثون 3 فيجب عليك تثبيتها من خلال مدير الحزم pip3 من خلال الأمر: pip3 install -U scikit-learn scipy matplotlib1 نقطة
-
يبدو أنك لم تقم بتثبيت المكتبة بشكل صحيح، يمكنك تثبيتها من خلال الأمر التالي: pip3 install -U scikit-learn scipy matplotlib العلم -U يعني upgrade لتحديث المكتبة أيضًا.1 نقطة
-
الخطأ هو عدم وجود sklearn في نظامك، ولحل المشكلة يجب تثبيت مكتبة sklearn ويمكنك ذلك عن طريق: أما فتح موجه الأوامر cmd وكتابة التعليمة التالية: pip install -U scikit-learn أو من خلال الكتابة على موجه الأوامر في anconda إن كنت تستخدمها: conda install scikit-learn أو من خلال الكتابة في jupyter notebook: pip install scikit-learn1 نقطة
-
1 نقطة
-
يمكن ذلك باستخدام مكتبة pands في البداية نقوم باستيراد المكتبة: import pandas as pd بعد ذلك نقوم بقراءة الملف وفق الرابط url: xls = pd.ExcelFile(url) بعد ذلك نقوم بحساب عدد ال sheet في الملف من خلال تطبيق len على قائمة الأسماء sheet_names، حيث أن xls.sheet_names قائمه تحوي أسماء ال sheet في الملف: namesheet=xls.sheet_names تعريف قائمة فارغة لوضع ال sheet فيها: frames=[] مرور حلقة على عدد ال sheet وقراءة كل شيت باستخدام read_excel من pandas وإضافتها للقائمه frames: for i in range(numsheet): df1 = pd.read_excel(xls,namesheet[i]) frames.append(df1) بعد ذلك نقوم بدمج جميع ال sheet الموجودة في frames باستخدام concat: df = pd.concat(frames) أي شيئ آخر؟1 نقطة
-
يمكن تحديد كل عنصر input من خلال التابع querySelector وتمرير محدد CSS إليه لتحديد كل عنصر input الذي نريده كالتالي: let name = document.querySelector('#name'); let nickname = document.querySelector('#nickname'); let nationalID = document.querySelector('#nationalID'); let clinicNumber = document.querySelector('#clinicNumber'); let address = document.querySelector('#address'); ثم يمكننا الحصول على محتوى كل عنصر من هذه العناصر عن طريق التابع value، بعد ذلك يمكن التحقق من هذه القيمة عن طريقة جملة if else if (name.value == "") { alert('يجب إدخال اسم'); } if (nickname.value == "") { alert('يجب إدخال اسم شهرة'); } if (nationalId.value.length != 11) { alert('يجب أن يكون الرقم الوطني 11 رقم'); } if (nationalId.value[0] != "0" || nationalId.value[0] != "1") { alert('يجب أن يبدأ الرقم الوطني بـ 0 أو 1'); } if (clinicNumber.value > 0 && clinicNumber.value < 100) { alert('يجب أن يكون رقم العيادة أكبر من 0 وأقل من 100'); } أسهل طريقة لعمل هذا الأمر هي إستعمال regular expression كالتالي: format = /^\w+( \w+)*$/ if (! format.test(address)) { alert('يجب أن يتكون العنوان من حروف وأرقام فقط'); } يمكن وضع كل جمل if هذه في حدث submit عند إرسال النموذج ليتم التحقق منها في كل مرة يضغط المستخدم على زر إرسال/حفظ.1 نقطة
-
يمكنك استخدام التابع sort الذي له الشكل التالي: list.sort(key = len,reverse=False) key و reverse وسيطان اختياريان. key يمثل تابع لتنفيذ الفرز على أساسه وفي حالتنا سنمرر له التابع len وبالتالي سيتم الفرز على أساس طول كل كلمة. في حال ضبط reverse على True سيكون الترتيب تنازلي. Hsoub = ['ali', 'ml', 'python'] Hsoub.sort(key = len,reverse=True) print(Hsoub) # output: ['python', 'ali', 'ml'] كما يمكنك استخدام هذا التابع لفرز العناصر داخل ال list مهما كان نوعها.1 نقطة
-
يعتمد الأمر على اتفاق فيما بينكم فيمكن أن قوم برفع سعر العقد فيما بينكما ليكون شاملاً للموقع نفسه و صيانة الدورية له او بالامكان عند حدوث مشكلة يتم دفع سعر حل المشكلة و لا يجب عليك أن تقوم بحل المشاكل مجانا, الأمر يرجع الى الاتفاق فيما بينكما و الأفضل لك أن قوم بفصل عقد برمجة الموقع عن عقد الصيانة الدورية, حتى تتمكن من تنظيم أعمالك و العقود1 نقطة
-
هناك عدة طرق في بايثون لإضافة متغيرات variables في سلاسل نصية string وهذه بعض الطرق بإستخدام التسلسل concatenation setting = input("Change settings") cr.execute("SELECT * FROM settings WHERE id = " + setting) setting = input("Change settings") cr.execute("SELECT * FROM settings WHERE id = " + str(setting)) بإستخدام العامل % setting = input("Change settings") cr.execute("SELECT * FROM settings WHERE id =%s"%setting) بإستخدام format setting = input("Change settings") cr.execute("SELECT * FROM settings WHERE id ={}".format(setting)) بإستخدام f-string setting = input("Change Settings ") print(f"SELECT * FROM settings WHERE id ={setting}")1 نقطة
-
بما أنك تستخدم VSCode والذي يعتبر محرر أكواد عام general purpose code editor ، بمعنى أنه يستخدم لأي لغة برمجة بشكل عام ، ولكن هناك متجر الإضافات الخاص ب VSCode والذي ستجد فيه الكثير من الإضافات الذي ستساعدك في البرمجة بأي لغة ويوجد ايضاً إضافات مخصصة لبايثون وأول إضافة يجب عليك تثبيتها هي python ويوجد فيها العديد من الميزات الذي ستساعدك بالتطوير ببايثون ، بالإكمال التلقائي وإظهار الأخطاء والإقتراحات وبما أن بايثون يعتمد بشكل أساسي على المسافات indentation فيمكنك تثبيت autopep8 بإستخدام pip pip install pep8 pip install --upgrade autopep81 نقطة
-
عادة يتم تسليم المشروع بعد التجريب من قبل المطور بقيمة X$ مثلا. وتنتهي الصفقة بعد تسليم الملفات. إن كان يريدك أن تكون جاهزا لمراجعة الشيفرة لمدة 4 شهور، إما تضيف قيمة ثابتة للعرض أنت تحدد قيمة الدعم الفني ل4 أشهر. أو يتم تسليم المشروع ثم يقوم بعمل مشروع آخر منفصل للدعم الفني. بما أنك من تقوم ببرمجة الموقع، يريدك صاحب العمل أن تتابع الموقع بنفسك مثلا لكي لا يقوم مبرمج آخر بالنظر للشيفرة البرمجية وربما نسخ أكواد منه. لذلك يريد دعم من نفس المطور وهو يفهم برنامجه بشكل أفضل أيضا. برأيي تقسيم المشروع لمرحلتين، تسليم الشيفرة و مرحلة الدعم الفني كل منهما لوحدها1 نقطة
-
عندما نريد تمرير أكثر من متغير، نمرر tuple كوسيط ثاني للدالة execute: في MySQLdb: cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3)) أما في sqlite3: cursor.execute("INSERT INTO table VALUES (?, ?, ?)", (var1, var2, var3)) ويمكن استخدام النمط الذي يدعم التسمية: (:var1, :var2, :var3) or (%(var1)s, %(var2)s, %(var3)s)1 نقطة
-
يمكنك استخدام الكود التالي setting = input("Change settings") cr.execute("SELECT * FROM settings WHERE id ='%s'", setting) حيث نعبر عن النص بالرمز %s ومن ثم نضع اسم المتغير بعد الفاصلة1 نقطة
-
عندما تقوم بسحب البيانات من الويب فانت تحصل عليها على شكل خام ثنائي binary أو byte type وعندما تقوم بحفظها في ملف بإستخدام الدالة المخصصة لذلك تحصل على هذا الخطأ TypeError: string argument expected, got 'bytes ومعناه أن دالة الحفظ تتوقع المدخل أن يكون من نوع سلسلة نصية string لحفظها في الملف ولكن حصلت على نوع byte type لهذا يجب عليك أولاً تحويل البيانات الذي تريد حفظها من نوع byte الى نوع string وإذا أردت تحويل من نوع byte b = b"content" type(b) # <class 'bytes'> الى نوع string فيمكنك إستخدام هذا الكود bytes = b'content' bytes.decode("utf-8") او يمكنك إستخدام هذا الكود ايضاً b = b'content' str(b, 'UTF-8')1 نقطة
-
أيضاً بالإضافة لاستخدام الدالة .decode بهذا الشكل .decode("utf-8") في الإصدار الثالث من python ال encoding الإفتراضي أصبح "utf-8" لذلك يمكنك استخدام الدالة بالشكل التالي في الإصدار الثالث print(b"Ahmed".decode())1 نقطة
-
هناك منطقة في الذاكرة memory تسمى بالتكدس Stack وهي خاصة بمتابعة سجل إستدعاء الدوال function calls ، فعلى سبيل المثال في دالتك التكرارية عند إستدعائها يتم تسجيلها في التكديس stack وعندما تقوم بإستدعائها مرة أخرى يتم تسجيل بشكل مكّدس يعني فوق الاستدعاء السابق function call وهكذا عند إستدعاء أي دالة يتم تكديسها فوق السابق وعندما الدالة تنتهي من عملها يتم إزالتها من التكديس والعودة الى الدالة الآخرى الذي قبلها وهكذا لكن الذاكرة memory محدودة ولها سجل تكديس محدود فعندما تتجاوز هذا الحد فإن هذا يؤدي الى فشل البرنامج وتسمى هذه المشكلة ب stack overflow لهذا لغة python قامت بوضع حد إفتراضي وهو 997 لكي لا تصل الى stack overflow ويمكنك تجاوز الحد الإفتراضي ولكن هذا خطر لأنك قد تتجاوز الحد النهائي ، وهذا الكود لتجاوز الحد الإفتراضي import sys sys.setrecursionlimit(1200) # هنا تقوم بتغيير الحد الإفتراضي لهذا إذا كانت الدالة تتجاوز الحد النهائي فيمكنك إستخدام الحلقات loops بدلاً من الدوال التكرارية recursive functions1 نقطة
-
يمكنك استخدام الدالة decode لتحويل من byte الى string , لاحظ المثال التالي print(b"Ahmed") سوف تكون النتيحة كالتالي b'Ahmed' بعد استخدام الدالة decode سوف يصبح شكل الكود كالتالي print(b"Ahmed".decode("utf-8")) النتيحة Ahmed1 نقطة
-
تعرفنا في القسم 2 على طريقتين لإضافة التنسيقات إلى التطبيق، الطريقة القديمة باستخدام ملف CSS واحد وطريقة التنسيق المباشر في المكان. مكتبات جاهزة للاستخدام مع واجهة المستخدم UI يمكن استخدام مكتبات جاهزة في إضافة التنسيقات إلى التطبيق. وتعتبر مكتبة الأدوات Bootstrap التي طورتها Twitter، من أكثر إطارات العمل مع واجهة المستخدم شعبية، والتي ستبقى كذلك ربما. ستجد حاليًا عددًا هائلًا من المكتبات التي تنسق واجهة المستخدم، ولديك خيارات واسعة جدًا لا يمكن حصرها في قائمة. تقدم العديد من إطارات عمل UI لمطوري تطبيقات الويب سمات جاهزة و"مكوّنات" كالأزرار والقوائم والجداول. ولقد وضعنا كلمة مكوّنات بين معترضتين لأننا لا نتكلم هنا عن مكوّنات React. تُستخدم إطارات عمل UI عادةً بإدراج ملفات CSS بالإضافة إلى ملفات JavaScript الخاصة بها ضمن التطبيق. تأتي العديد من إطارت عمل UI بنسخ مخصصة للاستخدام مع React، حيث حُوّلت العديد من مكوناتها إلى مكوّنات React. فهنالك نسخ مختلفة من Bootstap مخصصة للعمل مع React مثل reactstrap وreact-bootstrap. سنطلع تاليًا على إطاري العمل Bootstrap وMaterialUI. وسنستخدمهما لإضافة نفس التنسيقات إلى التطبيق الذي أنشأناه في فصل (مكتبة React-Router) من هذا القسم. استخدام إطار العمل React-Bootstrap لنلقي نظرة على Bootstrap مستخدمين الحزمة react-bootstrap. لنثبت هذه الحزمة إذًا: npm install react-bootstrap ثم سنضيف رابطًا لتحميل ملف تنسيق CSS الخاص بالإطار Bootstarp ضمن المعرّف <head> في الملف "public/index.html" <head> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous" /> // ... </head> سنلاحظ أنّ مظهر التطبيق قد أصبح أفضل عند إعادة تحميله: تصيّر جميع محتويات التطبيق عند استخدام Bootstrap داخل حاوية. ويتم ذلك عمليًا بإعطاء الصفة classname للعنصر <div> الجذري القيمة "container". const App = () => { // ... return ( <div className="container"> // ... </div> ) } وسنجد تأثير ذلك واضحًا على مظهر التطبيق، فلم يعد المحتوى ملاصقًا لحافة المتصفح كما كانت من قبل: لنجري بعض التعديلات على المكوّن Notes، بحيث يصيّر قائمة الملاحظات على شكل جدول. حيث تؤمن React-Bootstrap مكوّن الجدول جاهزًا للاستخدام، فلا حاجة لتعريف أية أصناف CSS مفصلة. const Notes = (props) => ( <div> <h2>Notes</h2> <Table striped> <tbody> {props.notes.map(note => <tr key={note.id}> <td> <Link to={`/notes/${note.id}`}> {note.content} </Link> </td> <td> {note.user} </td> </tr> )} </tbody> </Table> </div> ) لقد أصبح تنسيق التطبيق أفضل: لاحظ أنّه عليك إدراج مكونات React-Bootstrap بشكل منفصل من المكتبة كالتالي: import { Table } from 'react-bootstrap' النماذج لنحسّن نموذج تسجيل الدخول بمساعدة نماذج Bootstrap. تأتي المكتبة React-Bootstrap بمكوّنات مدمجة لإنشاء النماذج (على الرغم من النقص الواضح في توثيق هذا الموضوع): let Login = (props) => { // ... return ( <div> <h2>login</h2> <Form onSubmit={onSubmit}> <Form.Group> <Form.Label>username:</Form.Label> <Form.Control type="text" name="username" /> <Form.Label>password:</Form.Label> <Form.Control type="password" /> <Button variant="primary" type="submit"> login </Button> </Form.Group> </Form> </div> )} سيزداد عدد المكونات التي سندرجها شيئًا فشيئًا: import { Table, Form, Button } from 'react-bootstrap' سيبدو التطبيق بعد التحوّل إلى نموذج Bootstrap كالتالي: التنبيهات سنحسّن التنبيهات أيضًا في تطبيقنا بعد أن حسنّا مظهر النماذج.: لنضف رسالة إلى التنبيهات عندما يسجل المستخدم دخوله إلى التطبيق، وسنخزّنها ضمن المتغير message في حالة المكوّن App: const App = () => { const [notes, setNotes] = useState([ // ... ]) const [user, setUser] = useState(null) const [message, setMessage] = useState(null) const login = (user) => { setUser(user) setMessage(`welcome ${user}`) setTimeout(() => { setMessage(null) }, 10000) } // ... } سنصيّر الرسالة على أنها مكون تنبيه Bootstrap. ولاحظ مجددًا أنّ المكتبة React-Bootstrap ستزودنا بالمكون المقابل لمكون Recat. أدوات التنقل لنغيّر أخيرًا قائمة التنقل في التطبيق مستخدمين المكوّن Navbar من Bootstrap. تزوّدنا المكتبة Bootstrap بمكونات مدمجة مقابلة. لقد وجدنا من خلال المحاولة والخطأ حلًا مرضيًا، على الرغم من التوثيق غير الواضح: <Navbar collapseOnSelect expand="lg" bg="dark" variant="dark"> <Navbar.Toggle aria-controls="responsive-navbar-nav" /> <Navbar.Collapse id="responsive-navbar-nav"> <Nav className="mr-auto"> <Nav.Link href="#" as="span"> <Link style={padding} to="/">home</Link> </Nav.Link> <Nav.Link href="#" as="span"> <Link style={padding} to="/notes">notes</Link> </Nav.Link> <Nav.Link href="#" as="span"> <Link style={padding} to="/users">users</Link> </Nav.Link> <Nav.Link href="#" as="span"> {user ? <em>{user} logged in</em> : <Link to="/login">login</Link> } </Nav.Link> </Nav> </Navbar.Collapse> </Navbar> لشريط التنقل الناتج مظهرًا واضحًا ومريحًا: إن تغيّر حجم شاشة العرض على المتصفح، سنجد أن القائمة ستختفي تحت زر "الهامبرغر" وستظهر مجددًا بالنقر عليه: تقدم Bootstrap والعديد من أطر عمل UI تصاميم متجاوبة بحيث تُصيّر التطبيقات بشكل يناسب القياسات المختلفة لشاشات العرض. ستساعدك أدوات تطوير Chrome على محاكاة استخدام التطبيق ضمن متصفحات أنواع مختلفة من الهواتف النقالة: يمكنك أن تجد الشيفرة كاملة على GitHub. إطار العمل Material UI سنلقي نظرة الآن على المكتبة MaterialUI التي تعمل مع React، والتي تستخدم اللغة المرئية لتصميم المواد والمطوّرة من قبل Google. لنثبت هذه المكتبة: npm install @material-ui/core ثم سنضيف السطر التالي إلى المعرّف في الملف "public/index.html"، حيث يحمًل هذا السطر الخط "Roboto" من تصميم Google: <head> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /> // ... </head> سنستخدم الآن MaterialUI لتنفيذ نفس التغييرات التي أجريناها سابقًا باستخدام Bootstrap. سنصيّر التطبيق كاملًا ضمن حاوية: import Container from '@material-ui/core/Container' const App = () => { // ... return ( <Container> // ... </Container> ) } لنبدأ بالمكوّن Note ونصيّر قائمة الملاحظات ضمن جدول. const Notes = ({notes}) => ( <div> <h2>Notes</h2> <TableContainer component={Paper}> <Table> <TableBody> {notes.map(note => ( <TableRow key={note.id}> <TableCell> <Link to={`/notes/${note.id}`}>{note.content}</Link> </TableCell> <TableCell> {note.name} </TableCell> </TableRow> ))} </TableBody> </Table> </TableContainer> </div> ) ستبدو النتيجة كالتالي: إنّ إدراج كل مكون بشكل منفصل، هي إحدى المزايا غير المرضية في MaterialUI. إذ تكون قائمة المكوّنات التي سندرجها طويلة: import { Container, Table, TableBody, TableCell, TableContainer, TableRow, Paper, } from '@material-ui/core' النماذج سنحسّن مظهر نموذج تسجيل الدخول باستخدام المكوّنين TextField وButton const Login = (props) => { const history = useHistory() const onSubmit = (event) => { event.preventDefault() props.onLogin('mluukkai') history.push('/') } return ( <div> <h2>login</h2> <form onSubmit={onSubmit}> <div> <TextField label="username" /> </div> <div> <TextField label="password" type='password' /> </div> <div> <Button variant="contained" color="primary" type="submit"> login </Button> </div> </form> </div> ) } وستكون النتيجة كالتالي: لا تؤمن MaterialUi مكون خاص بالنماذج كما في Bootstrap. فالنموذج هنا هو نموذج HTML نظامي. وتذكر أن تدرج كل المكوّنات الموجودة في النموذج. التنبيهات يمكن عرض التنبيهات باستخدام المكون Alert والذي يشابه تمامًا المكون المقابل في Bootstarp: <div> {(message && <Alert severity="success"> {message} </Alert> )} </div> لا تتضمن الحزمة الأساسية للإطار MaterialUI المكوّن Alert بعد، لذلك لا بد من تثبيت الحزمة lab لاستخدامه: npm install @material-ui/lab وبعدها يمكننا إدراج المكوّن كالتالي: import { Alert } from '@material-ui/lab' سيبدو التنبيه أنيق المظهر: أدوات التنقل يمكننا إدراج أدوات التنقل باستخدام المكوّن AppBar . فلو استعملنا المثال الوارد في التوثيق: <AppBar position="static"> <Toolbar> <IconButton edge="start" color="inherit" aria-label="menu"> </IconButton> <Button color="inherit"> <Link to="/">home</Link> </Button> <Button color="inherit"> <Link to="/notes">notes</Link> </Button> <Button color="inherit"> <Link to="/users">users</Link> </Button> <Button color="inherit"> {user ? <em>{user} logged in</em> : <Link to="/login">login</Link> } </Button> </Toolbar> </AppBar> سيفي ذلك بالغرض، لكن يمكن أن نحسن شريط التنقل أكثر: حيث ستجد طرقًا أفضل ضمن التوثيق. إذ يمكن استخدام خصائص المكوّن لتحديد الطريقة التي يُصير بها العنصر الجذري لمكوّن MaterialUI، وذلك كما يلي: <Button color="inherit" component={Link} to="/"> home </Button> سيُصيّر المكون Botton بجيث يكون مكوّنه الأب (الجذري) هو المكوّن Link من المكتبة react-router-dom ويستقبل مساره من خلال الخاصية to. ستكون شيفرة أدوات التنقل كالتالي: <AppBar position="static"> <Toolbar> <Button color="inherit" component={Link} to="/"> home </Button> <Button color="inherit" component={Link} to="/notes"> notes </Button> <Button color="inherit" component={Link} to="/users"> users </Button> {user ? <em>{user} logged in</em> : <Button color="inherit" component={Link} to="/login"> login </Button> } </Toolbar> </AppBar> وستظهر النتيجة كالتالي: ستجد الشيفرة كاملة على GitHub. أفكار ختامية ليس هناك خلاف واسع بين react-bootstrap وMaterialUI. وسيكون الخيار لك. ويشير معد هذا المنهاج إلى أنه استخدم MaterialUi كثيرًا وكانت انطباعاته الأولى إيجابية. فتوثيق المكتبة أفضل قليلًا. وبناء على إحصاءات موقع https://www.npmtrends.com/ الذي يتابع شعبية مكتبات npm المختلفة فقد تجاوزت شعبية MaterialUI مكتبةReact-Bootstrap عام 2020: لقد استخدمنا في الأمثلة السابقة إطارات عمل UI بالاستعانة بمكتبات React-integration بدلًا من المكتبة React Bootstrap. وقد كان بمقدورنا استخدامها مباشرةً بتعريف أصناف CSS ضمن عناصر HTML في التطبيق، بدلًا من تعريف الجدول كمكوّن آخر هو Table على سبيل المثال: <Table striped> // ... </Table> حيث يمكننا استخدام جدول HTML اعتيادي وتزويده بصنف CSS المطلوب: <table className="table striped"> // ... </table> لن تتوضح فكرة استخدام React-Bootstrap من خلال هذا المثال. فبالإضافة إلى جعل شيفرة الواجهة الأمامية أكثر اختصارًا وأكثر قابلية للقراءة، فالفائدة الأخرى لاستخدام مكتبات React UI هي أنها تتضمن شيفرة JavaScript اللازمة لعمل بعض المكونات الخاصة. إذ تتطلب بعض مكونات React اعتماديات JavaScript والتي نفضل عدم إدراجها في تطبيقات React. إنّ بعض سلبيات استخدام إطارات عمل UI من خلال مكتبات التكامل بدلًا من استخدامها مباشرةً، هي احتمال وجود واجهات برمجية غير مستقرة ضمن تلك المكتبات أو أنها قد تعاني نقصًا في توثيقها. إلّا أنّ حالة Semantic UI React مثلًا أفضل بكثير من غيرها من إطارت عمل UI، كونها مكتبة تكامل رسمية من مكتبات React. وهنالك أيضًا تساؤلات حول ضرورة أو عدم ضرورة استخدام إطارت عمل UI أصلًا. وهذا أمر عائد لكل شخص. أما بالنسبة إلى الأشخاص الذين تنقصهم المعرفة بتنسيقات CSS وتصميمات الويب فهي قطعًا أدوات مفيدة. أطر عمل UI أخرى سنقدم لك قائمة بأكثر عمل UI قد تناسبك. فإن لم تجد الإطار المفضل لديك موجودًا بينها، فقدم طلب إلغاء هذه القائمة من مادة المنهاج. https://bulma.io https://ant.design https://get.foundation https://chakra-ui.com https://tailwindcss.com المكتبة Styled components هناك أيضًا طرق أخرى في تنسيق تطببيقات React لم نأت على ذكرها بعد. منها المكتبة والتي تقدم طريقة مهمة في تنسيق العناصر من خلال قوالب موسومة مجردة ظهرت للمرة الأولى مع ES6. لنجري بعض التغييرات على تنسيق تطبيقنا بمساعدة المكتبة السابقة، لكن علينا أولًا تثبيت الحزمة كالتالي: npm install styled-components لنعرّف مكوّنين يحملان معلومات تنسيق كالتالي: import styled from 'styled-components' const Button = styled.button` background: Bisque; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid Chocolate; border-radius: 3px; ` const Input = styled.input` margin: 0.25em; ` تنشئ الشيفرة السابقة نسختين تحملا تنسيقًا من عنصرين هما input وbutton، ومن ثم تسندهما إلى متغيرين يحملان الاسم ذاته. إنّ الطريقة التي يُعرّف بها التنسيق مميزة، حيث عُرّفت قواعد تنسيق CSS داخل أقواس. وسيعمل المكوّن الذي يحمل تنسيقًا بنفس الطريقة التي يعمل بها الزر أو عنصر الإدخال النظاميين، كما يمكن استخدامهما بالطريقة ذاتها: const Login = (props) => { // ... return ( <div> <h2>login</h2> <form onSubmit={onSubmit}> <div> username: <Input /> </div> <div> password: <Input type='password' /> </div> <Button type="submit" primary=''>login</Button> </form> </div> ) } لننشئ عدة مكونات أخرى لتنسيق هذا التطبيق، وهي عناصر خاضعة للتنسيق كالتالي: const Page = styled.div` padding: 1em; background: papayawhip; ` const Navigation = styled.div` background: BurlyWood; padding: 1em; ` const Footer = styled.div` background: Chocolate; padding: 1em; margin-top: 1em; ` لنستخدم هذه المكوّنات في التطبيق: const App = () => { // ... return ( <Page> <Navigation> <Link style={padding} to="/">home</Link> <Link style={padding} to="/notes">notes</Link> <Link style={padding} to="/users">users</Link> {user ? <em>{user} logged in</em> : <Link style={padding} to="/login">login</Link> } </Navigation> <Switch> <Route path="/notes/:id"> <Note note={note} /> </Route> <Route path="/notes"> <Notes notes={notes} /> </Route> <Route path="/users"> {user ? <Users /> : <Redirect to="/login" />} </Route> <Route path="/login"> <Login onLogin={login} /> </Route> <Route path="/"> <Home /> </Route> </Switch> <Footer> <em>Note app, Department of Computer Science 2020</em> </Footer> </Page> ) } ستظهر النتيجة كالتالي: تزداد شعبية هذه المكتبة في الآونة الأخيرة، وقد اعتبرها العديد من المطورين بأنها أفضل الطرق في تنسيق تطبيقات React. التمارين يمكن أن تجد التمارين التي تتعلق بمواضيع هذا الفصل، في الفصل الأخير من هذا القسم ضمن مجموعة التمارين (توسيع تطبيق قائمة المدونات). ترجمة -وبتصرف- للفصل More About Styles من سلسلة Deep Dive Into Modern Web Development1 نقطة