Skip to content

Instantly share code, notes, and snippets.

@rednafi
Last active August 20, 2024 07:14
Show Gist options
  • Save rednafi/61b0e6a685ffd9cd09bf978565e7f7f7 to your computer and use it in GitHub Desktop.
Save rednafi/61b0e6a685ffd9cd09bf978565e7f7f7 to your computer and use it in GitHub Desktop.
Generate a simple worklog.

What

The following script will generate a simple markdown worklog in the format listed below. It correctly handles the partial weeks at the beginning and the end of the year.

# Worklog

## Week 1 [2025-01-01 - 2025-01-03]

- **Wednesday**
- **Thursday**
- **Friday**

## Week 2 [2025-01-06 - 2025-01-10]

- **Monday**
- **Tuesday**
- **Wednesday**
- **Thursday**
- **Friday**


...


## Week 53 [2025-12-29 - 2025-12-31]

- **Monday**
- **Tuesday**
- **Wednesday**

Script

Run the script with python3.12 -m worklog -y <4-digit-year>.

# worklog.py

import argparse
from datetime import datetime, timedelta
from dataclasses import dataclass
from collections.abc import Iterator
import logging

# Set up logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)


@dataclass(slots=True)
class WeekLog:
    week_number: int
    start_of_week: str
    end_of_week: str
    days_of_week: list[str]


def generate_weeklogs(year: int) -> Iterator[WeekLog]:
    start_date = datetime(year, 1, 1)
    end_of_year = datetime(year, 12, 31)
    week_number = 1

    # Handle the first partial week
    if start_date.weekday() > 4:  # If Jan 1 is a Saturday or Sunday, skip to next Monday
        start_date += timedelta(days=(7 - start_date.weekday()))
    first_week_end = min(start_date + timedelta(days=4 - start_date.weekday()), end_of_year)

    yield WeekLog(
        week_number,
        start_date.strftime("%Y-%m-%d"),
        first_week_end.strftime("%Y-%m-%d"),
        [(start_date + timedelta(days=i)).strftime("%A") for i in range((first_week_end - start_date).days + 1)],
    )

    week_number += 1
    current_date = first_week_end + timedelta(days=3)  # Skip to next Monday
    days_of_week = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]

    # Handle full weeks
    while current_date <= end_of_year:
        start_of_week = current_date
        end_of_week = min(start_of_week + timedelta(days=4), end_of_year)

        if end_of_week == end_of_year:
            days_of_week = days_of_week[: (end_of_week - start_of_week).days + 1]

        yield WeekLog(week_number, start_of_week.strftime("%Y-%m-%d"), end_of_week.strftime("%Y-%m-%d"), days_of_week)

        week_number += 1
        current_date = end_of_week + timedelta(days=3)  # Skip to next Monday


def export_worklog_to_markdown(weeklogs: Iterator[WeekLog], filename: str) -> None:
    with open(filename, "w") as file:
        file.write("# Worklog\n\n")
        for week in weeklogs:
            file.write(f"## Week {week.week_number} [{week.start_of_week} - {week.end_of_week}]\n\n")
            for day in week.days_of_week:
                file.write(f"- **{day}**\n")
            file.write("\n")


def main():
    parser = argparse.ArgumentParser(description="Generate a worklog for a specified year.")
    parser.add_argument("-y", "--year", type=int, default=datetime.now().year)
    args = parser.parse_args()

    worklog = generate_weeklogs(args.year)
    export_worklog_to_markdown(worklog, f"worklog_{args.year}.md")
    logger.info(f"Worklog for {args.year} has been generated successfully.")


if __name__ == "__main__":
    main()

Unit test

The unittests can be run with python -m test_worklog.

# test_worklog.py

import unittest
from worklog import generate_weeklogs


class TestGenerateWeeklogs(unittest.TestCase):
    def test_full_year_start_on_monday(self):
        year = 2024  # 2024 is a leap year and starts on a Monday
        weeklogs = list(generate_weeklogs(year))

        # Check the first week
        self.assertEqual(weeklogs[0].start_of_week, "2024-01-01")
        self.assertEqual(weeklogs[0].end_of_week, "2024-01-05")
        self.assertEqual(weeklogs[0].days_of_week, ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"])

        # Check the last week
        self.assertEqual(weeklogs[-1].start_of_week, "2024-12-30")
        self.assertEqual(weeklogs[-1].end_of_week, "2024-12-31")
        self.assertEqual(weeklogs[-1].days_of_week, ["Monday", "Tuesday"])

    def test_partial_first_week(self):
        year = 2025  # 2025 starts on a Wednesday
        weeklogs = list(generate_weeklogs(year))

        # Check the first partial week
        self.assertEqual(weeklogs[0].start_of_week, "2025-01-01")
        self.assertEqual(weeklogs[0].end_of_week, "2025-01-03")
        self.assertEqual(weeklogs[0].days_of_week, ["Wednesday", "Thursday", "Friday"])

        # Check the second week
        self.assertEqual(weeklogs[1].start_of_week, "2025-01-06")
        self.assertEqual(weeklogs[1].end_of_week, "2025-01-10")
        self.assertEqual(weeklogs[1].days_of_week, ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"])

    def test_partial_last_week(self):
        year = 2023  # 2023 ends on a Sunday
        weeklogs = list(generate_weeklogs(year))

        # Check the last partial week
        self.assertEqual(weeklogs[-1].start_of_week, "2023-12-25")
        self.assertEqual(weeklogs[-1].end_of_week, "2023-12-29")
        self.assertEqual(weeklogs[-1].days_of_week, ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"])

    def test_start_on_weekend(self):
        year = 2022  # 2022 starts on a Saturday
        weeklogs = list(generate_weeklogs(year))

        # Check the first week starts on the next Monday
        self.assertEqual(weeklogs[0].start_of_week, "2022-01-03")
        self.assertEqual(weeklogs[0].end_of_week, "2022-01-07")
        self.assertEqual(weeklogs[0].days_of_week, ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"])

    def test_single_week_year(self):
        year = 2021  # Set up for a theoretical test with a very short year
        weeklogs = list(generate_weeklogs(year))

        # Check that it handles a year with only one week
        self.assertEqual(weeklogs[0].start_of_week, "2021-01-01")
        self.assertEqual(weeklogs[0].end_of_week, "2021-01-01")
        self.assertEqual(weeklogs[0].days_of_week, ["Friday"])

    def test_full_year(self):
        year = 2020  # 2020 is a leap year, starting on a Wednesday
        weeklogs = list(generate_weeklogs(year))

        # Ensure that all weeks are properly generated
        self.assertTrue(len(weeklogs) > 50)  # Check that there are at least 52 weeks
        self.assertEqual(weeklogs[0].start_of_week, "2020-01-01")
        self.assertEqual(weeklogs[-1].end_of_week, "2020-12-31")

    def test_leap_year(self):
        year = 2020  # 2020 is a leap year
        weeklogs = list(generate_weeklogs(year))

        # Ensure that the leap day is included in the correct week
        for week in weeklogs:
            if "Saturday" in week.days_of_week:
                self.assertIn("2020-02-29", week.start_of_week + " to " + week.end_of_week)


if __name__ == "__main__":
    unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment