Ejemplo n.º 1
0
def test_package_of_events():
    diary_data = {
        "October 23, 2010, Saturday": "Pizza Party",
        "November 20, 2010, Saturday": "Santa Clause parade lol",
        "December 10, 2010, Friday": "December is on the go",
        "December 30, 2010, Thursday": "NYE EVE",
        "December 31, 2010, Friday": "Friday NYE",
        "January 1, 2011, Saturday": "NYD",
    }

    test1_diary_expected = {
        "October 23, 2010, Saturday": "Pizza Party",
        "November 20, 2010, Saturday": "Santa Clause parade lol",
        "December 10, 2010, Friday": "December is on the go",
        "December 30, 2010, Thursday": "NYE EVE",
    }

    test2_diary_expected = {
        "October 23, 2010, Saturday": "Pizza Party",
        "November 20, 2010, Saturday": "Santa Clause parade lol",
        "December 10, 2010, Friday": "December is on the go",
        "December 30, 2010, Thursday": "NYE EVE",
        "December 31, 2010, Friday": "Friday NYE",
    }
    date = datetime(2010, 10, 22)
    diary = DiaryUtil(diary_data)
    TestCase().assertDictEqual(diary.package_of_events(date, 4),
                               test1_diary_expected)
    TestCase().assertDictEqual(diary.package_of_events(date, 5),
                               test2_diary_expected)
Ejemplo n.º 2
0
class TestDateMethods(unittest.TestCase):
    """Testing TodayAtMun Plugin"""

    parse = TodayAtMun.parse_diary()
    diary = DiaryUtil(parse)

    def test1_today_is_next(self):
        print("Start today_is_next tests")
        print("Success 1 - Test a formatted date that is not the same")
        self.assertEqual(self.diary.today_is_next("May 31, 2020, Monday"), "")
        print("Success 2 - Test a formatted date that is today")
        self.assertEqual(
            self.diary.today_is_next(self.diary.format_date(datetime.now())),
            "🔴")
        print("Success 3 - Test a date that is not formatted")
        self.assertNotEqual(datetime.now(), "🔴")

    def test2_time_delta_event(self):
        print("\nStart time_delta_event tests")
        print("Success 1 - Test a date ahead of the current date")
        self.assertEqual(self.diary.time_delta_event(datetime.now()), 0)
        print("Success 2 - Test date with y, m, d")
        self.assertEqual(
            self.diary.time_delta_event(datetime(2022, 10, 2),
                                        datetime(2022, 10, 1)), 1)
        print("Success 3 - Test date with y, m, d, hr, min")
        self.assertEqual(
            self.diary.time_delta_event(datetime(2022, 10, 3, 23, 0),
                                        datetime(2022, 10, 2, 22, 59)),
            1,
        )
Ejemplo n.º 3
0
 async def update_event_msg(self):
     diary_daily_channel = self.bot.get_guild(PRIMARY_GUILD).get_channel(DIARY_DAILY_CHANNEL)
     message = await diary_daily_channel.fetch_message(diary_daily_channel.last_message_id)
     message.embeds[0].set_author(name=self.diary_util.time_delta_emojify())
     edit_time = DiaryUtil.get_current_date().strftime("%Y%m%d%H%M%S")
     message.embeds[0].set_footer(
         text=f"Last update: {edit_time}", icon_url=MUN_LOGO
     )
     await message.edit(embed=message.embeds[0])
Ejemplo n.º 4
0
    async def post_new_events(self):
        date = DiaryUtil.get_current_time()
        next_event_date = self.diary_util.find_event(date)
        retrieve_event = await self.posted_events.find_one({"date": next_event_date})

        if retrieve_event is None:
            posted_message_id = await self.post_next_event(next_event_date)
            await self.notify_new_event(posted_message_id.jump_url)
        else:
            await self.update_event_msg(next_event_date)
        await asyncio.sleep(5.0)
