mphapi 0.5.0__py3-none-any.whl → 1.11.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.
mphapi/credentials.py ADDED
@@ -0,0 +1,256 @@
1
+ import getpass
2
+ import json
3
+ import os
4
+ import time
5
+ from pathlib import Path
6
+
7
+ import requests
8
+ from pydantic import BaseModel, Field, RootModel
9
+
10
+ from .fields import camel_case_model_config
11
+
12
+
13
+ class RawCredentials(BaseModel):
14
+ model_config = camel_case_model_config
15
+
16
+ email: str
17
+ id_token: str
18
+ refresh_token: str
19
+ expires_at: float
20
+
21
+ def store(
22
+ self, api_key: str, referer: str, credentials_path: Path
23
+ ) -> "Credentials":
24
+ stored_credentials = StoredCredentials(
25
+ api_key=api_key,
26
+ referer=referer,
27
+ email=self.email,
28
+ id_token=self.id_token,
29
+ refresh_token=self.refresh_token,
30
+ expires_at=self.expires_at,
31
+ )
32
+
33
+ os.makedirs(os.path.dirname(credentials_path), exist_ok=True)
34
+
35
+ with credentials_path.open("w+") as f:
36
+ json.dump(
37
+ stored_credentials.model_dump(
38
+ mode="json", by_alias=True, exclude_none=True
39
+ ),
40
+ f,
41
+ )
42
+
43
+ return Credentials(
44
+ referer=referer,
45
+ credentials_path=credentials_path,
46
+ api_key=api_key,
47
+ email=stored_credentials.email,
48
+ id_token=stored_credentials.id_token,
49
+ refresh_token=stored_credentials.refresh_token,
50
+ expires_at=stored_credentials.expires_at,
51
+ )
52
+
53
+
54
+ class StoredCredentials(RawCredentials):
55
+ api_key: str
56
+ referer: str
57
+
58
+
59
+ class Credentials(StoredCredentials):
60
+ credentials_path: Path = Field(..., exclude=True)
61
+
62
+ def refresh_if_needed(self) -> "Credentials | None":
63
+ # # There's more than 5 minutes until the credentials need refreshing. Don't bother.
64
+ if self.expires_at > time.time() + 5 * 60:
65
+ return None
66
+
67
+ new_credentials = refresh_token(self.api_key, self.referer, self.refresh_token)
68
+ return new_credentials.store(self.api_key, self.referer, self.credentials_path)
69
+
70
+
71
+ class GoogleException(Exception):
72
+ def __init__(self, message: str):
73
+ self.message = message
74
+
75
+
76
+ class GoogleError(BaseModel):
77
+ code: int
78
+ message: str
79
+ # errors omitted as unused
80
+
81
+
82
+ class GoogleResponseError(BaseModel):
83
+ model_config = camel_case_model_config
84
+
85
+ error: GoogleError
86
+
87
+ def to_exception(self) -> GoogleException:
88
+ return GoogleException(self.error.message)
89
+
90
+
91
+ class GoogleResponse[Result: BaseModel](RootModel[Result | GoogleResponseError]):
92
+ pass
93
+
94
+
95
+ class SignInResult(BaseModel):
96
+ """
97
+ The result of signing in.
98
+ See https://docs.cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/signInWithPassword
99
+ """
100
+
101
+ model_config = camel_case_model_config
102
+
103
+ email: str
104
+ """The email of the authenticated user. Always present in the response."""
105
+
106
+ id_token: str
107
+ """An Identity Platform ID token for the authenticated user."""
108
+
109
+ refresh_token: str
110
+ """An Identity Platform refresh token for the authenticated user."""
111
+
112
+ expires_in: int
113
+ """The number of seconds until the Identity Platform ID token expires."""
114
+
115
+
116
+ class RefreshTokenResult(BaseModel):
117
+ """
118
+ The result of refreshing a refresh_token.
119
+ See https://cloud.google.com/identity-platform/docs/use-rest-api#section-refresh-token
120
+ """
121
+
122
+ # NOT camelCase
123
+
124
+ expires_in: int
125
+ """The number of seconds in which the ID token expires."""
126
+
127
+ token_type: str
128
+ """The type of the refresh token, always "Bearer"."""
129
+
130
+ refresh_token: str
131
+ """The Identity Platform refresh token provided in the request or a new refresh token."""
132
+
133
+ id_token: str
134
+ """An Identity Platform ID token."""
135
+
136
+ user_id: str
137
+ """The uid corresponding to the provided ID token."""
138
+
139
+ project_id: str
140
+ """Your Google Cloud project ID."""
141
+
142
+
143
+ def get_credentials(api_key: str, referer: str) -> Credentials:
144
+ credentials_path = get_credentials_path()
145
+
146
+ credentials = get_stored_credentials(credentials_path)
147
+ if credentials is not None:
148
+ try:
149
+ credentials.refresh_if_needed()
150
+ except GoogleException as e:
151
+ # `INVALID_ID_TOKEN` means the user must login again.
152
+ if e.message != "INVALID_ID_TOKEN":
153
+ raise e
154
+
155
+ return credentials
156
+
157
+ email = input("Email: ")
158
+ login_credentials: RawCredentials | None = None
159
+
160
+ while True:
161
+ try:
162
+ password = getpass.getpass("Password: ")
163
+ login_credentials = sign_in(api_key, email, password)
164
+ break
165
+ except GoogleException as e:
166
+ if e.message == "INVALID_PASSWORD":
167
+ continue
168
+
169
+ raise e
170
+
171
+ return login_credentials.store(api_key, referer, credentials_path)
172
+
173
+
174
+ def get_credentials_path() -> Path:
175
+ home = Path.home()
176
+ return home.joinpath(".mph", "credentials.json")
177
+
178
+
179
+ def get_stored_credentials(credentials_path: Path) -> Credentials | None:
180
+ try:
181
+ with credentials_path.open() as f:
182
+ credentials_json = json.load(f)
183
+ stored_credentials = StoredCredentials.model_validate(credentials_json)
184
+ except FileNotFoundError:
185
+ return None
186
+
187
+ credentials = Credentials(
188
+ email=stored_credentials.email,
189
+ id_token=stored_credentials.id_token,
190
+ refresh_token=stored_credentials.refresh_token,
191
+ expires_at=stored_credentials.expires_at,
192
+ api_key=stored_credentials.api_key,
193
+ referer=stored_credentials.referer,
194
+ credentials_path=credentials_path,
195
+ )
196
+
197
+ return credentials
198
+
199
+
200
+ def refresh_token(api_key: str, referer: str, refresh_token: str) -> RawCredentials:
201
+ response = requests.post(
202
+ "https://securetoken.googleapis.com/v1/token",
203
+ data={"grant_type": "refresh_token", "refresh_token": refresh_token},
204
+ params={
205
+ "key": api_key,
206
+ },
207
+ headers={
208
+ "Content-Type": "application/x-www-form-urlencoded",
209
+ "Referer": referer,
210
+ },
211
+ )
212
+
213
+ result = (
214
+ GoogleResponse[RefreshTokenResult].model_validate_json(response.content).root
215
+ )
216
+
217
+ if isinstance(result, GoogleResponseError):
218
+ raise result.to_exception()
219
+
220
+ return RawCredentials(
221
+ email="",
222
+ id_token=result.id_token,
223
+ refresh_token=result.refresh_token,
224
+ expires_at=time.time() + result.expires_in,
225
+ )
226
+
227
+
228
+ def sign_in(api_key: str, email: str, password: str) -> RawCredentials:
229
+ response = requests.post(
230
+ "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword",
231
+ json={
232
+ "email": email,
233
+ "password": password,
234
+ "returnSecureToken": "true",
235
+ },
236
+ params={
237
+ "key": api_key,
238
+ },
239
+ headers={"Referer": "http://myprice.health"},
240
+ )
241
+
242
+ result = GoogleResponse[SignInResult].model_validate_json(response.content).root
243
+
244
+ if isinstance(result, GoogleResponseError):
245
+ raise result.to_exception()
246
+
247
+ sign_in_result = result
248
+
249
+ credentials = RawCredentials(
250
+ email=sign_in_result.email,
251
+ id_token=sign_in_result.id_token,
252
+ refresh_token=sign_in_result.refresh_token,
253
+ expires_at=time.time() + sign_in_result.expires_in,
254
+ )
255
+
256
+ return credentials
mphapi/date.py CHANGED
@@ -1,39 +1,54 @@
1
- from datetime import datetime
2
- from typing import Any
1
+ from abc import abstractmethod
2
+ from datetime import datetime as dt
3
+ from datetime import tzinfo
4
+ from typing import Any, ClassVar, Self, SupportsIndex
3
5
 
4
6
  from pydantic import GetCoreSchemaHandler
5
7
  from pydantic_core import CoreSchema, core_schema
6
8
 
7
9
 
8
- class Date:
9
- """Date is a custom type for representing dates in the format YYYYMMDD"""
10
-
11
- year: int
12
- month: int
13
- day: int
10
+ class AbstractDateTime:
11
+ format: ClassVar[str]
12
+ secondary_formats: ClassVar[list[str]] = []
13
+ datetime: dt
14
14
 
15
- def __init__(self, year: int, month: int, day: int):
16
- self.year = year
17
- self.month = month
18
- self.day = day
15
+ @classmethod
16
+ @abstractmethod
17
+ def from_datetime(cls, datetime: dt) -> Self:
18
+ pass
19
19
 
20
20
  def __str__(self):
21
- return f"{self.year}{self.month:02d}{self.day:02d}"
21
+ return self.datetime.strftime(self.format)
22
22
 
23
23
  # Based off of this: https://docs.pydantic.dev/2.1/usage/types/custom/#handling-third-party-types
24
24
  @classmethod
25
25
  def __get_pydantic_core_schema__(
26
26
  cls, source_type: Any, handler: GetCoreSchemaHandler
27
27
  ) -> CoreSchema:
28
- def to_date(value: str) -> Date:
29
- time = datetime.strptime(value, "%Y%m%d")
28
+ def to_datetime_cls(value: str) -> Self:
29
+ datetime: dt | None = None
30
+
31
+ if len(cls.secondary_formats) == 0:
32
+ datetime = dt.strptime(value, cls.format)
33
+ else:
34
+ formats = [cls.format, *cls.secondary_formats]
35
+ for format in formats:
36
+ try:
37
+ datetime = dt.strptime(value, format)
38
+ except ValueError:
39
+ continue
40
+
41
+ if datetime is None:
42
+ raise ValueError(
43
+ f"Could not parse date {repr(value)} with format {repr(cls.format)} or {repr(cls.secondary_formats)}"
44
+ )
30
45
 
