linkmerce 0.2.2__tar.gz → 0.3.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.
- {linkmerce-0.2.2 → linkmerce-0.3.0}/PKG-INFO +2 -3
- {linkmerce-0.2.2 → linkmerce-0.3.0}/pyproject.toml +2 -3
- linkmerce-0.3.0/src/linkmerce/.DS_Store +0 -0
- {linkmerce-0.2.2/src/linkmerce/auth → linkmerce-0.3.0/src/linkmerce/api}/__init__.py +0 -0
- {linkmerce-0.2.2/src/linkmerce/collect → linkmerce-0.3.0/src/linkmerce/api}/naver/__init__.py +0 -0
- linkmerce-0.3.0/src/linkmerce/api/naver/openapi.py +167 -0
- {linkmerce-0.2.2/src/linkmerce/collect → linkmerce-0.3.0/src/linkmerce/api}/searchad/__init__.py +0 -0
- linkmerce-0.3.0/src/linkmerce/api/searchad/manage.py +50 -0
- {linkmerce-0.2.2/src/linkmerce/collect → linkmerce-0.3.0/src/linkmerce/api}/smartstore/__init__.py +0 -0
- linkmerce-0.3.0/src/linkmerce/api/smartstore/brand.py +154 -0
- linkmerce-0.3.0/src/linkmerce/common/.DS_Store +0 -0
- linkmerce-0.3.0/src/linkmerce/common/api.py +156 -0
- linkmerce-0.3.0/src/linkmerce/common/exceptions.py +17 -0
- linkmerce-0.3.0/src/linkmerce/common/extract.py +439 -0
- linkmerce-0.3.0/src/linkmerce/common/load.py +195 -0
- linkmerce-0.3.0/src/linkmerce/common/models.py +83 -0
- {linkmerce-0.2.2/src/linkmerce/collect → linkmerce-0.3.0/src/linkmerce/common}/tasks.py +154 -120
- linkmerce-0.3.0/src/linkmerce/common/transform.py +399 -0
- {linkmerce-0.2.2/src/linkmerce/collect → linkmerce-0.3.0/src/linkmerce/core}/.DS_Store +0 -0
- {linkmerce-0.2.2/src/linkmerce/collect/smartstore → linkmerce-0.3.0/src/linkmerce/core/naver}/.DS_Store +0 -0
- {linkmerce-0.2.2/src/linkmerce/auth → linkmerce-0.3.0/src/linkmerce/core/naver/openapi}/.DS_Store +0 -0
- linkmerce-0.3.0/src/linkmerce/core/naver/openapi/__init__.py +1 -0
- linkmerce-0.3.0/src/linkmerce/core/naver/openapi/common.py +34 -0
- linkmerce-0.2.2/src/linkmerce/collect/naver/api/search.py → linkmerce-0.3.0/src/linkmerce/core/naver/openapi/search/extract.py +45 -57
- linkmerce-0.3.0/src/linkmerce/core/naver/openapi/search/models.sql +266 -0
- linkmerce-0.3.0/src/linkmerce/core/naver/openapi/search/transform.py +89 -0
- {linkmerce-0.2.2/src/linkmerce/collect/naver → linkmerce-0.3.0/src/linkmerce/core/searchad}/.DS_Store +0 -0
- {linkmerce-0.2.2/src/linkmerce/collect/naver/api → linkmerce-0.3.0/src/linkmerce/core/searchad/manage}/.DS_Store +0 -0
- linkmerce-0.3.0/src/linkmerce/core/searchad/manage/__init__.py +1 -0
- linkmerce-0.2.2/src/linkmerce/collect/searchad/manage/__init__.py → linkmerce-0.3.0/src/linkmerce/core/searchad/manage/common.py +24 -35
- {linkmerce-0.2.2/src/linkmerce/parse/smartstore/hcenter → linkmerce-0.3.0/src/linkmerce/core/searchad/manage/exposure}/__init__.py +0 -0
- linkmerce-0.2.2/src/linkmerce/collect/searchad/manage/exposure.py → linkmerce-0.3.0/src/linkmerce/core/searchad/manage/exposure/extract.py +15 -20
- linkmerce-0.3.0/src/linkmerce/core/searchad/manage/exposure/models.sql +108 -0
- linkmerce-0.3.0/src/linkmerce/core/searchad/manage/exposure/transform.py +59 -0
- linkmerce-0.3.0/src/linkmerce/core/smartstore/.DS_Store +0 -0
- {linkmerce-0.2.2/src/linkmerce/parse/smartstore → linkmerce-0.3.0/src/linkmerce/core/smartstore/brand}/.DS_Store +0 -0
- linkmerce-0.3.0/src/linkmerce/core/smartstore/brand/__init__.py +1 -0
- linkmerce-0.3.0/src/linkmerce/core/smartstore/brand/catalog/__init__.py +0 -0
- linkmerce-0.2.2/src/linkmerce/collect/smartstore/hcenter/catalog.py → linkmerce-0.3.0/src/linkmerce/core/smartstore/brand/catalog/extract.py +113 -92
- linkmerce-0.3.0/src/linkmerce/core/smartstore/brand/catalog/models.sql +176 -0
- linkmerce-0.3.0/src/linkmerce/core/smartstore/brand/catalog/transform.py +64 -0
- linkmerce-0.2.2/src/linkmerce/collect/smartstore/hcenter/__init__.py → linkmerce-0.3.0/src/linkmerce/core/smartstore/brand/common.py +2 -2
- linkmerce-0.3.0/src/linkmerce/core/smartstore/brand/sales/__init__.py +0 -0
- linkmerce-0.2.2/src/linkmerce/collect/smartstore/hcenter/sales.py → linkmerce-0.3.0/src/linkmerce/core/smartstore/brand/sales/extract.py +19 -45
- linkmerce-0.3.0/src/linkmerce/core/smartstore/brand/sales/models.sql +164 -0
- linkmerce-0.3.0/src/linkmerce/core/smartstore/brand/sales/transform.py +91 -0
- linkmerce-0.3.0/src/linkmerce/utils/__init__.py +0 -0
- {linkmerce-0.2.2 → linkmerce-0.3.0}/src/linkmerce/utils/duckdb.py +74 -93
- {linkmerce-0.2.2 → linkmerce-0.3.0}/src/linkmerce/utils/headers.py +1 -1
- linkmerce-0.2.2/src/linkmerce/collect/__init__.py +0 -1
- linkmerce-0.2.2/src/linkmerce/collect/base.py +0 -540
- linkmerce-0.2.2/src/linkmerce/collect/naver/api/__init__.py +0 -48
- linkmerce-0.2.2/src/linkmerce/common.py +0 -30
- linkmerce-0.2.2/src/linkmerce/parse/.DS_Store +0 -0
- linkmerce-0.2.2/src/linkmerce/parse/__init__.py +0 -1
- linkmerce-0.2.2/src/linkmerce/parse/base.py +0 -134
- linkmerce-0.2.2/src/linkmerce/parse/naver/.DS_Store +0 -0
- linkmerce-0.2.2/src/linkmerce/parse/naver/api/.DS_Store +0 -0
- linkmerce-0.2.2/src/linkmerce/parse/naver/api/search.py +0 -215
- linkmerce-0.2.2/src/linkmerce/parse/searchad/manage/exposure.py +0 -77
- linkmerce-0.2.2/src/linkmerce/parse/smartstore/hcenter/.DS_Store +0 -0
- linkmerce-0.2.2/src/linkmerce/parse/smartstore/hcenter/catalog.py +0 -153
- linkmerce-0.2.2/src/linkmerce/parse/smartstore/hcenter/sales.py +0 -172
- linkmerce-0.2.2/src/linkmerce/utils/pyarrow.py +0 -1136
- {linkmerce-0.2.2 → linkmerce-0.3.0}/README.md +0 -0
- {linkmerce-0.2.2 → linkmerce-0.3.0}/src/linkmerce/__init__.py +0 -0
- {linkmerce-0.2.2/src/linkmerce/parse/naver → linkmerce-0.3.0/src/linkmerce/common}/__init__.py +0 -0
- {linkmerce-0.2.2/src/linkmerce/parse/naver/api → linkmerce-0.3.0/src/linkmerce/core}/__init__.py +0 -0
- {linkmerce-0.2.2/src/linkmerce/parse/searchad → linkmerce-0.3.0/src/linkmerce/core/naver}/__init__.py +0 -0
- {linkmerce-0.2.2/src/linkmerce/parse/searchad/manage → linkmerce-0.3.0/src/linkmerce/core/naver/openapi/search}/__init__.py +0 -0
- {linkmerce-0.2.2/src/linkmerce/parse/smartstore → linkmerce-0.3.0/src/linkmerce/core/searchad}/__init__.py +0 -0
- {linkmerce-0.2.2/src/linkmerce/utils → linkmerce-0.3.0/src/linkmerce/core/smartstore}/__init__.py +0 -0
- {linkmerce-0.2.2 → linkmerce-0.3.0}/src/linkmerce/utils/.DS_Store +0 -0
- {linkmerce-0.2.2 → linkmerce-0.3.0}/src/linkmerce/utils/cast.py +0 -0
- /linkmerce-0.2.2/src/linkmerce/utils/datetime.py → /linkmerce-0.3.0/src/linkmerce/utils/date.py +0 -0
- {linkmerce-0.2.2 → linkmerce-0.3.0}/src/linkmerce/utils/graphql.py +0 -0
- {linkmerce-0.2.2 → linkmerce-0.3.0}/src/linkmerce/utils/jinja.py +0 -0
- {linkmerce-0.2.2 → linkmerce-0.3.0}/src/linkmerce/utils/map.py +0 -0
- {linkmerce-0.2.2 → linkmerce-0.3.0}/src/linkmerce/utils/tqdm.py +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: linkmerce
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: E-commerce API integration management
|
|
5
|
-
Requires-Dist: aiohttp>=3.12.
|
|
5
|
+
Requires-Dist: aiohttp>=3.12.15
|
|
6
6
|
Requires-Dist: bs4>=0.0.2
|
|
7
7
|
Requires-Dist: duckdb>=1.3.2
|
|
8
8
|
Requires-Dist: jinja2>=3.1.6
|
|
9
|
-
Requires-Dist: pyarrow>=21.0.0
|
|
10
9
|
Requires-Dist: pytz>=2025.2
|
|
11
10
|
Requires-Dist: requests>=2.32.4
|
|
12
11
|
Requires-Dist: tqdm>=4.67.1
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "linkmerce"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "E-commerce API integration management"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"aiohttp>=3.12.
|
|
8
|
+
"aiohttp>=3.12.15",
|
|
9
9
|
"bs4>=0.0.2",
|
|
10
10
|
"duckdb>=1.3.2",
|
|
11
11
|
"jinja2>=3.1.6",
|
|
12
|
-
"pyarrow>=21.0.0",
|
|
13
12
|
"pytz>=2025.2",
|
|
14
13
|
"requests>=2.32.4",
|
|
15
14
|
"tqdm>=4.67.1",
|
|
Binary file
|
|
File without changes
|
{linkmerce-0.2.2/src/linkmerce/collect → linkmerce-0.3.0/src/linkmerce/api}/naver/__init__.py
RENAMED
|
File without changes
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from linkmerce.common.api import run_with_duckdb
|
|
4
|
+
from linkmerce.common.api import get_table_from_options as get_table
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from typing import Iterable, Literal
|
|
10
|
+
from linkmerce.common.extract import JsonObject
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_module(name: str) -> str:
|
|
14
|
+
return (".naver.openapi" + name) if name.startswith('.') else name
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def search_blog(
|
|
18
|
+
client_id: str,
|
|
19
|
+
client_secret: str,
|
|
20
|
+
query: str | Iterable[str],
|
|
21
|
+
start: int | Iterable[int] = 1,
|
|
22
|
+
display: int = 100,
|
|
23
|
+
sort: Literal["sim","date"] = "sim",
|
|
24
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
25
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
26
|
+
extract_options: dict | None = None,
|
|
27
|
+
transform_options: dict | None = None,
|
|
28
|
+
) -> JsonObject:
|
|
29
|
+
args = (query, start, display, sort)
|
|
30
|
+
table = get_table(transform_options, "table")
|
|
31
|
+
extract_options = dict(extract_options or dict(), variables=dict(client_id=client_id, client_secret=client_secret))
|
|
32
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
33
|
+
return run_with_duckdb(get_module(".search"), "BlogSearch", "BlogSearch", how, table, return_type, args, **options)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def search_news(
|
|
37
|
+
client_id: str,
|
|
38
|
+
client_secret: str,
|
|
39
|
+
query: str | Iterable[str],
|
|
40
|
+
start: int | Iterable[int] = 1,
|
|
41
|
+
display: int = 100,
|
|
42
|
+
sort: Literal["sim","date"] = "sim",
|
|
43
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
44
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
45
|
+
extract_options: dict | None = None,
|
|
46
|
+
transform_options: dict | None = None,
|
|
47
|
+
) -> JsonObject:
|
|
48
|
+
args = (query, start, display, sort)
|
|
49
|
+
table = get_table(transform_options, "table")
|
|
50
|
+
extract_options = dict(extract_options or dict(), variables=dict(client_id=client_id, client_secret=client_secret))
|
|
51
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
52
|
+
return run_with_duckdb(get_module(".search"), "NewsSearch", "NewsSearch", how, table, return_type, args, **options)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def search_book(
|
|
56
|
+
client_id: str,
|
|
57
|
+
client_secret: str,
|
|
58
|
+
query: str | Iterable[str],
|
|
59
|
+
start: int | Iterable[int] = 1,
|
|
60
|
+
display: int = 100,
|
|
61
|
+
sort: Literal["sim","date"] = "sim",
|
|
62
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
63
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
64
|
+
extract_options: dict | None = None,
|
|
65
|
+
transform_options: dict | None = None,
|
|
66
|
+
) -> JsonObject:
|
|
67
|
+
args = (query, start, display, sort)
|
|
68
|
+
table = get_table(transform_options, "table")
|
|
69
|
+
extract_options = dict(extract_options or dict(), variables=dict(client_id=client_id, client_secret=client_secret))
|
|
70
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
71
|
+
return run_with_duckdb(get_module(".search"), "BookSearch", "BookSearch", how, table, return_type, args, **options)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def search_cafe(
|
|
75
|
+
client_id: str,
|
|
76
|
+
client_secret: str,
|
|
77
|
+
query: str | Iterable[str],
|
|
78
|
+
start: int | Iterable[int] = 1,
|
|
79
|
+
display: int = 100,
|
|
80
|
+
sort: Literal["sim","date"] = "sim",
|
|
81
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
82
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
83
|
+
extract_options: dict | None = None,
|
|
84
|
+
transform_options: dict | None = None,
|
|
85
|
+
) -> JsonObject:
|
|
86
|
+
args = (query, start, display, sort)
|
|
87
|
+
table = get_table(transform_options, "table")
|
|
88
|
+
extract_options = dict(extract_options or dict(), variables=dict(client_id=client_id, client_secret=client_secret))
|
|
89
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
90
|
+
return run_with_duckdb(get_module(".search"), "CafeSearch", "CafeSearch", how, table, return_type, args, **options)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def search_kin(
|
|
94
|
+
client_id: str,
|
|
95
|
+
client_secret: str,
|
|
96
|
+
query: str | Iterable[str],
|
|
97
|
+
start: int | Iterable[int] = 1,
|
|
98
|
+
display: int = 100,
|
|
99
|
+
sort: Literal["sim","date","point"] = "sim",
|
|
100
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
101
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
102
|
+
extract_options: dict | None = None,
|
|
103
|
+
transform_options: dict | None = None,
|
|
104
|
+
) -> JsonObject:
|
|
105
|
+
args = (query, start, display, sort)
|
|
106
|
+
table = get_table(transform_options, "table")
|
|
107
|
+
extract_options = dict(extract_options or dict(), variables=dict(client_id=client_id, client_secret=client_secret))
|
|
108
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
109
|
+
return run_with_duckdb(get_module(".search"), "KiNSearch", "KiNSearch", how, table, return_type, args, **options)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def search_image(
|
|
113
|
+
client_id: str,
|
|
114
|
+
client_secret: str,
|
|
115
|
+
query: str | Iterable[str],
|
|
116
|
+
start: int | Iterable[int] = 1,
|
|
117
|
+
display: int = 100,
|
|
118
|
+
sort: Literal["sim","date"] = "sim",
|
|
119
|
+
filter: Literal["all","large","medium","small"] = "all",
|
|
120
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
121
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
122
|
+
extract_options: dict | None = None,
|
|
123
|
+
transform_options: dict | None = None,
|
|
124
|
+
) -> JsonObject:
|
|
125
|
+
args = (query, start, display, sort, filter)
|
|
126
|
+
table = get_table(transform_options, "table")
|
|
127
|
+
extract_options = dict(extract_options or dict(), variables=dict(client_id=client_id, client_secret=client_secret))
|
|
128
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
129
|
+
return run_with_duckdb(get_module(".search"), "ImageSearch", "ImageSearch", how, table, return_type, args, **options)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def search_shop(
|
|
133
|
+
client_id: str,
|
|
134
|
+
client_secret: str,
|
|
135
|
+
query: str | Iterable[str],
|
|
136
|
+
start: int | Iterable[int] = 1,
|
|
137
|
+
display: int = 100,
|
|
138
|
+
sort: Literal["sim","date","asc","dsc"] = "sim",
|
|
139
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
140
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
141
|
+
extract_options: dict | None = None,
|
|
142
|
+
transform_options: dict | None = None,
|
|
143
|
+
) -> JsonObject:
|
|
144
|
+
args = (query, start, display, sort)
|
|
145
|
+
table = get_table(transform_options, "table")
|
|
146
|
+
extract_options = dict(extract_options or dict(), variables=dict(client_id=client_id, client_secret=client_secret))
|
|
147
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
148
|
+
return run_with_duckdb(get_module(".search"), "ShoppingSearch", "ShoppingSearch", how, table, return_type, args, **options)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def rank_shop(
|
|
152
|
+
client_id: str,
|
|
153
|
+
client_secret: str,
|
|
154
|
+
query: str | Iterable[str],
|
|
155
|
+
start: int | Iterable[int] = 1,
|
|
156
|
+
display: int = 100,
|
|
157
|
+
sort: Literal["sim","date","asc","dsc"] = "sim",
|
|
158
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
159
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
160
|
+
extract_options: dict | None = None,
|
|
161
|
+
transform_options: dict | None = None,
|
|
162
|
+
) -> dict[str,JsonObject]:
|
|
163
|
+
args = (query, start, display, sort)
|
|
164
|
+
table = [get_table(transform_options, "rank_table"), get_table(transform_options, "product_table", "product")]
|
|
165
|
+
extract_options = dict(extract_options or dict(), variables=dict(client_id=client_id, client_secret=client_secret))
|
|
166
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
167
|
+
return run_with_duckdb(get_module(".search"), "ShoppingRank", "ShoppingRank", how, table, return_type, args, **options)
|
{linkmerce-0.2.2/src/linkmerce/collect → linkmerce-0.3.0/src/linkmerce/api}/searchad/__init__.py
RENAMED
|
File without changes
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from linkmerce.common.api import run_with_duckdb
|
|
4
|
+
from linkmerce.common.api import get_table_from_options as get_table
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from typing import Iterable, Literal
|
|
10
|
+
from linkmerce.common.extract import JsonObject
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_module(name: str) -> str:
|
|
14
|
+
return (".searchad.manage" + name) if name.startswith('.') else name
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def exposure_diagnosis(
|
|
18
|
+
customer_id: str,
|
|
19
|
+
cookies: str,
|
|
20
|
+
keyword: str | Iterable[str],
|
|
21
|
+
domain: Literal["search","shopping"] = "search",
|
|
22
|
+
mobile: bool = True,
|
|
23
|
+
is_own: bool | None = None,
|
|
24
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
25
|
+
extract_options: dict | None = None,
|
|
26
|
+
transform_options: dict | None = None,
|
|
27
|
+
) -> JsonObject:
|
|
28
|
+
args = (keyword, domain, mobile, is_own)
|
|
29
|
+
table = get_table(transform_options, "table")
|
|
30
|
+
extract_options = dict(extract_options or dict(), headers=dict(cookies=cookies), variables=dict(customer_id=customer_id))
|
|
31
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
32
|
+
return run_with_duckdb(get_module(".exposure"), "ExposureDiagnosis", "ExposureDiagnosis", "sync", table, return_type, args, **options)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def exposure_rank(
|
|
36
|
+
customer_id: str,
|
|
37
|
+
cookies: str,
|
|
38
|
+
keyword: str | Iterable[str],
|
|
39
|
+
domain: Literal["search","shopping"] = "search",
|
|
40
|
+
mobile: bool = True,
|
|
41
|
+
is_own: bool | None = None,
|
|
42
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
43
|
+
extract_options: dict | None = None,
|
|
44
|
+
transform_options: dict | None = None,
|
|
45
|
+
) -> JsonObject:
|
|
46
|
+
args = (keyword, domain, mobile, is_own)
|
|
47
|
+
table = [get_table(transform_options, "rank_table"), get_table(transform_options, "product_table", "product")]
|
|
48
|
+
extract_options = dict(extract_options or dict(), headers=dict(cookies=cookies), variables=dict(customer_id=customer_id))
|
|
49
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
50
|
+
return run_with_duckdb(get_module(".exposure"), "ExposureRank", "ExposureRank", "sync", table, return_type, args, **options)
|
{linkmerce-0.2.2/src/linkmerce/collect → linkmerce-0.3.0/src/linkmerce/api}/smartstore/__init__.py
RENAMED
|
File without changes
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from linkmerce.common.api import run_with_duckdb
|
|
4
|
+
from linkmerce.common.api import get_table_from_options as get_table
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from typing import Iterable, Literal
|
|
10
|
+
from linkmerce.common.extract import JsonObject
|
|
11
|
+
import datetime as dt
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_module(name: str) -> str:
|
|
15
|
+
return (".smartstore.brand" + name) if name.startswith('.') else name
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def brand_catalog(
|
|
19
|
+
cookies: str,
|
|
20
|
+
brand_ids: str | Iterable[str],
|
|
21
|
+
sort_type: Literal["popular","recent","price"] = "poular",
|
|
22
|
+
is_brand_catalog: bool | None = None,
|
|
23
|
+
page: int | list[int] | None = 0,
|
|
24
|
+
page_size: int = 100,
|
|
25
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
26
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
27
|
+
extract_options: dict | None = None,
|
|
28
|
+
transform_options: dict | None = None,
|
|
29
|
+
) -> JsonObject:
|
|
30
|
+
args = (brand_ids, sort_type, is_brand_catalog, page, page_size)
|
|
31
|
+
table = get_table(transform_options, "table")
|
|
32
|
+
extract_options = dict(extract_options or dict(), headers=dict(cookies=cookies))
|
|
33
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
34
|
+
return run_with_duckdb(get_module(".catalog"), "BrandCatalog", "BrandCatalog", how, table, return_type, args, **options)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def brand_product(
|
|
38
|
+
cookies: str,
|
|
39
|
+
brand_ids: str | Iterable[str],
|
|
40
|
+
mall_seq: int | str | Iterable[int | str] | None = None,
|
|
41
|
+
sort_type: Literal["popular","recent","price"] = "poular",
|
|
42
|
+
is_brand_catalog: bool | None = None,
|
|
43
|
+
page: int | list[int] | None = 0,
|
|
44
|
+
page_size: int = 100,
|
|
45
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
46
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
47
|
+
extract_options: dict | None = None,
|
|
48
|
+
transform_options: dict | None = None,
|
|
49
|
+
) -> JsonObject:
|
|
50
|
+
args = (brand_ids, mall_seq, sort_type, is_brand_catalog, page, page_size)
|
|
51
|
+
table = get_table(transform_options, "table")
|
|
52
|
+
extract_options = dict(extract_options or dict(), headers=dict(cookies=cookies))
|
|
53
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
54
|
+
return run_with_duckdb(get_module(".catalog"), "BrandProduct", "BrandProduct", how, table, return_type, args, **options)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def brand_price(
|
|
58
|
+
cookies: str,
|
|
59
|
+
brand_ids: str | Iterable[str],
|
|
60
|
+
mall_seq: int | str | Iterable[int | str],
|
|
61
|
+
sort_type: Literal["popular","recent","price"] = "recent",
|
|
62
|
+
is_brand_catalog: bool | None = None,
|
|
63
|
+
page: int | list[int] | None = 0,
|
|
64
|
+
page_size: int = 100,
|
|
65
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
66
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
67
|
+
extract_options: dict | None = None,
|
|
68
|
+
transform_options: dict | None = None,
|
|
69
|
+
) -> JsonObject:
|
|
70
|
+
args = (brand_ids, mall_seq, sort_type, is_brand_catalog, page, page_size)
|
|
71
|
+
table = [get_table(transform_options, "price_table"), get_table(transform_options, "product_table", "product")]
|
|
72
|
+
extract_options = dict(extract_options or dict(), headers=dict(cookies=cookies))
|
|
73
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
74
|
+
return run_with_duckdb(get_module(".catalog"), "BrandPrice", "BrandPrice", how, table, return_type, args, **options)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def store_sales(
|
|
78
|
+
cookies: str,
|
|
79
|
+
mall_seq: int | str | Iterable[int | str],
|
|
80
|
+
start_date: dt.date | str,
|
|
81
|
+
end_date: dt.date | str,
|
|
82
|
+
date_type: Literal["daily","weekly","monthly"] = "daily",
|
|
83
|
+
page: int | Iterable[int] = 1,
|
|
84
|
+
page_size: int = 1000,
|
|
85
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
86
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
87
|
+
extract_options: dict | None = None,
|
|
88
|
+
transform_options: dict | None = None,
|
|
89
|
+
) -> JsonObject:
|
|
90
|
+
args = (mall_seq, start_date, end_date, date_type, page, page_size)
|
|
91
|
+
table = get_table(transform_options, "table")
|
|
92
|
+
extract_options = dict(extract_options or dict(), headers=dict(cookies=cookies))
|
|
93
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
94
|
+
return run_with_duckdb(get_module(".sales"), "StoreSales", "StoreSales", how, table, return_type, args, **options)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def category_sales(
|
|
98
|
+
cookies: str,
|
|
99
|
+
mall_seq: int | str | Iterable[int | str],
|
|
100
|
+
start_date: dt.date | str,
|
|
101
|
+
end_date: dt.date | str,
|
|
102
|
+
date_type: Literal["daily","weekly","monthly"] = "daily",
|
|
103
|
+
page: int | Iterable[int] = 1,
|
|
104
|
+
page_size: int = 1000,
|
|
105
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
106
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
107
|
+
extract_options: dict | None = None,
|
|
108
|
+
transform_options: dict | None = None,
|
|
109
|
+
) -> JsonObject:
|
|
110
|
+
args = (mall_seq, start_date, end_date, date_type, page, page_size)
|
|
111
|
+
table = get_table(transform_options, "table")
|
|
112
|
+
extract_options = dict(extract_options or dict(), headers=dict(cookies=cookies))
|
|
113
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
114
|
+
return run_with_duckdb(get_module(".sales"), "CategorySales", "CategorySales", how, table, return_type, args, **options)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def product_sales(
|
|
118
|
+
cookies: str,
|
|
119
|
+
mall_seq: int | str | Iterable[int | str],
|
|
120
|
+
start_date: dt.date | str,
|
|
121
|
+
end_date: dt.date | str,
|
|
122
|
+
date_type: Literal["daily","weekly","monthly"] = "daily",
|
|
123
|
+
page: int | Iterable[int] = 1,
|
|
124
|
+
page_size: int = 1000,
|
|
125
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
126
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
127
|
+
extract_options: dict | None = None,
|
|
128
|
+
transform_options: dict | None = None,
|
|
129
|
+
) -> JsonObject:
|
|
130
|
+
args = (mall_seq, start_date, end_date, date_type, page, page_size)
|
|
131
|
+
table = get_table(transform_options, "table")
|
|
132
|
+
extract_options = dict(extract_options or dict(), headers=dict(cookies=cookies))
|
|
133
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
134
|
+
return run_with_duckdb(get_module(".sales"), "ProductSales", "ProductSales", how, table, return_type, args, **options)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def aggregated_sales(
|
|
138
|
+
cookies: str,
|
|
139
|
+
mall_seq: int | str | Iterable[int | str],
|
|
140
|
+
start_date: dt.date | str,
|
|
141
|
+
end_date: dt.date | str,
|
|
142
|
+
date_type: Literal["daily","weekly","monthly"] = "daily",
|
|
143
|
+
page: int | Iterable[int] = 1,
|
|
144
|
+
page_size: int = 1000,
|
|
145
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
146
|
+
return_type: Literal["csv","json","parquet","raw"] = "json",
|
|
147
|
+
extract_options: dict | None = None,
|
|
148
|
+
transform_options: dict | None = None,
|
|
149
|
+
) -> JsonObject:
|
|
150
|
+
args = (mall_seq, start_date, end_date, date_type, page, page_size)
|
|
151
|
+
table = [get_table(transform_options, "sales_table"), get_table(transform_options, "product_table", "product")]
|
|
152
|
+
extract_options = dict(extract_options or dict(), headers=dict(cookies=cookies))
|
|
153
|
+
options = dict(extract_options=extract_options, transform_options=transform_options)
|
|
154
|
+
return run_with_duckdb(get_module(".sales"), "AggregatedSales", "AggregatedSales", how, table, return_type, args, **options)
|
|
Binary file
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Sequence, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from typing import Any, Literal, Type
|
|
7
|
+
from linkmerce.common.extract import Extractor, JsonObject
|
|
8
|
+
from linkmerce.common.transform import Transformer, DBTransformer, DuckDBTransformer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
###################################################################
|
|
12
|
+
############################## Import #############################
|
|
13
|
+
###################################################################
|
|
14
|
+
|
|
15
|
+
def import_extractor(module: str, attr: str) -> Type[Extractor]:
|
|
16
|
+
from importlib import import_module
|
|
17
|
+
obj = import_module(_join(module, "extract"))
|
|
18
|
+
return getattr(obj, attr)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def import_transformer(module: str, attr: str) -> Type[Transformer]:
|
|
22
|
+
from importlib import import_module
|
|
23
|
+
obj = import_module(_join(module, "transform"))
|
|
24
|
+
return getattr(obj, attr)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def import_dbt(module: str | None = None, attr: str | None = None) -> Type[DBTransformer]:
|
|
28
|
+
if module and attr:
|
|
29
|
+
return import_transformer(module, attr)
|
|
30
|
+
else:
|
|
31
|
+
from importlib import import_module
|
|
32
|
+
module = import_module("linkmerce.common.transform")
|
|
33
|
+
return getattr(module, "DummyDBTransformer")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _join(path: str, name: str) -> str:
|
|
37
|
+
if path.startswith('.'):
|
|
38
|
+
path = "linkmerce.core" + path
|
|
39
|
+
return path + '.' + name
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
###################################################################
|
|
43
|
+
############################### Run ###############################
|
|
44
|
+
###################################################################
|
|
45
|
+
|
|
46
|
+
def run(
|
|
47
|
+
module: str,
|
|
48
|
+
extractor: str,
|
|
49
|
+
transformer: str | None = None,
|
|
50
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
51
|
+
args: tuple = tuple(),
|
|
52
|
+
kwargs: dict = dict(),
|
|
53
|
+
extract_options: dict | None = None,
|
|
54
|
+
transform_options: dict | None = None,
|
|
55
|
+
) -> Any:
|
|
56
|
+
extract_options = extract_options.copy() if isinstance(extract_options, dict) else dict()
|
|
57
|
+
if transformer and ("parser" not in extract_options):
|
|
58
|
+
transformer_ = import_transformer(module, transformer)(**(transform_options or dict()))
|
|
59
|
+
extract_options["parser"] = transformer_.transform
|
|
60
|
+
extractor_ = import_extractor(module, extractor)(**extract_options)
|
|
61
|
+
return _extract(extractor_, how, args, kwargs)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def run_with_connection(
|
|
65
|
+
module: str,
|
|
66
|
+
extractor: str,
|
|
67
|
+
transformer: str | None = None,
|
|
68
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
69
|
+
args: tuple = tuple(),
|
|
70
|
+
kwargs: dict = dict(),
|
|
71
|
+
extract_options: dict | None = None,
|
|
72
|
+
transform_options: dict | None = None,
|
|
73
|
+
) -> Any:
|
|
74
|
+
extract_options = extract_options.copy() if isinstance(extract_options, dict) else dict()
|
|
75
|
+
has_parser = ("parser" not in extract_options)
|
|
76
|
+
with import_dbt(module if has_parser else None, transformer)(**(transform_options or dict())) as transformer_:
|
|
77
|
+
if transformer and has_parser:
|
|
78
|
+
extract_options["parser"] = transformer_.transform
|
|
79
|
+
extractor_ = import_extractor(module, extractor)(**extract_options)
|
|
80
|
+
return _extract(extractor_, how, args, kwargs)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _extract(
|
|
84
|
+
extractor: Extractor,
|
|
85
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
86
|
+
args: tuple = tuple(),
|
|
87
|
+
kwargs: dict = dict(),
|
|
88
|
+
) -> Any:
|
|
89
|
+
if how == "sync":
|
|
90
|
+
return extractor.extract(*args, **kwargs)
|
|
91
|
+
elif how == "async":
|
|
92
|
+
import asyncio
|
|
93
|
+
return asyncio.run(extractor.extract_async(*args, **kwargs))
|
|
94
|
+
elif how == "async_loop":
|
|
95
|
+
import asyncio, nest_asyncio
|
|
96
|
+
nest_asyncio.apply()
|
|
97
|
+
loop = asyncio.get_running_loop()
|
|
98
|
+
task = asyncio.create_task(extractor.extract_async(*args, **kwargs))
|
|
99
|
+
return loop.run_until_complete(task)
|
|
100
|
+
else:
|
|
101
|
+
raise ValueError("Invalid value for how to run. Supported values are: sync, async, async_loop.")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
###################################################################
|
|
105
|
+
######################### Run with DuckDB #########################
|
|
106
|
+
###################################################################
|
|
107
|
+
|
|
108
|
+
def run_with_duckdb(
|
|
109
|
+
module: str,
|
|
110
|
+
extractor: str,
|
|
111
|
+
transformer: str | None = None,
|
|
112
|
+
how: Literal["sync","async","async_loop"] = "sync",
|
|
113
|
+
table: str | Sequence[str] = ":default:",
|
|
114
|
+
return_type: Literal["raw","csv","json","parquet"] = "json",
|
|
115
|
+
args: tuple = tuple(),
|
|
116
|
+
kwargs: dict = dict(),
|
|
117
|
+
extract_options: dict | None = None,
|
|
118
|
+
transform_options: dict | None = None,
|
|
119
|
+
) -> Any | dict[str,Any]:
|
|
120
|
+
extract_options = extract_options.copy() if isinstance(extract_options, dict) else dict()
|
|
121
|
+
has_parser = not ((return_type == "raw") or ("parser" in extract_options))
|
|
122
|
+
with import_dbt(module if has_parser else None, transformer)(**(transform_options or dict())) as transformer_:
|
|
123
|
+
if transformer and has_parser:
|
|
124
|
+
extract_options["parser"] = transformer_.transform
|
|
125
|
+
extractor_ = import_extractor(module, extractor)(**extract_options)
|
|
126
|
+
results = _extract(extractor_, how, args, kwargs)
|
|
127
|
+
return _fetch_all_from_table(transformer_, results, table, return_type)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def get_table_from_options(transform_options: dict | None = None, key: str = "table", default: str = ":default:") -> str:
|
|
131
|
+
if isinstance(transform_options, dict):
|
|
132
|
+
create_options = transform_options.get("create_options")
|
|
133
|
+
if isinstance(create_options, dict):
|
|
134
|
+
return create_options.get(key, default)
|
|
135
|
+
return default
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _get_table(transformer: DuckDBTransformer, table: str = ":default:") -> str:
|
|
139
|
+
return transformer.default_table if table == ":default:" else table
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _fetch_all_from_table(
|
|
143
|
+
transformer: DuckDBTransformer,
|
|
144
|
+
results: JsonObject,
|
|
145
|
+
table: str | Sequence[str] = ":default:",
|
|
146
|
+
return_type: Literal["raw","csv","json","parquet"] = "json",
|
|
147
|
+
) -> Any | dict[str,Any]:
|
|
148
|
+
if (return_type == "raw") or (transformer.conn is None):
|
|
149
|
+
return results
|
|
150
|
+
fetch_all = getattr(transformer, f"fetch_all_to_{return_type}")
|
|
151
|
+
if isinstance(table, str):
|
|
152
|
+
return fetch_all(_get_table(transformer, table))
|
|
153
|
+
elif isinstance(table, Sequence):
|
|
154
|
+
return {_get_table(transformer, table_): fetch_all(table_) for table_ in table}
|
|
155
|
+
else:
|
|
156
|
+
raise TypeError("Invalid type for table. A string or sequence type is allowed.")
|