canvas 0.34.1__py3-none-any.whl → 0.35.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of canvas might be problematic. Click here for more details.

Files changed (59) hide show
  1. {canvas-0.34.1.dist-info → canvas-0.35.0.dist-info}/METADATA +1 -1
  2. {canvas-0.34.1.dist-info → canvas-0.35.0.dist-info}/RECORD +58 -50
  3. canvas_generated/messages/effects_pb2.py +4 -4
  4. canvas_generated/messages/effects_pb2.pyi +22 -2
  5. canvas_generated/messages/events_pb2.py +2 -2
  6. canvas_generated/messages/events_pb2.pyi +30 -0
  7. canvas_sdk/base.py +56 -0
  8. canvas_sdk/commands/base.py +22 -46
  9. canvas_sdk/commands/commands/adjust_prescription.py +0 -10
  10. canvas_sdk/commands/commands/allergy.py +0 -1
  11. canvas_sdk/commands/commands/assess.py +2 -2
  12. canvas_sdk/commands/commands/change_medication.py +58 -0
  13. canvas_sdk/commands/commands/close_goal.py +0 -1
  14. canvas_sdk/commands/commands/diagnose.py +0 -1
  15. canvas_sdk/commands/commands/exam.py +0 -1
  16. canvas_sdk/commands/commands/family_history.py +0 -1
  17. canvas_sdk/commands/commands/follow_up.py +4 -2
  18. canvas_sdk/commands/commands/goal.py +8 -7
  19. canvas_sdk/commands/commands/history_present_illness.py +0 -1
  20. canvas_sdk/commands/commands/imaging_order.py +9 -8
  21. canvas_sdk/commands/commands/instruct.py +2 -2
  22. canvas_sdk/commands/commands/lab_order.py +10 -9
  23. canvas_sdk/commands/commands/medical_history.py +0 -1
  24. canvas_sdk/commands/commands/medication_statement.py +0 -1
  25. canvas_sdk/commands/commands/past_surgical_history.py +0 -1
  26. canvas_sdk/commands/commands/perform.py +3 -2
  27. canvas_sdk/commands/commands/plan.py +0 -1
  28. canvas_sdk/commands/commands/prescribe.py +0 -9
  29. canvas_sdk/commands/commands/refer.py +10 -10
  30. canvas_sdk/commands/commands/refill.py +0 -9
  31. canvas_sdk/commands/commands/remove_allergy.py +0 -1
  32. canvas_sdk/commands/commands/resolve_condition.py +3 -2
  33. canvas_sdk/commands/commands/review_of_systems.py +0 -1
  34. canvas_sdk/commands/commands/stop_medication.py +0 -1
  35. canvas_sdk/commands/commands/structured_assessment.py +0 -1
  36. canvas_sdk/commands/commands/task.py +0 -4
  37. canvas_sdk/commands/commands/update_diagnosis.py +8 -6
  38. canvas_sdk/commands/commands/update_goal.py +0 -1
  39. canvas_sdk/commands/commands/vitals.py +0 -1
  40. canvas_sdk/effects/note/__init__.py +10 -0
  41. canvas_sdk/effects/note/appointment.py +148 -0
  42. canvas_sdk/effects/note/base.py +129 -0
  43. canvas_sdk/effects/note/note.py +79 -0
  44. canvas_sdk/effects/patient/__init__.py +3 -0
  45. canvas_sdk/effects/patient/base.py +123 -0
  46. canvas_sdk/utils/http.py +7 -26
  47. canvas_sdk/utils/metrics.py +192 -0
  48. canvas_sdk/utils/plugins.py +24 -0
  49. canvas_sdk/v1/data/__init__.py +4 -0
  50. canvas_sdk/v1/data/message.py +82 -0
  51. plugin_runner/load_all_plugins.py +0 -3
  52. plugin_runner/plugin_runner.py +107 -114
  53. plugin_runner/sandbox.py +3 -0
  54. protobufs/canvas_generated/messages/effects.proto +13 -0
  55. protobufs/canvas_generated/messages/events.proto +16 -0
  56. settings.py +4 -0
  57. canvas_sdk/utils/stats.py +0 -74
  58. {canvas-0.34.1.dist-info → canvas-0.35.0.dist-info}/WHEEL +0 -0
  59. {canvas-0.34.1.dist-info → canvas-0.35.0.dist-info}/entry_points.txt +0 -0
@@ -28,10 +28,6 @@ class TaskCommand(BaseCommand):
28
28
 
29
29
  class Meta:
30
30
  key = "task"
31
- commit_required_fields = (
32
- "title",
33
- "assign_to",
34
- )
35
31
 
36
32
  title: str = ""
37
33
  assign_to: TaskAssigner | None = None
@@ -1,3 +1,5 @@
1
+ from pydantic import Field
2
+
1
3
  from canvas_sdk.commands.base import _BaseCommand as BaseCommand
2
4
 
3
5
 
@@ -6,13 +8,13 @@ class UpdateDiagnosisCommand(BaseCommand):
6
8
 
7
9
  class Meta:
8
10
  key = "updateDiagnosis"
9
- commit_required_fields = (
10
- "condition_code",
11
- "new_condition_code",
12
- )
13
11
 
14
- condition_code: str | None = None
15
- new_condition_code: str | None = None
12
+ condition_code: str | None = Field(
13
+ default=None, json_schema_extra={"commands_api_name": "condition"}
14
+ )
15
+ new_condition_code: str | None = Field(
16
+ default=None, json_schema_extra={"commands_api_name": "new_condition"}
17
+ )
16
18
  background: str | None = None
17
19
  narrative: str | None = None
18
20
 
@@ -11,7 +11,6 @@ class UpdateGoalCommand(_BaseCommand):
11
11
 
12
12
  class Meta:
13
13
  key = "updateGoal"
14
- commit_required_fields = ("goal_id",)
15
14
 
16
15
  class AchievementStatus(Enum):
17
16
  IN_PROGRESS = "in-progress"
@@ -10,7 +10,6 @@ class VitalsCommand(BaseCommand):
10
10
 
11
11
  class Meta:
12
12
  key = "vitals"
13
- commit_required_fields = ()
14
13
 
15
14
  class BodyTemperatureSite(Enum):
16
15
  AXILLARY = 0