31
- return Date(time.year, time.month, time.day)
46
+ return cls.from_datetime(datetime)
32
47
 
33
48
  from_str = core_schema.chain_schema(
34
49
  [
35
50
  core_schema.str_schema(),
36
- core_schema.no_info_plain_validator_function(to_date),
51
+ core_schema.no_info_plain_validator_function(to_datetime_cls),
37
52
  ]
38
53
  )
39
54
 
@@ -41,7 +56,7 @@ class Date:
41
56
  json_schema=from_str,
42
57
  python_schema=core_schema.union_schema(
43
58
  [
44
- core_schema.is_instance_schema(Date),
59
+ core_schema.is_instance_schema(cls),
45
60
  from_str,
46
61
  ]
47
62
  ),
@@ -49,3 +64,62 @@ class Date:
49
64
  lambda date: str(date)
50
65
  ),
51
66
  )
67
+
68
+
69
+ class Date(AbstractDateTime):
70
+ """Date is a custom type for representing dates in the format YYYYMMDD"""
71
+
72
+ format = "%Y%m%d"
73
+
74
+ year: int
75
+ month: int
76
+ day: int
77
+
78
+ def __init__(self, year: int, month: int, day: int):
79
+ self.year = year
80
+ self.month = month
81
+ self.day = day
82
+ self.datetime = dt(year, month, day)
83
+
84
+ @classmethod
85
+ def from_datetime(cls, datetime: dt):
86
+ return cls(datetime.year, datetime.month, datetime.day)
87
+
88
+
89
+ class DateTime(AbstractDateTime):
90
+ """DateTime is a custom type for representing dates"""
91
+
92
+ # `%f` is padded to 6 characters but should be 9 overall.
93
+ format = "%Y-%m-%d %H:%M:%S.000%f%z"
94
+
95
+ # Same constructor as datetime has.
96
+ def __init__(
97
+ self,
98
+ year: SupportsIndex,
99
+ month: SupportsIndex,
100
+ day: SupportsIndex,
101
+ hour: SupportsIndex = 0,
102
+ minute: SupportsIndex = 0,
103
+ second: SupportsIndex = 0,
104
+ microsecond: SupportsIndex = 0,
105
+ tzinfo: tzinfo | None = None,
106
+ *,
107
+ fold: int = 0,
108
+ ):
109
+ self.datetime = dt(
110
+ year, month, day, hour, minute, second, microsecond, tzinfo, fold=fold
111
+ )
112
+
113
+ @classmethod
114
+ def from_datetime(cls, datetime: dt):
115
+ return cls(
116
+ datetime.year,
117
+ datetime.month,
118
+ datetime.day,
119
+ datetime.hour,
120
+ datetime.minute,
121
+ datetime.second,
122
+ datetime.microsecond,
123
+ datetime.tzinfo,
124
+ fold=datetime.fold,
125
+ )
mphapi/env.py ADDED
@@ -0,0 +1,24 @@
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ from dotenv import load_dotenv
5
+
6
+
7
+ def load_env():
8
+ path = Path(__file__)
9
+ root_dir = None
10
+ env_file = None
11
+ for parent in path.parents:
12
+ root_dir = parent
13
+ env_file = root_dir.joinpath(".env")
14
+ if env_file.exists():
15
+ break
16
+
17
+ if root_dir is None:
18
+ print("No .env file found", file=sys.stderr)
19
+ return
20
+
21
+ print(f"Using .env from {repr(str(root_dir))}.")
22
+
23
+ load_dotenv(root_dir.joinpath(".env"))
24
+ load_dotenv(root_dir.joinpath(".env.local"))
mphapi/pricing.py CHANGED
@@ -3,6 +3,8 @@ from typing import Annotated, Optional
3
3
 