Ejemplo n.º 5
0
    async def post_next_event(self, event: str):
        date = DiaryUtil.get_current_time()
        self.diary_util.find_event(date)
        next_embed = self.today_embed_next_template(self.diary_util.key)
        message_id = (
            await self.bot.get_guild(PRIMARY_GUILD)
            .get_channel(DIARY_DAILY_CHANNEL)
            .send(embed=next_embed)
        )
        await self.posted_events.insert_one({"date": event})

        return message_id
Ejemplo n.º 6
0
 async def update_event_msg(self, next_event_date: str):
     diary_daily_channel = self.bot.get_guild(PRIMARY_GUILD).get_channel(
         DIARY_DAILY_CHANNEL
     )
     message = await diary_daily_channel.fetch_message(
         diary_daily_channel.last_message_id
     )
     message.embeds[0].set_author(
         name=self.diary_util.time_delta_emojify(next_event_date)
     )
     edit_time = DiaryUtil.get_current_time("Canada/Newfoundland").strftime(
         "%-I:%M %p %Z %a %b %-d, %Y"
     )
     message.embeds[0].set_footer(
         text=f"Last update: {edit_time}", icon_url=MUN_CSS_LOGO
     )
     await message.edit(embed=message.embeds[0])
Ejemplo n.º 7
0
def test_find_event(parsed_diary):

    future_date = datetime.now() + timedelta(days=400)
    formatted_future_date = parsed_diary.format_date(future_date)
    diary_data = {f"{formatted_future_date}": "2 years away"}
    diary_util = DiaryUtil(diary_data)
    date = datetime.now()
    assert diary_util.find_event(date) == ""

    future_date2 = datetime.now() + timedelta(days=30)
    formated_date2 = parsed_diary.format_date(future_date2)
    diary_data = {f"{formated_date2}": "40 days away"}
    diary_util = DiaryUtil(diary_data)
    assert diary_data[diary_util.find_event(date)] == "40 days away"
Ejemplo n.º 8
0
 def __init__(self, manifest, bot: commands.Bot):
     super().__init__(manifest, bot)
     self.parse = DiaryParser()
     self.diary_util = DiaryUtil(self.parse.diary)
     self.posted_events = mongo_client.automata.mun_diary
     self.check_for_new_event.start()
