• בלוג
  • מתי תורי? סקריפט פייתון פשוט לחישוב זמן המתנה

מתי תורי? סקריפט פייתון פשוט לחישוב זמן המתנה

17/05/2022

הלכתי למשרד הפנים השבוע לחדש דרכון וגיליתי שני דברים - המקומם יותר הוא שצריך לשלם יותר כדי שהפרטים שלי לא יישמרו במאגר הביומטרי (כי הדרכון תקף רק לחמש שנים). ואני הייתי בטוח שאני חוסך להם מקום שם. הפחות מקומם הוא שאפילו אחרי שמזמינים תור חצי שנה מראש, עדיין צריך לחכות איזו שעה עד שנכנסים. בשביל להתמודד עם השני ובשביל לא להשתעמם בשעה הזאת, כתבתי סקריפט פייתון קצר שעוזר להבין כמה זמן המתנה עוד נשאר לך. הוא לא מדויק אבל כן אפשר ללמוד ממנו דבר או שניים לגבי טיפול בזמנים בפייתון, אז אני משתף.

1. איך עובדים עם timedelta ו datetime

יש מספר מחלקות לעבודה עם זמנים בפייתון. אלה שיעזרו לנו בסקריפט הן datetime, שמייצגת אוביקט תאריך ושעה, ו timedelta שמייצגת הפרש בין שני אוביקטי datetime. הפונקציה now של datetime מחזירה אוביקט שמתאים למה שיש עכשיו. הנה כמה דוגמאות לשימוש במחלקות:

>>> from datetime import datetime, timedelta
>>> datetime.now()
datetime.datetime(2022, 5, 16, 21, 34, 23, 446115)

>>> datetime.now() + timedelta(days=1)
datetime.datetime(2022, 5, 17, 21, 35, 3, 13974)

>>> datetime.now() - timedelta(minutes=10)
datetime.datetime(2022, 5, 16, 21, 25, 18, 937076)

אפשר גם ליצור אוביקט datetime מתאריך ושעה ספציפיים, או לקחת אוביקט קיים ולהחליף חלק מהשדות שלו. לדוגמה הקוד הבא לוקח אוביקט datetime של היום ומחליף רק את השעה והדקות לשעה ארבע אחר הצהריים:

>>> datetime.now().replace(hour=16, minute=0)
datetime.datetime(2022, 5, 16, 16, 0, 17, 369505)

2. הנתונים

עכשיו שאנחנו יודעים לעבוד עם datetime ו timedelta הגיע הזמן לאסוף קצת נתונים. אם אתם יושבים במשרד הפנים או בדואר אפשר פשוט לרשום בקובץ את מה ששומעים. זאת הרשימה שאני שמעתי:

16:02 484
16:06 501
16:07 504
16:09 508
16:11 510
16:12 513
16:14 511
16:15 516
16:21 521
16:22 523
16:26 478
16:30 524
16:32 525
16:33 530
16:34 532
16:36 527
16:37 531
16:39 534
16:42 542
16:45 541

העמודה הראשונה מייצגת שעה והעמודה השניה את המספר של מי שעכשיו נכנס. אני הייתי 547 ורציתי לדעת כמה זמן עוד נשאר לי לחכות. ועכשיו לסקריפט.

3. איך מחשבים

בשביל לחשב כמה זמן המתנה עוד נשאר, מספיק להסתכל על כמה שורות אחרונות בקובץ. אנחנו לא רוצים להסתכל על כל הקובץ כי יכול להיות שמישהו נתקע לפני חצי שעה, או שלפני שעה היו פחות פקידים ממה שיש עכשיו. חמש או עשר שורות אחרונות יתנו תוצאה מדויקת. שמתי לב גם שאנשים לא תמיד נכנסים לפי הסדר אבל התעלמתי מזה בסקריפט כי בסוף הגיע תורי.

בשביל החישוב יצרתי רשימה, שכל תא ברשימה מכיל אוביקט datetime של שעת הכניסה ומספר שמייצג את המספר שלו. תמיד אפשר להכניס עוד שורות לרשימה כשמקריאים עוד מספר, או לקרוא קובץ שכבר כל השורות נמצאות בו כדי ליצור את הרשימה מאפס. אנחנו כן צריכים לנקות פריטים ישנים אם גודל הרשימה עולה על גודל הדגימה הרצוי. זאת הפונקציה שמטפלת בזה:

def invite(when, who):
    if len(sample) == SAMPLE_SIZE:
        sample.pop()

    sample.insert(0, (when, who))

אחרי שאני בונה את כל הרשימה אני רוצה לבדוק כמה אנשים מחכים לפניי בתור. אני יודע שאנשים לא תמיד נכנסים לפי הסדר אז אני פשוט מחפש בדגימה הנוכחית מי המספר המקסימלי ומחסיר אותו מהמספר שלי:

def people_before_me(my_number):
    last_number = max(x[1] for x in sample)
    return my_number - last_number

האתגר הבא יהיה למצוא מה זמן ההמתנה הממוצע עבור הדגימה שבניתי. בשביל זה אני מחשב את ההפרש בין כל שתי שורות בדגימה - ההפרש מגיע בתור אוביקט timedelta - ואז מחשב את ממוצע ההפרשים:

def average_wait_time():
    wait_times = []
    prev = sample[0]
    for i in sample[1:]:
        wait_times.append(prev[0] - i[0])
        prev = i

    return sum(w.total_seconds() for w in wait_times) / len(wait_times)

הפונקציה total_seconds של timedelta מחזירה את הפרש הזמן הכולל בשניות. לסיום אני קורא את קובץ הקלט ומפענח אותו עם הפונקציות שכתבתי:

with open('myturn.txt') as f:
    for line in f:
        try:
            parse_line(line)
        except Exception:
            print(f"Skipped line: {line}")

print(f"Please wait {average_wait_time() * people_before_me(547)} Seconds")

בתיעוד של פייתון יש דף מידע מעולה על עבודה עם תאריכים שכולל את כל הדוגמאות מכאן והרבה יותר: https://docs.python.org/3/library/datetime.html