File: activity.py

package info (click to toggle)
nc-py-api 0.19.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, trixie
  • size: 2,320 kB
  • sloc: python: 12,415; makefile: 238; xml: 100; javascript: 56; sh: 14
file content (259 lines) | stat: -rw-r--r-- 9,574 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
"""API for working with Activity App."""

import dataclasses
import datetime
import typing

from ._exceptions import NextcloudExceptionNotModified
from ._misc import check_capabilities, nc_iso_time_to_datetime
from ._session import AsyncNcSessionBasic, NcSessionBasic


@dataclasses.dataclass
class ActivityFilter:
    """Activity filter description."""

    def __init__(self, raw_data: dict):
        self._raw_data = raw_data

    @property
    def icon(self) -> str:
        """Icon for filter."""
        return self._raw_data["icon"]

    @property
    def filter_id(self) -> str:
        """Filter ID."""
        return self._raw_data["id"]

    @property
    def name(self) -> str:
        """Filter name."""
        return self._raw_data["name"]

    @property
    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:
    """Description of one activity."""

    def __init__(self, raw_data: dict):
        self._raw_data = raw_data

    @property
    def activity_id(self) -> int:
        """Unique for one Nextcloud instance activity ID."""
        return self._raw_data["activity_id"]

    @property
    def app(self) -> str:
        """App that created the activity (e.g. 'files', 'files_sharing', etc.)."""
        return self._raw_data["app"]

    @property
    def activity_type(self) -> str:
        """String describing the activity type, depends on the **app** field."""
        return self._raw_data["type"]

    @property
    def actor_id(self) -> str:
        """User ID of the user that triggered/created this activity.

        .. note:: Can be empty in case of public link/remote share action.
        """
        return self._raw_data["user"]

    @property
    def subject(self) -> str:
        """Translated simple subject without markup, ready for use (e.g. 'You created hello.jpg')."""
        return self._raw_data["subject"]

    @property
    def subject_rich(self) -> list:
        """`0` is the string subject including placeholders, `1` is an array with the placeholders."""
        return self._raw_data["subject_rich"]

    @property
    def message(self) -> str:
        """Translated message without markup, ready for use (longer text, unused by core apps)."""
        return self._raw_data["message"]

    @property
    def message_rich(self) -> list:
        """See description of **subject_rich**."""
        return self._raw_data["message_rich"]

    @property
    def object_type(self) -> str:
        """The Type of the object this activity is about (e.g. 'files' is used for files and folders)."""
        return self._raw_data["object_type"]

    @property
    def object_id(self) -> int:
        """ID of the object this activity is about (e.g., ID in the file cache is used for files and folders)."""
        return self._raw_data["object_id"]

    @property
    def object_name(self) -> str:
        """The name of the object this activity is about (e.g., for files it's the relative path to the user's root)."""
        return self._raw_data["object_name"]

    @property
    def objects(self) -> dict:
        """Contains the objects involved in multi-object activities, like editing multiple files in a folder.

        .. note:: They are stored in objects as key-value pairs of the object_id and the object_name:
            { object_id: object_name}
        """
        return self._raw_data["objects"] if isinstance(self._raw_data["objects"], dict) else {}

    @property
    def link(self) -> str:
        """A full URL pointing to a suitable location (e.g. 'http://localhost/apps/files/?dir=%2Ffolder' for folder)."""
        return self._raw_data["link"]

    @property
    def icon(self) -> str:
        """URL of the icon."""
        return self._raw_data["icon"]

    @property
    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."""

    _ep_base: str = "/ocs/v1.php/apps/activity"
    last_given: int
    """Used by ``get_activities``, when **since** param is ``True``."""

    def __init__(self, session: NcSessionBasic):
        self._session = session
        self.last_given = 0

    @property
    def available(self) -> bool:
        """Returns True if the Nextcloud instance supports this feature, False otherwise."""
        return not check_capabilities("activity.apiv2", self._session.capabilities)

    def get_activities(
        self,
        filter_id: ActivityFilter | str = "",
        since: int | bool = 0,
        limit: int = 50,
        object_type: str = "",
        object_id: int = 0,
        sort: str = "desc",
    ) -> list[Activity]:
        """Returns activities for the current user.

        :param filter_id: Filter to apply, if needed.
        :param since: Last activity ID you have seen. When specified, only activities after provided are returned.
            Can be set to ``True`` to automatically use last ``last_given`` from previous calls. Default = **0**.
        :param limit: Max number of activities to be returned.
        :param object_type: Filter the activities to a given object.
        :param object_id: Filter the activities to a given object.
        :param sort: Sort activities ascending or descending. Default is ``desc``.

        .. note:: ``object_type`` and ``object_id`` should only appear together with ``filter_id`` unset.
        """
        if since is True:
            since = self.last_given
        url, params = _get_activities(filter_id, since, limit, object_type, object_id, sort)
        try:
            result = self._session.ocs("GET", self._ep_base + url, params=params)
        except NextcloudExceptionNotModified:
            return []
        self.last_given = int(self._session.response_headers["X-Activity-Last-Given"])
        return [Activity(i) for i in result]

    def get_filters(self) -> list[ActivityFilter]:
        """Returns avalaible activity filters."""
        return [ActivityFilter(i) for i in self._session.ocs("GET", self._ep_base + "/api/v2/activity/filters")]


class _AsyncActivityAPI:
    """The class provides the async Activity Application API."""

    _ep_base: str = "/ocs/v1.php/apps/activity"
    last_given: int
    """Used by ``get_activities``, when **since** param is ``True``."""

    def __init__(self, session: AsyncNcSessionBasic):
        self._session = session
        self.last_given = 0

    @property
    async def available(self) -> bool:
        """Returns True if the Nextcloud instance supports this feature, False otherwise."""
        return not check_capabilities("activity.apiv2", await self._session.capabilities)

    async def get_activities(
        self,
        filter_id: ActivityFilter | str = "",
        since: int | bool = 0,
        limit: int = 50,
        object_type: str = "",
        object_id: int = 0,
        sort: str = "desc",
    ) -> list[Activity]:
        """Returns activities for the current user.

        :param filter_id: Filter to apply, if needed.
        :param since: Last activity ID you have seen. When specified, only activities after provided are returned.
            Can be set to ``True`` to automatically use last ``last_given`` from previous calls. Default = **0**.
        :param limit: Max number of activities to be returned.
        :param object_type: Filter the activities to a given object.
        :param object_id: Filter the activities to a given object.
        :param sort: Sort activities ascending or descending. Default is ``desc``.

        .. note:: ``object_type`` and ``object_id`` should only appear together with ``filter_id`` unset.
        """
        if since is True:
            since = self.last_given
        url, params = _get_activities(filter_id, since, limit, object_type, object_id, sort)
        try:
            result = await self._session.ocs("GET", self._ep_base + url, params=params)
        except NextcloudExceptionNotModified:
            return []
        self.last_given = int(self._session.response_headers["X-Activity-Last-Given"])
        return [Activity(i) for i in result]

    async def get_filters(self) -> list[ActivityFilter]:
        """Returns avalaible activity filters."""
        return [ActivityFilter(i) for i in await self._session.ocs("GET", self._ep_base + "/api/v2/activity/filters")]


def _get_activities(
    filter_id: ActivityFilter | str, since: int | bool, limit: int, object_type: str, object_id: int, sort: str
) -> tuple[str, dict[str, typing.Any]]:
    if bool(object_id) != bool(object_type):
        raise ValueError("Either specify both `object_type` and `object_id`, or don't specify any at all.")
    filter_id = filter_id.filter_id if isinstance(filter_id, ActivityFilter) else filter_id
    params = {
        "since": since,
        "limit": limit,
        "object_type": object_type,
        "object_id": object_id,
        "sort": sort,
    }
    url = (
        f"/api/v2/activity/{filter_id}" if filter_id else "/api/v2/activity/filter" if object_id else "/api/v2/activity"
    )
    return url, params