Ejemplo n.º 9
0
class TodayAtMun(AutomataPlugin):
    """Provides a utility for MUN diary lookup specifically significant dates."""

    def __init__(self, manifest, bot: commands.Bot):
        super().__init__(manifest, bot)
        self.parse = DiaryParser()
        self.diary_util = DiaryUtil(self.parse.diary)
        self.posted_events = mongo_client.automata.mun_diary
        self.check_for_new_event.start()

    @staticmethod
    def today_embed_template():
        """Provides initial embed attributes."""
        embed = discord.Embed()
        mun_colours = [MUN_COLOUR_RED, MUN_COLOUR_WHITE, MUN_COLOUR_GREY]
        embed.colour = discord.Colour(choice(mun_colours))
        embed.set_footer(
            text="TodayAtMun\t\t\t\t\t!help diary",
            icon_url=MUN_LOGO,
        )
        return embed

    def today_embed_next_template(self, next_event_date: str) -> discord.Embed:
        embed = self.today_embed_template()
        embed.set_author(
            name=f"⏳ ~{self.diary_util.time_delta_event(self.diary_util.str_to_datetime(next_event_date))} days"
        )
        embed.add_field(
            name=f"{self.diary_util.today_is_next(next_event_date)} {next_event_date}",
            value=f"{self.diary_util.diary[self.diary_util.key]}.",
            inline=False,
        )
        return embed

    @commands.group(aliases=["d", "today"])
    async def diary(self, ctx: commands.Context):
        """Provides brief info of significant dates on the MUN calendar."""
        await ctx.trigger_typing()
        if ctx.invoked_subcommand is None:
            await ctx.reply(content="Invalid command, check !help diary for more.")

    @diary.command(name="next", aliases=["n"])
    async def today_next(self, ctx: commands.Context):
        """Sends next upcoming date on the MUN calendar."""
        self.diary_util.set_current_date()
        self.diary_util.find_event(self.diary_util.date)
        embed = self.today_embed_next_template(self.diary_util.key)
        await ctx.reply(embed=embed)

    @diary.command(name="later", aliases=["l"])
    async def today_after(self, ctx: commands.Context):
        """Sends the event after the 'next' event."""
        self.diary_util.find_following_event()
        embed = self.today_embed_template()
        embed.add_field(
            name=f"{self.diary_util.key}, ⌛ ~`{DiaryUtil.time_to_dt_delta(self.diary_util.key)}` days away.",
            value=f"{self.diary_util.this_date}",
            inline=False,
        )
        await ctx.reply(embed=embed)

    @diary.command(name="date")
    async def today_date(self, ctx: commands.Context):
        """Sends the current date at that instance."""
        self.diary_util.set_current_date()
        await ctx.reply(self.diary_util.format_date(self.diary_util.date))

    @diary.command(name="bundle", aliases=["b", "nextfive"])
    async def today_bundle(self, ctx: commands.Context, events:int = 5):
        """Sends the next n number of events coming up in MUN diary."""
        self.diary_util.set_current_date()
        packaged_events = self.diary_util.package_of_events(
            self.diary_util.date, events
        )
        packaged_keys = list(packaged_events.keys())
        embed = self.today_embed_template()
        embed.add_field(
            name=f"__**Showing next {len(packaged_keys)} upcoming events in MUN diary**__\n*{packaged_keys[0]}* **-** *{packaged_keys[len(packaged_keys)-1]}*",
            value="\u200b",
            inline=False,
        )
        for _, (date, context) in enumerate(packaged_events.items()):
            embed.add_field(
                name=f"{self.diary_util.today_is_next(date)} **{date}**:",
                value=f"{context}",
                inline=False,
            )
        await ctx.send(embed=embed)
    
    @today_bundle.error
    async def today_next_bundle_handler(self, ctx, error):
        error = getattr(error, 'original', error)
        if isinstance(error, commands.BadArgument):
            await ctx.reply("Invalid use of bundle, must use a number instead.")

    async def post_next_event(self, event: str):
        self.diary_util.set_current_date()
        self.diary_util.find_event(self.diary_util.date)
        next_embed = self.today_embed_next_template(self.diary_util.key)
        await self.bot.get_guild(PRIMARY_GUILD).get_channel(DIARY_DAILY_CHANNEL).send(
            embed=next_embed
        )
        await self.posted_events.insert_one({"date": event})

    async def post_new_events(self):
        self.diary_util.set_current_date()
        self.diary_util.find_event(self.diary_util.date)
        next_event_date = self.diary_util.key
        retrieve_event = await self.posted_events.find_one({"date": next_event_date})

        if retrieve_event is None:
            await self.post_next_event(next_event_date)
        else:
            await self.update_event_msg()
        await asyncio.sleep(5)

    @tasks.loop(hours=6)
    async def check_for_new_event(self):
        await self.post_new_events()

    @check_for_new_event.before_loop
    async def before_check_test(self):
        await self.bot.wait_until_ready()

    @diary.command("reset")
    @commands.has_permissions(view_audit_log=True)
    async def reset_recurrent_events(self, ctx):
        """Executive Use Only: Resets automated event posting."""
        await mongo_client.automata.drop_collection("mun_diary")
        await mongo_client.automata.mun_diary.insert_one({"data": "foo"})
        self.check_for_new_event.restart()

    async def update_event_msg(self):
        diary_daily_channel = self.bot.get_guild(PRIMARY_GUILD).get_channel(DIARY_DAILY_CHANNEL)
        message = await diary_daily_channel.fetch_message(diary_daily_channel.last_message_id)
        message.embeds[0].set_author(name=self.diary_util.time_delta_emojify())
        edit_time = DiaryUtil.get_current_date().strftime("%Y%m%d%H%M%S")
        message.embeds[0].set_footer(
            text=f"Last update: {edit_time}", icon_url=MUN_LOGO
        )
        await message.edit(embed=message.embeds[0])
