db2_hj3415 0.1.3__py2.py3-none-any.whl → 0.1.4__py2.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.
@@ -4,4 +4,21 @@ import numpy as np
4
4
 
5
5
  def df_to_dict_replace_nan(df: pd.DataFrame) -> list[dict]:
6
6
  # NaN → None으로 변환
7
- return df.replace({np.nan: None}).to_dict(orient="records")
7
+ return df.replace({np.nan: None}).to_dict(orient="records")
8
+
9
+ import json
10
+ from bson import json_util
11
+ from pydantic import BaseModel
12
+
13
+ def pretty_print(obj):
14
+ if isinstance(obj, BaseModel):
15
+ # Pydantic 모델이면 dict로 변환
16
+ data = obj.model_dump(by_alias=True)
17
+ elif isinstance(obj, list) and all(isinstance(o, BaseModel) for o in obj):
18
+ # 리스트 안에 BaseModel만 있다면 변환
19
+ data = [o.model_dump(by_alias=True) for o in obj]
20
+ else:
21
+ # 일반 dict나 기타 객체
22
+ data = obj
23
+
24
+ print(json.dumps(data, indent=2, ensure_ascii=False, default=json_util.default))
db2_hj3415/nfs/_c10346.py CHANGED
@@ -1,10 +1,12 @@
1
+ from unittest import case
2
+
1
3
  from deepdiff import DeepDiff
2
4
  from pymongo import ASCENDING, DESCENDING
3
5
  from motor.motor_asyncio import AsyncIOMotorClient
4
6
  import pprint
5
7
  import pandas as pd
6
8
  import json
7
- from db2_hj3415.nfs import DB_NAME, C103
9
+ from db2_hj3415.nfs import DB_NAME, C103, C104, C106
8
10
  from db2_hj3415.common.db_ops import get_collection
9
11
  from datetime import datetime, timezone
10
12
  import numpy as np
