contracts-hj3415 0.2.0__tar.gz → 0.5.0__tar.gz

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.
Files changed (31) hide show
  1. {contracts_hj3415-0.2.0 → contracts_hj3415-0.5.0}/PKG-INFO +1 -2
  2. {contracts_hj3415-0.2.0 → contracts_hj3415-0.5.0}/pyproject.toml +21 -4
  3. contracts_hj3415-0.5.0/src/contracts_hj3415/analysis/features.py +34 -0
  4. contracts_hj3415-0.5.0/src/contracts_hj3415/analysis/payloads.py +8 -0
  5. contracts_hj3415-0.5.0/src/contracts_hj3415/analysis/types.py +9 -0
  6. contracts_hj3415-0.5.0/src/contracts_hj3415/common/__init__.py +0 -0
  7. contracts_hj3415-0.5.0/src/contracts_hj3415/common/envelope.py +46 -0
  8. contracts_hj3415-0.5.0/src/contracts_hj3415/common/types.py +17 -0
  9. contracts_hj3415-0.5.0/src/contracts_hj3415/nfs/__init__.py +0 -0
  10. contracts_hj3415-0.5.0/src/contracts_hj3415/nfs/c101.py +28 -0
  11. contracts_hj3415-0.5.0/src/contracts_hj3415/nfs/c103.py +35 -0
  12. contracts_hj3415-0.5.0/src/contracts_hj3415/nfs/c104.py +40 -0
  13. contracts_hj3415-0.5.0/src/contracts_hj3415/nfs/c106.py +27 -0
  14. contracts_hj3415-0.5.0/src/contracts_hj3415/nfs/c108.py +19 -0
  15. contracts_hj3415-0.5.0/src/contracts_hj3415/nfs/payloads.py +14 -0
  16. contracts_hj3415-0.5.0/src/contracts_hj3415/nfs/types.py +49 -0
  17. contracts_hj3415-0.5.0/src/contracts_hj3415/py.typed +0 -0
  18. contracts_hj3415-0.5.0/src/contracts_hj3415/universe/__init__.py +0 -0
  19. contracts_hj3415-0.5.0/src/contracts_hj3415/universe/krx300.py +20 -0
  20. contracts_hj3415-0.5.0/src/contracts_hj3415/universe/payloads.py +8 -0
  21. contracts_hj3415-0.5.0/src/contracts_hj3415/universe/types.py +6 -0
  22. contracts_hj3415-0.2.0/src/contracts/.DS_Store +0 -0
  23. contracts_hj3415-0.2.0/src/contracts/nfs/c101.py +0 -52
  24. contracts_hj3415-0.2.0/src/contracts/nfs/c103.py +0 -19
  25. contracts_hj3415-0.2.0/src/contracts/nfs/c104.py +0 -24
  26. contracts_hj3415-0.2.0/src/contracts/nfs/c106.py +0 -37
  27. contracts_hj3415-0.2.0/src/contracts/nfs/c108.py +0 -61
  28. {contracts_hj3415-0.2.0 → contracts_hj3415-0.5.0}/LICENSE +0 -0
  29. {contracts_hj3415-0.2.0 → contracts_hj3415-0.5.0}/README.md +0 -0
  30. {contracts_hj3415-0.2.0/src/contracts → contracts_hj3415-0.5.0/src/contracts_hj3415}/__init__.py +0 -0
  31. {contracts_hj3415-0.2.0/src/contracts/nfs → contracts_hj3415-0.5.0/src/contracts_hj3415/analysis}/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: contracts-hj3415
3
- Version: 0.2.0
3
+ Version: 0.5.0
4
4
  Summary: DTO pakages for hj3415
5
5
  Keywords: example,demo
6
6
  Author-email: Hyungjin Kim <hj3415@gmail.com>
@@ -10,6 +10,5 @@ Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Typing :: Typed
12
12
  License-File: LICENSE
13
- Requires-Dist: pydantic>=2.7
14
13
 
15
14
 
@@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"
4
4
 
5
5
  [project]
6
6
  name = "contracts-hj3415" # PyPI 이름 (하이픈 허용)
7
- version = "0.2.0"
7
+ version = "0.5.0"
8
8
  description = "DTO pakages for hj3415"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -18,9 +18,26 @@ classifiers = [
18
18
  ]
19
19
 
20
20
  dependencies = [
21
- "pydantic>=2.7",
22
21
  ]
23
22
 
24
23
  [tool.flit.module]
