Skip to content

Commit 96985ca

Browse files
authored
Merge pull request #2 from RutgerRauws/feature/extending-agenda-item
Extending agenda item
2 parents 4e9638b + bad3613 commit 96985ca

File tree

6 files changed

+79
-9
lines changed

6 files changed

+79
-9
lines changed

examples/get_meeting.py

+3
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@
1616
print(meeting.title)
1717
for agenda_item in meeting.agenda_items:
1818
print(" {} - {}".format(agenda_item.start_date, agenda_item.title))
19+
20+
for sub_agenda_item in agenda_item.agenda_items:
21+
print(" - {}".format(sub_agenda_item.title))

notubiz/api/agenda_item.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from attrs import define
1+
from attrs import define, field
22
import cattrs
33

44
from datetime import datetime
55
from typing import Optional, Dict, Any
66

77
from notubiz.api._helpers import parse_date, get_attribute, get_title, get_description
8+
from notubiz.api.document import Document, NotubizDocument
89

910
@define
1011
class AgendaItem:
@@ -15,17 +16,28 @@ class AgendaItem:
1516
start_date : Optional[datetime]
1617
end_date : Optional[datetime]
1718
is_heading : bool
19+
documents: list[Document]
20+
agenda_items : list['AgendaItem'] = field(factory=list)
1821

1922

2023
def get_start_date(attributes) -> datetime:
21-
return parse_date(get_attribute(attributes, 82))
24+
try:
25+
return parse_date(get_attribute(attributes, 82))
26+
except Exception: # The nested agenda items seem to have no start dates.
27+
return None
2228

2329
def get_end_date(attributes) -> datetime:
24-
return parse_date(get_attribute(attributes, 83))
25-
30+
try:
31+
return parse_date(get_attribute(attributes, 83))
32+
except Exception: # The nested agenda items seem to have no end dates.
33+
return None
2634

2735
def agenda_item_structure_hook(data: Dict[str, Any], cls: type) -> AgendaItem:
28-
attributes = data["type_data"]["attributes"]
36+
type_data = data.get("type_data", {})
37+
attributes = type_data["attributes"]
38+
39+
documents = [NotubizDocument.from_json(item) for item in data["documents"]]
40+
agenda_items = NotubizAgendaItems.from_json(data["agenda_items"])
2941

3042
return AgendaItem(
3143
id=data["id"],
@@ -34,7 +46,9 @@ def agenda_item_structure_hook(data: Dict[str, Any], cls: type) -> AgendaItem:
3446
description = get_description(attributes),
3547
start_date = get_start_date(attributes),
3648
end_date = get_end_date(attributes),
37-
is_heading = data["type_data"]["heading"]
49+
is_heading = data["type_data"]["heading"],
50+
documents = documents,
51+
agenda_items = agenda_items
3852
)
3953

4054

@@ -45,7 +59,7 @@ def from_json(json_object : any) -> list[AgendaItem]:
4559
c.register_structure_hook(datetime, lambda date_string, _: parse_date(date_string))
4660
c.register_structure_hook(AgendaItem, agenda_item_structure_hook)
4761

48-
agenda_items = [c.structure(item, AgendaItem) for item in json_object]
62+
agenda_items = [c.structure(item, AgendaItem) for item in json_object]
4963

5064
return agenda_items
5165

notubiz/api/document.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from attrs import define, field
2+
import cattrs
3+
from typing import Optional, Dict, Any, List
4+
from datetime import datetime
5+
6+
@define
7+
class DocumentVersion:
8+
id: int
9+
file_name: str
10+
file_size: int
11+
mime_type: Optional[str] = field(default="") # Apparently some documents have no MIME type
12+
13+
@define
14+
class Document:
15+
last_modified: datetime
16+
title: str
17+
version: int
18+
url: str
19+
versions: List[DocumentVersion]
20+
id: Optional[int] = field(default=None) # Apparently some documents have no ID
21+
22+
class NotubizDocument:
23+
def from_json(json_object : any) -> Document:
24+
c = cattrs.Converter()
25+
c.register_structure_hook(datetime, lambda d, _: datetime.strptime(d, "%Y-%m-%d %H:%M:%S"))
26+
27+
return c.structure(json_object, Document)

notubiz/api_client.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ class ApiClient:
99
configuration : Configuration
1010

1111
def get_base_payload(self) -> dict:
12-
return {'format': 'json', 'organisation': self.configuration.organisation_id}
12+
return {
13+
'format': 'json',
14+
'version': self.configuration.api_version,
15+
'organisation': self.configuration.organisation_id
16+
}
1317

1418
def get(self, relative_path : str, extra_payload : dict = None) -> any:
1519
request_url = self.configuration.base_url + relative_path

notubiz/configuration.py

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
@define
44
class Configuration:
55
organisation_id : int
6+
api_version : str = "1.10.8" # To keep the used API stable
67
base_url : str = "https://api.notubiz.nl/"

test/test_meeting.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def input_json():
1616
def input_meeting():
1717
return NotubizMeeting.from_json(test_file_path)
1818

19-
def test_deserialization(input_json):
19+
def test_meeting_general_info(input_json):
2020
meeting = NotubizMeeting.from_json(input_json)
2121

2222
# We test all fields because they are not straight-up deserialized
@@ -26,6 +26,10 @@ def test_deserialization(input_json):
2626
assert meeting.location == "Raadzaal"
2727
assert len(meeting.agenda_items) == 7
2828

29+
30+
def test_basic_agenda_item(input_json):
31+
meeting = NotubizMeeting.from_json(input_json)
32+
2933
agenda_item = meeting.agenda_items[2]
3034
assert agenda_item.id == 8329704
3135
assert agenda_item.last_modified == datetime(2024, 3, 29, 10, 8, 56)
@@ -34,3 +38,20 @@ def test_deserialization(input_json):
3438
assert agenda_item.start_date == datetime(2024, 4, 16, 18, 0, 0)
3539
assert agenda_item.end_date == datetime(2024, 4, 16, 19, 0, 0)
3640
assert agenda_item.is_heading == True
41+
42+
def test_nested_agenda_items(input_json):
43+
meeting = NotubizMeeting.from_json(input_json)
44+
agenda_items = meeting.agenda_items
45+
46+
assert len(agenda_items[0].agenda_items) == 0
47+
assert len(agenda_items[1].agenda_items) == 0
48+
assert len(agenda_items[2].agenda_items) == 0
49+
assert len(agenda_items[3].agenda_items) == 1
50+
assert len(agenda_items[4].agenda_items) == 2
51+
assert len(agenda_items[5].agenda_items) == 0
52+
assert len(agenda_items[6].agenda_items) == 0
53+
54+
# Check content of the nested agenda items
55+
assert len(agenda_items[3].agenda_items[0].agenda_items) == 0
56+
assert len(agenda_items[4].agenda_items[0].agenda_items) == 0
57+
assert len(agenda_items[4].agenda_items[1].agenda_items) == 0

0 commit comments

Comments
 (0)