4
4
  from pydantic import BaseModel, Field
5
5
 
6
+ from .client import PriceConfig
7
+ from .date import DateTime
6
8
  from .fields import camel_case_model_config, field_name
7
9
  from .response import ResponseError
8
10
 
@@ -72,6 +74,7 @@ class MedicareSource(str, Enum):
72
74
  ManualPricing = "Manual Pricing"
73
75
  SNF = "SNF PPS"
74
76
  Synthetic = "Synthetic Medicare"
77
+ DMEFS = "DMEFS"
75
78
 
76
79
 
77
80
  class InpatientPriceDetail(BaseModel):
@@ -143,6 +146,25 @@ class OutpatientPriceDetail(BaseModel):
143
146
  """Wage index used for geographic adjustment"""
144
147
 
145
148
 
149
+ class AllowedRepricingFormula(BaseModel):
150
+ """The formula used to calculate the allowed amount"""
151
+
152
+ medicare_percent: Optional[float] = None
153
+ """Percentage of the Medicare amount used to calculate the allowed amount"""
154
+
155
+ billed_percent: Optional[float] = None
156
+ """Percentage of the billed amount used to calculate the allowed amount"""
157
+
158
+ fee_schedule: Optional[float] = None
159
+ """Fee schedule amount used as the allowed amount"""
160
+
161
+ fixed_amount: Optional[float] = None
162
+ """Fixed amount used as the allowed amount"""
163
+
164
+ per_diem: Optional[float] = None
165
+ """Per diem rate used to calculate the allowed amount"""
166
+
167
+
146
168
  class ProviderDetail(BaseModel):
