مشروع الدفق الثالث: تطوير مركز البيانات - معهد الكود.
تم إنشاء هذا المشروع باستخدام Flask Microframework، ويمكن استخدامه كساعة توقيت يدوية لتوقيت العديد من الرياضيين في السباحة والمضمار. الهدف من هذا التطبيق هو تحسين الكفاءة في التوقيت الرياضي من خلال تقليل عدد الأشخاص الذين يقومون بالتوقيت بشكل منفصل وتخزين الأوقات بشكل مباشر، بدلاً من الاحتفاظ بوثائق مكتوبة.
هذا التطبيق مخصص للاستخدام التعليمي فقط. هذا التطبيق ليس للاستخدام التجاري.
يمكن العثور على عرض حي لهذا المشروع هنا. تتم استضافة هذا التطبيق على Heroku. يتم تخزين الأوقات في MongoDB.
جاءت فكرة تطبيق Timing Assistant هذا من تعرضي للسباحة طوال طفولتي. لقد لاحظت أنه يجب أن يكون هناك عدد من الأشخاص المشاركين في عملية التوقيت، حيث أن لوحات اللمس الموجودة في المجموعة لا تنتج دائمًا أوقاتًا دقيقة أو قابلة للقراءة لأسباب مختلفة. علاوة على ذلك، لا يمكن للمدرب والرياضيين رؤية الأوقات النهائية أو الانقسامات إلا بعد انتهاء اللقاء، إلا إذا كان لديك عدة أشخاص يقومون بالتوقيت على سطح السفينة لكل حارة ويكتبون الأوقات في الحافظة.
لقد رأيت هذه الفرصة لإنشاء تطبيق من شأنه تقليل عدد الأشخاص المشاركين في عملية التوقيت، على أمل تحسين كفاءة اللقاءات وردود الفعل للرياضيين والمدربين. يمكن تحسين هذا التطبيق ليناسب أي رياضة محددة بوقت، ويعمل هذا الإصدار مع كل من السباحة وسباقات المضمار.
يمكن للمدربين والمؤقتين على حدٍ سواء اختيار رياضة، ولقاء، وحدث، ودرجة الحرارة، وأرقام الممرات التي تساعدهم جميعًا على تتبع ما يحدث في اللقاء. كما يسمح لهم أيضًا بتقديم تعليقات فورية للرياضيين بعد السباق، حيث تظهر الأوقات بعد حفظهم ثم الضغط على "عرض الأوقات". وأردت أيضًا التأكد من قدرتهم على رؤية النتائج التراكمية للاجتماع، بدلاً من حدث واحد فقط في كل مرة.
لم يتم استخدام أي موضوع لهذا التصميم، وتم اختيار التصميم الحديث لأنه مع وجود الكثير من الموقتات والبيانات على الصفحة، يمكن أن يبدو فوضويًا وغير منظم. لم أكن أرغب في ذلك، لذلك بينما كنت أرغب في وضع الموقتات والأوقات وخيارات الأحداث في صفحة واحدة، اعتقدت أن تقسيم الصفحة إلى ثلاثة أثلاث رأسيًا سيكون أفضل طريقة للقيام بذلك، مما يؤدي إلى إنشاء فرق ملحوظ بين كل ثلث.
تم إنشاء Timing Assistant باستخدام Flask Microframework في Python مع قاعدة بيانات NoSQL (MongoDB) على الواجهة الخلفية، مع HTML وCSS وJavascript وjQuery على الواجهة الأمامية ولوظيفة ساعة الإيقاف، المتصلة بالواجهة الخلفية مع AJAX.
في الصفحة الرئيسية للتطبيق، يقوم المستخدم بإدخال الرياضة التي يرغب في قضاء وقت فيها، واسم فريقه، واسم المستخدم الخاص به، واسم اللقاء. ينقلهم هذا الإجراء إلى صفحة ساعة الإيقاف، حيث يمكنهم بعد ذلك اختيار الحدث والبث المباشر الذي يرغبون في تحديد وقت له. يمكن للمستخدم أن يصل إلى ثلاثة ممرات ويأخذ انقسامات لجميع الممرات الثلاثة. سيتعين على المستخدم إيقاف المؤقت الرئيسي يدويًا بعد إيقاف المؤقتات الثلاثة الصغيرة، حيث يتم ضبطها بشكل فردي. ومع ذلك، قد يكون هذا مفيدًا بشكل خاص، إذا أراد المدرب معرفة المدة التي استغرقها السباق ومقارنة أوقات سباحيه مقابل وقت النهاية النهائية. لن يتم حفظ وقت المؤقت الرئيسي في قاعدة البيانات.
بمجرد النقر على "إرسال"، سيظهر الحدث والحرارة. ويمكنهم بعد ذلك بدء التوقيت لما يصل إلى ثلاثة مسارات عن طريق الضغط على زر "START" في ساعة الإيقاف الرئيسية. سيؤدي هذا إلى تشغيل جميع ساعات الإيقاف الأربع، ومع ذلك، سيتم حفظ الساعات الثلاث السفلية فقط في قاعدة البيانات. هناك خيار لتجميع الأوقات المقسمة لكل حارة على حدة باستخدام زر "SPLIT". يمكن إيقاف كل مؤقت وتشغيله بشكل فردي، حيث يتحكم المؤقت الرئيسي في جميع ساعات الإيقاف (البدء والإيقاف وإعادة التعيين).
بالنقر فوق "حفظ الأوقات"، سيتم حفظ الأوقات باستخدام استدعاء AJAX في ملف Javascript لدفع الأوقات إلى Flask، مع توصيل Flask بـ MongoDB. بمجرد حفظ الأوقات، بالنقر فوق "عرض الأوقات"، ستظهر الأوقات أسفل ساعات التوقف.
سيتم حفظ البيانات أيضًا بدون تحديد حدث أو حرارة (ستكون هذه الحقول فارغة عند عرض الأوقات). قد يكون هذا مفيدًا إذا أراد المدرب استخدام ساعات الإيقاف عمليًا وليس في مكان اللقاء.
أود أن أتمكن من منح المدربين فرصة تنزيل البيانات بصيغة PDF أو أي تنسيق ملف آخر. على المدى الطويل، سيسمح لهم ذلك بالاحتفاظ بجميع التوقيتات اليدوية لسجلاتهم دون الحاجة إلى الاعتماد على البيانات المكتوبة بخط اليد أو الاعتماد على هذا الموقع في كل مرة يريدون العودة وإلقاء نظرة على اللقاءات القديمة.
أود أيضًا أن أسمح للمدرب باختيار عدد ساعات الإيقاف التي يريد عرضها بناءً على عدد الرياضيين في كل جولة. حاليًا، يمكنك فقط تحديد الوقت لثلاثة رياضيين في المرة الواحدة، ولا يمكنك اختيار الوقت لأقل من 3.
أود أيضًا تنفيذ "وضع التدريب" و"وضع الاجتماع" الذي من شأنه أن يسمح بتوقيت أكثر تعقيدًا للاجتماعات والممارسات. سيخلق وضع اللقاء المزيد من القيود على اختيار حدث أو مباراة، وسيسمح للمدرب باختيار عدد الممرات التي يرغب في تخصيص وقت لها. سيسمح وضع التدريب للمدرب بتدوين الملاحظات في الأوقات التي يدخرها (لتمرين معين، وما إلى ذلك)، دون الحاجة إلى تحديد حدث أو حرارة.
تم إجراء جميع الاختبارات لهذا المشروع يدويًا. يتطلب النموذج الموجود على الصفحة المقصودة سمات على علامات الإدخال لمنع المستخدم من عدم ملء حقل في النموذج، حيث سيؤدي ذلك إلى خطأ 400، نظرًا لأن مسار تطبيق Flask يعتمد على هذه المدخلات.
تم اختبار وظيفة Ajax وزر حفظ الأوقات عبر وحدة التحكم والتحقق من ظهور البيانات بشكل صحيح في MongoDB. تم أيضًا اختبار البيانات التي تم جمعها من أجهزة ضبط الوقت وبنية البيانات المقصودة.
توفير الأوقات بشكل فردي مع مستند واحد لكل حارة:
أوقات عرض الممرات في نفس المستند (ملاحظة: لأغراض الاختبار، تم حفظ مسارين فقط هنا للتأكد من ظهور مسارين في نفس المستند.):
بنية البيانات الصحيحة:
تم إجراء اختبار ساعات الإيقاف يدويًا أيضًا للتأكد من أن زر إعادة الضبط الرئيسي يعيد ضبط ساعة الإيقاف ويمسح الانقسامات من جميع المؤقتات، بينما تقوم كل ساعة توقيت فردية بمسح الوقت والانقسامات الخاصة بها فقط. علاوة على ذلك، تم اختبار ذلك أيضًا لوظيفة البدء/الإيقاف، حيث تتحكم ساعة الإيقاف الرئيسية في جميع ساعات الإيقاف، في حين يجب أن تتحكم الساعات الفردية فقط في وظائف التشغيل/الإيقاف الخاصة بها.
تم أيضًا اختبار جميع مسارات Flask للتأكد من أن جميع الروابط تعمل وأنه يمكنه التعامل مع أي قيم غير شائعة في الإدخال، وأنه سيعرض المدخلات بشكل صحيح عبر Jinja في ملف HTML.
أثناء عملية الاختبار، أدركت أنه من الممكن أن يكون لدى مستخدمين نفس اسم الاجتماع أو نفس اسم النادي، مما يتيح للمستخدم الوصول إلى بيانات شخص آخر. لذا، لعرض الأوقات في القالب، قمت بتضمين ما يلي للتأكد من أن ثلاثة حقول إدخال في الصفحة المقصودة يجب أن تتطابق لعرض الأوقات المقابلة. يتطلب هذا اسم الفريق الصحيح واسم المستخدم واسم الاجتماع للمطابقة لمنع الحفظ المتبادل أو العرض المتبادل للأوقات.
{% if time.team == team %}
{% if time.username == username %}
{% if time.meet == meets %}
كانت الانقسامات لكل حارة تظهر في الأصل بالتنسيق التالي:
split: ["00:02.2300:01.45"]
ومع ذلك، أردت أن تظهر في قائمة مثل هذا:
split: ["00:02.23", "00:01.45"]
لذلك اضطررت إلى تنفيذ فهم القائمة لفصل هذه السلسلة إلى سلاسل متعددة في القائمة (إذا تم أخذ أكثر من تقسيم للمسار) كل 9 أحرف.
في HTML timer_page.html، يبدو النموذج الذي يرسل البيانات لوظيفة AJAX وكأنه يحتوي على علامة نهاية شاردة، ولكن من الضروري أن يشمل جميع الأوقات النهائية، والتقسيم، وبيانات المسار لحفظ الأوقات في MongoDB . نظرًا للتصميم والعناصر الأخرى التي يلزم عرضها، يبدو أن النموذج غير مرتب مع العناصر الأخرى. أيضًا، عند النظر إلى HTML، توجد علامات فارغة، ومع ذلك، هذا هو المكان الذي يتم فيه إدراج أوقات الانقسام في HTML باستخدام jQuery.
عند حفظ الأوقات، إذا اخترت نفس المسار لكل مؤقت (المسار 1، على سبيل المثال)، فسوف يظهر مسار واحد فقط من الأوقات 1 في أوقات العرض. ولكن عليك اختيار المسار، حيث لا يمكنك عرض الأوقات التي قمت بحفظها بدونه نظرًا لطبيعة بنية البيانات. يتم تعيين القائمة المنسدلة للمسار للحفظ في المسار 1 والأرض 2 والأرض 3 بشكل افتراضي في حالة عدم تحديد المستخدم للمسار. آمل أن يتم تنفيذ التحقق من الصحة الذي سيمنع حفظ الأوقات إذا اختار المستخدم نفس رقم المسار لمسارين في الحرارة.
تم تعديل وظائف Javascript التي تقوم بتشغيل ساعة الإيقاف من البرنامج التعليمي لساعة الإيقاف Coding with Sara لهذا التطبيق. تم أيضًا تصميم بعض HTML وفقًا لمثالها، ولكن تم تعديلها لتناسب التصميم والأزرار المتعددة والانقسامات والممرات.
بالنسبة لجافا سكريبت، تم تعديل وظائف إعادة التعيين لزر إعادة الضبط لإعادة ضبط جميع ساعات الإيقاف بدلاً من تحديث الصفحة، وتمت إزالة أزرار إعادة التعيين الفردية لكل ساعة توقيت صغيرة لأنها كانت ميزة غير ضرورية لتجربة المستخدم لهذا المشروع. تمت إضافة وظائف الانقسام أيضًا. تم تعديل وظائف البدء/الإيقاف لتغيير نمط الأزرار باستخدام jQuery. تمت إضافة ساعة توقيت رئيسية لتجربة المستخدم، وبالتالي تمكن المدرب من رؤية إجمالي الوقت المنقضي بالإضافة إلى الأوقات الفردية، على غرار لوحة نتائج السباحة. تمت إضافة زر حفظ لتمرير القيم إلى Flask وإلى MongoDB باستخدام Ajax.
تم تصميم وظيفة Ajax على غرار هذا المنشور من Stack Overflow وتم تعديلها لتناسب هذا المشروع من خلال النظر في أنماط استخدامات Ajax الأخرى وبناء الجملة. تمت إضافة منع افتراضي لمنع إعادة تحميل الصفحة عند إجراء استدعاء AJAX.
تم استخدام العودية في Jinja للتكرار على القواميس المتداخلة في Python لعرض الأوقات وتلبية البيانات بشكل صحيح من خلال التأكد من تكرار جميع الممرات وعرضها. تم اتباع هذه الطريقة من Stack Overflow كمبدأ توجيهي، وتم تعديلها لتناسب طبيعة بنية البيانات الخاصة بي.
تمت محاولة إعادة هيكلة وظيفة جافا سكريبت لساعات الإيقاف حتى يتمكن المستخدم من تحديد عدد ساعات الإيقاف التي يريد عرضها على الشاشة بناءً على عدد الممرات المختارة.
var stopwatches = [ ] ;
var i ;
for ( i = 0 ; i <= 1 ; i ++ ) {
var stopwatch = new timing ( "timerLabel" + i , "start" + i , "splitLabel" + i ) ;
stopwatches . push ( stopwatch ) ;
console . log ( i ) ;
document . getElementById ( "start" + i ) . onclick = function ( ) {
stopwatches [ i ] . start ( ) ;
}
document . getElementById ( "reset" + i ) . onclick = function ( ) {
stopwatches [ i ] . reset ( ) ;
}
document . getElementById ( "split" + i ) . onclick = function ( ) {
stopwatches [ i ] . split ( ) ;
}
console . log ( stopwatches ) ;
}
عند محاولة كتابة هذه العناصر في حلقة for مثل هذه، فإن ساعات الإيقاف[i].start() لن تقرأ i
كمتغير يمكن أن يتغير، ومع ذلك، عندما تم ترميزه بشكل ثابت، لم تكن هناك مشكلة:
document . getElementById ( "reset" + i ) . onclick = function ( ) {
stopwatches [ 0 ] . reset ( ) ;
}
document . getElementById ( "split" + i ) . onclick = function ( ) {
stopwatches [ 0 ] . split ( ) ;
}
لقد حاولت التعامل مع الأمر بطريقة مختلفة، والتي تضمنت عبارات للحصول على ساعات الإيقاف المقابلة للظهور.
جرت محاولة تمرير i
عبر دالة بدلاً من ذلك كوسيطة مأخوذة من حلقة for أعلاه، لكنها لم تنجح:
function chooseNumberOfStopwatches ( i ) {
if ( i == 1 ) {
stopwatches_one . start ( ) ;
}
else if ( i == 2 ) {
stopwatches_one . start ( ) ;
stopwatches_two . start ( ) ;
} else {
console . log ( 'else' ) ;
}
}
إذا كنت مهتمًا باستنساخ هذا المستودع، لإعداد كل شيء وتثبيته في ملف المتطلبات، قم بتشغيل الأمر التالي في الوحدة الطرفية:
$ sudo pip3 -r install requirements.txt
يرجى ملاحظة أنني استخدمت Cloud9 لهذا المشروع، لذلك إذا كنت تستخدم محررًا مختلفًا، فقد تختلف أوامر الوحدة الطرفية. يرجى الرجوع إلى المستندات الخاصة بالمحرر الذي تستخدمه للحصول على مزيد من المعلومات حول الأوامر الطرفية الخاصة بالمحرر. يجب الحصول على جميع المفاتيح السرية لـ MongoDB بشكل فردي، لأنها مخفية ومحددة بالنسبة لي.