diff --git a/moodle/utils/helper.py b/moodle/utils/helper.py index 356f860..ad05f91 100644 --- a/moodle/utils/helper.py +++ b/moodle/utils/helper.py @@ -7,46 +7,41 @@ T = TypeVar("T") -def to_dict(data: Any, name: str = "") -> Any: +def to_dict(data: dict, name: str = "") -> dict: """Properly format query string for webservice request + The passed object itself must be a dict, but the returned value will be + flattened to a dict with keys like `name[key]` or `name[index][key]`. Args: - data (Any): Query to be formated + data (dict): Query to be formated name (str, optional): The key of the data. Defaults to "". Returns: - Any: Formated data + dict: Formated data """ - if not data: - return data - if isinstance(data, list): - out = {} - for idx, val in enumerate(data): - val = to_dict(val) - if isinstance(val, dict): - for key, value in val.items(): - out[f"{name}[{idx}][{key}]"] = val[key] - else: - out_key = name - # Check if data required name prefix - if hasattr(data, "name"): - out_key += f"[{getattr(data, 'name')}]" - out_key += f"[{idx}]" - out[out_key] = val - return out - if isinstance(data, dict): - out = {} - for key, value in data.items(): - if isinstance(value, list): - out.update(to_dict(value, key)) - else: - out[key] = value - return out - if has(data): - return asdict_attr(data) - if isinstance(data, datetime): - return datetime.timestamp(data) - return data + + result = {} + def inner(prefix: str, data: Any) -> dict: + pairs = None + if isinstance(data, list): + pairs = enumerate(data) + elif isinstance(data, dict): + pairs = data.items() + elif has(data): + pairs = asdict_attr(data).items() + + if pairs is not None: + for key, value in pairs: + inner(f"{prefix}[{key}]", value) + else: + if isinstance(data, datetime): + data = datetime.timestamp(data) + result[prefix] = data + + for key, value in data.items(): + inner(key, value) + + return result def fromtimestamp(d: str): diff --git a/tests/conftest.py b/tests/conftest.py index 8fe3607..4ac5208 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,7 +13,7 @@ def domain() -> str: @fixture def moodle(domain: str) -> Moodle: username = "manager" - password = "moodle2024" + password = "moodle25" return Moodle.login(domain, username, password) diff --git a/tests/test_core/test_calendar.py b/tests/test_core/test_calendar.py index 057a5bf..3153aa8 100644 --- a/tests/test_core/test_calendar.py +++ b/tests/test_core/test_calendar.py @@ -39,7 +39,7 @@ def test_get_calendar_event_by_id(self, moodle): pass def test_get_calendar_events(self, moodle): - events = moodle.core.calendar.get_calendar_events() + events = moodle.core.calendar.get_calendar_events(options=Events.Options(userevents=1)) assert isinstance(events, Events) def test_get_calendar_monthly_view(self, moodle): diff --git a/tests/test_utils/__init__.py b/tests/test_utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_utils/test_helper.py b/tests/test_utils/test_helper.py new file mode 100644 index 0000000..f1995d8 --- /dev/null +++ b/tests/test_utils/test_helper.py @@ -0,0 +1,61 @@ +import attr +from datetime import datetime + +from moodle.utils.helper import to_dict + +class TestHelper: + def test_to_dict(self): + # empty dict + result = to_dict(dict()) + assert result == {} + + # dict with simple values + result = to_dict(dict(a=1, b=2)) + assert result == { 'a': 1, 'b': 2 } + + # dict with lists + result = to_dict(dict(a=[1], b=[2, 3])) + assert result == { 'a[0]': 1, 'b[0]': 2, 'b[1]': 3 } + + # dict with lists of dicts + result = to_dict(dict(a=[dict(x=1), dict(x=2)], b=[dict(y=2, z=3)])) + assert result == { 'a[0][x]': 1, 'a[1][x]': 2, 'b[0][y]': 2, 'b[0][z]': 3 } + + # dict with nested dicts + result = to_dict(dict(a=dict(x=1, y=2), b=dict(z=3))) + assert result == { 'a[x]': 1, 'a[y]': 2, 'b[z]': 3 } + + # dict with lists of lists + result = to_dict(dict(a=[[1, 2], [3, 4]], b=[[5], [6, 7]])) + assert result == { + 'a[0][0]': 1, 'a[0][1]': 2, 'a[1][0]': 3, 'a[1][1]': 4, + 'b[0][0]': 5, 'b[1][0]': 6, 'b[1][1]': 7, + } + + # dict with list of nested dicts + result = to_dict(dict(a=[dict(x=1, nested=dict(y=2))])) + assert result == { 'a[0][x]': 1, 'a[0][nested][y]': 2 } + + @attr.s + class MyAttrClass: + x = attr.ib() + y = attr.ib() + + # attr class + # result = to_dict(MyAttrClass(x=1, y=2)) + # assert result == { 'x': 1, 'y': 2 } + + # dict with attr class + result = to_dict(dict(a=MyAttrClass(x=1, y=2))) + assert result == { 'a[x]': 1, 'a[y]': 2 } + + # dict with datetime + result = to_dict(dict(a=datetime(2025, 1, 1))) + assert result == { 'a': datetime(2025, 1, 1).timestamp() } + + # dict with list of datetimes + result = to_dict(dict(a=[datetime(2025, 1, 1)])) + assert result == { 'a[0]': datetime(2025, 1, 1).timestamp() } + + def test_fromtimestamp(self): + pass