Coverage for src/cycloplanning/__init__.py: 90%

1 from dataclasses import dataclass

2 from datetime import datetime, timedelta

3 from bs4 import BeautifulSoup

4 import requests

5 import ics

6

7# Define column headers

8 HEADERS = [

9 "Jour",

10 "Date",

11 "Quoi",

12 "Adresse",

13 "Besoins Humains",

14 "Horaires",

15 "Bénévole 1",

16 "Bénévole 2",

17 "Bénévole 3",

18 "Bénévole 4",

19]

20

21

22@dataclass

23 class Event:

24 name: str

25 start_date: datetime

26 duration: timedelta

27 location: str

28 description: str

29 attendees: list[str]

30

31

32 def get_html(url: str) -> str:

33 response = requests.get(url)

34 return response.content

35

36

37 def parse_html(html: str) -> list[dict]:

38 soup = BeautifulSoup(html, "html.parser")

39

40 # Initialize an empty list to store dictionaries

41 events = []

42

43 # Find the header row to start processing

44 header_row = soup.find("td", string=lambda x: x and "Date" in x)

45 if header_row:

46 # Iterate over each subsequent <tr> for data extraction

47 for row in header_row.parent.find_next_siblings("tr"):

48 # Get all <td> elements in the row

49 columns = row.find_all("td")

50

51 # Create a dictionary for the row using headers as keys

52 row_dict = {

53 HEADERS[i]: col.get_text(strip=True)

54 for i, col in enumerate(columns[: len(HEADERS)])

55 }

56

57 # Skip header rows

58 if row_dict["Jour"] == "Date":

59 continue

60

61 events.append(row_dict)

62 return events

63

64

65 def parse_events(raw_events: list[dict]) -> list[Event]:

66 events = []

67 for raw_event in raw_events:

68 start_date = datetime.strptime(raw_event["Date"], "%d/%m")

69 start_date = start_date.replace(year=datetime.today().year)

70 hour_begin = int(raw_event["Horaires"].split("-")[0].split("h")[0])

71 hour_end = int(raw_event["Horaires"].split("-")[1].split("h")[0])

72 start_date = start_date.replace(hour=hour_begin)

73 duration = timedelta(hours=(hour_end - hour_begin))

74 attendees = [

75 raw_event["Bénévole 1"],

76 raw_event["Bénévole 2"],

77 raw_event["Bénévole 3"],

78 raw_event["Bénévole 4"],

79 ]

80

81 event = Event(

82 name=raw_event["Quoi"],

83 description=raw_event["Besoins Humains"],

84 start_date=start_date,

85 duration=duration,

86 location=raw_event["Adresse"],

87 attendees=attendees,

88 )

89 events.append(event)

90 return events

91

92

93 def create_ics(raw_events: list[Event]) -> ics.Calendar:

94 calendar = ics.Calendar()

95 for raw_event in raw_events:

96 event = ics.Event(

97 name=raw_event.name,

98 description=raw_event.description,

99 begin=raw_event.start_date,

100 duration=raw_event.duration,

101 location=raw_event.location,

102 attendees=raw_event.attendees,

103 )

104 calendar.events.add(event)

105 return calendar

106

107

108 def write_ics(calendar: ics.Calendar, output="/dev/stdout"):

109 with open(output, "w") as f:

110 f.writelines(calendar.serialize_iter())