From 32077abf5aa0523787028f0dc3dfbd42b6841ad5 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 7 Nov 2024 20:39:53 +0530 Subject: [PATCH 1/7] Localisation updates from https://translatewiki.net. (#5569) Translation updates --- app/src/main/res/values-ar/strings.xml | 96 ++++++++++----------- app/src/main/res/values-pcm-rNG/strings.xml | 18 ++-- app/src/main/res/values-pt-rBR/strings.xml | 18 ++-- app/src/main/res/values-sw/strings.xml | 16 ++-- 4 files changed, 74 insertions(+), 74 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 37238d2f821..251a04d4bcd 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -24,13 +24,13 @@ تشغيل الصوت إيقاف الصوت مؤقتًا صوت %s غير متوفر. - موافق + نعم إلغاء لغة الصوت غير متصل بالإنترنت من فضلك تأكد من وجود اتصال بالإنترنت عن طريق شبكة الوايفاي أو بيانات الهاتف، ثم حاول مرة أخرى. - حسنًا - موافق + نعم + نعم إلغاء متصل بالإنترنت عن طريق بيانات الهاتف تشغيل الصوت عبر الإنترنت قد يستخدم الكثير من بيانات الهاتف. @@ -92,10 +92,10 @@ الموضوع: %s موضوع المواضيع قيد التقدم - فصل %s: %s - تم انهاء الفصل %s الذي يحمل عنوان %s - الفصل %s الذي يحمل عنوان %s ما زال قيد التقدم - قم بإنهاء الفصل %s: %s لكي تفتح هذا الفصل. + الفصل %1$s: %2$s + تم الانتهاء من الفصل %1$s بعنوان %2$s + الفصل %1$s بعنوان %2$s قيد التنفيذ + أكمل الفصل %1$s : %2$s لفتح هذا الفصل. أكمل الفصل السابق لفتح هذا الفصل. أدخل النص. أدخل كسر عشري في الصيغة (بسط/مقام)، أو عدد كسري في الصيغة (عدد بسط/مقام). @@ -150,7 +150,7 @@ المواضيع التي تم تنزيلها تم التنزيل وضع الممارسة - السؤال %s من أصل %s + السؤال %1$s من %2$s تمّ تم الإنتهاء تم الإنتهاء من جميع الأسئلة! يمكنك بدأ مجموعة أسئلة أخرى، أو العودة للموضوع. @@ -169,11 +169,11 @@ من فضلك إبدأ إجابتك برقم (0 او 0.5 على سبيل المثال) من فضلك قم بإدخال رقم صالح. (–يمكن أن تحتوي الإجابة على 15 رقمًا (0-9) على الأكثر أو الرموز (. أو - من فضلك قم بكتابة نسبة تتكون من أرقام مفصولة بنقطتين رأسيتين (1:2 أو 1:2:3 على سبيل المثال). - من فضلك أدخل نسبة صحيحة (1:2 أو 1:2:3 على سبيل المثال) - إجابتك تحتوي على نقطتين رأسيتين (:) متتاليتين. - عدد الحدود لا يساوي العدد المطلوب. - لا يمكن أن تحتوي النسب على 0 كعنصر. + يرجى كتابة نسبة تتكون من أرقام مفصولة بعلامات نقطية (على سبيل المثال 1:2 أو 1:2:3). + الرجاء إدخال نسبة صالحة (على سبيل المثال 1:2 أو 1:2:3). + إجابتك تحتوي على علامتي نقطتين (:) بجانب بعضهما البعض. + عدد المصطلحات لا يساوي عدد المصطلحات المطلوبة. + لا يمكن للنسب أن تحتوي على 0 كعنصر. الحجم مجهول %s بايت %s ك.ب @@ -181,7 +181,7 @@ %s ج.ب صحيح! الموضوع: %s - + و لا فصل فصلًا واحدًا فصلان @@ -189,7 +189,7 @@ %s من الفصول %s من الفصول - + و لا قصة قصة واحدة قصتان @@ -198,14 +198,14 @@ %s من القصص - و لا فصل مكتمل - فصل %s مكتمل من %s - فصلان %s مكتملان من %s - %sفصول مكتملة من %s - %s فصول مكتملة من %s - %s فصول مكتملة من %s + لا فصل + %1$s من %2$s فصل مكتمل + فصلان + فصول قليلة + فصول كثيرة + %1$s من %2$s فصل مكتمل - + و لا درس درسًا واحدًا درسان @@ -213,7 +213,7 @@ %s درس %s درس - + و لا قصة مكتملة قصة واحدة مكتملة قصتان مكتملتان @@ -221,7 +221,7 @@ %s قصة مكتملة %s قصة مكتملة - + و لا موضوع في تقدم موضوع واحد في تقدم موضوعان في تقدم @@ -286,7 +286,7 @@ تخطي التالي ابدأ - شاشة العرض %s من %s + الشريحة %1$s من %2$s أهلًا، %s! من فضلك أدخل رقم التعريف الشخصي الخاص بالمشرف. من فضلك أدخل رقم التعريف الشخصي الخاص بك. @@ -351,7 +351,7 @@ اختيار من المعرض إعادة تسمية الملف الشخصي الاسم الجديد - حفظ + يحفظ إعادة ضبط رقم التعريف الشخصي أدخل رقم تعريف شخصي جديد للمستخدم كي يستخدمه عند الدخول إلى ملفه الشخصي. رقم تعريف شخصي مكون من 3 أرقام @@ -384,12 +384,12 @@ تم تثبيت آخر إصدار في %s. قم باستخدام رقم الإصدار في الأعلى للإبلاغ عن أعطال التطبيق. إصدار التطبيق لغة التطبيق - لغة الصوت الافتراضية + لغة الصوت المفضلة حجم النص المقروء حجم النص المقروء سيظهر نص القصة بهذا الشكل. أ - الصوت الافتراضي + اللغة الصوتية المفضلة لغة التطبيق حجم النص المقروء صغير @@ -419,7 +419,7 @@ لا يوجد تلميح جديد متاح إظهار الملاحظات والحل تلميح %s - العودة للسابق + يغلق الملاحظات عرض الحل عرض الحل @@ -444,26 +444,26 @@ تحريك العنصر إلى الأعلى عند %s أعلى أسفل - %s %s + %1$s %2$s topic_revision_recyclerview_tag ongoing_recycler_view_tag برجاء اختيار خيار واحد على الأقل. - إصدار تطبيق غير مدعوم - هذا الإصدار من التطبيق لم يعد مدعومًا. من فضلك قم بتحديث التطبيق من خلال متجر بلاي (Play Store) - إغلاق التطبيق + إصدار التطبيق غير مدعوم + لم يعد هذا الإصدار من التطبيق مدعومًا. يرجى تحديثه عبر متجر Play. + إغلاق التطبيق بناء المطور ألفا بيتا إشعار بيتا مرحبًا! يتم الآن تحديث تطبيقك إلى الإصدار التجريبي. إذا واجهت مشاكل أثناء استخدام التطبيق ، أو كانت لديك أسئلة ، فيرجى الاتصال بنا على android-feedback@oppia.org. لا تظهر هذه الرسالة مرة أخرى - نعم + نعم إشعار التوفر العام مرحبًا! يتم الآن تحديث تطبيقك إلى إصدار التوفر العام. إذا واجهت مشاكل أثناء استخدام التطبيق ، أو كانت لديك أسئلة ، فيرجى الاتصال بنا على android-feedback@oppia.org. لا تظهر هذه الرسالة مرة أخرى - نعم - إلى - أدخل نسبة في الصيغة س:ص. + نعم + ل + أدخل النسبة بالصيغة x:y. انقر هنا لإدخال نص. أصغر حجمًا للنص أكبر حجمًا للنص @@ -502,26 +502,26 @@ ما هي %s؟ من هو المشرف؟ كيف يمكنني إنشاء ملف تعريف(حساب) جديد؟ - كيف يمكنني الحصول على التطبيق بلغتي؟ - وجدت خلل. كيف يمكنني الإبلاغ عنه؟ - لماذا هناك فقط دروس رياضيات؟ - هل ستقومون بإنشاء مزيد من الدروس؟ + كيف أحصل على التطبيق باللغة الخاصة بي؟ + لقد وجدت خطأً. كيف يمكنني الإبلاغ عنه؟ + لماذا هناك دروس الرياضيات فقط؟ + هل ستقوم بعمل المزيد من الدروس؟ لماذا لا يتم تحميل مشغل الاستكشاف؟ لماذا لا يتم تشغيل الصوت الخاص بي؟ كيف يمكنني حذف ملف التعريف(حساب)؟ - كيف يمكنني تحديث التطبيق؟ - كيف يمكنني تحديث نظام التشغيل Android الخاص بي؟ + كيف أقوم بتحديث التطبيق؟ + كيف أقوم بتحديث نظام التشغيل Android الخاص بي؟ لا أجد سؤالي هنا. ماذا الان؟ <p>%1$s <i>\"أو-بي-يا\"</i>(فنلندية) - \"للتعلم\"</p><p><br></p><p>%1$sمهمتنا هي مساعدة أي شخص على تعلم أي شيء يريده بطريقة فعالة وممتعة.</p><p><br></p><p>من خلال إنشاء مجموعة من الدروس المجانية عالية الجودة والفعالة بشكل واضح بمساعدة معلمين من جميع أنحاء العالم ، تهدف %1$s إلى تزويد الطلاب بتعليم جيد - بغض النظر عن مكان وجودهم أو الموارد التقليدية التي يمكنهم الوصول إليها.</p><p><br></p><p>كطالب ، يمكنك أن تبدأ مغامرتك التعليمية من خلال تصفح الموضوعات المدرجة في الصفحة الرئيسية!</p> - <p>المشرف هو المستخدم الرئيسي الذي يدير ملفات التعريف والإعدادات لكل ملف تعريف على حسابه. هم على الأرجح والدك أو معلمك أو وصي عليك الذي أنشأ هذا الملف الشخصي لك.</p><p><br></p><p>يمكن للمسؤولين إدارة الملفات الشخصية وتعيين أرقام التعريف الشخصية وتغيير الإعدادات الأخرى ضمن حساباتهم. بناءً على ملف التعريف الخاص بك ، قد تكون أذونات المسؤول مطلوبة لبعض الميزات مثل تنزيل الموضوعات وتغيير رقم التعريف الشخصي وغير ذلك.</p><p><br></p><p>لمعرفة من هو المسؤول لديك ، انتقل إلى منتقي الملف الشخصي. الملف الشخصي الأول المدرج ولديه \"المسؤول\" مكتوب باسمه هو المسؤول.</p> - <p>إذا كانت هذه هي المرة الأولى التي تنشئ فيها ملفًا شخصيًا وليس لديك رقم تعريف شخصي: <ol><li> من منتقي الملف الشخصي ، اضغط على<strong> قم بإعداد ملفات تعريف متعددة</strong></li><li> قم بإنشاء رقم تعريف شخصي و<strong>احفظ</strong></li><li> املأ جميع البيانات للملف الشخصي.<ol><li>(اختياري) قم بتحميل صورة.</li> <li>إدخال اسم.</li> <li>(اختياري) قم بتعيين رقم تعريف شخصي مكون من 3 أرقام.</li></ol></li><li> اضغط<strong>إنشاء</strong> . تمت إضافة هذا الملف الشخصي إلى منتقي ملف التعريف الخاص بك!</li></ol></p><p> إذا قمت بإنشاء ملف تعريف من قبل ولديك رقم تعريف شخصي:<ol><li> من منتقي الملف الشخصي ، اضغط على<strong>إضافة الملف الشخصي</strong></li> <li> أدخل رقم التعريف الشخصي الخاص بك وانقر فوق<strong>إرسال</strong></li><li> املأ جميع الحقول للملف الشخصي. <ol> <li>(اختياري) قم بتحميل صورة.</li> <li>إدخال اسم.</li> <li>(اختياري) قم بتعيين رقم تعريف شخصي مكون من 3 أرقام. </li></ol></li><li> اضغط<strong>إنشاء</strong> . تمت إضافة هذا الملف الشخصي إلى منتقي ملف التعريف الخاص بك!</li></ol></p><br><p> ملاحظة: فقط ال<u>مدير</u> قادر على إدارة الملفات الشخصية.</p> + <p>المدير هو المستخدم الرئيسي الذي يدير الملفات الشخصية وإعدادات كل ملف على حسابه. من المحتمل أن يكونوا والديك أو معلمك أو وليكك الشخص الذي قام بإنشاء هذا الملف الشخصي لك.</p><p><br/></p><p>لدى المديرين القدرة على إدارة الملفات الشخصية وتعيين الرمز الرقمية والتحديد الإعدادات الأخرى تحت حسابهم. اعتمادا على ملفك الشخصي، قد تكون هناك حاجة إلى إذنات الإدارة لميزات معينة مثل تغيير رمز الرقم الشخصي، وغيرها.</p><p><br/></p><p>لمعرفة من هو مديرك، اذهب إلى خيار الملفات الشخصية. أول ملف تعريف مدرج ويتم كتابة \"المنظم\" تحت اسمهم هو المدير.</p> + <p>إذا كانت هذه هي المرة الأولى التي تقوم فيها بإنشاء ملف تعريف و لم يكن لديك رمز PIN: من خيار الملف تعريف، اضغط على إعداد ملف تعبير عدة. إخلق رقم PIN و حفظ. املأ جميع الحقول للملف الشخصي. (اختياري) تحميل صورة. أدخل اسمك (اختياري) تعيين رقم PIN 3 رقمي. اضغط على \"إنشاء\" هذا الملف الشخصي يضاف إلى اختيار الملف الشخصى الخاص بك!<ol><li>من اختيار الملفات الشخصية، اضغط على إعداد الملفات الملفات الأخرى.</li><li><strong>إبق</strong> رقم PIN و حفظ.</li><li>املأ جميع الحقول للملف الشخصي. (اختياري) تحميل صورة. أدخل اسمك (اختياري) تعيين رقم PIN 3 رقمي.<ol><li> (اختياري) تحميل صورة.</li><li>أدخل اسمك</li><li> (اختياري) تعيين رقم PIN 3 رقمي.</li></ol></li><li>اضغط على \"إنشاء\" هذا الملف الشخصي يضاف إلى اختيار الملف الشخصى الخاص بك!</li></ol></p><p>إذا كنت قد أنشأت ملفاً ملفاَ ملفاُ ملفاٌ ملفاٍ ملفاِ ملفا ً ملفا (PIN): من خيار الملفاً الملفاَ، اضغط على إضافة الملفاُ الملفاِ. أدخل رقم رسومك وتقرّب على \"إرسال\". املأ جميع الحقول للملف الشخصي. (اختياري) تحميل صورة. أدخل اسمك (اختياري) تعيين رقم PIN 3 رقمي. اضغط على \"إنشاء\" هذا الملف الشخصي يضاف إلى اختيار الملف الشخصى الخاص بك!<ol><li>من اختيار الملفات الشخصية، اضغط على إضافة الملفات.</li><li>أدخل رقم رسومك وتقرّب على \"<strong>إرسال</strong>\".</li><li>املأ جميع الحقول للملف الشخصي. (اختياري) تحميل صورة. أدخل اسمك (اختياري) تعيين رقم PIN 3 رقمي.<ol><li> (اختياري) تحميل صورة.</li><li>أدخل اسمك</li><li> (اختياري) تعيين رقم PIN 3 رقمي.</li></ol></li><li>اضغط على \"إنشاء\" هذا الملف الشخصي يضاف إلى اختيار الملف الشخصى الخاص بك!</li></ol></p><p><br/></p><p>ملاحظة: إدارة الملفات الشخصية يمكن <u>المدير</u> فقط.</p> <p>التطبيق %s يدعم حاليا اللغة الإنجليزية والبرتغالية البرازيلية والعربية والسواحيلية والبيدجينية النيجيرية. اختر إحدى هذه اللغات من القائمة، في الخيارات. لطلب التطبيق باللغة التي تتحدث بها، يرجى الاتصال بنا على <strong>admin@oppia.org</strong>.</p> <p><ol><li>من خلال %s شاشة التطبيق الرئيسية، اضغط على القائمة في أعلى الزاوية اليسرى</li><li>اضغط على <strong>مشاركة التعليقات</strong>.</li><li>اتبع التعليمات للإبلاغ عن الأخطاء أو مشاركة التعليقات</li></ol></p> <p>%1$s مهمتها مساعدة المتعلمين على اكتساب المهارات الحياتية اللازمة. الرياضيات مهارة أساسية في الحياة اليومية1%$s سوف تقدم دروسا جديدة في العلوم وغيرها من المواد قريبا!</p> <p>أجل, %s سوف تقدم دروسا جديدة في العلوم وفي مواد أخرى قريبا. الرجاء زيارتنا مجددا لمعرفة كل ماهو جديد!</p> - <p>إذا لم يتم تحميل مشغل الاستكشاف</p><p><br></p><p>تحقق لمعرفة ما إذا كان التطبيق محدثًا أم لا:</p><p> <ul> <li> انتقل إلى متجر Play وتأكد من تحديث التطبيق إلى أحدث إصدار </li></ul><p><br></p><p>تحقق من اتصالك بالإنترنت:</p><ul><li> إذا كان اتصالك بالإنترنت بطيئًا ، فحاول إعادة الاتصال بشبكة Wi-Fi أو الاتصال بشبكة أخرى. </li></ul><p>اطلب من المشرف التحقق من أجهزتهم واتصال الإنترنت:</p><ul><li> اطلب من المشرف استكشاف الأخطاء وإصلاحها باستخدام الخطوات المذكورة أعلاه </li></ul><p>أخبرنا إذا كنت لا تزال تواجه مشكلات في التحميل:</p><ul><li> أبلغ عن مشكلة عن طريق الاتصال بنا على admin@oppia.org. </li></ul> - <p>إذا لم يتم تشغيل الصوت الخاص بك</p><p><br></p><p>تحقق لمعرفة ما إذا كان التطبيق محدثًا أم لا:</p><ul><li>انتقل إلى متجر Play وتأكد من تحديث التطبيق إلى أحدث إصدار</li></ul><p><br></p><p>تحقق من اتصالك بالإنترنت:</p><ul><li>إذا كان اتصالك بالإنترنت بطيئًا ، فحاول إعادة الاتصال بشبكة Wi-Fi أو الاتصال بشبكة أخرى. قد يتسبب الإنترنت البطيء في تحميل الصوت بشكل غير منتظم ، مما يجعل من الصعب تشغيله.</li></ul><p><br></p><p>اطلب من المسؤول التحقق من أجهزتهم واتصال الإنترنت:</p><ul><li>اطلب من المسؤول استكشاف الأخطاء وإصلاحها باستخدام الخطوات المذكورة أعلاه</li></ul><p><br></p><p>أخبرنا إذا كنت لا تزال تواجه مشكلات في التحميل:</p><ul><li>أبلغ عن مشكلة عن طريق الاتصال بنا على admin@oppia.org.</li></ul> - <p>بمجرد حذف ملف التعريف:</p><ol><li>لا يمكن استعادة ملف التعريف.</li><li>سيتم حذف معلومات الملف الشخصي مثل الاسم والصور والتقدم بشكل دائم.</li></ol><p>لحذف ملف تعريف (باستثناء<u>المسؤول</u>):</p><ol><li> من الصفحة الرئيسية للمسؤول ، اضغط على زر القائمة أعلى اليسار.</li><li> اضغط على<strong>ضوابط المسؤول</strong>. </li><li> اضغط على<strong>تحرير ملفات التعريف</strong>.</li><li> اضغط على الملف الشخصي الذي ترغب في حذفه.</li><li> في الجزء السفلي من الشاشة ، انقر فوق<strong>حذف الملف الشخصي</strong>. </li><li> اضغط<strong>حذف</strong>لتأكيد الحذف. </li></ol><p><br></p><p> ملاحظة:<u>المسؤول</u>فقط هو القادر على إدارة الملفات الشخصية.</p> + <p>إذا لم يتم تحميل جهاز التحقيق</p><p><br/></p><p>تحقق من تحديث التطبيق:</p><ul><li>اذهب إلى متجر Play وتأكد من تحديث التطبيق إلى أحدث إصدار</li></ul><p><br/></p><p>تحقق من اتصالك بالإنترنت:</p><ul><li>إذا كان اتصال الإنترنت بطيء، حاول إعادة الاتصال بشبكة واي فاي الخاصة بك أو الاتصال بشبك آخر.</li></ul><p>اطلب من المدير التحقق من جهازك و اتصالك بالإنترنت:</p><ul><li>احصل على المدير لإصلاح المشاكل باستخدام الخطوات أعلاه</li></ul><p><br/></p><p>أخبرنا إذا كان لديك مشاكل مع الحملة:</p><ul><li>إبلاغ عن مشكلة عن طريق الاتصال بنا على admin@oppia.org.</li></ul> + <p>إذا كان صوتك لا يعزف</p><p><br/></p><p>تحقق من تحديث التطبيق:</p><ul><li>اذهب إلى متجر Play وتأكد من تحديث التطبيق إلى أحدث إصدار</li></ul><p><br/></p><p>تحقق من اتصالك بالإنترنت:</p><ul><li>إذا كان اتصال الإنترنت بطيء، حاول إعادة الاتصال بشبكة واي فاي الخاصة بك أو الاتصال بشبك آخر. إن الإنترنت البطيء قد يسبب تحميل الصوت بشكل غير منتظم مما يجعل من الصعب تشغيله.</li></ul><p><br/></p><p>اطلب من المدير التحقق من جهازك و اتصالك بالإنترنت:</p><ul><li>احصل على المدير لإصلاح المشاكل باستخدام الخطوات أعلاه</li></ul><p><br/></p><p>أخبرنا إذا كان لديك مشاكل مع الحملة:</p><ul><li>إبلاغ عن مشكلة عن طريق الاتصال بنا على admin@oppia.org.</li></ul> + <p>بمجرد حذف الملف الشخصي:</p><ol><li>لا يمكن استعادة الملف</li><li>سيتم حذف معلومات الملف الشخصي مثل الاسم والصور والتقدم بشكل دائم.</li></ol><p>لإزالة ملف تعريف (باستثناء <u>المدير</u>):</p><ol><li>من صفحة الرئيسية للمدير، اضغط على زر القائمة في الجزء العلوي اليسرى.</li><li>اضغط على <strong>التحكمات الإدارية</strong></li><li>اضغط على <strong>تحرير الملفات الشخصية</strong>.</li><li>اضغط على الملف الشخصي الذي تريد حذفه.</li><li>في أسفل الشاشة، اضغط على <strong>حذف الملف الشخصي</strong>.</li><li>اضغط على حذف لتأكيد الحذف.</li></ol><p><br/></p><p>ملاحظة: إدارة الملفات الشخصية يمكن <u>المدير</u> فقط.</p> <p><ol><li>افتح تطبيق Google Play Store</li><li>ابحث عن %s التطبيق</li><li>اضغط على تحديث</li></ol></p> <p><ol><li>اضغط على تطبيق الإعدادات في هاتفك</li><li>اضغط على تحديثات النظام</li><li>اضغط على تحديثات النظام واتبع التعليمات لتحديث نظام تشغيل Android الخاص بك</li></ol></p> <p> إذا لم تتمكن من العثور على سؤالك أو كنت ترغب في الإبلاغ عن خطأ ، فاتصل بنا على admin@oppia.org. </p> diff --git a/app/src/main/res/values-pcm-rNG/strings.xml b/app/src/main/res/values-pcm-rNG/strings.xml index 36fdceba18c..0774dae0c11 100644 --- a/app/src/main/res/values-pcm-rNG/strings.xml +++ b/app/src/main/res/values-pcm-rNG/strings.xml @@ -94,11 +94,11 @@ Topic: %s Topic Topics wey dey in Progress - Chapter %s: %s - Chapter %s with title %s don complete - Chapter %s with title %s dey in progress - Complete Chapter %s: %s to unlock dis chapter. - Chapter %s: %s dey locked currently. Abeg complete chapter %s: %s to fit unlock dis chapter. + Chapter %s: %s + Chapter %s with title %s don complete + Chapter %s with title %s dey in progress + Complete Chapter %s: %s to unlock dis chapter. + Chapter %s: %s dey locked currently. Abeg complete chapter %s: %s to fit unlock dis chapter. Finish the chapter wey dey before to fit open dis chapter Enter text. Enter fraction wey dey in di form x/x, or mixed nomba wey dey in di form x x/x. @@ -153,7 +153,7 @@ Topic Wey You Don Download You don download am Practice Mode - Question %s of %s + Question %s of %s Complete Finished You don finish all of di questions! You fit choose to play anoda set of questions, or go back to di topic. @@ -192,7 +192,7 @@ 1 Story %s Stories - + %s of %s Chapter Completed %s of %s Chapters Completed @@ -267,7 +267,7 @@ Skip Next Get Started - Slide %s of %s + Slide %s of %s Hi, %s! Abeg put your Administrator PIN. Abeg put your PIN. @@ -426,7 +426,7 @@ Move item up to %s Up Down - %s %s + %s %s 0 minutes ago a minute ago diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index a514d6dc65e..63042bc8413 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -104,11 +104,11 @@ Tópico: %s Tópico Tópicos em Andamento - Capítulo %s: %s - O capítulo %s com o título %s foi concluído - O capítulo %s com o título %s está em andamento - Conclua o capítulo %s: %s para desbloquear este capítulo. - O capítulo %s: %s está bloqueado no momento. Conclua o capítulo %s: %s para desbloquear esse capítulo. + Capítulo %s: %s + O capítulo %s com o título %s foi concluído + O capítulo %s com o título %s está em andamento + Conclua o capítulo %s: %s para desbloquear este capítulo. + O capítulo %s: %s está bloqueado no momento. Conclua o capítulo %s: %s para desbloquear esse capítulo. Conclua o capítulo anterior para desbloquear este capítulo. Inserir texto. Insira uma fração na forma x/x, ou um número misto na forma x x/x. @@ -166,7 +166,7 @@ Tópico Baixado Baixado Modo de Prática - Questão %s de %s + Questão %s de %s Concluído Concluído Você concluiu todas as perguntas! Você pode escolher reproduzir outro conjunto de perguntas ou retornar ao tópico. @@ -210,7 +210,7 @@ %s História %s Histórias - + %s de %s Capítulo Concluído %s de %s Capítulos Concluídos @@ -283,7 +283,7 @@ Pular Próximo Começar - Slide %s de %s + Slide %s de %s Olá, %s! Por favor, insira o PIN do Administrador. Por favor, insira seu PIN. @@ -445,7 +445,7 @@ Mover o item para cima para %s Para cima Para baixo - %s %s + %s %s topic_revision_recyclerview_tag ongoing_recycler_view_tag Por favor, selecione todas as alternativas corretas. diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index ddb536c4e3e..f827cae27ef 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -77,10 +77,10 @@ mada: %s mada mada zinazoendelea - Sura ya %s: %s - Sura ya %s yenye kichwa %s imekamilika - Sura ya %s yenye kichwa %s inaendelea - Kamilisha Sura ya %s: %s ili kufungua sura hii. + Sura ya %s: %s + Sura ya %s yenye kichwa %s imekamilika + Sura ya %s yenye kichwa %s inaendelea + Kamilisha Sura ya %s: %s ili kufungua sura hii. Ingiza maandishi. Ingiza sehemu katika mpangilio huu x/x, au nambari iliyochanganywa katika mpangilio huu x/x. Ingiza sehemu katika mpangilio huu X/X. @@ -134,7 +134,7 @@ Mada Imepakuliwa Imepakuliwa Hali ya Mazoezi - Swali %s kati ya %s + Swali %s kati ya %s imekamilika imekamilika Umemaliza maswali yote! Unaweza kuchagua kucheza seti nyingine ya maswali, au kurudi kwenye mada. @@ -173,7 +173,7 @@ Hadithi 1 Hadithi %s - + %s kati ya Sura %s Imekamilika %s ya Sura %s Imekamilika @@ -238,7 +238,7 @@ Ruka Inayofuata Anza - Slaidi %s ya %s + Slaidi %s ya %s Habari, %s! Tafadhali weka Nambari yako ya Siri ya Msimamizi. Tafadhali weka Nambari yako ya Siri. @@ -375,7 +375,7 @@ Hamisha kipengee juu hadi %s Juu Chini - %s %s + %s %s mada_marudio_mtazamo wa kuchakata tena_tag Inaendelea_kuchakata tena_mtazamo_tag Tafadhali chagua angalau chaguo moja. From 96f5289456de80b91a79ad82fd94e2e789f0e39b Mon Sep 17 00:00:00 2001 From: Manas <119405883+manas-yu@users.noreply.github.com> Date: Tue, 12 Nov 2024 00:41:15 +0530 Subject: [PATCH 2/7] Fix #5232: ConsoleLogger overwrites local log file for each line write (#5550) ## Explanation Fix #5232 This PR updates the `ConsoleLogger` class to improve logging functionality. The key changes include: - **Append Mode for Logs**: The `FileWriter` is now opened in append mode, ensuring that new log entries are added to the existing log file instead of overwriting it. - **Long-lived PrintWriter**: A long-lived `PrintWriter` instance has been implemented to enhance performance by reducing the overhead of opening and closing the log file multiple times. - **Close Log File**: A `closeLogFile()` method has been added to properly close the `PrintWriter`, preventing resource leaks after logging is complete. These changes address the issue of logs being overwritten and enhance performance while ensuring proper resource management. ## Essential Checklist - [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".) - [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation. - [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide). - [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)). - [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop". - [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)). --------- Co-authored-by: Adhiambo Peres <59600948+adhiamboperes@users.noreply.github.com> --- .../android/util/logging/ConsoleLogger.kt | 23 +++++--- .../android/util/logging/ConsoleLoggerTest.kt | 52 +++++++++++++++++-- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/utility/src/main/java/org/oppia/android/util/logging/ConsoleLogger.kt b/utility/src/main/java/org/oppia/android/util/logging/ConsoleLogger.kt index 2b1cd2637a1..3cd6f8707e1 100644 --- a/utility/src/main/java/org/oppia/android/util/logging/ConsoleLogger.kt +++ b/utility/src/main/java/org/oppia/android/util/logging/ConsoleLogger.kt @@ -11,6 +11,8 @@ import org.oppia.android.app.model.EventLog.ConsoleLoggerContext import org.oppia.android.util.locale.OppiaLocale import org.oppia.android.util.threading.BlockingDispatcher import java.io.File +import java.io.FileWriter +import java.io.PrintWriter import javax.inject.Inject import javax.inject.Singleton @@ -33,6 +35,8 @@ class ConsoleLogger @Inject constructor( */ val logErrorMessagesFlow: SharedFlow = _logErrorMessagesFlow + private var printWriter: PrintWriter? = null + /** Logs a verbose message with the specified tag. */ fun v(tag: String, msg: String) { writeLog(LogLevel.VERBOSE, tag, msg) @@ -73,12 +77,12 @@ class ConsoleLogger @Inject constructor( writeError(LogLevel.WARNING, tag, msg, tr) } - /** Logs a error message with the specified tag. */ + /** Logs an error message with the specified tag. */ fun e(tag: String, msg: String) { writeLog(LogLevel.ERROR, tag, msg) } - /** Logs a error message with the specified tag, message and exception. */ + /** Logs an error message with the specified tag, message and exception. */ fun e(tag: String, msg: String, tr: Throwable?) { writeError(LogLevel.ERROR, tag, msg, tr) } @@ -109,7 +113,7 @@ class ConsoleLogger @Inject constructor( } // Add the log to the error message flow so it can be logged to firebase. - CoroutineScope(blockingDispatcher).launch { + blockingScope.launch { // Only log error messages to firebase. if (logLevel == LogLevel.ERROR) { _logErrorMessagesFlow.emit( @@ -124,10 +128,17 @@ class ConsoleLogger @Inject constructor( } /** - * Writes the specified text line to file in a background thread to ensure that saving messages don't block the main - * thread. A blocking dispatcher is used to ensure messages are written in order. + * Writes the specified text line to file in a background thread to ensure that saving messages + * doesn't block the main thread. A blocking dispatcher is used to ensure messages are written + * in order. */ private fun logToFileInBackground(text: String) { - blockingScope.launch { logDirectory.printWriter().use { out -> out.println(text) } } + blockingScope.launch { + if (printWriter == null) { + printWriter = PrintWriter(FileWriter(logDirectory, true)) // Open in append mode. + } + printWriter?.println(text) + printWriter?.flush() + } } } diff --git a/utility/src/test/java/org/oppia/android/util/logging/ConsoleLoggerTest.kt b/utility/src/test/java/org/oppia/android/util/logging/ConsoleLoggerTest.kt index 6a314de75fe..05df6b8c3c7 100644 --- a/utility/src/test/java/org/oppia/android/util/logging/ConsoleLoggerTest.kt +++ b/utility/src/test/java/org/oppia/android/util/logging/ConsoleLoggerTest.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.toList +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -31,6 +32,8 @@ import org.oppia.android.util.data.DataProvidersInjectorProvider import org.oppia.android.util.locale.testing.LocaleTestModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode +import java.io.File +import java.io.PrintWriter import javax.inject.Inject import javax.inject.Singleton @@ -54,9 +57,31 @@ class ConsoleLoggerTest { @field:[Inject BackgroundTestDispatcher] lateinit var backgroundTestDispatcher: TestCoroutineDispatcher + private lateinit var logFile: File + @Before fun setUp() { setUpTestApplicationComponent() + logFile = File(context.filesDir, "oppia_app.log") + logFile.delete() + } + + @After + fun tearDown() { + logFile.delete() + } + + @Test + fun testConsoleLogger_multipleLogCalls_appendsToFile() { + consoleLogger.e(testTag, testMessage) + consoleLogger.e(testTag, "$testMessage 2") + testCoroutineDispatchers.advanceUntilIdle() + + val logContent = logFile.readText() + assertThat(logContent).contains(testMessage) + assertThat(logContent).contains("$testMessage 2") + assertThat(logContent.indexOf(testMessage)) + .isLessThan(logContent.indexOf("$testMessage 2")) } @Test @@ -76,6 +101,26 @@ class ConsoleLoggerTest { assertThat(firstErrorContext.fullErrorLog).isEqualTo(testMessage) } + @Test + fun testConsoleLogger_closeAndReopen_continuesToAppend() { + consoleLogger.e(testTag, "first $testMessage") + testCoroutineDispatchers.advanceUntilIdle() + + // Force close the PrintWriter to simulate app restart + val printWriterField = ConsoleLogger::class.java.getDeclaredField("printWriter") + printWriterField.isAccessible = true + (printWriterField.get(consoleLogger) as? PrintWriter)?.close() + printWriterField.set(consoleLogger, null) + + consoleLogger.e(testTag, "first $testMessage") + consoleLogger.e(testTag, "second $testMessage") + testCoroutineDispatchers.advanceUntilIdle() + + val logContent = logFile.readText() + assertThat(logContent).contains("first $testMessage") + assertThat(logContent).contains("second $testMessage") + } + private fun setUpTestApplicationComponent() { ApplicationProvider.getApplicationContext().inject(this) } @@ -84,9 +129,7 @@ class ConsoleLoggerTest { class TestModule { @Provides @Singleton - fun provideContext(application: Application): Context { - return application - } + fun provideContext(application: Application): Context = application @Provides @Singleton @@ -96,7 +139,7 @@ class ConsoleLoggerTest { @Provides @Singleton @EnableFileLog - fun provideEnableFileLog(): Boolean = false + fun provideEnableFileLog(): Boolean = true @Provides @Singleton @@ -113,7 +156,6 @@ class ConsoleLoggerTest { FakeOppiaClockModule::class, ] ) - interface TestApplicationComponent : DataProvidersInjector { @Component.Builder interface Builder { From a125e51e8c97a8e4027bb04cbb8058750da938c0 Mon Sep 17 00:00:00 2001 From: Manas <119405883+manas-yu@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:34:57 +0530 Subject: [PATCH 3/7] Fix #5566: Handle action_required status in code coverage (#5574) ## Explanation Fixes #5566: This PR addresses the issue of code coverage comments not being posted on pull requests when certain review status ("Request Changes") result in an `action_required` status. ### Changes: - Updated `comment_coverage.yml` to include `action_required` in the `allowed-conclusions` list for the `check_code_coverage_completed` job. This ensures that the workflow continues even if a review requires further action. ## Essential Checklist - [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".) - [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation. - [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide). - [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)). - [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop". - [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)). --- .github/workflows/comment_coverage_report.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/comment_coverage_report.yml b/.github/workflows/comment_coverage_report.yml index 16c1ae0da63..0cc70da29a2 100644 --- a/.github/workflows/comment_coverage_report.yml +++ b/.github/workflows/comment_coverage_report.yml @@ -17,6 +17,8 @@ jobs: check_code_coverage_completed: name: Check code coverage completed runs-on: ubuntu-latest + outputs: + conclusion: ${{ steps.wait-for-coverage.outputs.run-conclusion }} steps: - name: Wait for code coverage to complete id: wait-for-coverage @@ -27,6 +29,13 @@ jobs: allowed-conclusions: | success failure + action_required + + - name: Conclusion Analysis + if: steps.wait-for-coverage.outputs.run-conclusion == 'action_required' + run: | + echo "::error::First-time contributor workflows require manual approval. After approval, please re-run the comment coverage workflows to post the coverage report." + exit 1 comment_coverage_report: name: Comment Code Coverage Report @@ -60,7 +69,7 @@ jobs: const run = runs[0]; if(!run) { - core.setFailed('Could not find a succesful workflow run for the PR'); + core.setFailed('Could not find a successful workflow run for the PR'); return; } From 8a7fe0ab737f65cc3d9a0773627d9058edadb347 Mon Sep 17 00:00:00 2001 From: Ayush Yadav <143514610+theayushyadav11@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:23:57 +0530 Subject: [PATCH 4/7] Fixes #3641 Use finish() instead of intent for smoother navigation (#5557) ## Description: - Fixes #3641 This PR simplifies the navigation logic in `AddProfileActivity` and documents the reasoning behind not using `finish()` in `ExitProfileDialogFragment`. ### Changes Made: - **AddProfileActivity:** - Replaced the use of `Intent` with `FLAG_ACTIVITY_CLEAR_TOP` by calling `finish()`. - With the live data bug resolved, the activity stack can now be managed more cleanly without requiring a new intent. **Benefits:** - Avoids unnecessary activity recreation. - Improves navigation efficiency and reduces overhead. - **ExitProfileDialogFragment:** - Retained the use of an `Intent` to navigate to `ProfileChooserActivity` instead of switching to `finish()`. - **Reason:** - `ProfileChooserActivity` is not directly below the current activity in the stack. - If `finish()` were used, the app would incorrectly navigate to `PinPasswordActivity`, resulting in undesirable behavior. ### Testing: 1. Verified that `AddProfileActivity` correctly navigates back to `ProfileChooserActivity` using `finish()` without activity recreation. 2. Confirmed that `ExitProfileDialogFragment` behaves as expected, retaining the correct navigation path through `Intent` to `ProfileChooserActivity`. ### Summary: This PR ensures cleaner navigation for `AddProfileActivity` by using `finish()`, while maintaining correct behavior in `ExitProfileDialogFragment` by continuing to use an intent-based approach. This makes the app more efficient while preserving user experience. ### Video Demo as everything is working fine. https://github.com/user-attachments/assets/fb383845-5672-4236-8618-a87583cbbba0 ## Essential Checklist - [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".) - [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation. - [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide). - [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)). - [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop". - [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)). --- .../android/app/drawer/ExitProfileDialogFragment.kt | 1 - .../oppia/android/app/profile/AddProfileActivity.kt | 5 +---- .../android/app/profile/AddProfileActivityTest.kt | 10 ++++++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/drawer/ExitProfileDialogFragment.kt b/app/src/main/java/org/oppia/android/app/drawer/ExitProfileDialogFragment.kt index 7900163e9a5..2dfdd918fc7 100644 --- a/app/src/main/java/org/oppia/android/app/drawer/ExitProfileDialogFragment.kt +++ b/app/src/main/java/org/oppia/android/app/drawer/ExitProfileDialogFragment.kt @@ -70,7 +70,6 @@ class ExitProfileDialogFragment : InjectableDialogFragment() { dialog.dismiss() } .setPositiveButton(R.string.home_activity_back_dialog_exit) { _, _ -> - // TODO(#3641): Investigate on using finish instead of intent. val intent = ProfileChooserActivity.createProfileChooserActivity(activity!!) if (!restoreLastCheckedItem) { intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) diff --git a/app/src/main/java/org/oppia/android/app/profile/AddProfileActivity.kt b/app/src/main/java/org/oppia/android/app/profile/AddProfileActivity.kt index 1acff5fd581..c7d0e60857c 100644 --- a/app/src/main/java/org/oppia/android/app/profile/AddProfileActivity.kt +++ b/app/src/main/java/org/oppia/android/app/profile/AddProfileActivity.kt @@ -44,10 +44,7 @@ class AddProfileActivity : InjectableAutoLocalizedAppCompatActivity() { } override fun onSupportNavigateUp(): Boolean { - // TODO(#3641): Investigate on using finish instead of intent. - val intent = Intent(this, ProfileChooserActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - startActivity(intent) + finish() return false } diff --git a/app/src/sharedTest/java/org/oppia/android/app/profile/AddProfileActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/AddProfileActivityTest.kt index 0152d1056d7..b9aa2114cd6 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/profile/AddProfileActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/profile/AddProfileActivityTest.kt @@ -13,6 +13,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.test.core.app.ActivityScenario.launch import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.closeSoftKeyboard import androidx.test.espresso.action.ViewActions.pressImeActionButton @@ -1748,6 +1749,15 @@ class AddProfileActivityTest { assertThat(currentScreenName).isEqualTo(ScreenName.ADD_PROFILE_ACTIVITY) } + @Test + fun testAddProfileActivity_onBackPressed_finishActivity() { + val scenario = launch(AddProfileActivity::class.java) + onView(isRoot()).perform(ViewActions.pressBack()) + testCoroutineDispatchers.runCurrent() + scenario.onActivity { activity -> + assertThat(activity.isFinishing).isTrue() + } + } private fun createAddProfileActivityIntent(): Intent { return AddProfileActivity.createAddProfileActivityIntent( ApplicationProvider.getApplicationContext(), From 33d2b8faaaadf0be778f9948261c36fe72fc37b2 Mon Sep 17 00:00:00 2001 From: whyash8 <158801815+whyash8@users.noreply.github.com> Date: Sat, 16 Nov 2024 12:02:05 +0530 Subject: [PATCH 5/7] Fix#3146 : Create a generic utility for filtering enums (#5529) ## Explanation Fixes: #3146 This PR introduces a new utility function filterByEnumCondition to standardize filtering of enums across various parts of the oppia-android codebase. This utility function allows filtering of collections based on a condition applied to enum values. This PR introduces a new utility function filterByEnumCondition to standardize filtering of enums across various parts of the oppia-android codebase. This utility function allows filtering of collections based on a condition applied to enum values. **Key Changes**: Added Utility Function: filterByEnumCondition: A generic function to filter a collection based on a condition applied to an enum extracted from each item in the collection. **Updated Existing Code**: Refactored code in getLeastRecentMetricLogIndex, getLeastRecentMediumPriorityEventIndex, and other methods to utilize the new filterByEnumCondition function. Updated the calculation of completedChapterCount and inProgressChapterCount using the new utility function. ## Essential Checklist - [ x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".) - [x ] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation. - [x ] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide). - [x ] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)). - [ x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop". - [x ] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)). - --------- Co-authored-by: Adhiambo Peres <59600948+adhiamboperes@users.noreply.github.com> --- .github/CODEOWNERS | 1 + .../MarketFitItemsViewModel.kt | 9 ++- .../UserTypeItemsViewModel.kt | 69 ++++++++----------- .../lessons/TopicLessonsFragmentPresenter.kt | 23 ++++--- .../domain/oppialogger/analytics/BUILD.bazel | 1 + .../analytics/PerformanceMetricsController.kt | 17 +++-- scripts/assets/test_file_exemptions.textproto | 4 ++ .../oppia/android/util/enumfilter/BUILD.bazel | 13 ++++ .../android/util/enumfilter/EnumFilterUtil.kt | 20 ++++++ 9 files changed, 99 insertions(+), 58 deletions(-) create mode 100644 utility/src/main/java/org/oppia/android/util/enumfilter/BUILD.bazel create mode 100644 utility/src/main/java/org/oppia/android/util/enumfilter/EnumFilterUtil.kt diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d2c51eaa7e2..fdf7a2db439 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -163,6 +163,7 @@ # Utilities that are primarily used for frontend/UI purposes. /utility/src/*/java/org/oppia/android/util/accessibility/ @oppia/android-frontend-reviewers /utility/src/*/java/org/oppia/android/util/statusbar/ @oppia/android-frontend-reviewers +/utility/src/main/java/org/oppia/android/util/enumfilter/ @oppia/android-frontend-reviewers /utility/src/*/java/org/oppia/android/util/extensions/ @oppia/android-frontend-reviewers /utility/src/*/java/org/oppia/android/util/parser/html @oppia/android-frontend-reviewers /utility/src/*/res/**/*.xml @oppia/android-frontend-reviewers diff --git a/app/src/main/java/org/oppia/android/app/survey/surveyitemviewmodel/MarketFitItemsViewModel.kt b/app/src/main/java/org/oppia/android/app/survey/surveyitemviewmodel/MarketFitItemsViewModel.kt index e2b7fe43a5e..bef9347e3d0 100644 --- a/app/src/main/java/org/oppia/android/app/survey/surveyitemviewmodel/MarketFitItemsViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/survey/surveyitemviewmodel/MarketFitItemsViewModel.kt @@ -12,6 +12,7 @@ import org.oppia.android.app.survey.PreviousAnswerHandler import org.oppia.android.app.survey.SelectedAnswerAvailabilityReceiver import org.oppia.android.app.survey.SelectedAnswerHandler import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.util.enumfilter.filterByEnumCondition import javax.inject.Inject /** [SurveyAnswerItemViewModel] for the market fit question options. */ @@ -98,8 +99,12 @@ class MarketFitItemsViewModel @Inject constructor( private fun getMarketFitOptions(): ObservableList { val appName = resourceHandler.getStringInLocale(R.string.app_name) val observableList = ObservableArrayList() - observableList += MarketFitAnswer.values() - .filter { it.isValid() } + val filteredmarketFitAnswer = filterByEnumCondition( + MarketFitAnswer.values().toList(), + { marketFitAnswer -> marketFitAnswer }, + { marketFitAnswer -> marketFitAnswer.isValid() } + ) + observableList += filteredmarketFitAnswer .mapIndexed { index, marketFitAnswer -> when (marketFitAnswer) { MarketFitAnswer.VERY_DISAPPOINTED -> MultipleChoiceOptionContentViewModel( diff --git a/app/src/main/java/org/oppia/android/app/survey/surveyitemviewmodel/UserTypeItemsViewModel.kt b/app/src/main/java/org/oppia/android/app/survey/surveyitemviewmodel/UserTypeItemsViewModel.kt index 14482c2775e..a03a78d10c8 100644 --- a/app/src/main/java/org/oppia/android/app/survey/surveyitemviewmodel/UserTypeItemsViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/survey/surveyitemviewmodel/UserTypeItemsViewModel.kt @@ -12,6 +12,7 @@ import org.oppia.android.app.survey.SelectedAnswerAvailabilityReceiver import org.oppia.android.app.survey.SelectedAnswerHandler import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.viewmodel.ObservableArrayList +import org.oppia.android.util.enumfilter.filterByEnumCondition import javax.inject.Inject /** [SurveyAnswerItemViewModel] for providing the type of user question options. */ @@ -97,46 +98,36 @@ class UserTypeItemsViewModel @Inject constructor( private fun getUserTypeOptions(): ObservableArrayList { val observableList = ObservableArrayList() - observableList += UserTypeAnswer.values() - .filter { it.isValid() } - .mapIndexed { index, userTypeOption -> - when (userTypeOption) { - UserTypeAnswer.LEARNER -> - MultipleChoiceOptionContentViewModel( - resourceHandler.getStringInLocale( - R.string.user_type_answer_learner - ), - index, - this - ) - UserTypeAnswer.TEACHER -> MultipleChoiceOptionContentViewModel( - resourceHandler.getStringInLocale( - R.string.user_type_answer_teacher - ), - index, - this - ) - - UserTypeAnswer.PARENT -> - MultipleChoiceOptionContentViewModel( - resourceHandler.getStringInLocale( - R.string.user_type_answer_parent - ), - index, - this - ) - - UserTypeAnswer.OTHER -> - MultipleChoiceOptionContentViewModel( - resourceHandler.getStringInLocale( - R.string.user_type_answer_other - ), - index, - this - ) - else -> throw IllegalStateException("Invalid UserTypeAnswer") - } + val filteredUserTypes = filterByEnumCondition( + UserTypeAnswer.values().toList(), + { userTypeAnswer -> userTypeAnswer }, + { userTypeAnswer -> userTypeAnswer.isValid() } + ) + observableList += filteredUserTypes.mapIndexed { index, userTypeOption -> + when (userTypeOption) { + UserTypeAnswer.LEARNER -> MultipleChoiceOptionContentViewModel( + resourceHandler.getStringInLocale(R.string.user_type_answer_learner), + index, + this + ) + UserTypeAnswer.TEACHER -> MultipleChoiceOptionContentViewModel( + resourceHandler.getStringInLocale(R.string.user_type_answer_teacher), + index, + this + ) + UserTypeAnswer.PARENT -> MultipleChoiceOptionContentViewModel( + resourceHandler.getStringInLocale(R.string.user_type_answer_parent), + index, + this + ) + UserTypeAnswer.OTHER -> MultipleChoiceOptionContentViewModel( + resourceHandler.getStringInLocale(R.string.user_type_answer_other), + index, + this + ) + else -> throw IllegalStateException("Invalid UserTypeAnswer") } + } return observableList } diff --git a/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt index 66a5a32b2df..1f38909e9d3 100644 --- a/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt @@ -30,6 +30,7 @@ import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.util.accessibility.AccessibilityService import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.enumfilter.filterByEnumCondition import javax.inject.Inject /** The presenter for [TopicLessonsFragment]. */ @@ -161,18 +162,18 @@ class TopicLessonsFragmentPresenter @Inject constructor( val chapterSummaries = storySummaryViewModel .storySummary.chapterList - val completedChapterCount = - chapterSummaries.map(ChapterSummary::getChapterPlayState) - .filter { - it == ChapterPlayState.COMPLETED - } - .size + val completedChapterCount = filterByEnumCondition( + chapterSummaries.map(ChapterSummary::getChapterPlayState), + { it }, + { it == ChapterPlayState.COMPLETED } + ).size + val inProgressChapterCount = - chapterSummaries.map(ChapterSummary::getChapterPlayState) - .filter { - it == ChapterPlayState.IN_PROGRESS_SAVED - } - .size + filterByEnumCondition( + chapterSummaries.map(ChapterSummary::getChapterPlayState), + { it }, + { it == ChapterPlayState.IN_PROGRESS_SAVED } + ).size val storyPercentage: Int = (completedChapterCount * 100) / storySummaryViewModel.storySummary.chapterCount diff --git a/domain/src/main/java/org/oppia/android/domain/oppialogger/analytics/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/oppialogger/analytics/BUILD.bazel index f27fbd4b280..b5c7b54bf0b 100644 --- a/domain/src/main/java/org/oppia/android/domain/oppialogger/analytics/BUILD.bazel +++ b/domain/src/main/java/org/oppia/android/domain/oppialogger/analytics/BUILD.bazel @@ -49,6 +49,7 @@ kt_android_library( "//domain/src/main/java/org/oppia/android/domain/oppialogger:prod_module", "//model/src/main/proto:performance_metrics_event_logger_java_proto_lite", "//utility/src/main/java/org/oppia/android/util/data:data_provider", + "//utility/src/main/java/org/oppia/android/util/enumfilter:enum_filter_util", "//utility/src/main/java/org/oppia/android/util/logging:console_logger", "//utility/src/main/java/org/oppia/android/util/logging:exception_logger", "//utility/src/main/java/org/oppia/android/util/logging/performancemetrics:performance_metrics_assessor", diff --git a/domain/src/main/java/org/oppia/android/domain/oppialogger/analytics/PerformanceMetricsController.kt b/domain/src/main/java/org/oppia/android/domain/oppialogger/analytics/PerformanceMetricsController.kt index f518ff6121e..242eccbe1d3 100644 --- a/domain/src/main/java/org/oppia/android/domain/oppialogger/analytics/PerformanceMetricsController.kt +++ b/domain/src/main/java/org/oppia/android/domain/oppialogger/analytics/PerformanceMetricsController.kt @@ -7,6 +7,7 @@ import org.oppia.android.app.model.ScreenName import org.oppia.android.data.persistence.PersistentCacheStore import org.oppia.android.domain.oppialogger.PerformanceMetricsLogStorageCacheSize import org.oppia.android.util.data.DataProvider +import org.oppia.android.util.enumfilter.filterByEnumCondition import org.oppia.android.util.logging.ConsoleLogger import org.oppia.android.util.logging.ExceptionLogger import org.oppia.android.util.logging.performancemetrics.PerformanceMetricsAssessor @@ -128,9 +129,11 @@ class PerformanceMetricsController @Inject constructor( * priority is returned. */ private fun getLeastRecentMetricLogIndex(oppiaMetricLogs: OppiaMetricLogs): Int? = - oppiaMetricLogs.oppiaMetricLogList.withIndex() - .filter { it.value.priority == Priority.LOW_PRIORITY } - .minByOrNull { it.value.timestampMillis }?.index + filterByEnumCondition( + oppiaMetricLogs.oppiaMetricLogList.withIndex().toList(), + { it.value.priority }, + { it == Priority.LOW_PRIORITY } + ).minByOrNull { it.value.timestampMillis }?.index ?: getLeastRecentMediumPriorityEventIndex(oppiaMetricLogs) /** @@ -142,9 +145,11 @@ class PerformanceMetricsController @Inject constructor( * priority is returned. */ private fun getLeastRecentMediumPriorityEventIndex(oppiaMetricLogs: OppiaMetricLogs): Int? = - oppiaMetricLogs.oppiaMetricLogList.withIndex() - .filter { it.value.priority == Priority.MEDIUM_PRIORITY } - .minByOrNull { it.value.timestampMillis }?.index + filterByEnumCondition( + oppiaMetricLogs.oppiaMetricLogList.withIndex().toList(), + { it.value.priority }, + { it == Priority.MEDIUM_PRIORITY } + ).minByOrNull { it.value.timestampMillis }?.index ?: getLeastRecentGeneralEventIndex(oppiaMetricLogs) /** Returns the index of the least recent event regardless of their priority. */ diff --git a/scripts/assets/test_file_exemptions.textproto b/scripts/assets/test_file_exemptions.textproto index 2f4c4422ce9..1ea6be33967 100644 --- a/scripts/assets/test_file_exemptions.textproto +++ b/scripts/assets/test_file_exemptions.textproto @@ -4310,6 +4310,10 @@ test_file_exemption { exempted_file_path: "utility/src/main/java/org/oppia/android/util/extensions/ContextExtensions.kt" test_file_not_required: true } +test_file_exemption { + exempted_file_path: "utility/src/main/java/org/oppia/android/util/enumfilter/EnumFilterUtil.kt" + test_file_not_required: true +} test_file_exemption { exempted_file_path: "utility/src/main/java/org/oppia/android/util/extensions/StringExtensions.kt" source_file_is_incompatible_with_code_coverage: true diff --git a/utility/src/main/java/org/oppia/android/util/enumfilter/BUILD.bazel b/utility/src/main/java/org/oppia/android/util/enumfilter/BUILD.bazel new file mode 100644 index 00000000000..876c262fe1a --- /dev/null +++ b/utility/src/main/java/org/oppia/android/util/enumfilter/BUILD.bazel @@ -0,0 +1,13 @@ +""" +General purpose utility for filtering enums. +""" + +load("@io_bazel_rules_kotlin//kotlin:android.bzl", "kt_android_library") + +kt_android_library( + name = "enum_filter_util", + srcs = [ + "EnumFilterUtil.kt", + ], + visibility = ["//:oppia_api_visibility"], +) diff --git a/utility/src/main/java/org/oppia/android/util/enumfilter/EnumFilterUtil.kt b/utility/src/main/java/org/oppia/android/util/enumfilter/EnumFilterUtil.kt new file mode 100644 index 00000000000..6c2aae441d6 --- /dev/null +++ b/utility/src/main/java/org/oppia/android/util/enumfilter/EnumFilterUtil.kt @@ -0,0 +1,20 @@ +package org.oppia.android.util.enumfilter + +/** + * Filters a collection based on a condition applied to an enum property of each element. + * + * @param E the type of enum values. + * @param T the type of elements in the collection. + * @param collection the collection of elements to filter. + * @param enumExtractor a function that extracts the enum value from each element. + * @param condition a predicate function that determines if an enum value should be included in the result. + * @return a list of elements from the collection that satisfy the condition when their enum property is evaluated. + */ + +inline fun , T> filterByEnumCondition( + collection: Collection, + enumExtractor: (T) -> E, + condition: (E) -> Boolean +): List { + return collection.filter { condition(enumExtractor(it)) } +} From 7955f7f320065ce741c23d481ba516ced7d4af09 Mon Sep 17 00:00:00 2001 From: MOHIT GUPTA <76530270+MohitGupta121@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:32:05 +0530 Subject: [PATCH 6/7] Fix #5578 : Developer Videos Link are Broken (#5579) ## Explanation Fix #5578 : Developer Videos Link are Broken Update links to developer videos to point to the Oppia Channel. ## Essential Checklist - [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".) - [ ] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation. - [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide). - [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)). - [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop". - [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)). --- wiki/Accessibility-A11y-Guide.md | 8 ++++---- wiki/Interpreting-CI-Results.md | 2 +- wiki/Static-Analysis-Checks.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wiki/Accessibility-A11y-Guide.md b/wiki/Accessibility-A11y-Guide.md index a9e01c6c59e..d80d73f6efd 100644 --- a/wiki/Accessibility-A11y-Guide.md +++ b/wiki/Accessibility-A11y-Guide.md @@ -32,7 +32,7 @@ There are various manual and automated tests to check if app is accessible by al **[Accessibility Scanner](https://support.google.com/accessibility/android/answer/6376570?hl=en)** : Using Accessibility Scanner we can take screenshots of each and every screen in the Oppia-app manually and the Accessibility Scanner app will give the output for the individual screenshot mentioning all the errors. -[Here](https://youtu.be/LF5AgGI5H3A) is a video tutorial on how to set up and use the Accessibility Scanner. +[Here](https://youtu.be/zCIlMlbFf7I?si=Xn1L5iCJ-TJ4DRVq) is a video tutorial on how to set up and use the Accessibility Scanner. **Screen Reader**: Screen readers like **Talkback** can be used to test the app manually. Talkback app is used by blind people to navigate to different items in the screen and get audio based output. This app will not give any error like Accessibility Scanner. @@ -80,7 +80,7 @@ TalkBack is the Google **screen reader** included on Android devices. TalkBack g 5. Read all the instructions written on the screen as using Talkback requires specific steps. 6. Turn on **Use Service** -> **Allow** -[Here](https://youtu.be/xpIM9xlowjs) is a video tutorial on "How to use Talkback and what does its output mean?". +[Here](https://youtu.be/JBRV1dauxyI?si=VzrxFJSpU9r3pdqq) is a video tutorial on "How to use Talkback and what does its output mean?". ### Useful Resources * [Android A11Y Overview](https://support.google.com/accessibility/android/answer/6006564) @@ -89,9 +89,9 @@ TalkBack is the Google **screen reader** included on Android devices. TalkBack g * [Display speech output as Text: Talkback](https://developer.android.com/guide/topics/ui/accessibility/testing#optional_talkback_developer_settings) ### Developer Videos -* [How to use Accessibility Scanner? - Tutorial](https://youtu.be/LF5AgGI5H3A) +* [How to use Accessibility Scanner? - Tutorial](https://youtu.be/zCIlMlbFf7I?si=Xn1L5iCJ-TJ4DRVq) * [Presentation Slides](https://docs.google.com/presentation/d/1PM_gs3TV2LVKFv6WuF9CUQHWbK7koepAxypzxeZTFzE/edit?usp=sharing) -* [How to use Talkback and what does its output mean? - Tutorial](https://youtu.be/xpIM9xlowjs) +* [How to use Talkback and what does its output mean? - Tutorial](https://youtu.be/JBRV1dauxyI?si=VzrxFJSpU9r3pdqq) * [Presentation Slides](https://docs.google.com/presentation/d/17SeKJLKT-rUNa_Yupe97bMFSsjTNzp83jX-lZPKEtnQ/edit?usp=sharing) ## Using AccessibilityTestRule in Espresso Tests diff --git a/wiki/Interpreting-CI-Results.md b/wiki/Interpreting-CI-Results.md index 00b3c5667c7..14293e02f55 100644 --- a/wiki/Interpreting-CI-Results.md +++ b/wiki/Interpreting-CI-Results.md @@ -21,4 +21,4 @@ Example in the below check the second job has some error or failure Navigate to logs or search the keyword ‘error’ to find the error message to understand what might have caused the failure in the checks. ### Developer Video - Understanding CI check failures -Learn how to interpret and troubleshoot oppia-android CI check failures in this insightful [developer video](https://youtu.be/I2bRf6fvgJ0?si=35sAagbUFSk6bOBA). \ No newline at end of file +Learn how to interpret and troubleshoot oppia-android CI check failures in this insightful [developer video](https://youtu.be/HLzHQULZbJE?si=RLY9_8Yzv5cjYM7q). \ No newline at end of file diff --git a/wiki/Static-Analysis-Checks.md b/wiki/Static-Analysis-Checks.md index 372a9b27bc1..6cfd8b2e022 100644 --- a/wiki/Static-Analysis-Checks.md +++ b/wiki/Static-Analysis-Checks.md @@ -267,4 +267,4 @@ To fix failing tests from GitHub CI individually, follow the steps below. Note: Before running the script command in your local terminal, make sure you have Bazel installed. To learn how to set up Bazel for Oppia Android, follow these [instructions](https://github.com/oppia/oppia-android/wiki/Oppia-Bazel-Setup-Instructions). Also make sure you have oppia-android-tools installed since static checks rely on these tools to be able to perform some of the checks. To install oppia-android-tools, run `bash scripts/setup.sh` in the oppia-android directory. ### Developer Video - Understanding CI check failures -Learn how to interpret and troubleshoot oppia-android CI check failures in this insightful [developer video](https://youtu.be/I2bRf6fvgJ0?si=35sAagbUFSk6bOBA). \ No newline at end of file +Learn how to interpret and troubleshoot oppia-android CI check failures in this insightful [developer video](https://youtu.be/HLzHQULZbJE?si=RLY9_8Yzv5cjYM7q). \ No newline at end of file From 2f68639f90e84fde029b92da48a9bc925cea7cc8 Mon Sep 17 00:00:00 2001 From: Subhajit Mallick <153619690+subhajitxyz@users.noreply.github.com> Date: Tue, 19 Nov 2024 23:30:04 +0530 Subject: [PATCH 7/7] Fix #5431 : Todo Checks Should Check Exclusively Against Issues (#5564) ## Explanation Fix #5431 This pull request fixes issue by implementing the following changes: Filters out pull requests from the "open issues" list. Ensures all "To-Do" checks will pass only for open issues (not for prs). ## Essential Checklist - [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".) - [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation. - [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide). - [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)). - [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop". - [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)). ## For UI-specific PRs only If your PR includes UI-related changes, then: - Add screenshots for portrait/landscape for both a tablet & phone of the before & after UI changes - For the screenshots above, include both English and pseudo-localized (RTL) screenshots (see [RTL guide](https://github.com/oppia/oppia-android/wiki/RTL-Guidelines)) - Add a video showing the full UX flow with a screen reader enabled (see [accessibility guide](https://github.com/oppia/oppia-android/wiki/Accessibility-A11y-Guide)) - For PRs introducing new UI elements or color changes, both light and dark mode screenshots must be included - Add a screenshot demonstrating that you ran affected Espresso tests locally & that they're passing --------- Co-authored-by: Sneha Datta Co-authored-by: Adhiambo Peres <59600948+adhiamboperes@users.noreply.github.com> Co-authored-by: Mr. 17 --- scripts/assets/todo_open_exemptions.textproto | 5 ++ .../android/scripts/common/GitHubClient.kt | 3 +- .../scripts/common/model/GitHubIssue.kt | 16 ++++- .../android/scripts/todo/TodoOpenCheckTest.kt | 62 ++++++++++++++++++- 4 files changed, 81 insertions(+), 5 deletions(-) diff --git a/scripts/assets/todo_open_exemptions.textproto b/scripts/assets/todo_open_exemptions.textproto index 8dd53a51ce9..035e618e357 100644 --- a/scripts/assets/todo_open_exemptions.textproto +++ b/scripts/assets/todo_open_exemptions.textproto @@ -333,6 +333,11 @@ todo_open_exemption { line_number: 689 line_number: 737 line_number: 741 + line_number: 790 + line_number: 791 + line_number: 792 + line_number: 798 + line_number: 800 } todo_open_exemption { exempted_file_path: "scripts/static_checks.sh" diff --git a/scripts/src/java/org/oppia/android/scripts/common/GitHubClient.kt b/scripts/src/java/org/oppia/android/scripts/common/GitHubClient.kt index fd7cc105037..23348c05136 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/GitHubClient.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/GitHubClient.kt @@ -68,13 +68,14 @@ class GitHubClient( val call = gitHubService.fetchOpenIssues(repoOwner, repoName, authorizationBearer, pageNumber) // Deferred blocking I/O operation to the dedicated I/O dispatcher. val response = withContext(Dispatchers.IO) { call.execute() } + check(response.isSuccessful()) { "Failed to fetch issues at page $pageNumber: ${response.code()}\n${call.request()}" + "\n${response.errorBody()}." } return@async checkNotNull(response.body()) { "No issues response from GitHub for page: $pageNumber." - } + }.filter { it.pullRequest == null } } } diff --git a/scripts/src/java/org/oppia/android/scripts/common/model/GitHubIssue.kt b/scripts/src/java/org/oppia/android/scripts/common/model/GitHubIssue.kt index 7a824fc3120..67b814440fa 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/model/GitHubIssue.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/model/GitHubIssue.kt @@ -10,4 +10,18 @@ import com.squareup.moshi.JsonClass * 'issues/' in an issue's GitHub URL) */ @JsonClass(generateAdapter = true) -data class GitHubIssue(@Json(name = "number") val number: Int) +data class GitHubIssue( + @Json(name = "number") val number: Int, + @Json(name = "pull_request") val pullRequest: PullRequestInfo? = null +) + +/** + * Data class representing information about a pull request associated with a GitHub issue. + * + * @property url the URL of the pull request, if it exists. This provides the link to the specific + * pull request associated with the issue on GitHub. + */ +@JsonClass(generateAdapter = true) +data class PullRequestInfo( + @Json(name = "url") val url: String? = null +) diff --git a/scripts/src/javatests/org/oppia/android/scripts/todo/TodoOpenCheckTest.kt b/scripts/src/javatests/org/oppia/android/scripts/todo/TodoOpenCheckTest.kt index b614bc361d7..b5a3f783b70 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/todo/TodoOpenCheckTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/todo/TodoOpenCheckTest.kt @@ -777,11 +777,67 @@ class TodoOpenCheckTest { assertThat(outContent.toString().trim()).isEqualTo(failureMessage) } - private fun setUpGitHubService(issueNumbers: List) { - val issueJsons = issueNumbers.joinToString(separator = ",") { "{\"number\":$it}" } + @Test + fun testTodoCheck_PrPresent_checkShouldFail() { + setUpGitHubService( + issueNumbers = listOf(11004, 11003, 11002, 11001), + pullRequestNumbers = listOf(11005) + ) + val tempFile1 = tempFolder.newFile("testfiles/TempFile1.kt") + val tempFile2 = tempFolder.newFile("testfiles/TempFile2.kt") + val testContent1 = + """ + // TODO(#11002): test summary 1. + # TODO(#11004): test summary 2. + # TODO(#11001): test summary 3. + test Todo + test TODO + """.trimIndent() + val testContent2 = + """ + // TODO(#11005): test summary 3. + todo + + + """.trimIndent() + tempFile1.writeText(testContent1) + tempFile2.writeText(testContent2) + + val exception = assertThrows() { runScript() } + + assertThat(exception).hasMessageThat().contains(TODO_SYNTAX_CHECK_FAILED_OUTPUT_INDICATOR) + val failureMessage = + """ + TODOs not corresponding to open issues on GitHub: + - TempFile2.kt:1 + + $wikiReferenceNote + + $regenerateNote + """.trimIndent() + assertThat(outContent.toString().trim()).isEqualTo(failureMessage) + } + + private fun setUpGitHubService( + issueNumbers: List, + pullRequestNumbers: List = emptyList() + ) { + // Create JSON for issues with "pull_request" set to null + val issueJsons = issueNumbers + .joinToString(separator = ",") { "{\"number\":$it,\"pull_request\":null}" } + + // Create JSON for pull requests with "pull_request" as an empty object + val pullRequestJsons = pullRequestNumbers + .joinToString(separator = ",") { "{\"number\":$it,\"pull_request\":{}}" } + + // Combine issues and pull requests into one JSON array + val combinedJsons = + "[$issueJsons${if (pullRequestNumbers.isNotEmpty()) ", $pullRequestJsons" else ""}]" + val mockWebServer = MockWebServer() mockWebServer.enqueue(MockResponse().setBody("[$issueJsons]")) - mockWebServer.enqueue(MockResponse().setBody("[]")) // No more issues. + mockWebServer.enqueue(MockResponse().setBody(combinedJsons)) + mockWebServer.enqueue(MockResponse().setBody("[]")) GitHubClient.remoteApiUrl = mockWebServer.url("/").toString() }