@@ -0,0 +1,10 @@
1
+ from canvas_sdk.effects.note.appointment import Appointment, ScheduleEvent
2
+ from canvas_sdk.effects.note.base import AppointmentIdentifier
3
+ from canvas_sdk.effects.note.note import Note
4
+
5
+ __all__ = __exports__ = (
6
+ "AppointmentIdentifier",
7
+ "Note",
8
+ "Appointment",
9
+ "ScheduleEvent",
10
+ )
@@ -0,0 +1,148 @@
1
+ from typing import Any
2
+ from uuid import UUID
3
+
4
+ from pydantic_core import InitErrorDetails
5
+
6
+ from canvas_sdk.effects.note.base import AppointmentABC
7
+ from canvas_sdk.v1.data import NoteType, Patient
8
+ from canvas_sdk.v1.data.note import NoteTypeCategories
9
+
10
+
11
+ class ScheduleEvent(AppointmentABC):
12
+ """
13
+ Effect to create a schedule event.
14
+
15
+ Attributes:
16
+ note_type_id (UUID | str): The ID of the note type.
17
+ patient_id (str | None): The ID of the patient, if applicable.
18
+ description (str | None): The description of the schedule event, if applicable.
19
+ """
20
+
21
+ class Meta:
22
+ effect_type = "SCHEDULE_EVENT"
23
+
24
+ note_type_id: UUID | str
25
+ patient_id: str | None = None
26
+ description: str | None = None
27
+
28
+ def _get_error_details(self, method: Any) -> list[InitErrorDetails]:
29
+ """
30
+ Validates the schedule event note type and returns a list of error details if validation fails.
31
+
32
+ Args:
33
+ method (Any): The method being validated.
34
+
35
+ Returns:
36
+ list[InitErrorDetails]: A list of error details for invalid schedule event note types.
37
+ """
38
+ errors = super()._get_error_details(method)
39
+
40
+ note_type = (
41
+ NoteType.objects.values("category", "is_patient_required", "allow_custom_title")
42
+ .filter(id=self.note_type_id)
43
+ .first()
44
+ )
45
+
46
+ if not note_type:
47
+ errors.append(
48
+ self._create_error_detail(
49
+ "value",
50
+ f"Note type with ID {self.note_type_id} does not exist.",
51
+ self.note_type_id,
52
+ )
53
+ )
54
+ return errors
55
+
56
+ if note_type["category"] != NoteTypeCategories.SCHEDULE_EVENT:
57
+ errors.append(
58
+ self._create_error_detail(
59
+ "value",
60
+ f"Schedule event note type must be of type: {NoteTypeCategories.SCHEDULE_EVENT}.",
61
+ self.note_type_id,
62
+ )
63
+ )
64
+
65
+ if note_type["is_patient_required"] and not self.patient_id:
66
+ errors.append(
67
+ self._create_error_detail(
68
+ "value",
69
+ "Patient ID is required for this note type.",
70
+ self.note_type_id,
71
+ )
72
+ )
73
+
74
+ if not note_type["allow_custom_title"] and self.description:
75
+ errors.append(
76
+ self._create_error_detail(
77
+ "value",
78
+ "Description is not allowed for this note type.",
79
+ self.note_type_id,
80
+ )
81
+ )
82
+
83
+ return errors
84
+
85
+
86
+ class Appointment(AppointmentABC):
87
+ """
88
+ Effect to create an appointment.
89
+
90
+ Attributes:
91
+ appointment_note_type_id (UUID | str): The ID of the appointment note type.
92
+ meeting_link (str | None): The meeting link for the appointment, if any.
93
+ patient_id (str): The ID of the patient.
94
+ """
95
+
96
+ class Meta:
97
+ effect_type = "APPOINTMENT"
98
+
99
+ appointment_note_type_id: UUID | str
100
+ meeting_link: str | None = None
101
+ patient_id: str
102
+
103
+ def _get_error_details(self, method: Any) -> list[InitErrorDetails]:
104
+ """
105
+ Validates the appointment note type and returns a list of error details if validation fails.
106
+
107
+ Args:
108
+ method (Any): The method being validated.
109
+
110
+ Returns:
111
+ list[InitErrorDetails]: A list of error details for invalid appointment note types or missing patient IDs.
112
+ """
113
+ errors = super()._get_error_details(method)
114
+
115
+ if not Patient.objects.filter(id=self.patient_id).exists():
116
+ errors.append(
117
+ self._create_error_detail(
118
+ "value",
119
+ "Patient with ID {self.patient_id} does not exist.",
120
+ self.patient_id,
121
+ )
122
+ )
123
+
124
+ category, is_scheduleable = NoteType.objects.values_list("category", "is_scheduleable").get(
125
+ id=self.appointment_note_type_id
126
+ )
127
+ if category != NoteTypeCategories.ENCOUNTER:
128
+ errors.append(
129
+ self._create_error_detail(
130
+ "value",
131
+ f"Appointment note type must be of type, {NoteTypeCategories.ENCOUNTER} but got: {category}.",
132
+ self.appointment_note_type_id,
133
+ )
134
+ )
135
+
136
+ if not is_scheduleable:
137
+ errors.append(
138
+ self._create_error_detail(
139
+ "value",
140
+ "Appointment note type must be scheduleable.",
141
+ self.appointment_note_type_id,
142
+ )
143
+ )
144
+
145
+ return errors
146
+
147
+
148
+ __exports__ = ("ScheduleEvent", "Appointment")
@@ -0,0 +1,129 @@
1
+ import datetime
2
+ import json
3
+ from abc import ABC
4
+ from dataclasses import dataclass
5
+ from typing import Any
6
+ from uuid import UUID
7
+
8
+ from pydantic_core import InitErrorDetails
9
+
10
+ from canvas_generated.messages.effects_pb2 import Effect
11
+ from canvas_sdk.base import TrackableFieldsModel
12
+ from canvas_sdk.v1.data import PracticeLocation, Staff
13
+ from canvas_sdk.v1.data.appointment import AppointmentProgressStatus
14
+
15
+
16
+ @dataclass
17
+ class AppointmentIdentifier:
18
+ """
19
+ Dataclass for appointment identifiers.
20
+
21
+ Attributes:
22
+ system (str): The system identifier for the appointment.
23
+ value (str): The value associated with the system identifier.
24
+ """
25
+
26
+ system: str
27
+ value: str
28
+
29
+
30
+ class NoteOrAppointmentABC(TrackableFieldsModel, ABC):
31
+ """
32
+ Base class for all note effects.
33
+
34
+ Attributes:
35
+ practice_location_id (UUID | str): The ID of the practice location.
36
+ provider_id (str): The ID of the provider.
37
+ """
38
+
39
+ class Meta:
40
+ effect_type = None
41
+
42
+ practice_location_id: UUID | str
43
+ provider_id: str
44
+
45
+ def _get_error_details(self, method: Any) -> list[InitErrorDetails]:
46
+ """
47
+ Validates the practice location and provider IDs and returns a list of error details if validation fails.
48
+
49
+ Args:
50
+ method (Any): The method being validated.
51
+
52
+ Returns:
53
+ list[InitErrorDetails]: A list of error details for invalid practice location or provider IDs.
54
+ """
55
+ errors = super()._get_error_details(method)
56
+
57
+ if not PracticeLocation.objects.filter(id=self.practice_location_id).exists():
58
+ errors.append(
59
+ self._create_error_detail(
60
+ "value",
61
+ f"Practice location with ID {self.practice_location_id} does not exist.",
62
+ self.practice_location_id,
63
+ )
64
+ )
65
+
66
+ if not Staff.objects.filter(id=self.provider_id).exists():
67
+ errors.append(
68
+ self._create_error_detail(
69
+ "value",
70
+ "Provider with ID {self.provider_id} does not exist.",
71
+ self.provider_id,
72
+ )
73
+ )
74
+
75
+ return errors
76
+
77
+ def create(self) -> Effect:
78
+ """Originate a new command in the note body."""
79
+ self._validate_before_effect("create")
80
+ return Effect(
81
+ type=f"CREATE_{self.Meta.effect_type}",
82
+ payload=json.dumps(
83
+ {
84
+ "data": self.values,
85
+ }
86
+ ),
87
+ )
88
+
89
+
90
+ class AppointmentABC(NoteOrAppointmentABC, ABC):
91
+ """
92
+ Base class for appointment creation effects.
93
+
94
+ Attributes:
95
+ start_time (datetime.datetime): The start time of the appointment.
96
+ duration_minutes (int): The duration of the appointment in minutes.
97
+ status (AppointmentProgressStatus | None): The status of the appointment.
98
+ external_identifiers (list[AppointmentIdentifier] | None): List of external identifiers for the appointment.
99
+ """
100
+
101
+ class Meta:
102
+ effect_type = "APPOINTMENT"
103
+
104
+ _dirty_excluded_keys = [
105
+ "external_identifiers",
106
+ ]
107
+
108
+ start_time: datetime.datetime
109
+ duration_minutes: int
110
+ status: AppointmentProgressStatus | None = None
111
+ external_identifiers: list[AppointmentIdentifier] | None = None
112
+
113
+ @property
114
+ def values(self) -> dict:
115
+ """
116
+ Returns a dictionary of modified attributes with type-specific transformations.
117
+ """
118
+ values = super().values
119
+
120
+ if self.external_identifiers:
121
+ values["external_identifiers"] = [
122
+ {"system": identifier.system, "value": identifier.value}
123
+ for identifier in self.external_identifiers
124
+ ]
125
+
126
+ return values
127
+
128
+
129
+ __exports__ = ("AppointmentIdentifier", "NoteOrAppointmentABC", "AppointmentABC")
@@ -0,0 +1,79 @@
1
+ import datetime
2
+ from typing import Any
3
+ from uuid import UUID
4
+
5
+ from pydantic_core import InitErrorDetails
6
+
7
+ from canvas_sdk.effects.note.base import NoteOrAppointmentABC
8
+ from canvas_sdk.v1.data import NoteType, Patient
9
+ from canvas_sdk.v1.data.note import NoteTypeCategories
10
+
11
+
12
+ class Note(NoteOrAppointmentABC):
13
+ """
14
+ Effect to create a visit note.
15
+
16
+ Attributes:
17
+ note_type_id (UUID | str): The ID of the note type.
18
+ datetime_of_service (datetime.datetime): The date and time of the service.
19
+ patient_id (str): The ID of the patient.
20
+ """
21
+
22
+ class Meta:
23
+ effect_type = "NOTE"
24
+
25
+ note_type_id: UUID | str
26
+ datetime_of_service: datetime.datetime
27
+ patient_id: str
28
+
29
+ def _get_error_details(self, method: Any) -> list[InitErrorDetails]:
30
+ """
31
+ Validates the note type category and returns a list of error details if validation fails.
32
+
33
+ Args:
34
+ method (Any): The method being validated.
35
+
36
+ Returns:
37
+ list[InitErrorDetails]: A list of error details for invalid note type categories.
38
+ """
39
+ errors = super()._get_error_details(method)
40
+
41
+ note_type_category = (
42
+ NoteType.objects.values_list("category", flat=True).filter(id=self.note_type_id).first()
43
+ )
44
+
45
+ if not note_type_category:
46
+ errors.append(
47
+ self._create_error_detail(
48
+ "value",
49
+ f"Note type with ID {self.note_type_id} does not exist.",
50
+ self.note_type_id,
51
+ )
52
+ )
53
+ elif note_type_category in (
54
+ NoteTypeCategories.APPOINTMENT,
55
+ NoteTypeCategories.SCHEDULE_EVENT,
56
+ NoteTypeCategories.MESSAGE,
57
+ NoteTypeCategories.LETTER,
58
+ ):
59
+ errors.append(
60
+ self._create_error_detail(
61
+ "value",
62
+ f"Visit note type cannot be of type: {note_type_category}.",
63
+ self.note_type_id,
64
+ )
65
+ )
66
+
67
+ if not Patient.objects.filter(id=self.patient_id).exists():
68
+ errors.append(
69
+ self._create_error_detail(
70
+ "value",
71
+ f"Patient with ID {self.patient_id} does not exist.",
72
+ self.patient_id,
73
+ )
74
+ )
75
+
76
+ return errors
77
+
78
+
79
+ __exports__ = ("Note",)
@@ -0,0 +1,3 @@
1
+ from canvas_sdk.effects.patient.base import Patient, PatientContactPoint
2
+
3
+ __all__ = __exports__ = ("Patient", "PatientContactPoint")
@@ -0,0 +1,123 @@
1
+ import datetime
2
+ import json
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+ from pydantic_core import InitErrorDetails
7
+
8
+ from canvas_generated.messages.effects_pb2 import Effect
9
+ from canvas_sdk.base import TrackableFieldsModel
10
+ from canvas_sdk.v1.data import PracticeLocation, Staff
11
+ from canvas_sdk.v1.data.common import ContactPointSystem, ContactPointUse, PersonSex
12
+
13
+
14
+ @dataclass
15
+ class PatientContactPoint:
16
+ """A class representing a patient contact point."""
17
+
18
+ system: ContactPointSystem
19
+ value: str
20
+ use: ContactPointUse
21
+ rank: int
22
+ has_consent: bool | None = None
23
+
24
+ def to_dict(self) -> dict[str, Any]:
25
+ """Convert the contact point to a dictionary."""
26
+ return {
27
+ "system": self.system.value,
28
+ "value": self.value,
29
+ "use": self.use.value,
30
+ "rank": self.rank,
31
+ "has_consent": self.has_consent,
32
+ }
33
+
34
+
35
+ class Patient(TrackableFieldsModel):
36
+ """Effect to create a Patient record."""
37
+
38
+ class Meta:
39
+ effect_type = "PATIENT"
40
+
41
+ first_name: str
42
+ last_name: str
43
+ middle_name: str | None = None
44
+ birthdate: datetime.date | None = None
45
+ prefix: str | None = None
46
+ suffix: str | None = None
47
+ sex_at_birth: PersonSex | None = None
48
+ nickname: str | None = None
49
+ social_security_number: str | None = None
50
+ administrative_note: str | None = None
51
+ clinical_note: str | None = None
52
+ default_location_id: str | None = None
53
+ default_provider_id: str | None = None
54
+ previous_names: list[str] | None = None
55
+ contact_points: list[PatientContactPoint] | None = None
56
+
57
+ @property
58
+ def values(self) -> dict[str, Any]:
59
+ """Return the values of the patient as a dictionary."""
60
+ return {
61
+ "first_name": self.first_name,
62
+ "last_name": self.last_name,
63
+ "middle_name": self.middle_name,
64
+ "previous_names": self.previous_names or [],
65
+ "birthdate": self.birthdate.isoformat() if self.birthdate else None,
66
+ "prefix": self.prefix,
67
+ "suffix": self.suffix,
68
+ "sex_at_birth": self.sex_at_birth.value if self.sex_at_birth else None,
69
+ "nickname": self.nickname,
70
+ "social_security_number": self.social_security_number,
71
+ "administrative_note": self.administrative_note,
72
+ "clinical_note": self.clinical_note,
73
+ "default_location": self.default_location_id,
74
+ "default_provider": self.default_provider_id,
75
+ "contact_points": [cp.to_dict() for cp in self.contact_points]
76
+ if self.contact_points
77
+ else None,
78
+ }
79
+
80
+ def _get_error_details(self, method: Any) -> list[InitErrorDetails]:
81
+ errors = super()._get_error_details(method)
82
+
83
+ if (
84
+ self.default_location_id
85
+ and not PracticeLocation.objects.filter(id=self.default_location_id).exists()
86
+ ):
87
+ errors.append(
88
+ self._create_error_detail(
89
+ "value",
90
+ f"Practice location with ID {self.default_location_id} does not exist.",
91
+ self.default_location_id,
92
+ )
93
+ )
94
+
95
+ if (
96
+ self.default_provider_id
97
+ and not Staff.objects.filter(id=self.default_provider_id).exists()
98
+ ):
99
+ errors.append(
100
+ self._create_error_detail(
101
+ "value",
102
+ f"Provider with ID {self.default_provider_id} does not exist.",
103
+ self.default_provider_id,
104
+ )
105
+ )
106
+
107
+ return errors
108
+
109
+ def create(self) -> Effect:
110
+ """Create a new Patient."""
111
+ self._validate_before_effect("create")
112
+
113
+ return Effect(
114
+ type=f"CREATE_{self.Meta.effect_type}",
115
+ payload=json.dumps(
116
+ {
117
+ "data": self.values,
118
+ }
119
+ ),
120
+ )
121
+
122
+
123
+ __exports__ = ("Patient", "PatientContactPoint")
canvas_sdk/utils/http.py CHANGED
@@ -1,16 +1,14 @@
1
1
  import concurrent
