hackerrank 2026.7.19__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.
hackerrank/types.py ADDED
@@ -0,0 +1,1086 @@
1
+ """Types for the HackerRank for Work API."""
2
+
3
+ from collections.abc import Iterable, Mapping, Sequence
4
+ from dataclasses import dataclass
5
+ from typing import Any, ClassVar, Self
6
+
7
+ from beartype import beartype
8
+
9
+ from hackerrank._dict_types import (
10
+ ATSCodePairDict,
11
+ ATSCodeScreenDict,
12
+ AuditLogDict,
13
+ CandidateDetailDict,
14
+ EnvironmentDict,
15
+ EnvironmentRuntimeDict,
16
+ InterviewDict,
17
+ InterviewTemplateDict,
18
+ InterviewTranscriptDict,
19
+ InterviewTranscriptMessageDict,
20
+ InviterDict,
21
+ QuestionDict,
22
+ SCIMTeamDict,
23
+ SCIMUserDict,
24
+ TeamDict,
25
+ TemplateDict,
26
+ TestCandidateDict,
27
+ TestDict,
28
+ UserDict,
29
+ UserTeamMembershipDict,
30
+ )
31
+
32
+ type JSONValue = (
33
+ str | int | float | bool | None | Sequence[Any] | Mapping[str, Any]
34
+ )
35
+ """A JSON-compatible value.
36
+
37
+ Used for free-form fields such as ``metadata``, ``candidate``,
38
+ ``accommodations`` and ``webhook_authentication`` — anything where
39
+ the HackerRank API accepts or returns an opaque blob constrained
40
+ only to be JSON-serialisable.
41
+
42
+ ``Sequence`` / ``Mapping`` (rather than ``list`` / ``dict``) make
43
+ the alias accept narrower concrete types such as ``dict[str, str]``
44
+ without invariance issues. The inner ``Any`` keeps the alias
45
+ non-recursive so ``@beartype`` can resolve it at runtime.
46
+ """
47
+
48
+
49
+ @beartype
50
+ class Page[T](list[T]):
51
+ """A page of results with HackerRank's pagination metadata."""
52
+
53
+ def __init__(
54
+ self,
55
+ iterable: Iterable[T] = (),
56
+ /,
57
+ *,
58
+ page_total: int,
59
+ offset: int,
60
+ previous: str,
61
+ next_: str,
62
+ first: str,
63
+ last: str,
64
+ total: int,
65
+ ) -> None:
66
+ """Create a new page.
67
+
68
+ Args:
69
+ iterable: The items for the list.
70
+ page_total: Count of items in the current page.
71
+ offset: The offset of the current page.
72
+ previous: URL of the previous page.
73
+ next_: URL of the next page.
74
+ first: URL of the first page.
75
+ last: URL of the last page.
76
+ total: Total number of items across all pages.
77
+ """
78
+ super().__init__(iterable)
79
+ self.page_total = page_total
80
+ self.offset = offset
81
+ self.previous = previous
82
+ self.next = next_
83
+ self.first = first
84
+ self.last = last
85
+ self.total = total
86
+
87
+ @property
88
+ def data(self) -> list[T]:
89
+ """Return the items as a plain list.
90
+
91
+ Returns:
92
+ A copy of the items in this page.
93
+ """
94
+ return list(self)
95
+
96
+
97
+ @beartype
98
+ class SCIMPage[T](list[T]):
99
+ """A SCIM v2 paginated response.
100
+
101
+ SCIM uses a different envelope from the v3 API.
102
+ """
103
+
104
+ def __init__(
105
+ self,
106
+ iterable: Iterable[T] = (),
107
+ /,
108
+ *,
109
+ schemas: list[str],
110
+ start_index: int,
111
+ items_per_page: int,
112
+ total_results: int,
113
+ ) -> None:
114
+ """Create a new SCIM page.
115
+
116
+ Args:
117
+ iterable: The items for the list.
118
+ schemas: The SCIM schemas for the response.
119
+ start_index: 1-based index of the first item.
120
+ items_per_page: Number of items per page.
121
+ total_results: Total number of items.
122
+ """
123
+ super().__init__(iterable)
124
+ self.schemas = schemas
125
+ self.start_index = start_index
126
+ self.items_per_page = items_per_page
127
+ self.total_results = total_results
128
+
129
+
130
+ @beartype
131
+ @dataclass(frozen=True, kw_only=True)
132
+ class CandidateDetail:
133
+ """A custom candidate detail field."""
134
+
135
+ field_name: str
136
+ title: str
137
+ value: str
138
+
139
+ @classmethod
140
+ def from_dict(cls, data: CandidateDetailDict) -> Self:
141
+ """Create from an API response dictionary.
142
+
143
+ Args:
144
+ data: The dictionary to convert.
145
+
146
+ Returns:
147
+ A new instance.
148
+ """
149
+ return cls(
150
+ field_name=data["field_name"],
151
+ title=data["title"],
152
+ value=data["value"],
153
+ )
154
+
155
+
156
+ @beartype
157
+ @dataclass(frozen=True, kw_only=True)
158
+ class Interview:
159
+ """A HackerRank interview."""
160
+
161
+ id: str
162
+ status: str
163
+ url: str
164
+ title: str | None = None
165
+ feedback: str | None = None
166
+ thumbs_up: int | None = None
167
+ notes: str | None = None
168
+ resume_url: str | None = None
169
+ interviewers: list[str] | None = None
170
+ result_url: str | None = None
171
+ candidate: dict[str, JSONValue] | None = None
172
+ metadata: dict[str, JSONValue] | None = None
173
+ report_url: str | None = None
174
+ ended_at: str | None = None
175
+ interview_template_id: int | None = None
176
+ created_at: str | None = None
177
+ updated_at: str | None = None
178
+ user: int | None = None
179
+ send_email: bool | None = None
180
+
181
+ @classmethod
182
+ def from_dict(cls, data: InterviewDict) -> Self:
183
+ """Create from an API response dictionary.
184
+
185
+ Args:
186
+ data: The dictionary to convert.
187
+
188
+ Returns:
189
+ A new instance.
190
+ """
191
+ return cls(
192
+ id=data["id"],
193
+ status=data["status"],
194
+ url=data["url"],
195
+ title=data.get("title"),
196
+ feedback=data.get("feedback"),
197
+ thumbs_up=data.get("thumbs_up"),
198
+ notes=data.get("notes"),
199
+ resume_url=data.get("resume_url"),
200
+ interviewers=data.get("interviewers"),
201
+ result_url=data.get("result_url"),
202
+ candidate=data.get("candidate"),
203
+ metadata=data.get("metadata"),
204
+ report_url=data.get("report_url"),
205
+ ended_at=data.get("ended_at"),
206
+ interview_template_id=data.get(
207
+ "interview_template_id",
208
+ ),
209
+ created_at=data.get("created_at"),
210
+ updated_at=data.get("updated_at"),
211
+ user=data.get("user"),
212
+ send_email=data.get("send_email"),
213
+ )
214
+
215
+
216
+ @beartype
217
+ @dataclass(frozen=True, kw_only=True)
218
+ class InterviewTranscriptMessage:
219
+ """A single message in an interview transcript."""
220
+
221
+ author: str
222
+ timestamp: int
223
+ text: str
224
+ candidate: bool
225
+ message_id: str
226
+ email: str | None = None
227
+
228
+ @classmethod
229
+ def from_dict(
230
+ cls,
231
+ data: InterviewTranscriptMessageDict,
232
+ ) -> Self:
233
+ """Create from an API response dictionary.
234
+
235
+ Args:
236
+ data: The dictionary to convert.
237
+
238
+ Returns:
239
+ A new instance.
240
+ """
241
+ return cls(
242
+ author=data["author"],
243
+ timestamp=data["timestamp"],
244
+ text=data["text"],
245
+ candidate=data["candidate"],
246
+ message_id=data["messageId"],
247
+ email=data.get("email"),
248
+ )
249
+
250
+
251
+ @beartype
252
+ @dataclass(frozen=True, kw_only=True)
253
+ class InterviewTranscript:
254
+ """The transcript for an interview."""
255
+
256
+ messages: list[InterviewTranscriptMessage]
257
+
258
+ @classmethod
259
+ def from_dict(cls, data: InterviewTranscriptDict) -> Self:
260
+ """Create from an API response dictionary.
261
+
262
+ Args:
263
+ data: The dictionary to convert.
264
+
265
+ Returns:
266
+ A new instance.
267
+ """
268
+ return cls(
269
+ messages=[
270
+ InterviewTranscriptMessage.from_dict(data=m)
271
+ for m in data["messages"]
272
+ ],
273
+ )
274
+
275
+
276
+ @beartype
277
+ @dataclass(frozen=True, kw_only=True)
278
+ class InterviewTemplate:
279
+ """A HackerRank interview template."""
280
+
281
+ id: int
282
+ name: str
283
+ created_at: str | None = None
284
+ status: int | None = None
285
+ user: int | None = None
286
+ roles: list[str] | None = None
287
+ team_share: int | None = None
288
+ questions: list[str] | None = None
289
+ scorecard: int | None = None
290
+ import_template: bool | None = None
291
+ editor_access: bool | None = None
292
+
293
+ @classmethod
294
+ def from_dict(cls, data: InterviewTemplateDict) -> Self:
295
+ """Create from an API response dictionary.
296
+
297
+ Args:
298
+ data: The dictionary to convert.
299
+
300
+ Returns:
301
+ A new instance.
302
+ """
303
+ return cls(
304
+ id=data["id"],
305
+ name=data["name"],
306
+ created_at=data.get("created_at"),
307
+ status=data.get("status"),
308
+ user=data.get("user"),
309
+ roles=data.get("roles"),
310
+ team_share=data.get("team_share"),
311
+ questions=data.get("questions"),
312
+ scorecard=data.get("scorecard"),
313
+ import_template=data.get("import_template"),
314
+ editor_access=data.get("editor_access"),
315
+ )
316
+
317
+
318
+ @beartype
319
+ @dataclass(frozen=True, kw_only=True)
320
+ class EnvironmentRuntime:
321
+ """A runtime component for a project-question environment."""
322
+
323
+ name: str
324
+ version: str
325
+
326
+ @classmethod
327
+ def from_dict(cls, data: EnvironmentRuntimeDict) -> Self:
328
+ """Create from an API response dictionary.
329
+
330
+ Args:
331
+ data: The dictionary to convert.
332
+
333
+ Returns:
334
+ A new instance.
335
+ """
336
+ return cls(name=data["name"], version=data["version"])
337
+
338
+
339
+ @beartype
340
+ @dataclass(frozen=True, kw_only=True)
341
+ class Environment:
342
+ """A project-question environment."""
343
+
344
+ id: int
345
+ name: str
346
+ tags: list[str]
347
+ runtime: list[EnvironmentRuntime]
348
+ active: bool | None = None
349
+ sample_project_url: str | None = None
350
+
351
+ @classmethod
352
+ def from_dict(cls, data: EnvironmentDict) -> Self:
353
+ """Create from an API response dictionary.
354
+
355
+ Args:
356
+ data: The dictionary to convert.
357
+
358
+ Returns:
359
+ A new instance.
360
+ """
361
+ return cls(
362
+ id=data["id"],
363
+ name=data["name"],
364
+ tags=data["tags"],
365
+ runtime=[
366
+ EnvironmentRuntime.from_dict(data=item)
367
+ for item in data["runtime"]
368
+ ],
369
+ active=data.get("active"),
370
+ sample_project_url=data.get("sample_project_url"),
371
+ )
372
+
373
+
374
+ @beartype
375
+ @dataclass(frozen=True, kw_only=True)
376
+ class Question:
377
+ """A HackerRank question."""
378
+
379
+ id: str
380
+ type: str
381
+ name: str
382
+ unique_id: str | None = None
383
+ owner: str | None = None
384
+ created_at: str | None = None
385
+ status: str | None = None
386
+ internal_notes: str | None = None
387
+ languages: list[str] | None = None
388
+ problem_statement: str | None = None
389
+ recommended_duration: int | None = None
390
+ tags: list[str] | None = None
391
+ max_score: float | None = None
392
+ options: list[str] | None = None
393
+ answer: int | list[int] | None = None
394
+ test_case_count: int | None = None
395
+ role_type: str | None = None
396
+ environment_id: int | None = None
397
+ file_url: str | None = None
398
+ file_path: str | None = None
399
+ has_valid_stacks: bool | None = None
400
+ fullstack_project_details: dict[str, JSONValue] | None = None
401
+
402
+ @classmethod
403
+ def from_dict(cls, data: QuestionDict) -> Self:
404
+ """Create from an API response dictionary.
405
+
406
+ Args:
407
+ data: The dictionary to convert.
408
+
409
+ Returns:
410
+ A new instance.
411
+ """
412
+ return cls(
413
+ id=data["id"],
414
+ type=data["type"],
415
+ name=data["name"],
416
+ unique_id=data.get("unique_id"),
417
+ owner=data.get("owner"),
418
+ created_at=data.get("created_at"),
419
+ status=data.get("status"),
420
+ internal_notes=data.get("internal_notes"),
421
+ languages=data.get("languages"),
422
+ problem_statement=data.get("problem_statement"),
423
+ recommended_duration=data.get(
424
+ "recommended_duration",
425
+ ),
426
+ tags=data.get("tags"),
427
+ max_score=data.get("max_score"),
428
+ options=data.get("options"),
429
+ answer=data.get("answer"),
430
+ test_case_count=data.get("test_case_count"),
431
+ role_type=data.get("role_type"),
432
+ environment_id=data.get("environment_id"),
433
+ file_url=data.get("file_url"),
434
+ file_path=data.get("file_path"),
435
+ has_valid_stacks=data.get("has_valid_stacks"),
436
+ fullstack_project_details=data.get(
437
+ "fullstack_project_details",
438
+ ),
439
+ )
440
+
441
+
442
+ @beartype
443
+ @dataclass(frozen=True, kw_only=True)
444
+ class Test:
445
+ """A HackerRank test."""
446
+
447
+ __test__: ClassVar[bool] = False
448
+
449
+ id: str
450
+ name: str
451
+ unique_id: str | None = None
452
+ starttime: str | None = None
453
+ endtime: str | None = None
454
+ duration: int | None = None
455
+ owner: str | None = None
456
+ instructions: str | None = None
457
+ starred: bool | None = None
458
+ created_at: str | None = None
459
+ state: str | None = None
460
+ locked: bool | None = None
461
+ draft: bool | None = None
462
+ languages: list[str] | None = None
463
+ candidate_details: list[str] | None = None
464
+ custom_acknowledge_text: str | None = None
465
+ cutoff_score: int | None = None
466
+ master_password: str | None = None
467
+ hide_compile_test: bool | None = None
468
+ tags: list[str] | None = None
469
+ role_ids: list[str] | None = None
470
+ experience: list[str] | None = None
471
+ questions: list[str] | None = None
472
+ sections: list[dict[str, JSONValue]] | None = None
473
+ mcq_incorrect_score: int | None = None
474
+ mcq_correct_score: int | None = None
475
+ locked_by: str | None = None
476
+ short_login_url: str | None = None
477
+ public_login_url: str | None = None
478
+ shuffle_questions: bool | None = None
479
+ test_admins: list[str] | None = None
480
+ hide_template: bool | None = None
481
+ enable_acknowledgement: bool | None = None
482
+ enable_proctoring: bool | None = None
483
+ enable_advanced_proctoring: bool | None = None
484
+ enable_secure_assessment_mode: bool | None = None
485
+ enable_ml_plagiarism_analysis: bool | None = None
486
+ enable_photo_identification: bool | None = None
487
+ ide_config: str | None = None
488
+
489
+ @classmethod
490
+ def from_dict(cls, data: TestDict) -> Self:
491
+ """Create from an API response dictionary.
492
+
493
+ Args:
494
+ data: The dictionary to convert.
495
+
496
+ Returns:
497
+ A new instance.
498
+ """
499
+ return cls(
500
+ id=data["id"],
501
+ name=data["name"],
502
+ unique_id=data.get("unique_id"),
503
+ starttime=data.get("starttime"),
504
+ endtime=data.get("endtime"),
505
+ duration=data.get("duration"),
506
+ owner=data.get("owner"),
507
+ instructions=data.get("instructions"),
508
+ starred=data.get("starred"),
509
+ created_at=data.get("created_at"),
510
+ state=data.get("state"),
511
+ locked=data.get("locked"),
512
+ draft=data.get("draft"),
513
+ languages=data.get("languages"),
514
+ candidate_details=data.get("candidate_details"),
515
+ custom_acknowledge_text=data.get(
516
+ "custom_acknowledge_text",
517
+ ),
518
+ cutoff_score=data.get("cutoff_score"),
519
+ master_password=data.get("master_password"),
520
+ hide_compile_test=data.get("hide_compile_test"),
521
+ tags=data.get("tags"),
522
+ role_ids=data.get("role_ids"),
523
+ experience=data.get("experience"),
524
+ questions=data.get("questions"),
525
+ sections=data.get("sections"),
526
+ mcq_incorrect_score=data.get("mcq_incorrect_score"),
527
+ mcq_correct_score=data.get("mcq_correct_score"),
528
+ locked_by=data.get("locked_by"),
529
+ short_login_url=data.get("short_login_url"),
530
+ public_login_url=data.get("public_login_url"),
531
+ shuffle_questions=data.get("shuffle_questions"),
532
+ test_admins=data.get("test_admins"),
533
+ hide_template=data.get("hide_template"),
534
+ enable_acknowledgement=data.get(
535
+ "enable_acknowledgement",
536
+ ),
537
+ enable_proctoring=data.get("enable_proctoring"),
538
+ enable_advanced_proctoring=data.get(
539
+ "enable_advanced_proctoring",
540
+ ),
541
+ enable_secure_assessment_mode=data.get(
542
+ "enable_secure_assessment_mode",
543
+ ),
544
+ enable_ml_plagiarism_analysis=data.get(
545
+ "enable_ml_plagiarism_analysis",
546
+ ),
547
+ enable_photo_identification=data.get(
548
+ "enable_photo_identification",
549
+ ),
550
+ ide_config=data.get("ide_config"),
551
+ )
552
+
553
+
554
+ @beartype
555
+ @dataclass(frozen=True, kw_only=True)
556
+ class TestCandidate:
557
+ """A candidate associated with a test."""
558
+
559
+ __test__: ClassVar[bool] = False
560
+
561
+ id: str
562
+ email: str
563
+ full_name: str | None = None
564
+ score: float | None = None
565
+ test: str | None = None
566
+ user: str | None = None
567
+ attempt_starttime: str | None = None
568
+ attempt_endtime: str | None = None
569
+ attempt_events: list[str] | None = None
570
+ status: int | None = None
571
+ ats_state: int | None = None
572
+ integrity_status: str | None = None
573
+ integrity_summary: str | None = None
574
+ invite_email_done: bool | None = None
575
+ invite_valid: bool | None = None
576
+ invited_on: str | None = None
577
+ invite_valid_from: str | None = None
578
+ invite_valid_to: str | None = None
579
+ invite_link: str | None = None
580
+ invite_metadata: dict[str, JSONValue] | None = None
581
+ evaluator_email: str | None = None
582
+ test_finish_url: str | None = None
583
+ test_result_url: str | None = None
584
+ accept_result_updates: bool | None = None
585
+ tags: list[str] | None = None
586
+ report_url: str | None = None
587
+ authenticated_report_url: str | None = None
588
+ pdf_url: str | None = None
589
+ scores_tags_split: dict[str, JSONValue] | None = None
590
+ scores_skills_split: dict[str, JSONValue] | None = None
591
+ added_time: int | None = None
592
+ unclaimed_added_time: int | None = None
593
+ comments: dict[str, JSONValue] | None = None
594
+ performance_summary: str | None = None
595
+ ip_address: str | None = None
596
+ questions: dict[str, JSONValue] | None = None
597
+ plagiarism: dict[str, JSONValue] | None = None
598
+ plagiarism_status: bool | None = None
599
+ max_code_similarity: dict[str, JSONValue] | None = None
600
+ feedback: str | None = None
601
+ percentage_score: float | None = None
602
+ candidate_details: list[CandidateDetail] | None = None
603
+ out_of_window_events: int | None = None
604
+ out_of_window_duration: float | None = None
605
+ editor_paste_count: int | None = None
606
+ proctor_images: list[str] | None = None
607
+
608
+ @classmethod
609
+ def from_dict(cls, data: TestCandidateDict) -> Self:
610
+ """Create from an API response dictionary.
611
+
612
+ Args:
613
+ data: The dictionary to convert.
614
+
615
+ Returns:
616
+ A new instance.
617
+ """
618
+ raw_details = data.get("candidate_details")
619
+ return cls(
620
+ id=data["id"],
621
+ email=data["email"],
622
+ full_name=data.get("full_name"),
623
+ score=data.get("score"),
624
+ test=data.get("test"),
625
+ user=data.get("user"),
626
+ attempt_starttime=data.get("attempt_starttime"),
627
+ attempt_endtime=data.get("attempt_endtime"),
628
+ attempt_events=data.get("attempt_events"),
629
+ status=data.get("status"),
630
+ ats_state=data.get("ats_state"),
631
+ integrity_status=data.get("integrity_status"),
632
+ integrity_summary=data.get("integrity_summary"),
633
+ invite_email_done=data.get("invite_email_done"),
634
+ invite_valid=data.get("invite_valid"),
635
+ invited_on=data.get("invited_on"),
636
+ invite_valid_from=data.get("invite_valid_from"),
637
+ invite_valid_to=data.get("invite_valid_to"),
638
+ invite_link=data.get("invite_link"),
639
+ invite_metadata=data.get("invite_metadata"),
640
+ evaluator_email=data.get("evaluator_email"),
641
+ test_finish_url=data.get("test_finish_url"),
642
+ test_result_url=data.get("test_result_url"),
643
+ accept_result_updates=data.get(
644
+ "accept_result_updates",
645
+ ),
646
+ tags=data.get("tags"),
647
+ report_url=data.get("report_url"),
648
+ authenticated_report_url=data.get(
649
+ "authenticated_report_url",
650
+ ),
651
+ pdf_url=data.get("pdf_url"),
652
+ scores_tags_split=data.get("scores_tags_split"),
653
+ scores_skills_split=data.get(
654
+ "scores_skills_split",
655
+ ),
656
+ added_time=data.get("added_time"),
657
+ unclaimed_added_time=data.get(
658
+ "unclaimed_added_time",
659
+ ),
660
+ comments=data.get("comments"),
661
+ performance_summary=data.get("performance_summary"),
662
+ ip_address=data.get("ip_address"),
663
+ questions=data.get("questions"),
664
+ plagiarism=data.get("plagiarism"),
665
+ plagiarism_status=data.get("plagiarism_status"),
666
+ max_code_similarity=data.get(
667
+ "max_code_similarity",
668
+ ),
669
+ feedback=data.get("feedback"),
670
+ percentage_score=data.get("percentage_score"),
671
+ candidate_details=[
672
+ CandidateDetail.from_dict(data=item) for item in raw_details
673
+ ]
674
+ if raw_details is not None
675
+ else None,
676
+ out_of_window_events=data.get(
677
+ "out_of_window_events",
678
+ ),
679
+ out_of_window_duration=data.get(
680
+ "out_of_window_duration",
681
+ ),
682
+ editor_paste_count=data.get("editor_paste_count"),
683
+ proctor_images=data.get("proctor_images"),
684
+ )
685
+
686
+
687
+ @beartype
688
+ @dataclass(frozen=True, kw_only=True)
689
+ class Inviter:
690
+ """A user permitted to invite candidates to a test."""
691
+
692
+ id: str
693
+ email: str
694
+ firstname: str | None = None
695
+ lastname: str | None = None
696
+ role: str | None = None
697
+ status: str | None = None
698
+ phone: str | None = None
699
+ timezone: str | None = None
700
+ questions_permission: int | None = None
701
+ tests_permission: int | None = None
702
+ interviews_permission: int | None = None
703
+ candidates_permission: int | None = None
704
+ teams: list[str] | None = None
705
+ candidates_invited: int | None = None
706
+ activated: bool | None = None
707
+
708
+ @classmethod
709
+ def from_dict(cls, data: InviterDict) -> Self:
710
+ """Create from an API response dictionary.
711
+
712
+ Args:
713
+ data: The dictionary to convert.
714
+
715
+ Returns:
716
+ A new instance.
717
+ """
718
+ return cls(
719
+ id=data["id"],
720
+ email=data["email"],
721
+ firstname=data.get("firstname"),
722
+ lastname=data.get("lastname"),
723
+ role=data.get("role"),
724
+ status=data.get("status"),
725
+ phone=data.get("phone"),
726
+ timezone=data.get("timezone"),
727
+ questions_permission=data.get(
728
+ "questions_permission",
729
+ ),
730
+ tests_permission=data.get("tests_permission"),
731
+ interviews_permission=data.get(
732
+ "interviews_permission",
733
+ ),
734
+ candidates_permission=data.get(
735
+ "candidates_permission",
736
+ ),
737
+ teams=data.get("teams"),
738
+ candidates_invited=data.get("candidates_invited"),
739
+ activated=data.get("activated"),
740
+ )
741
+
742
+
743
+ @beartype
744
+ @dataclass(frozen=True, kw_only=True)
745
+ class User:
746
+ """A HackerRank user."""
747
+
748
+ id: str
749
+ email: str
750
+ firstname: str | None = None
751
+ lastname: str | None = None
752
+ country: str | None = None
753
+ role: str | None = None
754
+ status: str | None = None
755
+ phone: str | None = None
756
+ timezone: str | None = None
757
+ questions_permission: int | None = None
758
+ tests_permission: int | None = None
759
+ interviews_permission: int | None = None
760
+ candidates_permission: int | None = None
761
+ shared_questions_permission: int | None = None
762
+ shared_tests_permission: int | None = None
763
+ shared_interviews_permission: int | None = None
764
+ shared_candidates_permission: int | None = None
765
+ company_admin: bool | None = None
766
+ team_admin: bool | None = None
767
+ teams: list[str] | None = None
768
+ activated: bool | None = None
769
+ last_activity_time: str | None = None
770
+
771
+ @classmethod
772
+ def from_dict(cls, data: UserDict) -> Self:
773
+ """Create from an API response dictionary.
774
+
775
+ Args:
776
+ data: The dictionary to convert.
777
+
778
+ Returns:
779
+ A new instance.
780
+ """
781
+ return cls(
782
+ id=data["id"],
783
+ email=data["email"],
784
+ firstname=data.get("firstname"),
785
+ lastname=data.get("lastname"),
786
+ country=data.get("country"),
787
+ role=data.get("role"),
788
+ status=data.get("status"),
789
+ phone=data.get("phone"),
790
+ timezone=data.get("timezone"),
791
+ questions_permission=data.get(
792
+ "questions_permission",
793
+ ),
794
+ tests_permission=data.get("tests_permission"),
795
+ interviews_permission=data.get(
796
+ "interviews_permission",
797
+ ),
798
+ candidates_permission=data.get(
799
+ "candidates_permission",
800
+ ),
801
+ shared_questions_permission=data.get(
802
+ "shared_questions_permission",
803
+ ),
804
+ shared_tests_permission=data.get(
805
+ "shared_tests_permission",
806
+ ),
807
+ shared_interviews_permission=data.get(
808
+ "shared_interviews_permission",
809
+ ),
810
+ shared_candidates_permission=data.get(
811
+ "shared_candidates_permission",
812
+ ),
813
+ company_admin=data.get("company_admin"),
814
+ team_admin=data.get("team_admin"),
815
+ teams=data.get("teams"),
816
+ activated=data.get("activated"),
817
+ last_activity_time=data.get("last_activity_time"),
818
+ )
819
+
820
+
821
+ @beartype
822
+ @dataclass(frozen=True, kw_only=True)
823
+ class Team:
824
+ """A HackerRank team."""
825
+
826
+ id: str
827
+ name: str
828
+ owner: str | None = None
829
+ created_at: str | None = None
830
+ recruiter_count: int | None = None
831
+ developer_count: int | None = None
832
+ recruiter_cap: int | None = None
833
+ developer_cap: int | None = None
834
+ invite_as: str | None = None
835
+ locations: list[str] | None = None
836
+ departments: list[str] | None = None
837
+
838
+ @classmethod
839
+ def from_dict(cls, data: TeamDict) -> Self:
840
+ """Create from an API response dictionary.
841
+
842
+ Args:
843
+ data: The dictionary to convert.
844
+
845
+ Returns:
846
+ A new instance.
847
+ """
848
+ return cls(
849
+ id=data["id"],
850
+ name=data["name"],
851
+ owner=data.get("owner"),
852
+ created_at=data.get("created_at"),
853
+ recruiter_count=data.get("recruiter_count"),
854
+ developer_count=data.get("developer_count"),
855
+ recruiter_cap=data.get("recruiter_cap"),
856
+ developer_cap=data.get("developer_cap"),
857
+ invite_as=data.get("invite_as"),
858
+ locations=data.get("locations"),
859
+ departments=data.get("departments"),
860
+ )
861
+
862
+
863
+ @beartype
864
+ @dataclass(frozen=True, kw_only=True)
865
+ class UserTeamMembership:
866
+ """A membership of a user in a team."""
867
+
868
+ team: str
869
+ user: str
870
+
871
+ @classmethod
872
+ def from_dict(cls, data: UserTeamMembershipDict) -> Self:
873
+ """Create from an API response dictionary.
874
+
875
+ Args:
876
+ data: The dictionary to convert.
877
+
878
+ Returns:
879
+ A new instance.
880
+ """
881
+ return cls(
882
+ team=data["team"],
883
+ user=data["user"],
884
+ )
885
+
886
+
887
+ @beartype
888
+ @dataclass(frozen=True, kw_only=True)
889
+ class Template:
890
+ """An invite-email template."""
891
+
892
+ id: str
893
+ name: str
894
+ subject: str | None = None
895
+ content: str | None = None
896
+ default: bool | None = None
897
+ created_at: str | None = None
898
+ updated_at: str | None = None
899
+ user: str | None = None
900
+
901
+ @classmethod
902
+ def from_dict(cls, data: TemplateDict) -> Self:
903
+ """Create from an API response dictionary.
904
+
905
+ Args:
906
+ data: The dictionary to convert.
907
+
908
+ Returns:
909
+ A new instance.
910
+ """
911
+ return cls(
912
+ id=data["id"],
913
+ name=data["name"],
914
+ subject=data.get("subject"),
915
+ content=data.get("content"),
916
+ default=data.get("default"),
917
+ created_at=data.get("created_at"),
918
+ updated_at=data.get("updated_at"),
919
+ user=data.get("user"),
920
+ )
921
+
922
+
923
+ @beartype
924
+ @dataclass(frozen=True, kw_only=True)
925
+ class AuditLog:
926
+ """An audit log entry."""
927
+
928
+ source_id: int
929
+ source_type: str
930
+ action: str
931
+ user: str | None = None
932
+ modified_fields: list[str] | None = None
933
+ modified_values: dict[str, JSONValue] | None = None
934
+ ip_address: str | None = None
935
+ created_at: str | None = None
936
+
937
+ @classmethod
938
+ def from_dict(cls, data: AuditLogDict) -> Self:
939
+ """Create from an API response dictionary.
940
+
941
+ Args:
942
+ data: The dictionary to convert.
943
+
944
+ Returns:
945
+ A new instance.
946
+ """
947
+ return cls(
948
+ source_id=data["source_id"],
949
+ source_type=data["source_type"],
950
+ action=data["action"],
951
+ user=data.get("user"),
952
+ modified_fields=data.get("modified_fields"),
953
+ modified_values=data.get("modified_values"),
954
+ ip_address=data.get("ip_address"),
955
+ created_at=data.get("created_at"),
956
+ )
957
+
958
+
959
+ @beartype
960
+ @dataclass(frozen=True, kw_only=True)
961
+ class ATSCodePair:
962
+ """Result of an ATS Codepair invite."""
963
+
964
+ title: str | None = None
965
+ requisition_id: str | None = None
966
+ candidate_id: str | None = None
967
+ candidate: dict[str, JSONValue] | None = None
968
+ send_email: bool | None = None
969
+ interview_metadata: dict[str, JSONValue] | None = None
970
+
971
+ @classmethod
972
+ def from_dict(cls, data: ATSCodePairDict) -> Self:
973
+ """Create from an API response dictionary.
974
+
975
+ Args:
976
+ data: The dictionary to convert.
977
+
978
+ Returns:
979
+ A new instance.
980
+ """
981
+ return cls(
982
+ title=data.get("title"),
983
+ requisition_id=data.get("requisition_id"),
984
+ candidate_id=data.get("candidate_id"),
985
+ candidate=data.get("candidate"),
986
+ send_email=data.get("send_email"),
987
+ interview_metadata=data.get("interview_metadata"),
988
+ )
989
+
990
+
991
+ @beartype
992
+ @dataclass(frozen=True, kw_only=True)
993
+ class ATSCodeScreen:
994
+ """Result of an ATS CodeScreen invite."""
995
+
996
+ test_id: str | None = None
997
+ requisition_id: str | None = None
998
+ candidate_id: str | None = None
999
+ email: str | None = None
1000
+ test_result_url: str | None = None
1001
+ accept_result_updates: bool | None = None
1002
+
1003
+ @classmethod
1004
+ def from_dict(cls, data: ATSCodeScreenDict) -> Self:
1005
+ """Create from an API response dictionary.
1006
+
1007
+ Args:
1008
+ data: The dictionary to convert.
1009
+
1010
+ Returns:
1011
+ A new instance.
1012
+ """
1013
+ return cls(
1014
+ test_id=data.get("test_id"),
1015
+ requisition_id=data.get("requisition_id"),
1016
+ candidate_id=data.get("candidate_id"),
1017
+ email=data.get("email"),
1018
+ test_result_url=data.get("test_result_url"),
1019
+ accept_result_updates=data.get(
1020
+ "accept_result_updates",
1021
+ ),
1022
+ )
1023
+
1024
+
1025
+ @beartype
1026
+ @dataclass(frozen=True, kw_only=True)
1027
+ class SCIMUser:
1028
+ """A SCIM v2 user."""
1029
+
1030
+ id: str
1031
+ user_name: str
1032
+ name: dict[str, JSONValue] | None = None
1033
+ active: bool | None = None
1034
+ role: str | None = None
1035
+ team_admin: bool | None = None
1036
+ company_admin: bool | None = None
1037
+ emails: list[dict[str, JSONValue]] | None = None
1038
+ schemas: list[str] | None = None
1039
+
1040
+ @classmethod
1041
+ def from_dict(cls, data: SCIMUserDict) -> Self:
1042
+ """Create from an API response dictionary.
1043
+
1044
+ Args:
1045
+ data: The dictionary to convert.
1046
+
1047
+ Returns:
1048
+ A new instance.
1049
+ """
1050
+ return cls(
1051
+ id=data["id"],
1052
+ user_name=data["userName"],
1053
+ name=data.get("name"),
1054
+ active=data.get("active"),
1055
+ role=data.get("role"),
1056
+ team_admin=data.get("team_admin"),
1057
+ company_admin=data.get("company_admin"),
1058
+ emails=data.get("emails"),
1059
+ schemas=data.get("schemas"),
1060
+ )
1061
+
1062
+
1063
+ @beartype
1064
+ @dataclass(frozen=True, kw_only=True)
1065
+ class SCIMTeam:
1066
+ """A SCIM v2 team."""
1067
+
1068
+ id: str
1069
+ display_name: str | None = None
1070
+ schemas: list[str] | None = None
1071
+
1072
+ @classmethod
1073
+ def from_dict(cls, data: SCIMTeamDict) -> Self:
1074
+ """Create from an API response dictionary.
1075
+
1076
+ Args:
1077
+ data: The dictionary to convert.
1078
+
1079
+ Returns:
1080
+ A new instance.
1081
+ """
1082
+ return cls(
1083
+ id=data["id"],
1084
+ display_name=data.get("displayName") or data.get("diplayName"),
1085
+ schemas=data.get("schemas"),
1086
+ )