Ejemplo n.º 10
0
class TodayAtMun(AutomataPlugin):
    """Provides a utility for MUN diary lookup specifically significant dates."""

    def __init__(self, manifest, bot: commands.Bot):
        super().__init__(manifest, bot)
        self.parse = TodayAtMun.parse_diary()
        self.diary_util = DiaryUtil(self.parse)
        self.posted_events = mongo_client.automata.mun_diary
        self.check_for_new_event.start()

    @staticmethod
    def today_embed_template():
        """Provides initial embed attributes."""
        embed = nextcord.Embed()
        mun_colours = [MUN_COLOUR_RED, MUN_COLOUR_WHITE, MUN_COLOUR_GREY]
        embed.colour = nextcord.Colour(choice(mun_colours))
        embed.set_footer(
            text="TodayAtMun ● !help TodayAtMun",
            icon_url=MUN_CSS_LOGO,
        )
        return embed

    def today_embed_next_template(self, next_event_date: str) -> nextcord.Embed:
        embed = self.today_embed_template()
        embed.set_author(
            name=f"⏳ ~{self.diary_util.time_delta_event(self.diary_util.str_to_datetime(next_event_date), datetime.now())} day(s)"
        )
        embed.add_field(
            name=f"{self.diary_util.today_is_next(next_event_date)} {next_event_date}",
            value=f"{self.diary_util.diary[self.diary_util.key]}.",
            inline=False,
        )
        return embed

    @commands.group(aliases=["d", "today"])
    async def diary(self, ctx: commands.Context):
        """Provides brief info of significant dates on the MUN calendar.
        Examples: !d next, !d later, !d bundle 10
        """
        await ctx.trigger_typing()
        if ctx.invoked_subcommand is None:
            await ctx.reply(content="Invalid command, check !help diary for more.")

    @diary.command(name="next", aliases=["n"])
    async def today_next(self, ctx: commands.Context):
        """Sends next upcoming date on the MUN calendar."""
        self.diary_util.set_current_date()
        self.diary_util.find_event(self.diary_util.date)
        embed = self.today_embed_next_template(self.diary_util.key)
        await ctx.reply(embed=embed)

    @diary.command(name="later", aliases=["l"])
    async def today_after(self, ctx: commands.Context):
        """Sends the event after the 'next' event."""
        self.diary_util.find_following_event()
        embed = self.today_embed_template()
        embed.add_field(
            name=f"{self.diary_util.key}, ⌛ ~`{DiaryUtil.time_to_dt_delta(self.diary_util.key)}` days away.",
            value=f"{self.diary_util.event_desc}",
            inline=False,
        )
        await ctx.reply(embed=embed)

    @diary.command(name="date")
    async def today_date(self, ctx: commands.Context):
        """Sends the current date at that instance."""
        self.diary_util.set_current_date()
        await ctx.reply(self.diary_util.format_date(self.diary_util.date))

    @diary.command(name="bundle", aliases=["b", "nextfive"])
    async def today_bundle(self, ctx: commands.Context, events: int = 5):
        """Sends the next n number of events coming up in MUN diary.
        Usage: !diary bundle <size>
        Example: !diary bundle 10
        """
        self.diary_util.set_current_date()
        packaged_events = self.diary_util.package_of_events(
            self.diary_util.date, events
        )
        packaged_keys = list(packaged_events.keys())
        first_event_date = packaged_keys[0]
        last_event_data = packaged_keys[-1]
        bundle_size = len(packaged_events)
        embed = self.today_embed_template()
        embed.add_field(
            name=f"__**Showing next {bundle_size} upcoming events in MUN diary**__",
            value=f"*{first_event_date}* **-** *{last_event_data}*",
            inline=False,
        )
        for _, (date, context) in enumerate(packaged_events.items()):
            embed.add_field(
                name=f"{self.diary_util.today_is_next(date)} **{date}**:",
                value=f"{context}",
                inline=False,
            )
        await ctx.send(embed=embed)

    @today_bundle.error
    async def today_next_bundle_handler(self, ctx, error):
        error = getattr(error, "original", error)
        if isinstance(error, commands.BadArgument):
            await ctx.reply("Invalid use of bundle, Usage: !d bundle <1 - 10 : int>")

    async def post_next_event(self, event: str):
        date = DiaryUtil.get_current_time()
        self.diary_util.find_event(date)
        next_embed = self.today_embed_next_template(self.diary_util.key)
        message_id = (
            await self.bot.get_guild(PRIMARY_GUILD)
            .get_channel(DIARY_DAILY_CHANNEL)
            .send(embed=next_embed)
        )
        await self.posted_events.insert_one({"date": event})

        return message_id

    async def notify_new_event(self, message_link):
        embed = self.today_embed_template()
        embed.add_field(
            name="**📅 New Upcoming MUN Calendar Event**",
            value=f"[**Click to view**]({message_link})",
            inline=False,
        )
        await self.bot.get_guild(PRIMARY_GUILD).get_channel(GENERAL_CHANNEL).send(
            embed=embed
        )

    async def post_new_events(self):
        date = DiaryUtil.get_current_time()
        next_event_date = self.diary_util.find_event(date)
        retrieve_event = await self.posted_events.find_one({"date": next_event_date})

        if retrieve_event is None:
            posted_message_id = await self.post_next_event(next_event_date)
            await self.notify_new_event(posted_message_id.jump_url)
        else:
            await self.update_event_msg(next_event_date)
        await asyncio.sleep(5.0)

    @tasks.loop(hours=2.0)
    async def check_for_new_event(self):
        await self.post_new_events()

    @check_for_new_event.before_loop
    async def before_check_test(self):
        await self.bot.wait_until_ready()

    @diary.command("restart")
    @commands.has_permissions(view_audit_log=True)
    async def reset_recurrent_events(self, ctx):
        """Executive Use Only: Resets automated event posting."""
        await mongo_client.automata.drop_collection("mun_diary")
        await mongo_client.automata.mun_diary.insert_one({"date": "init"})
        self.check_for_new_event.restart()

    async def update_event_msg(self, next_event_date: str):
        diary_daily_channel = self.bot.get_guild(PRIMARY_GUILD).get_channel(
            DIARY_DAILY_CHANNEL
        )
        message = await diary_daily_channel.fetch_message(
            diary_daily_channel.last_message_id
        )
        message.embeds[0].set_author(
            name=self.diary_util.time_delta_emojify(next_event_date)
        )
        edit_time = DiaryUtil.get_current_time("Canada/Newfoundland").strftime(
            "%-I:%M %p %Z %a %b %-d, %Y"
        )
        message.embeds[0].set_footer(
            text=f"Last update: {edit_time}", icon_url=MUN_CSS_LOGO
        )
        await message.edit(embed=message.embeds[0])

    @commands.group(aliases=["e"])
    @commands.cooldown(3, 60.0)
    async def exam(
        self,
        ctx: commands.Context,
        subj: str = "",
        course_num: str = "",
        sec_numb: str = "",
        crn: str = "",
    ) -> None:
        """Provides Exam Info for current semester
        Usage: !exam <subject> <course_number> <section_number> <crn>
        Example: !exam COMP 1003
        """
        if ctx.invoked_subcommand is None:
            await ctx.reply(content="Invalid command, check !help exam for more.")
            return

        sched_heading, table_heading, exams_parsed = TodayAtMun.get_exams(
            subj, course_num, sec_numb, crn
        )
        embed = self.today_embed_template()
        embed.title = sched_heading
        embed.add_field(name=table_heading, value="\u200b", inline=False)
        for exam in exams_parsed:
            embed.add_field(name=" | ".join(exam), value="\u200b", inline=False)
        await ctx.send(embed=embed)

    @exam.error
    async def exam_handler(self, ctx, error):
        error = getattr(error, "original", error)
        await ctx.reply(error)

    @staticmethod
    def parse_diary() -> dict[str, str]:
        diary = {}
        resp = httpx.get(DIARY_DATA_SOURCE)
        mun_request = resp.text
        soup = BeautifulSoup(mun_request, "html.parser")
        dates_in_diary = soup.find_all("td", attrs={"align": "left"})
        description_of_date = soup.find_all("td", attrs={"align": "justify"})

        for left_item, right_item in zip(dates_in_diary, description_of_date):
            right_item_parse = right_item.get_text().split()
            try:
                diary[left_item.find("p").get_text().strip("\n\t")] = " ".join(
                    right_item_parse
                )
            except AttributeError:
                diary[left_item.find("li").get_text().strip("\n\t")] = " ".join(
                    right_item_parse
                )
        return diary

    @staticmethod
    def submit_form(
        subj: str = "", course_num: str = "", sec_numb: str = "", crn: str = ""
    ) -> BeautifulSoup:
        browser = mechanicalsoup.StatefulBrowser(
            soup_config={"features": "html.parser"}
        )
        browser.open(EXAMS_DATA_SOURCE)
        browser.select_form('form[method="post"]')

        browser["p_subj_code"] = subj
        browser["p_crse_numb"] = course_num
        browser["p_seq_numb"] = sec_numb
        browser["p_crn"] = crn

        browser.submit_selected()
        return browser.page

    @staticmethod
    def parse_sched_heading(page) -> str:
        return page.find("div", class_="infotextdiv").find("b").get_text().strip()

    @staticmethod
    def parse_headings(page: BeautifulSoup) -> str:
        return " | ".join(
            f"{heading.get_text()}"
            for heading in page.find_all("td", class_="dbheader")
        )

    @staticmethod
    def parse_form(page: BeautifulSoup) -> list[str]:
        exam_context = []
        exams = []
        for data_cell, course in enumerate(
            page.find_all("td", class_="dbdefault"), start=1
        ):
            exam_context.append(course.get_text())
            if data_cell % 6 == 0:
                exams.append(exam_context)
                exam_context = []
        return exams

    @staticmethod
    def get_exams(
        subj: str = "", course_num: str = "", sec_numb: str = "", crn: str = ""
    ) -> tuple[str, str, list[str]]:
        """Provides exam info - schedule brief, table heading and exam details"""
        page = TodayAtMun.submit_form(subj, course_num, sec_numb, crn)
        sched_heading = TodayAtMun.parse_sched_heading(page)
        headings = TodayAtMun.parse_headings(page)
        exams = TodayAtMun.parse_form(page)
        return sched_heading, headings, exams