25
- name = "contracts"
26
- path = "src/contracts"
24
+ name = "contracts_hj3415"
25
+ path = "src/contracts_hj3415"
26
+
27
+
28
+ [tool.pyright]
29
+ typeCheckingMode = "strict"
30
+
31
+ pythonVersion = "3.11"
32
+ pythonPlatform = "All"
33
+
34
+ reportMissingImports = true
35
+ reportMissingTypeStubs = false
36
+
37
+ exclude = [
38
+ "**/__pycache__",
39
+ ".venv",
40
+ "build",
41
+ "tests",
42
+ "dist",
43
+ ]
@@ -0,0 +1,34 @@
1
+ # contracts_hj3415/analysis/features.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Any, Literal, TypedDict
5
+
6
+ from contracts_hj3415.nfs.types import Endpoint, MetricKey
7
+
8
+ AggKind = Literal["sum", "avg"]
9
+ PeriodKind = Literal["y", "q"]
10
+
11
+
12
+ class FeatureValue(TypedDict):
13
+ value: float | None
14
+ picked: str | None
15
+ from_kind: PeriodKind | None
16
+
17
+
18
+ class MetricDiagnostics(TypedDict):
19
+ ok: bool
20
+ reason: str | None
21
+ details: dict[str, Any] # pyright: ignore[reportExplicitAny]
22
+
23
+
24
+ class LatestAndTtm(TypedDict):
25
+ latest_y: FeatureValue
26
+ latest_q: FeatureValue
27
+ ttm_q: FeatureValue
28
+ chosen: FeatureValue
29
+ diag: MetricDiagnostics
30
+
31
+
32
+ class FeaturesPayload(TypedDict):
33
+ endpoint: Endpoint
34
+ blocks: dict[MetricKey, LatestAndTtm]
@@ -0,0 +1,8 @@
1
+ # contracts_hj3415/analysis/payloads.py
2
+ from __future__ import annotations
3
+
4
+ from typing import TypeAlias
5
+
6
+ from contracts_hj3415.analysis.features import FeaturesPayload
7
+
8
+ AnalysisPayloadDTO: TypeAlias = FeaturesPayload
@@ -0,0 +1,9 @@
1
+ # contracts_hj3415/analysis/types.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Literal, TypeAlias
5
+
6
+ AnalysisService: TypeAlias = Literal[
7
+ "features",
8
+ "red",
9
+ ]
@@ -0,0 +1,46 @@
1
+ # contracts_hj3415/common/envelope.py
2
+ from __future__ import annotations
3
+
4
+ from datetime import datetime
5
+ from typing import TypedDict
6
+
7
+ from contracts_hj3415.analysis.payloads import AnalysisPayloadDTO
8
+ from contracts_hj3415.common.types import EnvelopeMeta, Topic
9
+
10
+ # topic별 payload 유니온
11
+ from contracts_hj3415.nfs.payloads import NfsPayloadDTO
12
+ from contracts_hj3415.universe.payloads import UniversePayloadDTO
13
+
14
+
15
+ class EnvelopeDTO(TypedDict):
16
+ """
17
+ 모노레포 공통 운반 상자 DTO
18
+
19
+ - key: topic별 식별자(문자열). 예)
20
+ - nfs: "005930"
21
+ - universe: "krx300"
22
+ - analysis: "red:005930"
23
+ """
24
+
25
+ topic: Topic
26
+ key: str
27
+ asof: datetime
28
+ payload: NfsPayloadDTO | UniversePayloadDTO | AnalysisPayloadDTO
29
+ meta: EnvelopeMeta
30
+
31
+
32
+ def make_envelope(
33
+ *,
34
+ topic: Topic,
35
+ key: str,
36
+ asof: datetime,
37
+ payload: NfsPayloadDTO | UniversePayloadDTO | AnalysisPayloadDTO,
38
+ meta: EnvelopeMeta | None = None,
39
+ ) -> EnvelopeDTO:
40
+ return {
41
+ "topic": topic,
42
+ "key": key,
43
+ "asof": asof,
44
+ "payload": payload,
45
+ "meta": meta or EnvelopeMeta(),
46
+ }
@@ -0,0 +1,17 @@
1
+ # contracts_hj3415/common/types.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Literal, TypeAlias, TypedDict
5
+
6
+ Topic: TypeAlias = Literal["nfs", "universe", "analysis"]
7
+
8
+ CodeKey: TypeAlias = str
9
+
10
+ Num: TypeAlias = float | int | None
11
+
12
+
13
+ class EnvelopeMeta(TypedDict, total=False):
14
+ source: str # "scraper2" / "krx" / "analyser2" / ...
15
+ trace_id: str
16
+ note: str
17
+ tags: list[str]
@@ -0,0 +1,28 @@
1
+ # contracts_hj3415/nfs/c101.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Any, Literal, TypeAlias
5
+
6
+ from contracts_hj3415.common.types import Num
7
+ from typing_extensions import TypedDict
8
+
9
+ from .types import MetricKey, PeriodKey
10
+
11
+ Scalar: TypeAlias = str | float | int | None
12
+ ValuesMap: TypeAlias = dict[PeriodKey, Num]
13
+
14
+
15
+ class C101Blocks(TypedDict):
16
+ 요약: dict[str, Scalar]
17
+ 시세: dict[str, Scalar]
18
+ 주주현황: list[dict[str, Scalar]]
19
+ 기업개요: dict[str, Scalar]
20
+ 펀더멘털: dict[MetricKey, ValuesMap]
21
+
22
+ 어닝서프라이즈: dict[str, Any] # pyright: ignore[reportExplicitAny]
23
+ 연간컨센서스: dict[MetricKey, ValuesMap]
24
+
25
+
26
+ class C101Payload(TypedDict):
27
+ endpoint: Literal["c101"]
28
+ blocks: C101Blocks
@@ -0,0 +1,35 @@
1
+ # contracts_hj3415/nfs/c103.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Literal, TypeAlias
5
+
6
+ from contracts_hj3415.common.types import Num
7
+ from typing_extensions import TypedDict
8
+
9
+ from .types import MetricKey, PeriodKey
10
+
11
+ C103ValuesMap: TypeAlias = dict[PeriodKey, Num]
12
+
13
+
14
+ class C103Blocks(TypedDict):
15
+ 손익계산서y: dict[MetricKey, C103ValuesMap]
16
+ 손익계산서q: dict[MetricKey, C103ValuesMap]
17
+ 재무상태표y: dict[MetricKey, C103ValuesMap]
18
+ 재무상태표q: dict[MetricKey, C103ValuesMap]
19
+ 현금흐름표y: dict[MetricKey, C103ValuesMap]
20
+ 현금흐름표q: dict[MetricKey, C103ValuesMap]
21
+
22
+
23
+ class C103Labels(TypedDict):
24
+ 손익계산서y: dict[MetricKey, str]
25
+ 손익계산서q: dict[MetricKey, str]
26
+ 재무상태표y: dict[MetricKey, str]
27
+ 재무상태표q: dict[MetricKey, str]
28
+ 현금흐름표y: dict[MetricKey, str]
29
+ 현금흐름표q: dict[MetricKey, str]
30
+
31
+
32
+ class C103Payload(TypedDict):
33
+ endpoint: Literal["c103"]
34
+ blocks: C103Blocks
35
+ labels: C103Labels
@@ -0,0 +1,40 @@
1
+ # contracts_hj3415/nfs/c104.py
2
+ from __future__ import annotations
3
+ from typing import Literal, TypeAlias
4
+
5
+ from contracts_hj3415.common.types import Num
6
+ from typing_extensions import TypedDict
7
+ from .types import MetricKey, PeriodKey
8
+
9
+ C104ValuesMap:TypeAlias = dict[PeriodKey, Num]
10
+
11
+ class C104Blocks(TypedDict):
12
+ 수익성y: dict[MetricKey, C104ValuesMap]
13
+ 성장성y: dict[MetricKey, C104ValuesMap]
14
+ 안정성y: dict[MetricKey, C104ValuesMap]
15
+ 활동성y: dict[MetricKey, C104ValuesMap]
16
+ 가치분석y: dict[MetricKey, C104ValuesMap]
17
+ 수익성q: dict[MetricKey, C104ValuesMap]
18
+ 성장성q: dict[MetricKey, C104ValuesMap]
19
+ 안정성q: dict[MetricKey, C104ValuesMap]
20
+ 활동성q: dict[MetricKey, C104ValuesMap]
21
+ 가치분석q: dict[MetricKey, C104ValuesMap]
22
+
23
+
24
+ class C104Labels(TypedDict):
25
+ 수익성y: dict[MetricKey, str]
26
+ 성장성y: dict[MetricKey, str]
27
+ 안정성y: dict[MetricKey, str]
28
+ 활동성y: dict[MetricKey, str]
29
+ 가치분석y: dict[MetricKey, str]
30
+ 수익성q: dict[MetricKey, str]
31
+ 성장성q: dict[MetricKey, str]
32
+ 안정성q: dict[MetricKey, str]
33
+ 활동성q: dict[MetricKey, str]
34
+ 가치분석q: dict[MetricKey, str]
35
+
36
+
37
+ class C104Payload(TypedDict):
38
+ endpoint: Literal["c104"]
39
+ blocks: C104Blocks
40
+ labels: C104Labels
@@ -0,0 +1,27 @@
1
+ # contracts_hj3415/nfs/c106.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Literal, TypeAlias
5
+
6
+ from contracts_hj3415.common.types import CodeKey, Num
7
+ from typing_extensions import TypedDict
8
+
9
+ from .types import MetricKey
10
+
11
+ C106ValuesMap: TypeAlias = dict[CodeKey, Num]
12
+
13
+
14
+ class C106Blocks(TypedDict):
15
+ y: dict[MetricKey, C106ValuesMap]
16
+ q: dict[MetricKey, C106ValuesMap]
17
+
18
+
19
+ class C106Labels(TypedDict):
20
+ y: dict[MetricKey, str]
21
+ q: dict[MetricKey, str]
22
+
23
+
24
+ class C106Payload(TypedDict):
25
+ endpoint: Literal["c106"]
26
+ blocks: C106Blocks
27
+ labels: C106Labels
@@ -0,0 +1,19 @@
1
+ # contracts_hj3415/nfs/c108.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Literal, TypeAlias
5
+
6
+ from typing_extensions import TypedDict
7
+
8
+ from .types import MetricKey
9
+
10
+ C108ValuesMap: TypeAlias = dict[MetricKey, str | int | None]
11
+
12
+
13
+ class C108Blocks(TypedDict):
14
+ 리포트: list[C108ValuesMap]
15
+
16
+
17
+ class C108Payload(TypedDict):
18
+ endpoint: Literal["c108"]
19
+ blocks: C108Blocks
@@ -0,0 +1,14 @@
1
+ # contracts_hj3415/nfs/payloads.py
2
+ from __future__ import annotations
3
+
4
+ from typing import TypeAlias
5
+
6
+ from contracts_hj3415.nfs.c101 import C101Payload
7
+ from contracts_hj3415.nfs.c103 import C103Payload
8
+ from contracts_hj3415.nfs.c104 import C104Payload
9
+ from contracts_hj3415.nfs.c106 import C106Payload
10
+ from contracts_hj3415.nfs.c108 import C108Payload
11
+
12
+ NfsPayloadDTO: TypeAlias = (
13
+ C101Payload | C103Payload | C104Payload | C106Payload | C108Payload
14
+ )
@@ -0,0 +1,49 @@
1
+ # contracts_hj3415/nfs/types.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Literal, TypeAlias
5
+
6
+ C101BlockKey: TypeAlias = Literal[
7
+ "요약", "시세", "주주현황", "기업개요", "펀더멘털", "어닝서프라이즈", "연간컨센서스"
8
+ ]
9
+
10
+ C103BlockKey: TypeAlias = Literal[
11
+ "손익계산서y",
12
+ "손익계산서q",
13
+ "재무상태표y",
14
+ "재무상태표q",
15
+ "현금흐름표y",
16
+ "현금흐름표q",
17
+ ]
18
+
19
+ C104BlockKey: TypeAlias = Literal[
20
+ "수익성y",
21
+ "성장성y",
22
+ "안정성y",
23
+ "활동성y",
24
+ "가치분석y",
25
+ "수익성q",
26
+ "성장성q",
27
+ "안정성q",
28
+ "활동성q",
29
+ "가치분석q",
30
+ ]
31
+
32
+ C106BlockKey: TypeAlias = Literal[
33
+ "y",
34
+ "q",
35
+ ]
36
+
37
+ C108BlockKey: TypeAlias = Literal["리포트",]
38
+
39
+
40
+ # General====================
41
+
42
+ MetricKey: TypeAlias = str
43
+ PeriodKey: TypeAlias = str
44
+
45
+ Endpoint: TypeAlias = Literal["c101", "c103", "c104", "c106", "c108"]
46
+
47
+ BlockKey: TypeAlias = (
48
+ C101BlockKey | C103BlockKey | C104BlockKey | C106BlockKey | C108BlockKey
49
+ )
File without changes
@@ -0,0 +1,20 @@
1
+ # contracts_hj3415/universe/dto.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Any, TypedDict
5
+
6
+ from contracts_hj3415.common.types import CodeKey
7
+ from contracts_hj3415.universe.types import Universe
8
+
9
+
10
+ class UniverseItem(TypedDict, total=False):
11
+ code: CodeKey
12
+ name: str
13
+ market: str
14
+ meta: dict[str, Any] # pyright: ignore[reportExplicitAny]
15
+
16
+
17
+ class KRX300Payload(TypedDict):
18
+ source: str
19
+ universe: Universe
20
+ blocks: list[UniverseItem]
@@ -0,0 +1,8 @@
1
+ # contracts_hj3415/universe/payloads.py
2
+ from __future__ import annotations
3
+
4
+ from typing import TypeAlias
5
+
6
+ from contracts_hj3415.universe.krx300 import KRX300Payload
7
+
8
+ UniversePayloadDTO: TypeAlias = KRX300Payload
@@ -0,0 +1,6 @@
1
+ # contracts_hj3415/universe/types.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Literal, TypeAlias
5
+
6
+ Universe: TypeAlias = Literal["KRX300",]
@@ -1,52 +0,0 @@
1
- # contracts/nfs/c101.py
2
- from __future__ import annotations
3
-
4
- from pydantic import BaseModel, Field
5
- from typing import Optional
6
-
7
-
8
- class C101DTO(BaseModel):
9
- # 기본 식별 정보
10
- 종목명: str
11
- 코드: str
12
- 날짜: str
13
- 업종: str
14
-
15
- # 재무 지표
16
- eps: Optional[int] = None
17
- bps: Optional[int] = None
18
- per: Optional[float] = None
19
- 업종per: Optional[float] = None
20
- pbr: Optional[float] = None
21
- 배당수익률: Optional[float] = None
22
-
23
- # 주가 정보
24
- 주가: Optional[int] = None
25
- 전일대비: Optional[int] = None
26
- 수익률: Optional[float] = None
27
-
28
- 최고52: Optional[int] = None
29
- 최저52: Optional[int] = None
30
-
31
- 거래량: Optional[int] = None
32
- 거래대금: Optional[int] = None
33
- 시가총액: Optional[int] = None
34
-
35
- 베타52주: Optional[float] = None
36
-
37
- 발행주식: Optional[int] = None
38
- 유동비율: Optional[float] = None
39
- 외국인지분율: Optional[float] = None
40
-
41
- # 기간 수익률
42
- 수익률1M: Optional[float] = None
43
- 수익률3M: Optional[float] = None
44
- 수익률6M: Optional[float] = None
45
- 수익률1Y: Optional[float] = None
46
-
47
- # 텍스트 정보
48
- 개요: str = ""
49
-
50
- class Config:
51
- # 한글 필드명을 그대로 쓰기 위한 옵션
52
- populate_by_name = True
@@ -1,19 +0,0 @@
1
- # contracts/nfs/c103.py
2
- from __future__ import annotations
3
-
4
- from pydantic import BaseModel, ConfigDict, Field
5
-
6
- Num = float | int | None
7
- Row = dict[str, Num] # 예: {"2020/12": 1.0, "전년대비": 2.0, ...}
8
- ItemsMap = dict[str, Row] # 예: {"매출액(수익)": {...}, ...}
9
-
10
- class C103DTO(BaseModel):
11
- 코드: str
12
- 손익계산서y: ItemsMap = Field(default_factory=dict)
13
- 손익계산서q: ItemsMap = Field(default_factory=dict)
14
- 재무상태표y: ItemsMap = Field(default_factory=dict)
15
- 재무상태표q: ItemsMap = Field(default_factory=dict)
16
- 현금흐름표y: ItemsMap = Field(default_factory=dict)
17
- 현금흐름표q: ItemsMap = Field(default_factory=dict)
18
-
19
- model_config = ConfigDict(extra="ignore")
@@ -1,24 +0,0 @@
1
- # contracts/nfs/c104.py
2
- from __future__ import annotations
3
-
4
- from pydantic import BaseModel, ConfigDict, Field
5
-
6
- Num = float | int | None
7
- Row = dict[str, Num] # 예: {"2020/12": 1.0, "전년대비": 2.0, ...}
8
- ItemsMap = dict[str, Row] # 예: {"ROE": {...}, ...}
9
-
10
- class C104DTO(BaseModel):
11
- 코드: str
12
- 수익성y: ItemsMap = Field(default_factory=dict)
13
- 수익성q: ItemsMap = Field(default_factory=dict)
14
- 성장성y: ItemsMap = Field(default_factory=dict)
15
- 성장성q: ItemsMap = Field(default_factory=dict)
16
- 안정성y: ItemsMap = Field(default_factory=dict)
17
- 안정성q: ItemsMap = Field(default_factory=dict)
18
- 활동성y: ItemsMap = Field(default_factory=dict)
19
- 활동성q: ItemsMap = Field(default_factory=dict)
20
- 가치분석y: ItemsMap = Field(default_factory=dict)
21
- 가치분석q: ItemsMap = Field(default_factory=dict)
22
-
23
-
24
- model_config = ConfigDict(extra="ignore")
@@ -1,37 +0,0 @@
1
- # contracts/nfs/c106.py
2
- from __future__ import annotations
3
-
4
- from pydantic import BaseModel, Field, ConfigDict
5
-
6
- NumMap = dict[str, float | None]
7
-
8
-
9
- class C106Block(BaseModel):
10
- 전일종가: NumMap = Field(default_factory=dict)
11
- 시가총액: NumMap = Field(default_factory=dict)
12
- 자산총계: NumMap = Field(default_factory=dict)
13
- 부채총계: NumMap = Field(default_factory=dict)
14
-
15
- 매출액: NumMap = Field(default_factory=dict)
16
- 영업이익: NumMap = Field(default_factory=dict)
17
- 당기순이익: NumMap = Field(default_factory=dict)
18
- 당기순이익_지배: NumMap = Field(default_factory=dict)
19
-
20
- 영업이익률: NumMap = Field(default_factory=dict)
21
- 순이익률: NumMap = Field(default_factory=dict)
22
- ROE: NumMap = Field(default_factory=dict)
23
- 부채비율: NumMap = Field(default_factory=dict)
24
-
25
- PER: NumMap = Field(default_factory=dict)
26
- PBR: NumMap = Field(default_factory=dict)
27
-
28
- 투자의견: NumMap = Field(default_factory=dict)
29
- 목표주가: NumMap = Field(default_factory=dict)
30
-
31
- model_config = ConfigDict(extra="ignore")
32
-
33
-
34
- class C106DTO(BaseModel):
35
- 코드: str
36
- q: C106Block
37
- y: C106Block
@@ -1,61 +0,0 @@
1
- import math
2
- from pydantic import BaseModel, field_validator
3
- from typing import List, Optional, Any
4
- import re
5
-
6
- class C108DTO(BaseModel):
7
- 코드: str
8
- 날짜: Optional[str]
9
- 제목: str
10
- 작성자: Optional[str]
11
- 제공처: Optional[str]
12
- 투자의견: Optional[str]
13
- 목표가: Optional[float]
14
- 분량: Optional[str]
15
- 내용: List[str]
16
-
17
-
18
- @field_validator("투자의견", mode="before")
19
- @classmethod
20
- def _coerce_opinion(cls, v):
21
- # pandas/bs4 parsing sometimes yields float('nan') for empty cells
22
- if v is None:
23
- return None
24
- if isinstance(v, float) and math.isnan(v):
25
- return None
26
- if isinstance(v, str):
27
- s = v.strip()
28
- if not s or s.lower() == "nan" or s in {"-", "N/A", "NA"}:
29
- return None
30
- return s
31
- # Any other type -> string fallback (keeps validation tolerant)
32
- return str(v)
33
-
34
- @field_validator("목표가", mode="before")
35
- @classmethod
36
- def _v_target_price(cls, v: Any):
37
- if v is None:
38
- return None
39
-
40
- # nan 방어
41
- if isinstance(v, float) and math.isnan(v):
42
- return None
43
-
44
- # 안내 문구/결측 문자열 방어
45
- if isinstance(v, str):
46
- s = v.strip()
47
- if not s or s in {"-", "N/A"} or "검색된 데이터가 없습니다" in s:
48
- return None
49
-
50
- # "123,456원" 같은 케이스에서 숫자만 추출
51
- m = re.findall(r"\d+(?:,\d+)*", s)
52
- if not m:
53
- return None
54
- return float(m[0].replace(",", ""))
55
-
56
- # 숫자면 그대로 float로
57
- if isinstance(v, (int, float)):
58
- return float(v)
59
-
60
- # 그 외 타입은 None 처리(또는 raise로 엄격하게)
61
- return None