fin-infra 0.1.83__py3-none-any.whl → 0.1.85__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.
@@ -7,7 +7,7 @@ from __future__ import annotations
7
7
 
8
8
  from datetime import datetime, timedelta
9
9
  from decimal import Decimal
10
- from typing import Any, Optional
10
+ from typing import Any
11
11
 
12
12
  from ..models import Transaction
13
13
  from .models import CashFlowAnalysis
@@ -17,7 +17,7 @@ async def calculate_cash_flow(
17
17
  user_id: str,
18
18
  start_date: str | datetime,
19
19
  end_date: str | datetime,
20
- accounts: Optional[list[str]] = None,
20
+ accounts: list[str] | None = None,
21
21
  *,
22
22
  banking_provider=None,
23
23
  categorization_provider=None,
@@ -117,7 +117,7 @@ async def calculate_cash_flow(
117
117
  async def forecast_cash_flow(
118
118
  user_id: str,
119
119
  months: int = 6,
120
- assumptions: Optional[dict[str, Any]] = None,
120
+ assumptions: dict[str, Any] | None = None,
121
121
  *,
122
122
  recurring_provider=None,
123
123
  ) -> list[CashFlowAnalysis]:
@@ -7,7 +7,7 @@ from __future__ import annotations
7
7
 
8
8
  from datetime import datetime
9
9
  from enum import Enum
10
- from typing import Any, Optional
10
+ from typing import Any
11
11
 
12
12
  from pydantic import BaseModel, ConfigDict, Field
13
13
 
@@ -72,7 +72,7 @@ class SavingsRateData(BaseModel):
72
72
  expenses: float = Field(..., description="Total expenses for period")
73
73
  period: Period = Field(..., description="Period type")
74
74
  definition: SavingsDefinition = Field(..., description="Calculation method used")
75
- trend: Optional[TrendDirection] = Field(None, description="Trend over time")
75
+ trend: TrendDirection | None = Field(None, description="Trend over time")
76
76
 
77
77
 
78
78
  class SpendingAnomaly(BaseModel):
@@ -132,7 +132,7 @@ class PersonalizedSpendingAdvice(BaseModel):
132
132
  alerts: list[str] = Field(
133
133
  default_factory=list, description="Urgent spending issues requiring attention"
134
134
  )
135
- estimated_monthly_savings: Optional[float] = Field(
135
+ estimated_monthly_savings: float | None = Field(
136
136
  None, description="Potential monthly savings if recommendations followed"
137
137
  )
138
138
 
@@ -183,7 +183,7 @@ class BenchmarkComparison(BaseModel):
183
183
  benchmark_return_percent: float = Field(..., description="Benchmark return percentage")
184
184
  benchmark_symbol: str = Field(..., description="Benchmark ticker (e.g., SPY)")
185
185
  alpha: float = Field(..., description="Portfolio alpha (excess return)")
186
- beta: Optional[float] = Field(None, description="Portfolio beta (volatility vs benchmark)")
186
+ beta: float | None = Field(None, description="Portfolio beta (volatility vs benchmark)")
187
187
  period: str = Field(..., description="Comparison period (1y, 3y, 5y, etc.)")
188
188
 
189
189
 
@@ -213,6 +213,6 @@ class GrowthProjection(BaseModel):
213
213
  assumptions: dict[str, Any] = Field(
214
214
  default_factory=dict, description="Assumptions used (inflation, returns, etc.)"
215
215
  )
216
- confidence_intervals: Optional[dict[str, tuple[float, float]]] = Field(
216
+ confidence_intervals: dict[str, tuple[float, float]] | None = Field(
217
217
  None, description="95% confidence intervals by scenario"
218
218
  )
@@ -45,7 +45,6 @@ Examples:
45
45
  from collections import defaultdict
46
46
  from datetime import timedelta
47
47
  from decimal import Decimal
48
- from typing import Optional
49
48
 
50
49
  from fin_infra.analytics.models import (
51
50
  PersonalizedSpendingAdvice,
@@ -60,7 +59,7 @@ async def analyze_spending(
60
59
  user_id: str,
61
60
  *,
62
61
  period: str = "30d",
63
- categories: Optional[list[str]] = None,
62
+ categories: list[str] | None = None,
64
63
  banking_provider=None,
65
64
  categorization_provider=None,
66
65
  ) -> SpendingInsight:
@@ -426,7 +425,7 @@ def _generate_mock_transactions(days: int) -> list[Transaction]:
426
425
  async def generate_spending_insights(
427
426
  spending_insight: SpendingInsight,
428
427
  *,
429
- user_context: Optional[dict] = None,
428
+ user_context: dict | None = None,
430
429
  llm_provider=None,
431
430
  ) -> "PersonalizedSpendingAdvice":
432
431
  """Generate personalized spending insights using LLM.
@@ -521,7 +520,7 @@ async def generate_spending_insights(
521
520
 
522
521
  def _build_spending_insights_prompt(
523
522
  spending_insight: SpendingInsight,
524
- user_context: Optional[dict] = None,
523
+ user_context: dict | None = None,
525
524
  ) -> str:
526
525
  """Build LLM prompt with financial context.
527
526
 
@@ -621,7 +620,7 @@ Be specific, encouraging, and actionable. Focus on realistic savings, not extrem
621
620
 
622
621
  def _generate_rule_based_insights(
623
622
  spending_insight: SpendingInsight,
624
- user_context: Optional[dict] = None,
623
+ user_context: dict | None = None,
625
624
  ) -> "PersonalizedSpendingAdvice":
626
625
  """Generate rule-based insights when LLM is unavailable.
627
626
 
@@ -35,7 +35,7 @@ Example:
35
35
  from __future__ import annotations
36
36
 
37
37
  from datetime import datetime
38
- from typing import TYPE_CHECKING, Optional
38
+ from typing import TYPE_CHECKING
39
39
 
40
40
  from fin_infra.budgets.models import (
41
41
  AlertSeverity,
@@ -51,7 +51,7 @@ if TYPE_CHECKING:
51
51
  async def check_budget_alerts(
52
52
  budget_id: str,
53
53
  tracker: BudgetTracker,
54
- thresholds: Optional[dict[str, float]] = None,
54
+ thresholds: dict[str, float] | None = None,
55
55
  ) -> list[BudgetAlert]:
56
56
  """
57
57
  Check budget for alerts (overspending, approaching limits, unusual patterns).
fin_infra/budgets/ease.py CHANGED
@@ -12,7 +12,6 @@ Generic Design:
12
12
  from __future__ import annotations
13
13
 
14
14
  import os
15
- from typing import Optional
16
15
 
17
16
  from sqlalchemy.ext.asyncio import create_async_engine
18
17
 
@@ -20,7 +19,7 @@ from fin_infra.budgets.tracker import BudgetTracker
20
19
 
21
20
 
22
21
  def easy_budgets(
23
- db_url: Optional[str] = None,
22
+ db_url: str | None = None,
24
23
  pool_size: int = 5,
25
24
  max_overflow: int = 10,
26
25
  pool_pre_ping: bool = True,
@@ -8,7 +8,6 @@ from __future__ import annotations
8
8
 
9
9
  from datetime import datetime
10
10
  from enum import Enum
11
- from typing import Optional
12
11
 
13
12
  from pydantic import BaseModel, ConfigDict, Field
14
13
 
@@ -296,7 +295,7 @@ class BudgetAlert(BaseModel):
296
295
  """
297
296
 
298
297
  budget_id: str = Field(..., description="Budget identifier")
299
- category: Optional[str] = Field(None, description="Category triggering alert")
298
+ category: str | None = Field(None, description="Category triggering alert")
300
299
  alert_type: AlertType = Field(..., description="Type of alert")
301
300
  threshold: float = Field(..., description="Threshold that triggered alert")
302
301
  message: str = Field(..., description="Human-readable alert message")
@@ -16,7 +16,7 @@ Generic Design:
16
16
  from __future__ import annotations
17
17
 
18
18
  from datetime import datetime
19
- from typing import TYPE_CHECKING, Optional
19
+ from typing import TYPE_CHECKING
20
20
 
21
21
  from fin_infra.budgets.models import Budget, BudgetPeriod, BudgetType
22
22
 
@@ -177,9 +177,9 @@ async def apply_template(
177
177
  template_name: str,
178
178
  total_income: float,
179
179
  tracker: BudgetTracker,
180
- budget_name: Optional[str] = None,
181
- start_date: Optional[datetime] = None,
182
- custom_template: Optional[BudgetTemplate] = None,
180
+ budget_name: str | None = None,
181
+ start_date: datetime | None = None,
182
+ custom_template: BudgetTemplate | None = None,
183
183
  ) -> Budget:
184
184
  """Apply a budget template to create a new budget.
185
185
 
@@ -36,7 +36,7 @@ from __future__ import annotations
36
36
 
37
37
  import uuid
38
38
  from datetime import datetime, timedelta
39
- from typing import TYPE_CHECKING, Optional
39
+ from typing import TYPE_CHECKING
40
40
 
41
41
  from sqlalchemy.ext.asyncio import async_sessionmaker
42
42
 
@@ -116,7 +116,7 @@ class BudgetTracker:
116
116
  type: str, # BudgetType value
117
117
  period: str, # BudgetPeriod value
118
118
  categories: dict[str, float],
119
- start_date: Optional[datetime] = None,
119
+ start_date: datetime | None = None,
120
120
  rollover_enabled: bool = False,
121
121
  ) -> Budget:
122
122
  """
@@ -205,7 +205,7 @@ class BudgetTracker:
205
205
  async def get_budgets(
206
206
  self,
207
207
  user_id: str,
208
- type: Optional[str] = None,
208
+ type: str | None = None,
209
209
  ) -> list[Budget]:
210
210
  """
211
211
  Get all budgets for a user.
@@ -5,7 +5,7 @@ Provides one-line setup with sensible defaults.
5
5
  """
6
6
 
7
7
  from pathlib import Path
8
- from typing import Literal, Optional
8
+ from typing import Literal
9
9
 
10
10
  from .engine import CategorizationEngine
11
11
 
@@ -21,10 +21,10 @@ def easy_categorization(
21
21
  taxonomy: str = "mx",
22
22
  enable_ml: bool = False,
23
23
  confidence_threshold: float = 0.6,
24
- model_path: Optional[Path] = None,
24
+ model_path: Path | None = None,
25
25
  # LLM-specific parameters (V2)
26
26
  llm_provider: Literal["google", "openai", "anthropic", "none"] = "google",
27
- llm_model: Optional[str] = None,
27
+ llm_model: str | None = None,
28
28
  llm_confidence_threshold: float = 0.6,
29
29
  llm_cache_ttl: int = 86400, # 24 hours
30
30
  llm_max_cost_per_day: float = 0.10, # $0.10/day
@@ -4,7 +4,6 @@ Pydantic models for transaction categorization.
4
4
 
5
5
  from datetime import datetime
6
6
  from enum import Enum
7
- from typing import Optional
8
7
 
9
8
  from pydantic import BaseModel, ConfigDict, Field
10
9
 
@@ -34,7 +33,7 @@ class CategoryPrediction(BaseModel):
34
33
  default_factory=list,
35
34
  description="Alternative predictions (category, confidence)",
36
35
  )
37
- reasoning: Optional[str] = Field(None, description="Explanation of prediction (for LLM)")
36
+ reasoning: str | None = Field(None, description="Explanation of prediction (for LLM)")
38
37
 
39
38
  model_config = ConfigDict(
40
39
  json_schema_extra={
@@ -100,7 +99,7 @@ class CategorizationRequest(BaseModel):
100
99
  """Request to categorize a merchant."""
101
100
 
102
101
  merchant_name: str = Field(..., description="Merchant name to categorize")
103
- user_id: Optional[str] = Field(None, description="User ID for personalized overrides")
102
+ user_id: str | None = Field(None, description="User ID for personalized overrides")
104
103
  include_alternatives: bool = Field(default=False, description="Include alternative predictions")
105
104
  min_confidence: float = Field(
106
105
  default=0.0,
@@ -152,7 +151,7 @@ class CategoryStats(BaseModel):
152
151
  total_categories: int = Field(..., description="Total number of categories")
153
152
  categories_by_group: dict[str, int] = Field(..., description="Category counts by group")
154
153
  total_rules: int = Field(..., description="Total number of rules")
155
- cache_hit_rate: Optional[float] = Field(None, description="Cache hit rate (0-1)")
154
+ cache_hit_rate: float | None = Field(None, description="Cache hit rate (0-1)")
156
155
 
157
156
  model_config = ConfigDict(
158
157
  json_schema_extra={
@@ -31,7 +31,6 @@ from __future__ import annotations
31
31
 
32
32
  from datetime import datetime
33
33
  from enum import Enum
34
- from typing import Optional
35
34
 
36
35
  from pydantic import BaseModel, ConfigDict, Field
37
36
  from svc_infra.documents import Document as BaseDocument
@@ -111,8 +110,8 @@ class FinancialDocument(BaseDocument):
111
110
 
112
111
  # Financial-specific fields
113
112
  type: DocumentType = Field(..., description="Document type category (financial-specific)")
114
- tax_year: Optional[int] = Field(None, description="Tax year for tax documents (e.g., 2024)")
115
- form_type: Optional[str] = Field(None, description="Tax form type (W-2, 1099-INT, 1040, etc.)")
113
+ tax_year: int | None = Field(None, description="Tax year for tax documents (e.g., 2024)")
114
+ form_type: str | None = Field(None, description="Tax form type (W-2, 1099-INT, 1040, etc.)")
116
115
 
117
116
 
118
117
  # Backward compatibility: alias for existing code
@@ -8,13 +8,13 @@ and SnapTrade (retail brokerages) for maximum coverage.
8
8
  from __future__ import annotations
9
9
 
10
10
  import os
11
- from typing import Any, Literal, Optional
11
+ from typing import Any, Literal
12
12
 
13
13
  from .providers.base import InvestmentProvider
14
14
 
15
15
 
16
16
  def easy_investments(
17
- provider: Optional[Literal["plaid", "snaptrade"]] = None,
17
+ provider: Literal["plaid", "snaptrade"] | None = None,
18
18
  **config: Any,
19
19
  ) -> InvestmentProvider:
20
20
  """Create investment provider with auto-configuration.
@@ -20,7 +20,7 @@ from __future__ import annotations
20
20
  from datetime import date
21
21
  from decimal import Decimal
22
22
  from enum import Enum
23
- from typing import TYPE_CHECKING, Optional
23
+ from typing import TYPE_CHECKING
24
24
 
25
25
  from pydantic import BaseModel, ConfigDict, Field, computed_field
26
26
 
@@ -121,22 +121,20 @@ class Security(BaseModel):
121
121
 
122
122
  # Identifiers (at least one required for matching)
123
123
  security_id: str = Field(..., description="Provider-specific security ID")
124
- cusip: Optional[str] = Field(None, description="CUSIP identifier (US securities)")
125
- isin: Optional[str] = Field(None, description="ISIN identifier (international)")
126
- sedol: Optional[str] = Field(None, description="SEDOL identifier (UK securities)")
127
- ticker_symbol: Optional[str] = Field(None, description="Trading symbol (AAPL, GOOGL)")
124
+ cusip: str | None = Field(None, description="CUSIP identifier (US securities)")
125
+ isin: str | None = Field(None, description="ISIN identifier (international)")
126
+ sedol: str | None = Field(None, description="SEDOL identifier (UK securities)")
127
+ ticker_symbol: str | None = Field(None, description="Trading symbol (AAPL, GOOGL)")
128
128
 
129
129
  # Basic info
130
130
  name: str = Field(..., description="Security name")
131
131
  type: SecurityType = Field(..., description="Security type (equity, etf, bond, etc.)")
132
- sector: Optional[str] = Field(
133
- None, description="Sector classification (Technology, Healthcare)"
134
- )
132
+ sector: str | None = Field(None, description="Sector classification (Technology, Healthcare)")
135
133
 
136
134
  # Market data
137
- close_price: Optional[Decimal] = Field(None, ge=0, description="Latest closing price")
138
- close_price_as_of: Optional[date] = Field(None, description="Date of close_price")
139
- exchange: Optional[str] = Field(None, description="Exchange (NASDAQ, NYSE, etc.)")
135
+ close_price: Decimal | None = Field(None, ge=0, description="Latest closing price")
136
+ close_price_as_of: date | None = Field(None, description="Date of close_price")
137
+ exchange: str | None = Field(None, description="Exchange (NASDAQ, NYSE, etc.)")
140
138
  currency: str = Field("USD", description="Currency code (USD, EUR, etc.)")
141
139
 
142
140
 
@@ -198,26 +196,26 @@ class Holding(BaseModel):
198
196
  institution_value: Decimal = Field(
199
197
  ..., ge=0, description="Current market value (quantity × price)"
200
198
  )
201
- cost_basis: Optional[Decimal] = Field(
199
+ cost_basis: Decimal | None = Field(
202
200
  None, ge=0, description="Total cost basis (original purchase price)"
203
201
  )
204
202
 
205
203
  # Additional data
206
204
  currency: str = Field("USD", description="Currency code")
207
- unofficial_currency_code: Optional[str] = Field(None, description="For crypto/alt currencies")
208
- as_of_date: Optional[date] = Field(None, description="Date of pricing data")
205
+ unofficial_currency_code: str | None = Field(None, description="For crypto/alt currencies")
206
+ as_of_date: date | None = Field(None, description="Date of pricing data")
209
207
 
210
208
  if TYPE_CHECKING:
211
209
 
212
210
  @property
213
- def unrealized_gain_loss(self) -> Optional[Decimal]:
211
+ def unrealized_gain_loss(self) -> Decimal | None:
214
212
  """Calculate unrealized gain/loss (current value - cost basis)."""
215
213
  if self.cost_basis is None:
216
214
  return None
217
215
  return self.institution_value - self.cost_basis
218
216
 
219
217
  @property
220
- def unrealized_gain_loss_percent(self) -> Optional[Decimal]:
218
+ def unrealized_gain_loss_percent(self) -> Decimal | None:
221
219
  """Calculate unrealized gain/loss percentage."""
222
220
  if self.cost_basis is None or self.cost_basis == 0:
223
221
  return None
@@ -228,7 +226,7 @@ class Holding(BaseModel):
228
226
 
229
227
  @computed_field
230
228
  @property
231
- def unrealized_gain_loss(self) -> Optional[Decimal]:
229
+ def unrealized_gain_loss(self) -> Decimal | None:
232
230
  """Calculate unrealized gain/loss (current value - cost basis)."""
233
231
  if self.cost_basis is None:
234
232
  return None
@@ -236,7 +234,7 @@ class Holding(BaseModel):
236
234
 
237
235
  @computed_field
238
236
  @property
239
- def unrealized_gain_loss_percent(self) -> Optional[Decimal]:
237
+ def unrealized_gain_loss_percent(self) -> Decimal | None:
240
238
  """Calculate unrealized gain/loss percentage."""
241
239
  if self.cost_basis is None or self.cost_basis == 0:
242
240
  return None
@@ -304,17 +302,17 @@ class InvestmentTransaction(BaseModel):
304
302
  transaction_type: TransactionType = Field(
305
303
  ..., alias="type", description="Transaction type (buy, sell, dividend)"
306
304
  )
307
- subtype: Optional[str] = Field(None, description="Provider-specific subtype")
305
+ subtype: str | None = Field(None, description="Provider-specific subtype")
308
306
 
309
307
  # Amounts
310
308
  quantity: Decimal = Field(..., description="Number of shares (0 for fees/dividends)")
311
309
  amount: Decimal = Field(..., description="Transaction amount (negative for purchases)")
312
- price: Optional[Decimal] = Field(None, ge=0, description="Price per share")
313
- fees: Optional[Decimal] = Field(None, ge=0, description="Transaction fees")
310
+ price: Decimal | None = Field(None, ge=0, description="Price per share")
311
+ fees: Decimal | None = Field(None, ge=0, description="Transaction fees")
314
312
 
315
313
  # Additional data
316
314
  currency: str = Field("USD", description="Currency code")
317
- unofficial_currency_code: Optional[str] = Field(None, description="For crypto/alt currencies")
315
+ unofficial_currency_code: str | None = Field(None, description="For crypto/alt currencies")
318
316
 
319
317
 
320
318
  class InvestmentAccount(BaseModel):
@@ -371,10 +369,10 @@ class InvestmentAccount(BaseModel):
371
369
  account_id: str = Field(..., description="Account identifier")
372
370
  name: str = Field(..., description="Account name (Fidelity 401k)")
373
371
  type: str = Field(..., description="Account type (investment)")
374
- subtype: Optional[str] = Field(None, description="Account subtype (401k, ira, brokerage)")
372
+ subtype: str | None = Field(None, description="Account subtype (401k, ira, brokerage)")
375
373
 
376
374
  # Balances
377
- balances: dict[str, Optional[Decimal]] = Field(
375
+ balances: dict[str, Decimal | None] = Field(
378
376
  ..., description="Current, available, and limit balances"
379
377
  )
380
378
 
@@ -405,7 +403,7 @@ class InvestmentAccount(BaseModel):
405
403
  return holdings_value - self.total_cost_basis
406
404
 
407
405
  @property
408
- def total_unrealized_gain_loss_percent(self) -> Optional[Decimal]:
406
+ def total_unrealized_gain_loss_percent(self) -> Decimal | None:
409
407
  """Calculate total unrealized P&L percentage."""
410
408
  if self.total_cost_basis == 0:
411
409
  return None
@@ -439,7 +437,7 @@ class InvestmentAccount(BaseModel):
439
437
 
440
438
  @computed_field
441
439
  @property
442
- def total_unrealized_gain_loss_percent(self) -> Optional[Decimal]:
440
+ def total_unrealized_gain_loss_percent(self) -> Decimal | None:
443
441
  """Calculate total unrealized P&L percentage."""
444
442
  if self.total_cost_basis == 0:
445
443
  return None
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from decimal import Decimal
4
4
  from enum import Enum
5
- from typing import Optional
6
5
 
7
6
  from pydantic import BaseModel, field_validator
8
7
 
@@ -26,11 +25,11 @@ class Account(BaseModel):
26
25
  id: str
27
26
  name: str
28
27
  type: AccountType
29
- mask: Optional[str] = None
28
+ mask: str | None = None
30
29
  currency: str = "USD"
31
- institution: Optional[str] = None
32
- balance_available: Optional[Decimal] = None
33
- balance_current: Optional[Decimal] = None
30
+ institution: str | None = None
31
+ balance_available: Decimal | None = None
32
+ balance_current: Decimal | None = None
34
33
 
35
34
  @field_validator("balance_available", "balance_current", mode="before")
36
35
  @classmethod
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from datetime import date
3
+ import datetime
4
4
  from decimal import Decimal
5
5
 
6
6
  from pydantic import BaseModel, field_validator
@@ -15,7 +15,7 @@ class Transaction(BaseModel):
15
15
 
16
16
  id: str
17
17
  account_id: str
18
- date: date
18
+ date: datetime.date
19
19
  amount: Decimal
20
20
  currency: str = "USD"
21
21
  description: str | None = None
@@ -2,7 +2,6 @@
2
2
 
3
3
  import logging
4
4
  from datetime import date as DateType
5
- from typing import Optional
6
5
 
7
6
  from fin_infra.exceptions import CurrencyNotSupportedError, ExchangeRateAPIError
8
7
  from fin_infra.normalization.models import CurrencyConversionResult
@@ -25,7 +24,7 @@ class CurrencyConverter:
25
24
  Supports 160+ currencies including crypto (BTC, ETH).
26
25
  """
27
26
 
28
- def __init__(self, api_key: Optional[str] = None):
27
+ def __init__(self, api_key: str | None = None):
29
28
  """
30
29
  Initialize currency converter.
31
30
 
@@ -39,7 +38,7 @@ class CurrencyConverter:
39
38
  amount: float,
40
39
  from_currency: str,
41
40
  to_currency: str,
42
- date: Optional[DateType] = None,
41
+ date: DateType | None = None,
43
42
  ) -> float:
44
43
  """
45
44
  Convert amount from one currency to another.
@@ -79,7 +78,7 @@ class CurrencyConverter:
79
78
  self,
80
79
  from_currency: str,
81
80
  to_currency: str,
82
- date: Optional[DateType] = None,
81
+ date: DateType | None = None,
83
82
  ) -> float:
84
83
  """
85
84
  Get exchange rate between two currencies.
@@ -142,7 +141,7 @@ class CurrencyConverter:
142
141
  amount: float,
143
142
  from_currency: str,
144
143
  to_currency: str,
145
- date: Optional[DateType] = None,
144
+ date: DateType | None = None,
146
145
  ) -> CurrencyConversionResult:
147
146
  """
148
147
  Convert amount with detailed result information.
@@ -1,7 +1,6 @@
1
1
  """Data models for normalization module."""
2
2
 
3
3
  from datetime import date as DateType
4
- from typing import Optional
5
4
 
6
5
  from pydantic import BaseModel, Field
7
6
 
@@ -11,12 +10,12 @@ class SymbolMetadata(BaseModel):
11
10
 
12
11
  ticker: str = Field(..., description="Standard ticker symbol")
13
12
  name: str = Field(..., description="Company or asset name")
14
- exchange: Optional[str] = Field(None, description="Primary exchange (e.g., NASDAQ, NYSE)")
15
- cusip: Optional[str] = Field(None, description="CUSIP identifier")
16
- isin: Optional[str] = Field(None, description="ISIN identifier")
17
- sector: Optional[str] = Field(None, description="Business sector")
18
- industry: Optional[str] = Field(None, description="Industry classification")
19
- market_cap: Optional[float] = Field(None, description="Market capitalization in USD")
13
+ exchange: str | None = Field(None, description="Primary exchange (e.g., NASDAQ, NYSE)")
14
+ cusip: str | None = Field(None, description="CUSIP identifier")
15
+ isin: str | None = Field(None, description="ISIN identifier")
16
+ sector: str | None = Field(None, description="Business sector")
17
+ industry: str | None = Field(None, description="Industry classification")
18
+ market_cap: float | None = Field(None, description="Market capitalization in USD")
20
19
  asset_type: str = Field(default="stock", description="Asset type: stock, etf, crypto, forex")
21
20
 
22
21
 
@@ -26,8 +25,8 @@ class ExchangeRate(BaseModel):
26
25
  from_currency: str = Field(..., description="Source currency code (e.g., USD)")
27
26
  to_currency: str = Field(..., description="Target currency code (e.g., EUR)")
28
27
  rate: float = Field(..., description="Exchange rate (1 from_currency = rate to_currency)")
29
- date: Optional[DateType] = Field(None, description="Rate date (None = current)")
30
- timestamp: Optional[int] = Field(None, description="Unix timestamp of rate")
28
+ date: DateType | None = Field(None, description="Rate date (None = current)")
29
+ timestamp: int | None = Field(None, description="Unix timestamp of rate")
31
30
 
32
31
 
33
32
  class CurrencyConversionResult(BaseModel):
@@ -38,4 +37,4 @@ class CurrencyConversionResult(BaseModel):
38
37
  to_currency: str = Field(..., description="Target currency")
39
38
  converted: float = Field(..., description="Converted amount")
40
39
  rate: float = Field(..., description="Exchange rate used")
41
- date: Optional[DateType] = Field(None, description="Rate date")
40
+ date: DateType | None = Field(None, description="Rate date")
@@ -1,7 +1,6 @@
1
1
  """Symbol resolver for converting between ticker formats."""
2
2
 
3
3
  import logging
4
- from typing import Optional
5
4
 
6
5
  from fin_infra.exceptions import SymbolNotFoundError
7
6
  from fin_infra.normalization.models import SymbolMetadata
@@ -231,9 +230,9 @@ class SymbolResolver:
231
230
  def add_mapping(
232
231
  self,
233
232
  ticker: str,
234
- cusip: Optional[str] = None,
235
- isin: Optional[str] = None,
236
- metadata: Optional[dict] = None,
233
+ cusip: str | None = None,
234
+ isin: str | None = None,
235
+ metadata: dict | None = None,
237
236
  ):
238
237
  """
239
238
  Add or override a symbol mapping (useful for custom symbols).
fin_infra/security/add.py CHANGED
@@ -5,7 +5,6 @@ Easy integration of financial PII masking and token encryption.
5
5
  """
6
6
 
7
7
  import logging
8
- from typing import Optional
9
8
 
10
9
  from fastapi import FastAPI
11
10
 
@@ -15,7 +14,7 @@ from .pii_filter import FinancialPIIFilter
15
14
 
16
15
  def add_financial_security(
17
16
  app: FastAPI,
18
- encryption_key: Optional[bytes] = None,
17
+ encryption_key: bytes | None = None,
19
18
  enable_pii_filter: bool = True,
20
19
  enable_audit_log: bool = True,
21
20
  mask_emails: bool = False,
@@ -6,7 +6,6 @@ Track access to sensitive financial data for compliance.
6
6
 
7
7
  import logging
8
8
  from datetime import datetime
9
- from typing import Optional
10
9
 
11
10
  from .models import PIIAccessLog
12
11
 
@@ -23,10 +22,10 @@ async def log_pii_access(
23
22
  pii_type: str,
24
23
  action: str,
25
24
  resource: str,
26
- ip_address: Optional[str] = None,
27
- user_agent: Optional[str] = None,
25
+ ip_address: str | None = None,
26
+ user_agent: str | None = None,
28
27
  success: bool = True,
29
- error_message: Optional[str] = None,
28
+ error_message: str | None = None,
30
29
  ) -> PIIAccessLog:
31
30
  """
32
31
  Log PII access for audit trail.
@@ -86,9 +85,9 @@ async def log_pii_access(
86
85
 
87
86
 
88
87
  def get_audit_logs(
89
- user_id: Optional[str] = None,
90
- pii_type: Optional[str] = None,
91
- action: Optional[str] = None,
88
+ user_id: str | None = None,
89
+ pii_type: str | None = None,
90
+ action: str | None = None,
92
91
  limit: int = 100,
93
92
  ) -> list[PIIAccessLog]:
94
93
  """
@@ -5,7 +5,6 @@ Database operations for encrypted provider tokens.
5
5
  """
6
6
 
7
7
  from datetime import datetime
8
- from typing import Optional
9
8
 
10
9
  from sqlalchemy import Column, DateTime, String, Text, select, update
11
10
  from sqlalchemy.dialects.postgresql import UUID
@@ -48,8 +47,8 @@ async def store_provider_token(
48
47
  provider: str,
49
48
  token: str,
50
49
  encryption: ProviderTokenEncryption,
51
- expires_at: Optional[datetime] = None,
52
- key_id: Optional[str] = None,
50
+ expires_at: datetime | None = None,
51
+ key_id: str | None = None,
53
52
  ) -> ProviderTokenMetadata:
54
53
  """
55
54
  Store encrypted provider token in database.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fin-infra
3
- Version: 0.1.83
3
+ Version: 0.1.85
4
4
  Summary: Financial infrastructure toolkit: banking connections, market data, credit, cashflows, and brokerage integrations
5
5
  License: MIT
6
6
  Keywords: finance,banking,plaid,brokerage,markets,credit,tax,cashflow,fintech,infra
@@ -2,39 +2,39 @@ fin_infra/__init__.py,sha256=7oL-CCsALNifBODAn9LriicaIrzgJkmVPvE-9duP0mw,1574
2
2
  fin_infra/__main__.py,sha256=1qNP7j0ffw0wFs1dBwDcJ9TNXlC6FcYuulzoV87pMi8,262
3
3
  fin_infra/analytics/__init__.py,sha256=zjAOFkI9xjg76ozZStYBXEPNwIsioj5fUBltja5JLyc,2327
4
4
  fin_infra/analytics/add.py,sha256=-iAqKdvf9t0gQjjo3uOO56qA_ei4omKYDOynxb4SZPI,12654
5
- fin_infra/analytics/cash_flow.py,sha256=lH-J6RIUzcV9Fuy56i12N6vuIdPaGz-rhWquSgjkldU,10300
5
+ fin_infra/analytics/cash_flow.py,sha256=VSsQHwTi6r6VFoScH9nu9Fj8XHC4O4B9TkAI8Alobm4,10284
6
6
  fin_infra/analytics/ease.py,sha256=aTAPc-Lmh6XP7REPVlAom38MtKCqeS-auNW-rpXRZJs,14282
7
- fin_infra/analytics/models.py,sha256=cK6VKLZ763zXfn9REXOnLGx0v3Q8bUa-vH8aoNs2w-A,8215
7
+ fin_infra/analytics/models.py,sha256=XFHeVZoPE0PcSFj59dNsdxISFVwdrTLU4sDCtExumrg,8193
8
8
  fin_infra/analytics/portfolio.py,sha256=d39sZGa3TQVX1xN2nplPHuxhsE-rahuiyzd612DeZ_Y,26417
9
9
  fin_infra/analytics/projections.py,sha256=T7LLG0Pe5f-WwgfDITdTMk-l8cCLZTM9cEC_Vxf6mkc,9154
10
10
  fin_infra/analytics/rebalancing.py,sha256=VM8MgoJofmrCXPK1rbmVqWGB4FauNmHCL5HOEbrZR2g,14610
11
11
  fin_infra/analytics/savings.py,sha256=n3rGNFP8TU5mW-uz9kOuqX_mDiVnDyAeDN06Q7Abotw,7570
12
12
  fin_infra/analytics/scenarios.py,sha256=LE_dZVkbxxAx5sxitGhiOhZfWTlYtVbIvS9pEXkijLc,12246
13
- fin_infra/analytics/spending.py,sha256=wCa8mhtdMTnI3eG9W28ljCgB_AQRVGxkrRA2ZwLi_RQ,26249
13
+ fin_infra/analytics/spending.py,sha256=ogLcfF5ZOLMkBIj02RISnA3hiY_PsLWZ2_AAzA7FenY,26209
14
14
  fin_infra/banking/__init__.py,sha256=6wwGNITzyehC9MBQc5jy0ewSTuNetyQu2AdND51O55w,22450
15
15
  fin_infra/banking/history.py,sha256=BYvKLI_F198TBrtP7vMCCcBcszzFxZkmD-ClCezSVW8,10575
16
16
  fin_infra/banking/utils.py,sha256=HhxZbeaA8zqVttgMiJGnShTo_r_0DaD7T3IMq8n8340,15252
17
17
  fin_infra/brokerage/__init__.py,sha256=wJpgdOMWNxKt01eUIbu2oSmcFlL189nlWR0-PhLLVFc,17154
18
18
  fin_infra/budgets/__init__.py,sha256=GgfujfgdVld6_yUf8bhkW-bsIXBLb4THlJMocVpA4mM,3986
19
19
  fin_infra/budgets/add.py,sha256=i7Av--Z14sYQDJHETiF9VL71ouWBZcs3H3VZ62gqFEo,13508
20
- fin_infra/budgets/alerts.py,sha256=KlK3r2qPzMw4naFMHsFjqkwKDxdj_2b_y-ofD1nldkg,9714
21
- fin_infra/budgets/ease.py,sha256=vK5O8rvKzzJ1MUiwi7p9egayDFqyB23hPbbEhb1mhXE,8203
22
- fin_infra/budgets/models.py,sha256=qd6bcjl2cOtFqRtNe1Xso_05cQlGG-4qhBkiTQKchy0,14335
20
+ fin_infra/budgets/alerts.py,sha256=XDSUPpRR1LNgSQU0Szlht0bLWI7RRBh1ElbcwGexbPs,9701
21
+ fin_infra/budgets/ease.py,sha256=SB0hEuKw7avb32Ax1jY471hh7Y4Nhc4YtNOL7z1BOs0,8172
22
+ fin_infra/budgets/models.py,sha256=q_6aqzC3fT-h5lJfTy2YjwBJgHAJha9e18KkuyxG2Dc,14304
23
23
  fin_infra/budgets/scaffold_templates/README.md,sha256=FBtRSrQSkg7Xp8SPiuSmzGR94I2zzjOckb0_vKtcksY,14084
24
24
  fin_infra/budgets/scaffold_templates/__init__.py,sha256=zQXi_vY4QS5a4Ddi2mVXMLM--rP021h5D94ohIC1dkU,172
25
25
  fin_infra/budgets/scaffold_templates/models.py.tmpl,sha256=rpKhXwnx1gQjV_GGVqs8CvkFNHctrYcagC9swE14pVU,7553
26
26
  fin_infra/budgets/scaffold_templates/repository.py.tmpl,sha256=khFgnQnVNnOo8DWYDmYz58MvdeSJpoS9QvTXcGhsa8g,10023
27
27
  fin_infra/budgets/scaffold_templates/schemas.py.tmpl,sha256=x5gSQ7Kiuq08tum5joKmeY0ib2r3ekLKk09dFOl0PS0,5658
28
- fin_infra/budgets/templates.py,sha256=Sbc7RcHXscq34g4t7J8OXM2Kfkt5DHuvqVnFU0Jiddc,12112
29
- fin_infra/budgets/tracker.py,sha256=gF68a6sivdSfVDZanSwdGdhEcg6SEic0K_rO9ed7lvI,16460
28
+ fin_infra/budgets/templates.py,sha256=V1Hfh0P_6iMdaSLtnFm9iEvXDUyFb7yqBObat98hDjU,12093
29
+ fin_infra/budgets/tracker.py,sha256=RDPfYhOLSAp-RFlTWFTpYaB5mysu3QfQhhcqJzmVWUU,16444
30
30
  fin_infra/cashflows/__init__.py,sha256=N9ZGKwIrQs-eiZZxqCow2aLBhW1MKcJoasQjGtyV6l4,8517
31
31
  fin_infra/cashflows/core.py,sha256=YWvF0DVOfBkvO_MuDODjE-V_g52H2ixFRH_TjMXExDE,541
32
32
  fin_infra/categorization/__init__.py,sha256=efLje12AW-ec9Vs5ynb41r4XCIWx5a-Z9WoGb3kQdIE,2030
33
33
  fin_infra/categorization/add.py,sha256=-I-V7R8vWj2-B8yjtFn_pNryQV5uascFR18a1Ltcqm0,6247
34
- fin_infra/categorization/ease.py,sha256=bomEtJAgwk9uiemNt1rk-IsTjJIhyJn0GJ_c58YEmJs,5836
34
+ fin_infra/categorization/ease.py,sha256=gvlVb_TL7kwm5oM_gWdrhZt4cJOSvYEgGipNJKK8CpM,5820
35
35
  fin_infra/categorization/engine.py,sha256=u1ZYDmuB0gZeJHn-CKvvwd7Z1HFiB-S91YuV_ZrK_Rc,12117
36
36
  fin_infra/categorization/llm_layer.py,sha256=CSe6q-cKQgnKUKSItdDQwzMd8K1dsp-Mwua7SWZ9Ux0,12670
37
- fin_infra/categorization/models.py,sha256=-rGXR0RW2EU_FQ7ZfDWBIXxx8QGJDxeBF9zKGYyVgqY,5931
37
+ fin_infra/categorization/models.py,sha256=A8m3hAIbsUUV3eNjXpTiPWD1sYGjSdXEByTS3ZZASEA,5894
38
38
  fin_infra/categorization/rules.py,sha256=IpDnHBeuykRdu5vs3Lph4Y9-3RseIjjleQ5hZphQvNk,12849
39
39
  fin_infra/categorization/taxonomy.py,sha256=p-tSOwJ0O-rFZ1LIlHSYdaYdSc65084j0fdMI_6LW84,13251
40
40
  fin_infra/chat/__init__.py,sha256=-gufQz-rOZVXkcTUcCcXdG2oGL8csJBxwSuOMN_5aVc,6324
@@ -61,7 +61,7 @@ fin_infra/documents/__init__.py,sha256=Ub1hbX3PTrBSsBdcbL8PFf6oq8jSH4pYxW45-qOYP
61
61
  fin_infra/documents/add.py,sha256=2RSf_i39-JUT6c_jVsEQwW3FfQSlH4H9Z0t_H0vz86U,8091
62
62
  fin_infra/documents/analysis.py,sha256=UWKTR_qXZoumItXUwHFMH3LAG2vFVlPITbIC8Y_r0s8,13970
63
63
  fin_infra/documents/ease.py,sha256=z8hLaYhO1znBbcA9F10uNVa1BnH4rm3Jm5pexQwyiJY,9574
64
- fin_infra/documents/models.py,sha256=RpazzhE9qBcTRk_NO7Dl_uvcHw9UpGtnpXFcJURMtYc,6865
64
+ fin_infra/documents/models.py,sha256=VEkJynM2REAf37ZachIz7_MfTevpzmG4Cmpku388YwE,6831
65
65
  fin_infra/documents/ocr.py,sha256=8I0XhiEdDhew55jUxOB5whoO5OcSXD71xiv8HhnyYno,9565
66
66
  fin_infra/documents/storage.py,sha256=HXfubl-4PspvA-U8czB-4TgZKI1kOlw440jYHRDzjoY,10300
67
67
  fin_infra/exceptions.py,sha256=k7L3vfXcONM2-AqmqNhQvOHobVBrLPTYp44cMUxuP3o,16951
@@ -81,8 +81,8 @@ fin_infra/insights/aggregator.py,sha256=HtaJipSA-O_HVComBcyQdkGs6guoW81sYwRYHXGd
81
81
  fin_infra/insights/models.py,sha256=xov_YV8oBLJt3YdyVjbryRfcXqmGeGiPvZsZHSbvtl8,3202
82
82
  fin_infra/investments/__init__.py,sha256=DQJa32LdXspbtxydric-AfT3o5iDkqIByAsSriSfGak,6729
83
83
  fin_infra/investments/add.py,sha256=UV2_99z1p8cUiifVFJXOF0lGd0ucMgZ5s9N7IFyE_NY,17193
84
- fin_infra/investments/ease.py,sha256=d5ISfxpCius6JM2LZNReztW6-IizaqoxNU4aEbXWA74,9487
85
- fin_infra/investments/models.py,sha256=UyOvtU1-uxrgE0zLP5WY3wpn3YOdOv6_b05oGismxNU,18412
84
+ fin_infra/investments/ease.py,sha256=s716nYv5kzkJ-vGbVeIumqAN0-TEl1ai-If84uP9h68,9474
85
+ fin_infra/investments/models.py,sha256=Es1GdrB640k50gtdzTtfh_66nxvYxORq0xbtXryoRGo,18319
86
86
  fin_infra/investments/providers/__init__.py,sha256=V1eIzz6EnGJ-pq-9L3S2-evmcExF-YdZfd5P6JMyDtc,383
87
87
  fin_infra/investments/providers/base.py,sha256=1plM-o_fV6jQyyPnpZT5q8ir96oyxvh0zwA3qpiVIFY,9810
88
88
  fin_infra/investments/providers/plaid.py,sha256=X42cMpsHNcOPcYvl73jrP8x7nQ399n2_it3Ko9UIkI0,18751
@@ -94,14 +94,14 @@ fin_infra/investments/scaffold_templates/repository.py.tmpl,sha256=XwOEpQZfuXut1
94
94
  fin_infra/investments/scaffold_templates/schemas.py.tmpl,sha256=knWmn-Kyr7AdgPD4ZPMb6T49ZuPXeuOMqmjYNyA0CA0,5451
95
95
  fin_infra/markets/__init__.py,sha256=Ba9iUTfDkkla8GwPo3gULTJYUDQh5gSgGN15BxHINpo,9865
96
96
  fin_infra/models/__init__.py,sha256=y94RJ_1-bzgNUCxqE76X56WIOk3-El_Jueqy7uB0rb8,860
97
- fin_infra/models/accounts.py,sha256=ExQimE2O5dyugFMW7nCboWbWcaX3Nnl5Gg-B1XLSdIk,1138
97
+ fin_infra/models/accounts.py,sha256=m_HdYHOe_m0GLnc_f5njo9n-zscWu-C0rJB6SAd5-aY,1098
98
98
  fin_infra/models/brokerage.py,sha256=TV5KMe78e-ttjcUbZIfdGo3x0NisAQ3puwv_ehtgSHc,8312
99
99
  fin_infra/models/candle.py,sha256=5JeqUvqlFfYE61uViBeLKZCh2wKdJZH7Pvi-TLzN4FI,536
100
100
  fin_infra/models/credit.py,sha256=rSdSURsMe9_i2gxmwPTDwNQWOuM2zutL-OhvHsnbtmw,12144
101
101
  fin_infra/models/money.py,sha256=63pdGD1WBMHicJ1w7pbU1g5fqt4gIzPuqQQ2-NSlBuc,401
102
102
  fin_infra/models/quotes.py,sha256=-YBzgnjjCihRAprUdaPRtfwKrgliDIGgqAnaM69VbDU,521
103
103
  fin_infra/models/tax.py,sha256=as40J9h24BB7LmeaIfay509UaYEctmk5CPW9CfcjWZc,15657
104
- fin_infra/models/transactions.py,sha256=8C4taS9ZGLeFYGqG7JGkLq5ZinvAjHPLrKyRu_Sy5_0,791
104
+ fin_infra/models/transactions.py,sha256=gzc6hLKKU6VUWGshqw2WChU_x0qyXhdvOGwmj9H_H3I,790
105
105
  fin_infra/net_worth/__init__.py,sha256=4LzVM2Y0OkBlZoD5FUAbLwtORp1okYcsYc4zj0P8C7k,3452
106
106
  fin_infra/net_worth/add.py,sha256=QWfHIHJs2CV99WRBqjQ2OteiOrn5cR9nurmxTF9v5rg,23191
107
107
  fin_infra/net_worth/aggregator.py,sha256=9Kx2vUR71QwqYZdGaCfmYrJ1hNxzd1EEuAdWJoNjqTI,12780
@@ -116,12 +116,12 @@ fin_infra/net_worth/scaffold_templates/models.py.tmpl,sha256=9BKsoD08RZbSdOm0wFT
116
116
  fin_infra/net_worth/scaffold_templates/repository.py.tmpl,sha256=DSErnNxeAe4pWeefARRK3bU0hHltqdIFffENfVwdd7c,12798
117
117
  fin_infra/net_worth/scaffold_templates/schemas.py.tmpl,sha256=VkFsxyZx4DFDhXDhn-7KT0IgrXCvgaS5ZdWbjyezWj0,4709
118
118
  fin_infra/normalization/__init__.py,sha256=O-8YvPbKWgfdC6E2qzXuCFLJ6cM-CS8GmBO8TDcJ9X0,6246
119
- fin_infra/normalization/currency_converter.py,sha256=uuu8ASa5ppEniWLEVEpiDxXjZzln9nopWrhrATcD6Z4,7058
120
- fin_infra/normalization/models.py,sha256=gNC9chpbQPRN58V2j__VEPVNReO1N8jH_AHObwGPWu0,1928
119
+ fin_infra/normalization/currency_converter.py,sha256=dhiSAYD7onQgCiT17TtGzL4czeX_qJ9_yW2zsvvBErg,7018
120
+ fin_infra/normalization/models.py,sha256=h8zC2n642WwVWC1yvx_VCILDOgUwPDrZwjb0QIbA8cE,1873
121
121
  fin_infra/normalization/providers/__init__.py,sha256=LFU1tB2hVO42Yrkw-IDpPexD4mIlxob9lRrJEeGYqpE,559
122
122
  fin_infra/normalization/providers/exchangerate.py,sha256=I_2TK_LLOcYCy3HcN9Nut3UwqTWEOX26GktmX3cczvY,6340
123
123
  fin_infra/normalization/providers/static_mappings.py,sha256=m14VHmTZipbqrgyE0ABToabVx-pDcyB577LNWrACEUM,6809
124
- fin_infra/normalization/symbol_resolver.py,sha256=M7Li7LFiH4xpvxXcYQlJyk0iqgqpwaj6zQKsTzWZzas,8130
124
+ fin_infra/normalization/symbol_resolver.py,sha256=UAfhxmiINpQhE8tPLh-GMCfDZGmK5wf2DwtYZP3sSUo,8093
125
125
  fin_infra/obs/__init__.py,sha256=kMMVl0fdwtJtZeKiusTuw0iO61Jo9-HNXsLmn3ffLRE,631
126
126
  fin_infra/obs/classifier.py,sha256=JasWCqSkYjllJNZ5Gwbrd53ZhLwhYNZ0i2nbTcklEog,5155
127
127
  fin_infra/providers/__init__.py,sha256=jxhQm79T6DVXf7Wpy7luL-p50cE_IMUbjt4o3apzJQU,768
@@ -158,13 +158,13 @@ fin_infra/scaffold/__init__.py,sha256=IfL_CHHMpQB1efqY37BlIu07356tLaeVI2Mv3C0qYD
158
158
  fin_infra/scaffold/budgets.py,sha256=qLnyPh-a-ZrslstaxRtR__Bpwlz0EncSdVnCM7fDBI4,9481
159
159
  fin_infra/scaffold/goals.py,sha256=C1mexWRbWV3aRdmiT65LpKMMWzhTWTX_upeCcsjTcWw,9684
160
160
  fin_infra/security/__init__.py,sha256=wM341EZioKtfTawpFCciaFh0FlpHHkoEoviW9pGYqpI,1363
161
- fin_infra/security/add.py,sha256=Y_XXNd-FTpSaHmO4xkYvkW4CLlFGCuQWe9gJ7WuwiLY,2746
162
- fin_infra/security/audit.py,sha256=TekYWCOUT9Sf1sDS2-EEREtW7nhWo3H7iaLVbLPx308,3322
161
+ fin_infra/security/add.py,sha256=SIMBlSQ4FWCNiBfzgbzeVpRoo2JS3dqW-51uvq3hQU0,2715
162
+ fin_infra/security/audit.py,sha256=nb4H2Jq7XwalHLOx6LqleUtSXkBRz6u65LJ7lyuQNDo,3276
163
163
  fin_infra/security/encryption.py,sha256=Cr_9xhBiUvYx1tDjCiLTzyY9NRTlqljWGgswqXT499Y,6139
164
164
  fin_infra/security/models.py,sha256=s8dsvpxP-7DThUZl3Fvr3S6KVAfL5r3TKcd4uj_hCTE,1689
165
165
  fin_infra/security/pii_filter.py,sha256=nVqzbG_-Z6m8fzzS1KBy9yFwaZ8eRrU00Q72fSkVyU8,8307
166
166
  fin_infra/security/pii_patterns.py,sha256=SM-o7cL6NdgkOmtBedsN2nJZ5QPbeYehZdYmAujk8Y8,3070
167
- fin_infra/security/token_store.py,sha256=qgxhBKwhtVpchyHv30mM-cttuGZlzZvZLC4Oa-gTTeg,6075
167
+ fin_infra/security/token_store.py,sha256=FsfoAkIMQ2NRfkBuyG1eH30nnfO3w_V5tDPAZwUj9Os,6041
168
168
  fin_infra/settings.py,sha256=11JgIhjGwWnwixV-hveEWpoWd_JC0ixLnOQoLWCiwNo,1387
169
169
  fin_infra/tax/__init__.py,sha256=U0EUKQwbDqnAYwU8WRx6AD07TaB-ELdKGISOQ-904lw,6103
170
170
  fin_infra/tax/add.py,sha256=dYigcgQFEP38NLoNuhh7vpSoy07sWX6Z_DTp1tWFJXw,14553
@@ -173,8 +173,8 @@ fin_infra/utils/__init__.py,sha256=gKacLSWMAis--pasd8AuVN7ap0e9Z1TjRGur0J23EDo,6
173
173
  fin_infra/utils/http.py,sha256=rDEgYsEBrEe75ml5RA-iSs3xeU5W-3j-czJlT7WbrM4,632
174
174
  fin_infra/utils/retry.py,sha256=YiyTgy26eJ1ah7fE2_-ZPa4hv4bIT4OzjYolkNWb5j0,1057
175
175
  fin_infra/version.py,sha256=4t_crzhrLum--oyowUMxtjBTzUtWp7oRTF22ewEvJG4,49
176
- fin_infra-0.1.83.dist-info/LICENSE,sha256=wK-Ya7Ylxa38dSIZRhvNj1ZVLIrHC-BAI8v38PNADiA,1061
177
- fin_infra-0.1.83.dist-info/METADATA,sha256=X4R2GNABxoN7u8D1d90fXL8XWlJwnyFUaprYaAW6s9o,10479
178
- fin_infra-0.1.83.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
179
- fin_infra-0.1.83.dist-info/entry_points.txt,sha256=Sr1uikvALZMeKm-DIkeKG4L9c4SNqysXGO_IRF8_9eU,53
180
- fin_infra-0.1.83.dist-info/RECORD,,
176
+ fin_infra-0.1.85.dist-info/LICENSE,sha256=wK-Ya7Ylxa38dSIZRhvNj1ZVLIrHC-BAI8v38PNADiA,1061
177
+ fin_infra-0.1.85.dist-info/METADATA,sha256=y13UC--sOtnmoLfYI4s1Z_THKBcpLzH3JaYaXOH41y8,10479
178
+ fin_infra-0.1.85.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
179
+ fin_infra-0.1.85.dist-info/entry_points.txt,sha256=Sr1uikvALZMeKm-DIkeKG4L9c4SNqysXGO_IRF8_9eU,53
180
+ fin_infra-0.1.85.dist-info/RECORD,,