Skip to content

Commit

Permalink
__repr__ method (#147)
Browse files Browse the repository at this point in the history
* `__repr__` method added for most objects(previously it was only
present for `FsNode`).
* `notifications.Notification` class has been rewritten in the same
format as all the others.

---------

Signed-off-by: Alexander Piskun <[email protected]>
  • Loading branch information
bigcat88 authored Oct 14, 2023
1 parent 5750e9a commit e411fca
Show file tree
Hide file tree
Showing 22 changed files with 162 additions and 64 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ All notable changes to this project will be documented in this file.
## [0.4.0 - 2023-10-2x]

As the project moves closer to `beta`, final unification changes are being made.
This release contains some breaking changes in `users` API.
This release contains some breaking changes in `users`, `notifications` API.

### Added

- `__repr__` method added for most objects(previously it was only present for `FsNode`).

### Changed

- `users.get_details` renamed to `get_user` and returns a class instead of a dictionary. #145
- Optional argument `displayname` in `users.create` renamed to `display_name`.
- The `apps.ExAppInfo` class has been rewritten in the same format as all the others.
- The `apps.ExAppInfo` class has been rewritten in the same format as all the others. #146
- `notifications.Notification` class has been rewritten in the same format as all the others.

### Fixed

Expand Down
3 changes: 0 additions & 3 deletions docs/reference/Users/Notifications.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,3 @@ Notifications

.. autoclass:: nc_py_api.notifications.Notification
:members:

.. autoclass:: nc_py_api.notifications.NotificationInfo
:members:
11 changes: 10 additions & 1 deletion nc_py_api/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ def priority(self) -> int:
"""Arrangement priority in ascending order. Values from 0 to 99."""
return self._raw_data["priority"]

def __repr__(self):
return f"<{self.__class__.__name__} id={self.filter_id}, name={self.name}, priority={self.priority}>"


@dataclasses.dataclass
class Activity:
Expand Down Expand Up @@ -122,10 +125,16 @@ def icon(self) -> str:
return self._raw_data["icon"]

@property
def activity_time(self) -> datetime.datetime:
def time(self) -> datetime.datetime:
"""Time when the activity occurred."""
return nc_iso_time_to_datetime(self._raw_data["datetime"])

def __repr__(self):
return (
f"<{self.__class__.__name__} id={self.activity_id}, app={self.app}, type={self.activity_type},"
f" time={self.time}>"
)


class _ActivityAPI:
"""The class provides the Activity Application API."""
Expand Down
3 changes: 3 additions & 0 deletions nc_py_api/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ def system(self) -> bool:
"""Flag indicating if the application is a system application."""
return bool(self._raw_data["system"])

def __repr__(self):
return f"<{self.__class__.__name__} id={self.app_id}, ver={self.version}>"


class _AppsAPI:
"""The class provides the application management API on the Nextcloud server."""
Expand Down
3 changes: 3 additions & 0 deletions nc_py_api/files/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ def user_assignable(self) -> bool:
"""Flag indicating if User can assign this Tag."""
return bool(self._raw_data.get("oc:user-assignable", "false").lower() == "true")

def __repr__(self):
return f"<{self.__class__.__name__} id={self.tag_id}, name={self.display_name}>"


class ShareType(enum.IntEnum):
"""Type of the object that will receive share."""
Expand Down
3 changes: 3 additions & 0 deletions nc_py_api/notes.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ def last_modified(self) -> datetime.datetime:
modified = self._raw_data.get("modified", 0)
return datetime.datetime.utcfromtimestamp(modified).replace(tzinfo=datetime.timezone.utc)

def __repr__(self):
return f"<{self.__class__.__name__} id={self.note_id}, title={self.title}, last_modified={self.last_modified}>"


class NotesSettings(typing.TypedDict):
"""Settings of Notes App."""
Expand Down
101 changes: 58 additions & 43 deletions nc_py_api/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,53 +13,68 @@
from ._session import NcSessionApp, NcSessionBasic


@dataclasses.dataclass
class NotificationInfo:
"""Extra Notification attributes from Nextcloud."""

app_name: str
"""Application name that generated notification."""
user_id: str
"""User name for which this notification is."""
time: datetime.datetime
"""Time when the notification was created."""
subject: str
"""Subject of the notification."""
message: str
"""Message of the notification."""
link: str
"""Link which will be opened when user clicks on notification."""
icon: str
"""Relative to instance url of the icon image."""

def __init__(self, raw_info: dict):
self.app_name = raw_info["app"]
self.user_id = raw_info["user"]
self.time = nc_iso_time_to_datetime(raw_info["datetime"])
self.subject = raw_info["subject"]
self.message = raw_info["message"]
self.link = raw_info.get("link", "")
self.icon = raw_info.get("icon", "")


@dataclasses.dataclass
class Notification:
"""Class representing information about Nextcloud notification."""

notification_id: int
"""ID of the notification."""
object_id: str
"""Randomly generated unique object ID"""
object_type: str
"""Currently not used."""
info: NotificationInfo
"""Additional extra information for the object"""

def __init__(self, raw_info: dict):
self.notification_id = raw_info["notification_id"]
self.object_id = raw_info["object_id"]
self.object_type = raw_info["object_type"]
self.info = NotificationInfo(raw_info)
def __init__(self, raw_data: dict):
self._raw_data = raw_data

@property
def notification_id(self) -> int:
"""ID of the notification."""
return self._raw_data["notification_id"]

@property
def object_id(self) -> str:
"""Randomly generated unique object ID."""
return self._raw_data["object_id"]

@property
def object_type(self) -> str:
"""Currently not used."""
return self._raw_data["object_type"]

@property
def app_name(self) -> str:
"""Application name that generated notification."""
return self._raw_data["app"]

@property
def user_id(self) -> str:
"""User ID of user for which this notification is."""
return self._raw_data["user"]

@property
def subject(self) -> str:
"""Subject of the notification."""
return self._raw_data["subject"]

@property
def message(self) -> str:
"""Message of the notification."""
return self._raw_data["message"]

@property
def time(self) -> datetime.datetime:
"""Time when the notification was created."""
return nc_iso_time_to_datetime(self._raw_data["datetime"])

@property
def link(self) -> str:
"""Link, which will be opened when user clicks on notification."""
return self._raw_data.get("link", "")

@property
def icon(self) -> str:
"""Relative to instance url of the icon image."""
return self._raw_data.get("icon", "")

def __repr__(self):
return (
f"<{self.__class__.__name__} id={self.notification_id}, app_name={self.app_name}, user_id={self.user_id},"
f" time={self.time}>"
)


class _NotificationsAPI:
Expand Down
29 changes: 28 additions & 1 deletion nc_py_api/talk.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Nextcloud Talk API definitions."""

import dataclasses
import datetime
import enum
import os
import typing
Expand Down Expand Up @@ -280,6 +281,12 @@ def markdown(self) -> bool:
"""Whether the message should be rendered as markdown or shown as plain text."""
return self._raw_data.get("markdown", False)

def __repr__(self):
return (
f"<{self.__class__.__name__} id={self.message_id}, author={self.actor_display_name},"
f" time={datetime.datetime.utcfromtimestamp(self.timestamp).replace(tzinfo=datetime.timezone.utc)}>"
)


class TalkFileMessage(TalkMessage):
"""Subclass of Talk Message representing message-containing file."""
Expand Down Expand Up @@ -625,6 +632,12 @@ def status_clear_at(self) -> typing.Optional[int]:
"""
return self._raw_data.get("statusClearAt", None)

def __repr__(self):
return (
f"<{self.__class__.__name__} id={self.conversation_id}, name={self.display_name},"
f" type={self.conversation_type.name}>"
)


@dataclasses.dataclass(init=False)
class Participant(_TalkUserStatus):
Expand Down Expand Up @@ -657,7 +670,7 @@ def participant_type(self) -> ParticipantType:

@property
def last_ping(self) -> int:
"""Timestamp of the last ping. Should be used for sorting."""
"""Timestamp of the last ping. Should be used for sorting."""
return self._raw_data["lastPing"]

@property
Expand Down Expand Up @@ -685,6 +698,11 @@ def breakout_token(self) -> str:
"""Only available with breakout-rooms-v1 capability."""
return self._raw_data.get("roomToken", "")

def __repr__(self):
return (
f"<{self.__class__.__name__} id={self.attendee_id}, name={self.display_name}, last_ping={self.last_ping}>"
)


@dataclasses.dataclass
class BotInfoBasic:
Expand Down Expand Up @@ -713,6 +731,9 @@ def state(self) -> int:
"""One of the Bot states: ``0`` - Disabled, ``1`` - enabled, ``2`` - **No setup**."""
return self._raw_data["state"]

def __repr__(self):
return f"<{self.__class__.__name__} id={self.bot_id}, name={self.bot_name}>"


@dataclasses.dataclass(init=False)
class BotInfo(BotInfoBasic):
Expand Down Expand Up @@ -771,6 +792,9 @@ def option(self) -> int:
"""The option that was voted for."""
return self._raw_data["optionId"]

def __repr__(self):
return f"<{self.__class__.__name__} actor={self.actor_display_name}, voted_for={self.option}>"


@dataclasses.dataclass
class Poll:
Expand Down Expand Up @@ -856,3 +880,6 @@ def num_voters(self) -> int:
def details(self) -> list[PollDetail]:
"""Detailed list who voted for which option (only available for public closed polls)."""
return [PollDetail(i) for i in self._raw_data.get("details", [])]

def __repr__(self):
return f"<{self.__class__.__name__} id={self.poll_id}, author={self.actor_display_name}>"
3 changes: 3 additions & 0 deletions nc_py_api/talk_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ def conversation_name(self) -> str:
"""The name of the conversation in which the message was posted."""
return self._raw_data["target"]["name"]

def __repr__(self):
return f"<{self.__class__.__name__} conversation={self.conversation_name}, actor={self.actor_display_name}>"


class TalkBot:
"""A class that implements the TalkBot functionality."""
Expand Down
9 changes: 9 additions & 0 deletions nc_py_api/user_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ def status_type(self) -> str:
"""Status type, on of the: online, away, dnd, invisible, offline."""
return self._raw_data.get("status", "")

def __repr__(self):
return f"<{self.__class__.__name__} user_id={self.user_id}, status_type={self.status_type}>"


@dataclasses.dataclass(init=False)
class CurrentUserStatus(UserStatus):
Expand All @@ -97,6 +100,12 @@ def status_type_defined(self) -> bool:
"""*True* if :py:attr:`UserStatus.status_type` is set by user, *False* otherwise."""
return self._raw_data["statusIsUserDefined"]

def __repr__(self):
return (
f"<{self.__class__.__name__} user_id={self.user_id}, status_type={self.status_type},"
f" status_id={self.status_id}>"
)


class _UserStatusAPI:
"""Class providing the user status management API on the Nextcloud server."""
Expand Down
3 changes: 3 additions & 0 deletions nc_py_api/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ def backend_capabilities(self) -> dict:
"""By default, only the ``setDisplayName`` and ``setPassword`` keys are available."""
return self._raw_data["backendCapabilities"]

def __repr__(self):
return f"<{self.__class__.__name__} id={self.user_id}, backend={self.backend}, last_login={self.last_login}>"


class _UsersAPI:
"""The class provides the user API on the Nextcloud server.
Expand Down
3 changes: 3 additions & 0 deletions nc_py_api/users_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ def can_remove(self) -> bool:
"""Flag indicating the caller has enough rights to remove users from this group."""
return bool(self._raw_data["canRemove"])

def __repr__(self):
return f"<{self.__class__.__name__} id={self.group_id}, user_count={self.user_count}, disabled={self.disabled}>"


class _UsersGroupsAPI:
"""Class providing an API for managing user groups on the Nextcloud server.
Expand Down
1 change: 1 addition & 0 deletions tests/_talk_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def coverage_talk_bot_process_request(message: talk_bot.TalkBotMessage, request:
request._url = URL("sample_url")
talk_bot_app(request)
assert e.value.status_code == 500
assert str(message).find("conversation=") != -1


@APP.post("/talk_bot_coverage")
Expand Down
4 changes: 3 additions & 1 deletion tests/actual_tests/activity_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def test_get_filters(nc_any):
assert isinstance(i.icon, str)
assert i.name
assert isinstance(i.priority, int)
assert str(i).find("name=") != -1


def test_get_activities(nc_any):
Expand All @@ -37,7 +38,8 @@ def test_get_activities(nc_any):
assert isinstance(i.objects, dict)
assert isinstance(i.link, str)
assert isinstance(i.icon, str)
assert i.activity_time > datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
assert i.time > datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
assert str(i).find("app=") != -1
r2 = nc_any.activity.get_activities(since=True)
if r2:
old_activities_id = [i.activity_id for i in r]
Expand Down
1 change: 1 addition & 0 deletions tests/actual_tests/apps_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,4 @@ def test_ex_app_get_list(nc, nc_app):
assert isinstance(app.system, bool)
if app.app_id == "nc_py_api":
assert app.system is True
assert str(app).find("id=") != -1 and str(app).find("ver=") != -1
2 changes: 2 additions & 0 deletions tests/actual_tests/files_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,8 @@ def test_create_update_delete_tag(nc_any):
assert tag.display_name == "test_nc_py_api2"
assert tag.user_visible is False
assert tag.user_assignable is False
for i in nc_any.files.list_tags():
assert str(i).find("name=") != -1
nc_any.files.delete_tag(tag)
with pytest.raises(ValueError):
nc_any.files.update_tag(tag)
Expand Down
1 change: 1 addition & 0 deletions tests/actual_tests/notes_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def test_create_delete(nc_client):
assert new_note.readonly is False
assert new_note.favorite is False
assert isinstance(new_note.last_modified, datetime)
assert str(new_note).find("title=") != -1


def test_get_update_note(nc_client):
Expand Down
Loading

0 comments on commit e411fca

Please sign in to comment.