Ejemplo n.º 11
0
def test_time_delta_event(today_time, event_time, expected):
    diary = TodayAtMun.parse_diary()
    diary = DiaryUtil(diary)
    assert diary.time_delta_event(today_time, event_time) == expected
Ejemplo n.º 12
0
def test_today_is_next(date, expected):
    parse = TodayAtMun.parse_diary()
    diary = DiaryUtil(parse)
    date = diary.format_date(diary.truncate_date_time(date))
    assert diary.today_is_next(date) == expected
Ejemplo n.º 13
0
def parsed_diary():
    diary = TodayAtMun.parse_diary()
    return DiaryUtil(diary)
Ejemplo n.º 14
0
def test_daily_time_delta():
    time = datetime(2021, 10, 22)
    diary = {
        (time + timedelta(days=10)).strftime("%B %-d, %Y, %A"): "First event",
        (time + timedelta(days=20)).strftime("%B %-d, %Y, %A"): "Second event",
        (time + timedelta(days=30)).strftime("%B %-d, %Y, %A"): "Third event",
        (time + timedelta(days=40)).strftime("%B %-d, %Y, %A"): "Fourth event",
    }
    diary_key_list = list(diary)
    diary_util = DiaryUtil(diary)

    assert diary_util.find_event(time) == diary_key_list[0]
    next_event_date = diary_util.find_event(time)
    assert (DiaryUtil.time_delta_event(
        DiaryUtil.str_to_datetime(next_event_date), time) == 10)
    time = time + timedelta(days=1)
    assert (DiaryUtil.time_delta_event(
        DiaryUtil.str_to_datetime(next_event_date), time) == 9)
    time = time + timedelta(days=1)
    assert (DiaryUtil.time_delta_event(
        DiaryUtil.str_to_datetime(next_event_date), time) == 8)
    time = datetime(2021, 10, 31)
    next_event_date = diary_util.find_event(time)
    assert (DiaryUtil.time_delta_event(
        DiaryUtil.str_to_datetime(next_event_date), time) == 1)