@@ -125,7 +127,7 @@ async def save_many(col: str, many_data: dict[str, dict[str, pd.DataFrame]], cli
125
127
  return results
126
128
 
127
129
 
128
- async def get_latest(col: str, code: str, client: AsyncIOMotorClient) -> C103 | None:
130
+ async def get_latest(col: str, code: str, client: AsyncIOMotorClient) -> C103 | C104 | C106 | None:
129
131
  collection = get_collection(client, DB_NAME, col)
130
132
 
131
133
  # 최신 날짜 기준으로 정렬하여 1건만 조회
@@ -141,7 +143,14 @@ async def get_latest(col: str, code: str, client: AsyncIOMotorClient) -> C103 |
141
143
  latest_doc["_id"] = str(latest_doc["_id"])
142
144
 
143
145
  try:
144
- return C103(**latest_doc)
146
+ match col:
147
+ case 'c103':
148
+ return C103(**latest_doc)
149
+ case 'c104':
150
+ return C104(**latest_doc)
151
+ case 'c106':
152
+ return C106(**latest_doc)
153
+
145
154
  except Exception as e:
146
155
  print(f"[{code}] C103 파싱 실패: {e}")
147
156
  return None
db2_hj3415/nfs/c103.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from motor.motor_asyncio import AsyncIOMotorClient
2
2
  import pandas as pd
3
3
 
4
- from db2_hj3415.nfs import _c10346
4
+ from db2_hj3415.nfs import _c10346, C103
5
5
  from utils_hj3415 import setup_logger
6
6
 
7
7
  mylogger = setup_logger(__name__, 'WARNING')
@@ -17,8 +17,8 @@ async def save_many(many_data: dict[str, dict[str, pd.DataFrame]], client: Async
17
17
  return await _c10346.save_many(COL_NAME, many_data, client)
18
18
 
19
19
 
20
- async def get_latest(code: str, page: str, client: AsyncIOMotorClient) -> pd.DataFrame | None:
21
- return await _c10346.get_latest(COL_NAME, code, page, client)
20
+ async def get_latest(code: str, client: AsyncIOMotorClient) -> C103 | None:
21
+ return await _c10346.get_latest(COL_NAME, code, client)
22
22
 
23
23
 
24
24
  async def has_doc_changed(code: str, client: AsyncIOMotorClient) -> bool:
db2_hj3415/nfs/c104.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from motor.motor_asyncio import AsyncIOMotorClient
2
2
  import pandas as pd
3
3
 
4
- from db2_hj3415.nfs import _c10346
4
+ from db2_hj3415.nfs import _c10346, C104
5
5
  from utils_hj3415 import setup_logger
6
6
 
7
7
  mylogger = setup_logger(__name__, 'WARNING')
@@ -17,8 +17,8 @@ async def save_many(many_data: dict[str, dict[str, pd.DataFrame]], client: Async
17
17
  return await _c10346.save_many(COL_NAME, many_data, client)
18
18
 
19
19
 
20
- async def get_latest(code: str, page: str, client: AsyncIOMotorClient) -> pd.DataFrame | None:
21
- return await _c10346.get_latest(COL_NAME, code, page, client)
20
+ async def get_latest(code: str, client: AsyncIOMotorClient) -> C104 | None:
21
+ return await _c10346.get_latest(COL_NAME, code, client)
22
22
 
23
23
 
24
24
  async def has_doc_changed(code: str, client: AsyncIOMotorClient) -> bool:
db2_hj3415/nfs/c106.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from motor.motor_asyncio import AsyncIOMotorClient
2
2
  import pandas as pd
3
3
 
4
- from db2_hj3415.nfs import _c10346
4
+ from db2_hj3415.nfs import _c10346, C106
5
5
  from utils_hj3415 import setup_logger
6
6
 
7
7
  mylogger = setup_logger(__name__, 'WARNING')
@@ -17,8 +17,8 @@ async def save_many(many_data: dict[str, dict[str, pd.DataFrame]], client: Async
17
17
  return await _c10346.save_many(COL_NAME, many_data, client)
18
18
 
19
19
 
20
- async def get_latest(code: str, page: str, client: AsyncIOMotorClient) -> pd.DataFrame | None:
21
- return await _c10346.get_latest(COL_NAME, code, page, client)
20
+ async def get_latest(code: str, client: AsyncIOMotorClient) -> C106 | None:
21
+ return await _c10346.get_latest(COL_NAME, code, client)
22
22
 
23
23
 
24
24
  async def has_doc_changed(code: str, client: AsyncIOMotorClient) -> bool:
db2_hj3415/nfs/c108.py CHANGED
@@ -1,9 +1,9 @@
1
1
  from motor.motor_asyncio import AsyncIOMotorClient
2
- from pymongo import ASCENDING, UpdateOne
2
+ from pymongo import ASCENDING, UpdateOne, DESCENDING
3
3
  import pandas as pd
4
- from datetime import datetime, timezone
4
+ from datetime import datetime, timezone, timedelta
5
5
 
6
- from db2_hj3415.nfs import DATE_FORMAT, DB_NAME
6
+ from db2_hj3415.nfs import DATE_FORMAT, DB_NAME, C108
7
7
  from db2_hj3415.common.db_ops import get_collection
8
8
  from utils_hj3415 import setup_logger
9
9
 
@@ -75,3 +75,37 @@ async def save_many(many_data: dict[str, pd.DataFrame], client: AsyncIOMotorClie
75
75
  return total_result
76
76
 
77
77
 
78
+ async def get_latest(code: str, client: AsyncIOMotorClient, days:int = 30) -> list[C108]:
79
+ """
80
+ 최근 N일 이내의 C108 리포트 도큐먼트를 조회합니다.
81
+
82
+ Args:
83
+ code (str): 종목 코드 (예: "005930").
84
+ client (AsyncIOMotorClient): 비동기 MongoDB 클라이언트.
85
+ days (int, optional): 현재 시점에서 며칠 전까지의 데이터를 조회할지 설정합니다. 기본값은 30일.
86
+
87
+ Returns:
88
+ list[C108]: 조건에 해당하는 C108 도큐먼트 리스트. 일치하는 문서가 없을 경우 빈 리스트를 반환합니다.
89
+ """
90
+ collection = get_collection(client, DB_NAME, COL_NAME)
91
+ now = datetime.now(timezone.utc)
92
+ cutoff = now - timedelta(days=days)
93
+
94
+ cursor = collection.find(
95
+ {
96
+ "코드": code,
97
+ "날짜": {"$gte": cutoff}
98
+ }
99
+ ).sort("날짜", DESCENDING)
100
+
101
+ docs = await cursor.to_list(length=None)
102
+ if not docs:
103
+ return []
104
+ else:
105
+ c108s = []
106
+ for doc in docs:
107
+ doc["_id"] = str(doc["_id"])
108
+ c108s.append(C108(**doc))
109
+ return c108s
110
+
111
+
db2_hj3415/nfs/models.py CHANGED
@@ -40,7 +40,7 @@ class C101(BaseModel):
40
40
  str_strip_whitespace=True,
41
41
  )
42
42
 
43
- class C103재무항목y(BaseModel):
43
+ class 항목값y(BaseModel):
44
44
  항목: str
45
45
  전년대비: float | None
46
46
  전년대비_1: float | None = Field(default=None, alias="전년대비 1")
@@ -50,25 +50,98 @@ class C103재무항목y(BaseModel):
50
50
  extra="allow"
51
51
  )
52
52
 
53
- class C103재무항목q(BaseModel):
53
+ class 항목값q(BaseModel):
54
54
  항목: str
55
55
  전분기대비: float | None
56
56
 
57
- model_config = ConfigDict(extra="allow") # 나머지 키들 허용
57
+ model_config = ConfigDict(extra="allow")
58
+
58
59
 
59
60
  class C103(BaseModel):
60
61
  id: str = Field(alias="_id")
61
62
  코드: str
62
63
  날짜: datetime
63
- 손익계산서q: list[C103재무항목q]
64
- 손익계산서y: list[C103재무항목y]
65
- 재무상태표q: list[C103재무항목q]
66
- 재무상태표y: list[C103재무항목y]
67
- 현금흐름표q: list[C103재무항목q]
68
- 현금흐름표y: list[C103재무항목y]
64
+ 손익계산서q: list[항목값q]
65
+ 손익계산서y: list[항목값y]
66
+ 재무상태표q: list[항목값q]
67
+ 재무상태표y: list[항목값y]
68
+ 현금흐름표q: list[항목값q]
69
+ 현금흐름표y: list[항목값y]
70
+
71
+ @field_serializer("날짜")
72
+ def serialize_date(self, v: datetime) -> str:
73
+ return v.isoformat()
74
+
75
+ model_config = ConfigDict(populate_by_name=True)
76
+
77
+
78
+ class C104(BaseModel):
79
+ id: str = Field(alias="_id")
80
+ 코드: str
81
+ 날짜: datetime
82
+
83
+ 수익성y: list[항목값y] | None = None
84
+ 성장성y: list[항목값y] | None = None
85
+ 안정성y: list[항목값y] | None = None
86
+ 활동성y: list[항목값y] | None = None
87
+ 가치분석y: list[항목값y] | None = None
88
+
89
+ 수익성q: list[항목값q] | None = None
90
+ 성장성q: list[항목값q] | None = None
91
+ 안정성q: list[항목값q] | None = None
92
+ 활동성q: list[항목값q] | None = None
93
+ 가치분석q: list[항목값q] | None = None
94
+
95
+ @field_serializer("날짜")
96
+ def serialize_date(self, v: datetime) -> str:
97
+ return v.isoformat()
98
+
99
+ model_config = ConfigDict(populate_by_name=True)
100
+
101
+
102
+ class 기업데이터(BaseModel):
103
+ 항목: str
104
+ 항목2: str
105
+
106
+ model_config = ConfigDict(
107
+ populate_by_name=True,
108
+ extra="allow"
109
+ )
110
+
111
+ class C106(BaseModel):
112
+ id: str = Field(alias="_id")
113
+ 코드: str
114
+ 날짜: datetime
115
+
116
+ q: list[기업데이터]
117
+ y: list[기업데이터]
118
+
119
+ @field_serializer("날짜")
120
+ def serialize_date(self, v: datetime) -> str:
121
+ return v.isoformat()
122
+
123
+ model_config = ConfigDict(populate_by_name=True)
124
+
125
+
126
+ class C108(BaseModel):
127
+ id: str = Field(alias="_id")
128
+ 코드: str
129
+ 날짜: datetime
130
+
131
+ 제목: str | None = None
132
+ 내용: list[str] | None = None
133
+ 목표가: int | None = None
134
+ 분량: str | None = None
135
+ 작성자: str | None = None
136
+ 제공처: str | None = None
137
+ 투자의견: str | None = None
69
138
 
139
+ # 날짜 필드 ISO 직렬화
70
140
  @field_serializer("날짜")
71
141
  def serialize_date(self, v: datetime) -> str:
72
142
  return v.isoformat()
73
143
 
74
- model_config = ConfigDict(populate_by_name=True)
144
+ model_config = ConfigDict(
145
+ populate_by_name=True,
146
+ str_strip_whitespace=True
147
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: db2_hj3415
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Gathering the stock data by playwright
5
5
  Author-email: Hyungjin Kim <hj3415@gmail.com>
6
6
  Description-Content-Type: text/markdown
@@ -1,11 +1,10 @@
1
1
  db2_hj3415/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- db2_hj3415/redis_manager.py,sha256=yc8dxEwfhz9k4qwWRnUyhIfuPBHdqg35sL2w1lhG0_4,411
3
2
  db2_hj3415/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
3
  db2_hj3415/cli/db.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
4
  db2_hj3415/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
5
  db2_hj3415/common/connection.py,sha256=QpjDtDHy6By0MCj81z5upJkWeuIgqeIsbXsihHcpKX4,774
7
6
  db2_hj3415/common/db_ops.py,sha256=CajXob5FofIzsuyFvtlOEo1UkdYMfsLUwKDmbb29Kas,212
8
- db2_hj3415/common/utils.py,sha256=uL5EqR9ZPkeoGex983Mhwl7CckIfZtGGlQA9mIsl8uY,235
7
+ db2_hj3415/common/utils.py,sha256=TfbsAR1FWgbuxZUybXRdrFg6QSeJ8DInn6b8aw_NagE,805
9
8
  db2_hj3415/mi/__init__.py,sha256=4XIwdkLfdUCiq2zLqCnR5ypuI45nzX02qez80xWQ70A,159
10
9
  db2_hj3415/mi/_ops.py,sha256=Xm2ZpnKxAymIieut8JyK3epHED8uMlhO77ScACfoB7g,3988
11
10
  db2_hj3415/mi/aud.py,sha256=w4w4EC0hn6kKHjNkeIUL5ZsvHH4IzRGBpTKRptHapVY,765
@@ -20,16 +19,16 @@ db2_hj3415/mi/usdidx.py,sha256=cT6w-Y-Ysv0KI9yeFqDQYCdT0KPElwn8IZ0JF5oZ_MA,550
20
19
  db2_hj3415/mi/usdkrw.py,sha256=QjcHqFSpSgyzI5lzRfYycNUzolzM-ikOvPDFy4FPadM,876
21
20
  db2_hj3415/mi/wti.py,sha256=wMoS5yHGOlUS2b2P7Eok5U_59eehYXQGfqWa56J_dkk,768
22
21
  db2_hj3415/nfs/__init__.py,sha256=w5QUgr_qWQNHRVcLrT8G8Rx6pNry2PnHfyezBDI5D2A,229
23
- db2_hj3415/nfs/_c10346.py,sha256=ni2Nrqxwuuy4qYTtSpodFQcHPeGTHnR5-kQtsw1x1UU,6699
22
+ db2_hj3415/nfs/_c10346.py,sha256=yKBOA4QkY2eAUMzOiYWz4BB1c4cW6X4s4D2Nj9rAgOA,6939
24
23
  db2_hj3415/nfs/_ops.py,sha256=lqrBBhEFcPupB2dQBXZU-Gftvd9dYTlmgJGTb5LdX-o,1193
25
24
  db2_hj3415/nfs/c101.py,sha256=ORCNXr3pq43zn4naSnyu1erDxFmDc2HLpXO19gv0KNo,4853
26
- db2_hj3415/nfs/c103.py,sha256=n5xwq8Ire5nBslFrZuwfNHTJZDRZh-_UnbU1KhHFEAM,1479
27
- db2_hj3415/nfs/c104.py,sha256=QpYOct3JCoX6scyM98YNO_9J_RB3T_odkXa5186UvfI,1479
28
- db2_hj3415/nfs/c106.py,sha256=-kJ90dzUzyqamjBPxhihOjl9rw0Swfb0bVoThApCBfI,1479
29
- db2_hj3415/nfs/c108.py,sha256=vmm_vaxEixDZeOWMsfgLzAj4LvlcyCqe_V4E9WnIVHo,2604
25
+ db2_hj3415/nfs/c103.py,sha256=NY2e_BTfSHkELIEUJ8XhhYdNETQaRGOrF4qNaewB2_E,1460
26
+ db2_hj3415/nfs/c104.py,sha256=aVLchUoxomlthS7FZ0adskBXmxIWQISGfc2EgWkWZDI,1460
27
+ db2_hj3415/nfs/c106.py,sha256=dAVSnEM5E-sJZExMTfqDoQlozTtH05JOtenzbIUdvtQ,1460
28
+ db2_hj3415/nfs/c108.py,sha256=F_kP1HMhpe9E6VKm_m-aDl6PZ-FqVs5-O2_-rU8f34Y,3761
30
29
  db2_hj3415/nfs/dart.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- db2_hj3415/nfs/models.py,sha256=fJ5ZizdAXTUOdovHVpcZ87PELjmXKFnBeddkiM0u5IE,2053
32
- db2_hj3415-0.1.3.dist-info/entry_points.txt,sha256=JAGFsxKtvbNgaKCSKo-QRYwCyToabWmdRW0MZWtg9kg,45
33
- db2_hj3415-0.1.3.dist-info/WHEEL,sha256=Dyt6SBfaasWElUrURkknVFAZDHSTwxg3PaTza7RSbkY,100
34
- db2_hj3415-0.1.3.dist-info/METADATA,sha256=Jhv1_XJPNrsXny3QBc-q2O_QnNCuSr_hsuVUzrbjsyc,709
35
- db2_hj3415-0.1.3.dist-info/RECORD,,
30
+ db2_hj3415/nfs/models.py,sha256=4RtPkD2DdSd8UuqI8DrJhY2Iyr4UNiCADbTVcqpb3MY,3765
31
+ db2_hj3415-0.1.4.dist-info/entry_points.txt,sha256=JAGFsxKtvbNgaKCSKo-QRYwCyToabWmdRW0MZWtg9kg,45
32
+ db2_hj3415-0.1.4.dist-info/WHEEL,sha256=Dyt6SBfaasWElUrURkknVFAZDHSTwxg3PaTza7RSbkY,100
33
+ db2_hj3415-0.1.4.dist-info/METADATA,sha256=ww11ReGoCpq6kLlnVaHRTTSQpx92oNfR7tnecsWdjJk,709
34
+ db2_hj3415-0.1.4.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- from db2_hj3415.common import connection
2
- import json
3
-
4
- async def get_or_set_cache(key, ttl, fetch_func, force_refresh=False):
5
- redis = await connection.get_redis()
6
- if not force_refresh:
7
- cached = await redis.get(key)
8
- if cached:
9
- return json.loads(cached)
10
-
11
- result = await fetch_func()
12
- if result:
13
- await redis.setex(key, ttl, json.dumps(result))
14
- return result
15
-
16
-