147
169
  """
148
170
  ProviderDetail contains basic information about the provider and/or locality used for pricing.
@@ -161,6 +183,12 @@ class ProviderDetail(BaseModel):
161
183
  locality: Optional[int] = None
162
184
  """Geographic locality number used for pricing"""
163
185
 
186
+ geographic_cbsa: Annotated[Optional[int], field_name("geographicCBSA")] = None
187
+ """Core-Based Statistical Area (CBSA) number for provider ZIP"""
188
+
189
+ state_cbsa: Annotated[Optional[int], field_name("stateCBSA")] = None
190
+ """State Core-Based Statistical Area (CBSA) number"""
191
+
164
192
  rural_indicator: Optional[RuralIndicator] = None
165
193
  """Indicates whether provider is Rural (R), Super Rural (B), or Urban (blank)"""
166
194
 
@@ -224,9 +252,6 @@ class LineEdits(BaseModel):
224
252
 
225
253
  model_config = camel_case_model_config
226
254
 
227
- denial_or_rejection_text: Optional[str] = None
228
- """The overall explanation for why this line item was denied or rejected by the claim editor"""
229
-
230
255
  procedure_edits: Optional[list[str]] = None
231
256
  """Detailed description of each procedure code edit error (from outpatient editor)"""
232
257
 
@@ -251,9 +276,6 @@ class LineEdits(BaseModel):
251
276
  revenue_edits: Optional[list[str]] = None
252
277
  """Detailed description of each revenue code edit error (from outpatient editor)"""
253
278
 
254
- professional_edits: Optional[list[str]] = None
255
- """Detailed description of each professional claim edit error"""
256
-
257
279
 
258
280
  class PricedService(BaseModel):
259
281
  """PricedService contains the results of a pricing request for a single service line"""
@@ -278,12 +300,18 @@ class PricedService(BaseModel):
278
300
  medicare_repricing_note: Optional[str] = None
279
301
  """Note explaining approach for pricing or reason for error"""
280
302
 
303
+ network_code: Optional[str] = None
304
+ """Code describing the network used for allowed amount pricing"""
305
+
281
306
  allowed_repricing_code: Optional[LineRepricingCode] = None
282
307
  """Explains the methodology used to calculate allowed amount"""
283
308
 
284
309
  allowed_repricing_note: Optional[str] = None
285
310
  """Note explaining approach for pricing or reason for error"""
286
311
 
312
+ allowed_repricing_formula: Optional[AllowedRepricingFormula] = None
313
+ """Formula used to calculate the allowed amount"""
314
+
287
315
  technical_component_amount: Optional[float] = None
288
316
  """Amount Medicare would pay for the technical component"""
289
317
 
@@ -305,6 +333,30 @@ class PricedService(BaseModel):
305
333
  payment_indicator: Optional[str] = None
306
334
  """Text which explains the type of payment for Medicare"""
307
335
 
336
+ discount_formula: Optional[str] = None
337
+ """The multi-procedure discount formula used to calculate the allowed amount (outpatient only)"""
338
+
339
+ line_item_denial_or_rejection_flag: Optional[str] = None
340
+ """Identifies how a line item was denied or rejected and how the rejection can be overridden (outpatient only)"""
341
+
342
+ packaging_flag: Optional[str] = None
343
+ """Indicates if the service is packaged and the reason for packaging (outpatient only)"""
344
+
345
+ payment_adjustment_flag: Optional[str] = None
346
+ """Identifies special adjustments made to the payment (outpatient only)"""
347
+
348
+ payment_adjustment_flag2: Optional[str] = None
349
+ """Identifies special adjustments made to the payment (outpatient only)"""
350
+
351
+ payment_method_flag: Optional[str] = None
352
+ """The method used to calculate the allowed amount (outpatient only)"""
353
+
354
+ composite_adjustment_flag: Optional[str] = None
355
+ """Assists in composite APC determination (outpatient only)"""
356
+
357
+ hcpcs_apc: Annotated[Optional[str], field_name("hcpcsAPC")] = None
358
+ """Ambulatory Payment Classification code of the line item HCPCS (outpatient only)"""
359
+
308
360
  payment_apc: Annotated[Optional[str], field_name("paymentAPC")] = None
309
361
  """Ambulatory Payment Classification"""
310
362
 
@@ -332,6 +384,9 @@ class Pricing(BaseModel):
332
384
  medicare_repricing_note: Optional[str] = None
333
385
  """Note explaining approach for pricing or reason for error"""
334
386
 
387
+ network_code: Optional[str] = None
388
+ """Code describing the network used for allowed amount pricing"""
389
+
335
390
  allowed_repricing_code: Optional[ClaimRepricingCode] = None
336
391
  """Explains the methodology used to calculate allowed amount (CON, RBP, SCA, or IFO)"""
337
392
 
@@ -359,8 +414,145 @@ class Pricing(BaseModel):
359
414
  pricer_result: Optional[str] = None
360
415
  """Pricer return details"""
361
416
 
417
+ price_config: Optional[PriceConfig] = None
418
+ """The configuration used for pricing the claim"""
419
+
362
420
  services: list[PricedService] = Field(min_length=1)
363
421
  """Pricing for each service line on the claim"""
364
422
 
365
423
  edit_error: Optional[ResponseError] = None
366
424
  """An error that occurred during some step of the pricing process"""
425
+
426
+
427
+ class Step(str, Enum):
428
+ new = "New"
429
+ received = "Received"
430
+ pending = "Pending"
431
+ held = "Held"
432
+ error = "Error"
433
+ input_validated = "Input Validated"
434
+ provider_matched = "Provider Matched"
435
+ edit_complete = "Edit Complete"
436
+ medicare_priced = "Medicare Priced"
437
+ primary_allowed_priced = "Primary Allowed Priced"
438
+ network_allowed_priced = "Network Allowed Priced"
439
+ out_of_network = "Out of Network"
440
+ request_more_info = "Request More Info"
441
+ priced = "Priced"
442
+ returned = "Returned"
443
+
444
+
445
+ class Status(str, Enum):
446
+ pending_claim_input_validation = "Claim Input Validation"
447
+ pending_claim_edit_review = "Claim Edit Review"
448
+ pending_provider_matching = "Provider Matching"
449
+ pending_medicare_review = "Medicare Review"
450
+ pending_medicare_calculation = "Medicare Calculation"
451
+ pending_primary_allowed_review = "Primary Allowed Review"
452
+ pending_network_allowed_review = "Network Allowed Review"
453
+ pending_primary_allowed_determination = "Primary Allowed Determination"
454
+ pending_network_allowed_determination = "Network Allowed Determination"
455
+
456
+
457
+ class StepAndStatus(BaseModel):
458
+ model_config = camel_case_model_config
459
+
460
+ step: Step
461
+ status: Optional[Status] = None
462
+
463
+
464
+ class ClaimStatus(StepAndStatus):
465
+ model_config = camel_case_model_config
466
+
467
+ updated_by: Optional[str] = None
468
+ updated_at: Optional[DateTime] = None
469
+ pricing: Optional[Pricing] = None
470
+ error: Optional[ResponseError] = None
471
+
472
+
473
+ status_new = StepAndStatus(step=Step.new)
474
+ """created by TPA. We use the transaction date as a proxy for this date"""
475
+
476
+ status_received = StepAndStatus(step=Step.received)
477
+ """received and ready for processing. This is modified date of the file we get from SFTP"""
478
+
479
+ status_held = StepAndStatus(step=Step.held)
480
+ """held for various reasons"""
481
+
482
+ status_error = StepAndStatus(step=Step.error)
483
+ """claim encountered an error during processing"""
484
+
485
+ status_input_validated = StepAndStatus(step=Step.input_validated)
486
+ """claim input has been validated"""
487
+
488
+ status_provider_matched = StepAndStatus(step=Step.provider_matched)
489
+ """providers in the claim have been matched to the provider system of record"""
490
+
491
+ status_edit_complete = StepAndStatus(step=Step.edit_complete)
492
+ """claim has been edited and is ready for pricing"""
493
+
494
+ status_medicare_priced = StepAndStatus(step=Step.medicare_priced)
495
+ """claim has been priced according to Medicare"""
496
+
497
+ status_primary_allowed_priced = StepAndStatus(step=Step.primary_allowed_priced)
498
+ """claim has been priced according to the primary allowed amount (e.g. contract, RBP, etc.)"""
499
+
500
+ status_network_allowed_priced = StepAndStatus(step=Step.network_allowed_priced)
501
+ """claim has been priced according to the allowed amount of the network"""
502
+
503
+ status_out_of_network = StepAndStatus(step=Step.out_of_network)
504
+ """is out of network"""
505
+
506
+ status_request_more_info = StepAndStatus(step=Step.request_more_info)
507
+ """return claim to trading partner for more information to enable correct processing"""
508
+
509
+ status_priced = StepAndStatus(step=Step.priced)
510
+ """done pricing"""
511
+
512
+ status_returned = StepAndStatus(step=Step.returned)
513
+ """returned to TPA"""
514
+
515
+ status_pending_claim_input_validation = StepAndStatus(
516
+ step=Step.pending, status=Status.pending_claim_input_validation
517
+ )
518
+ """waiting for claim input validation"""
519
+
520
+ status_pending_claim_edit_review = StepAndStatus(
521
+ step=Step.pending, status=Status.pending_claim_edit_review
522
+ )
523
+ """waiting for claim edit review"""
524
+
525
+ status_pending_provider_matching = StepAndStatus(
526
+ step=Step.pending, status=Status.pending_provider_matching
527
+ )
528
+ """waiting for provider matching"""
529
+
530
+ status_pending_medicare_review = StepAndStatus(
531
+ step=Step.pending, status=Status.pending_medicare_review
532
+ )
533
+ """waiting for Medicare amount review"""
534
+
535
+ status_pending_medicare_calculation = StepAndStatus(
536
+ step=Step.pending, status=Status.pending_medicare_calculation
537
+ )
538
+ """waiting for Medicare amount calculation"""
539
+
540
+ status_pending_primary_allowed_review = StepAndStatus(
541
+ step=Step.pending, status=Status.pending_primary_allowed_review
542
+ )
543
+ """waiting for primary allowed amount review"""
544
+
545
+ status_pending_network_allowed_review = StepAndStatus(
546
+ step=Step.pending, status=Status.pending_network_allowed_review
547
+ )
548
+ """waiting for network allowed amount review"""
549
+
550
+ status_pending_primary_allowed_determination = StepAndStatus(
551
+ step=Step.pending, status=Status.pending_primary_allowed_determination
552
+ )
553
+ """waiting for the primary allowed amount (e.g. contract, RBP rate, etc.) to be determined"""
554
+
555
+ status_pending_network_allowed_determination = StepAndStatus(
556
+ step=Step.pending, status=Status.pending_network_allowed_determination
557
+ )
558
+ """waiting for allowed amount from the network"""
mphapi/py.typed ADDED
File without changes