CodeSignal:  Website Calendar

CodeSignal: Website Calendar

Python - Picturing the Parsibilities

·

3 min read

Another coding problem

You're creating your own website for Harry Potter fans. As you all believe in magic, you're waiting for your personal letter from Hogwarts, that will definitely arrive to you some day with a magnificent owl. To be prepared for this exciting moment you decided to embed a calendar to your website on which you will mark the days when Hogwarts owls can bring letters.

Given a month and a year, return an HTML table representing the desired calendar. Follow the same format as provided in the example but omit all whitespace characters (i.e. tabs, newlines and whitespaces) where it is possible, because you care about space efficiency.

Example

For month = 11 and year = 2016, the output should be

solution(month, year) =
"<table border="0" cellpadding="0" cellspacing="0" class="month">
  <tr>
    <th colspan="7" class="month">November 2016</th>
  </tr>
  <tr>
    <th class="mon">Mon</th>
    <th class="tue">Tue</th>
    <th class="wed">Wed</th>
    <th class="thu">Thu</th>
    <th class="fri">Fri</th>
    <th class="sat">Sat</th>
    <th class="sun">Sun</th>
  </tr>
  <tr>
    <td class="noday">&nbsp;</td>
    <td class="tue">1</td>
    <td class="wed">2</td>
    <td class="thu">3</td>
    <td class="fri">4</td>
    <td class="sat">5</td>
    <td class="sun">6</td>
  </tr>
  <tr>
    <td class="mon">7</td>
    <td class="tue">8</td>
    <td class="wed">9</td>
    <td class="thu">10</td>
    <td class="fri">11</td>
    <td class="sat">12</td>
    <td class="sun">13</td>
  </tr>
  <tr>
    <td class="mon">14</td>
    <td class="tue">15</td>
    <td class="wed">16</td>
    <td class="thu">17</td>
    <td class="fri">18</td>
    <td class="sat">19</td>
    <td class="sun">20</td>
  </tr>
  <tr>
    <td class="mon">21</td>
    <td class="tue">22</td>
    <td class="wed">23</td>
    <td class="thu">24</td>
    <td class="fri">25</td>
    <td class="sat">26</td>
    <td class="sun">27</td>
  </tr>
  <tr>
    <td class="mon">28</td>
    <td class="tue">29</td>
    <td class="wed">30</td>
    <td class="noday">&nbsp;</td>
    <td class="noday">&nbsp;</td>
    <td class="noday">&nbsp;</td>
    <td class="noday">&nbsp;</td>
  </tr>
</table>"
Here is how this calendar would look on your website:

November 2016
Mon    Tue    Wed    Thu    Fri    Sat    Sun
     1    2    3    4    5    6
7    8    9    10    11    12    13
14    15    16    17    18    19    20
21    22    23    24    25    26    27
28    29    30

I methodically worked through and learned some new things about the calendar package.

def solution(month, year):
    import calendar
    from calendar import monthrange
    import math
    from datetime import datetime

    days_in_week = 7
    first_day, last_day = monthrange(year, month)
    first_day = 1 # Always start at 1 intead of offset.
    last_day += 1

    get_date = lambda d: datetime.strptime(f"{month}-{d}-{year}",  '%m-%d-%Y')
    days_of_month = [(d, get_date(d).weekday(), calendar.day_name[get_date(d).weekday()][:3]) 
                        for d in range(first_day, last_day)]
    days_in_month = len(days_of_month)

    offset_days = days_in_week - days_of_month[0][1]
    pad_start = [None for x in range(days_of_month[0][1])]
    remaining_days = days_in_month % days_in_week  # or days_in_month - 4*7
    pad_end = [None for x in range(days_in_week - (remaining_days - offset_days))] 

    print(pad_start, pad_end, offset_days, remaining_days)
    days_of_month = pad_start + days_of_month + pad_end
    weeks = math.ceil(len(days_of_month) / days_in_week) if len(pad_end) < 7 else (days_in_month // days_in_week) + 1
    print(days_of_month, days_in_month, weeks)


    table_tag = f"<table border='0' cellpadding='0' cellspacing='0' class='month'>"
    tr_start, tr_end = "<tr>", "</tr>"
    header = f"<th colspan='7' class='month'>{calendar.month_name[month]} {year}</th>"
    cell = lambda x, d, day: f"<t{x} class='{str(day).lower()}'>{d}</t{x}>"

    calendar_month = []
    for i in range(weeks):
        c = i * 7
        week = []
        for dow in days_of_month[c:c+days_in_week]:
            week.append(dow)
        calendar_month.append(week)

    res = table_tag + tr_start + header + tr_end

    res += tr_start
    for name in calendar.day_name: res += cell('h', name[:3], name[:3])
    res += tr_end

    for week in calendar_month:
        res += tr_start

        for d in week:
            if d:
                res += cell('d', d[0], d[2])
            else:
                res += cell('d', '&nbsp;', 'noday')
        res += tr_end

    res += "</table>"

    return res.replace("'", '"')

All tests and hidden tests pass. Yay.

Top-ranked solution:

def solution_getta_outta_here(month, year):
    import calendar
    return calendar.HTMLCalendar().formatmonth(year, month).replace("\n", "")

Whelp, learned something else about calendar package. (╯°□°)╯︵ ┻━┻