2
2
  import functools
3
3
  import os
4
- import time
5
4
  import urllib.parse
6
5
  from collections.abc import Callable, Iterable, Mapping
7
6
  from concurrent.futures import ThreadPoolExecutor
8
- from functools import wraps
9
- from typing import Any, Literal, Protocol, TypeVar, cast
7
+ from typing import Any, Literal, Protocol, TypeVar
10
8
 
11
9
  import requests
12
10
 
13
- from canvas_sdk.utils.stats import StatsDClientProxy
11
+ from canvas_sdk.utils.metrics import measured
14
12
 
15
13
  F = TypeVar("F", bound=Callable)
16
14
 
@@ -117,24 +115,7 @@ class Http:
117
115
  self._base_url = base_url
118
116
  self._session = requests.Session()
119
117
 
120
- self.statsd_client = StatsDClientProxy()
121
-
122
- @staticmethod
123
- def measure_time(fn: F) -> F:
124
- """A decorator to store timing of HTTP calls."""
125
-
126
- @wraps(fn)
127
- def wrapper(self: "Http", *args: Any, **kwargs: Any) -> Any:
128
- start_time = time.time()
129
- result = fn(self, *args, **kwargs)
130
- end_time = time.time()
131
- timing = int((end_time - start_time) * 1000)
132
- self.statsd_client.timing(f"plugins.http_{fn.__name__}", timing, tags={})
133
- return result
134
-
135
- return cast(F, wrapper)
136
-
137
- @measure_time
118
+ @measured(track_plugins_usage=True)
138
119
  def get(
139
120
  self, url: str, headers: Mapping[str, str | bytes | None] | None = None
140
121
  ) -> requests.Response:
@@ -147,7 +128,7 @@ class Http:
147
128
  timeout=self._MAX_REQUEST_TIMEOUT_SECONDS,
148
129
  )
149
130
 
150
- @measure_time
131
+ @measured(track_plugins_usage=True)
151
132
  def post(
152
133
  self,
153
134
  url: str,
@@ -164,7 +145,7 @@ class Http:
164
145
  timeout=self._MAX_REQUEST_TIMEOUT_SECONDS,
165
146
  )
166
147
 
167
- @measure_time
148
+ @measured(track_plugins_usage=True)
168
149
  def put(
169
150
  self,
170
151
  url: str,
@@ -181,7 +162,7 @@ class Http:
181
162
  timeout=self._MAX_REQUEST_TIMEOUT_SECONDS,
182
163
  )
183
164
 
184
- @measure_time
165
+ @measured(track_plugins_usage=True)
185
166
  def patch(
186
167
  self,
187
168
  url: str,
@@ -198,7 +179,7 @@ class Http:
198
179
  timeout=self._MAX_REQUEST_TIMEOUT_SECONDS,
199
180
  )
200
181
 
201
- @measure_time
182
+ @measured(track_plugins_usage=True)
202
183
  def batch_requests(
203
184
  self,
204
185
  batch_requests: Iterable[BatchableRequest],