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

51 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-22 02:03 +0200

1from dataclasses import dataclass 

2from datetime import datetime, timedelta 

3from bs4 import BeautifulSoup 

4import requests 

5import ics 

6 

7# Define column headers 

8HEADERS = [ 

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 

23class Event: 

24 name: str 

25 start_date: datetime 

26 duration: timedelta 

27 location: str 

28 description: str 

29 attendees: list[str] 

30 

31 

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

33 response = requests.get(url) 

34 return response.content 

35 

36 

37def 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 

65def 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 

93def 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 

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

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

110 f.writelines(calendar.serialize_iter())