Skip to content

Commit c0a9f8b

Browse files
authored
feat: adds metrics view events (#170)
1 parent 90b6f83 commit c0a9f8b

File tree

9 files changed

+624
-55
lines changed

9 files changed

+624
-55
lines changed

src/posit/connect/client.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
from requests import Response, Session
66
from typing import Optional
77

8-
from . import hooks, me, urls
8+
from . import config, hooks, me, metrics, urls
99

1010
from .auth import Auth
1111
from .config import Config
1212
from .oauth import OAuthIntegration
1313
from .content import Content
14-
from .usage import Usage
14+
from .metrics.usage import Usage
1515
from .users import User, Users
16-
from .visits import Visits
16+
from .metrics.visits import Visits
1717

1818

1919
class Client:
@@ -98,12 +98,30 @@ def content(self) -> Content:
9898
return Content(config=self.config, session=self.session)
9999

100100
@property
101-
def usage(self) -> Usage:
102-
return Usage(self.config, self.session)
101+
def metrics(self) -> metrics.Metrics:
102+
"""The Metrics API interface.
103103
104-
@property
105-
def visits(self) -> Visits:
106-
return Visits(self.config, self.session)
104+
The Metrics API is designed for capturing, retrieving, and managing
105+
quantitative measurements of Connect interactions. It is commonly used
106+
for monitoring and analyzing system performance, user behavior, and
107+
business processes. This API facilitates real-time data collection and
108+
accessibility, enabling organizations to make informed decisions based
109+
on key performance indicators (KPIs).
110+
111+
Returns
112+
-------
113+
metrics.Metrics
114+
115+
Examples
116+
--------
117+
>>> from posit import connect
118+
>>> client = connect.Client()
119+
>>> content_guid = "2243770d-ace0-4782-87f9-fe2aeca14fc8"
120+
>>> view_events = client.metrics.views.find(content_guid=content_guid)
121+
>>> len(view_events)
122+
24
123+
"""
124+
return metrics.Metrics(self.config, self.session)
107125

108126
def __del__(self):
109127
"""Close the session when the Client instance is deleted."""
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from .. import resources
2+
3+
from . import views
4+
5+
6+
class Metrics(resources.Resources):
7+
@property
8+
def views(self) -> views.Views:
9+
return views.Views(self.config, self.session)

src/posit/connect/usage.py renamed to src/posit/connect/metrics/usage.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
from typing import List, overload
44

5-
from . import urls
5+
from .. import urls
66

7-
from .cursors import CursorPaginator
8-
from .resources import Resource, Resources
7+
from ..cursors import CursorPaginator
8+
from ..resources import Resource, Resources
99

1010

1111
class UsageEvent(Resource):

src/posit/connect/metrics/views.py

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
from __future__ import annotations
2+
3+
import itertools
4+
5+
from typing import List, overload
6+
7+
from requests.sessions import Session as Session
8+
9+
from . import usage, visits
10+
11+
from .. import resources
12+
13+
14+
class ViewEvent(resources.Resource):
15+
@staticmethod
16+
def from_event(event: visits.VisitEvent | usage.UsageEvent) -> ViewEvent:
17+
if type(event) == visits.VisitEvent:
18+
return ViewEvent.from_visit_event(event)
19+
20+
if type(event) == usage.UsageEvent:
21+
return ViewEvent.from_usage_event(event)
22+
23+
raise TypeError
24+
25+
@staticmethod
26+
def from_visit_event(event: visits.VisitEvent) -> ViewEvent:
27+
return ViewEvent(
28+
event.config,
29+
event.session,
30+
content_guid=event.content_guid,
31+
user_guid=event.user_guid,
32+
variant_key=event.variant_key,
33+
rendering_id=event.rendering_id,
34+
bundle_id=event.bundle_id,
35+
started=event.time,
36+
ended=event.time,
37+
data_version=event.data_version,
38+
path=event.path,
39+
)
40+
41+
@staticmethod
42+
def from_usage_event(event: usage.UsageEvent) -> ViewEvent:
43+
return ViewEvent(
44+
event.config,
45+
event.session,
46+
content_guid=event.content_guid,
47+
user_guid=event.user_guid,
48+
started=event.started,
49+
ended=event.ended,
50+
data_version=event.data_version,
51+
)
52+
53+
def __init__(self, config: resources.Config, session: Session, **kwargs):
54+
super().__init__(config, session, **kwargs)
55+
56+
@property
57+
def content_guid(self) -> str:
58+
"""The associated unique content identifier.
59+
60+
Returns
61+
-------
62+
str
63+
"""
64+
return self["content_guid"]
65+
66+
@property
67+
def user_guid(self) -> str:
68+
"""The associated unique user identifier.
69+
70+
Returns
71+
-------
72+
str
73+
"""
74+
return self["user_guid"]
75+
76+
@property
77+
def variant_key(self) -> str | None:
78+
"""The variant key associated with the visit.
79+
80+
Returns
81+
-------
82+
str | None
83+
The variant key, or None if the associated content type is static.
84+
"""
85+
return self.get("variant_key")
86+
87+
@property
88+
def rendering_id(self) -> int | None:
89+
"""The render id associated with the visit.
90+
91+
Returns
92+
-------
93+
int | None
94+
The render id, or None if the associated content type is static.
95+
"""
96+
return self.get("rendering_id")
97+
98+
@property
99+
def bundle_id(self) -> int | None:
100+
"""The bundle id associated with the visit.
101+
102+
Returns
103+
-------
104+
int
105+
"""
106+
return self.get("bundle_id")
107+
108+
@property
109+
def started(self) -> str:
110+
"""The visit timestamp.
111+
112+
Returns
113+
-------
114+
str
115+
"""
116+
return self["started"]
117+
118+
@property
119+
def ended(self) -> str:
120+
"""The visit timestamp.
121+
122+
Returns
123+
-------
124+
str
125+
"""
126+
return self["ended"]
127+
128+
@property
129+
def data_version(self) -> int:
130+
"""The data version.
131+
132+
Returns
133+
-------
134+
int
135+
"""
136+
return self["data_version"]
137+
138+
@property
139+
def path(self) -> str | None:
140+
"""The path requested by the user.
141+
142+
Returns
143+
-------
144+
str
145+
"""
146+
return self.get("path")
147+
148+
149+
class Views(resources.Resources):
150+
@overload
151+
def find(
152+
self,
153+
content_guid: str = ...,
154+
min_data_version: int = ...,
155+
start: str = ...,
156+
end: str = ...,
157+
) -> List[ViewEvent]:
158+
"""Find view events.
159+
160+
Parameters
161+
----------
162+
content_guid : str, optional
163+
Filter by an associated unique content identifer, by default ...
164+
min_data_version : int, optional
165+
Filter by a minimum data version, by default ...
166+
start : str, optional
167+
Filter by the start time, by default ...
168+
end : str, optional
169+
Filter by the end time, by default ...
170+
171+
Returns
172+
-------
173+
List[ViewEvent]
174+
"""
175+
...
176+
177+
@overload
178+
def find(self, *args, **kwargs) -> List[ViewEvent]:
179+
"""Find view events.
180+
181+
Returns
182+
-------
183+
List[ViewEvent]
184+
"""
185+
...
186+
187+
def find(self, *args, **kwargs) -> List[ViewEvent]:
188+
"""Find view events.
189+
190+
Returns
191+
-------
192+
List[ViewEvent]
193+
"""
194+
events = []
195+
finders = (visits.Visits, usage.Usage)
196+
for finder in finders:
197+
instance = finder(self.config, self.session)
198+
events.extend(
199+
[
200+
ViewEvent.from_event(event)
201+
for event in instance.find(*args, **kwargs) # type: ignore[attr-defined]
202+
]
203+
)
204+
return events
205+
206+
@overload
207+
def find_one(
208+
self,
209+
content_guid: str = ...,
210+
min_data_version: int = ...,
211+
start: str = ...,
212+
end: str = ...,
213+
) -> ViewEvent | None:
214+
"""Find a view event.
215+
216+
Parameters
217+
----------
218+
content_guid : str, optional
219+
Filter by an associated unique content identifer, by default ...
220+
min_data_version : int, optional
221+
Filter by a minimum data version, by default ...
222+
start : str, optional
223+
Filter by the start time, by default ...
224+
end : str, optional
225+
Filter by the end time, by default ...
226+
227+
Returns
228+
-------
229+
Visit | None
230+
"""
231+
...
232+
233+
@overload
234+
def find_one(self, *args, **kwargs) -> ViewEvent | None:
235+
"""Find a view event.
236+
237+
Returns
238+
-------
239+
Visit | None
240+
"""
241+
...
242+
243+
def find_one(self, *args, **kwargs) -> ViewEvent | None:
244+
"""Find a view event.
245+
246+
Returns
247+
-------
248+
ViewEvent | None
249+
"""
250+
finders = (visits.Visits, usage.Usage)
251+
for finder in finders:
252+
instance = finder(self.config, self.session)
253+
event = instance.find_one(*args, **kwargs) # type: ignore[attr-defined]
254+
if event:
255+
return ViewEvent.from_event(event)
256+
return None

0 commit comments

Comments
 (0)