kpubdata 0.1.0__tar.gz → 0.2.1__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.
- kpubdata-0.2.1/.github/workflows/publish-pypi.yml +175 -0
- kpubdata-0.2.1/.sisyphus/remove-get-record-plan.md +18 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/API_SPEC.md +14 -6
- {kpubdata-0.1.0 → kpubdata-0.2.1}/ARCHITECTURE.md +106 -1
- {kpubdata-0.1.0 → kpubdata-0.2.1}/CANONICAL_MODEL.md +0 -2
- {kpubdata-0.1.0 → kpubdata-0.2.1}/CHANGELOG.md +18 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/PACKAGING.md +49 -5
- {kpubdata-0.1.0 → kpubdata-0.2.1}/PKG-INFO +36 -1
- {kpubdata-0.1.0 → kpubdata-0.2.1}/PRD.md +2 -12
- {kpubdata-0.1.0 → kpubdata-0.2.1}/PROVIDER_ADAPTER_CONTRACT.md +0 -3
- {kpubdata-0.1.0 → kpubdata-0.2.1}/README.md +35 -0
- kpubdata-0.2.1/SUPPORTED_DATA.md +56 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/VALIDATION.md +0 -1
- {kpubdata-0.1.0 → kpubdata-0.2.1}/docs/architecture-diagrams.md +2 -6
- {kpubdata-0.1.0 → kpubdata-0.2.1}/docs/datago-api-reference.md +3 -3
- {kpubdata-0.1.0 → kpubdata-0.2.1}/docs/quickstart.md +23 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/pyproject.toml +1 -1
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/core/dataset.py +32 -20
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/core/models.py +17 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/core/protocol.py +0 -5
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/bok/adapter.py +25 -38
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/datago/adapter.py +44 -51
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/kosis/adapter.py +1 -3
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/lofin/adapter.py +20 -32
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/lofin/catalogue.json +31 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/registry.py +0 -1
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/contract/test_bok.py +0 -5
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/contract/test_datago.py +0 -5
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/contract/test_kosis.py +0 -5
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/contract/test_lofin.py +25 -6
- kpubdata-0.2.1/tests/fixtures/lofin/success_fiacrv.json +50 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/integration/test_client_flow.py +1 -22
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/integration/test_exception_propagation.py +1 -41
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/integration/test_lofin_live.py +37 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/core/test_dataset.py +67 -34
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/core/test_dataset_coverage.py +16 -3
- kpubdata-0.2.1/tests/unit/core/test_to_pandas.py +82 -0
- kpubdata-0.2.1/tests/unit/providers/bok/test_bok_adapter.py +86 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/providers/datago/test_adapter.py +35 -30
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/providers/datago/test_adapter_coverage.py +9 -18
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/providers/datago/test_fixtures.py +7 -4
- kpubdata-0.2.1/tests/unit/providers/lofin/test_lofin_adapter.py +85 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/test_catalog.py +4 -6
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/test_catalog_coverage.py +0 -3
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/test_client_coverage.py +0 -6
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/test_client_transport_requirements.py +0 -6
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/test_registry.py +6 -7
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/test_registry_coverage.py +0 -4
- {kpubdata-0.1.0 → kpubdata-0.2.1}/uv.lock +1 -1
- kpubdata-0.1.0/.github/workflows/publish-pypi.yml +0 -60
- kpubdata-0.1.0/SUPPORTED_DATA.md +0 -50
- {kpubdata-0.1.0 → kpubdata-0.2.1}/.github/ISSUE_TEMPLATE/new-provider-adapter.yml +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/.github/workflows/ci.yml +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/.github/workflows/integration.yml +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/.gitignore +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/AGENTS.md +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/CONTRIBUTING.md +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/LICENSE +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/ROADMAP.md +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/docs/adrs/0001-dialect-inspired-architecture.md +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/docs/adrs/0002-standardize-ux-not-native-shape.md +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/docs/product-family-architecture.md +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/docs/providers/bok.md +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/docs/providers/datago.md +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/docs/providers/kosis.md +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/docs/tutorial-ai-agent-workflow.md +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/catalog.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/client.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/config.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/core/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/core/capability.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/core/representation.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/exceptions.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/_common.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/bok/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/bok/catalogue.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/datago/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/datago/catalogue.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/kosis/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/kosis/catalogue.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/providers/lofin/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/py.typed +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/transport/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/transport/decode.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/transport/http.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/src/kpubdata/transport/retry.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/contract/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/contract/provider_adapter.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/bok/error_auth.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/bok/success_empty.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/bok/success_single_page.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/error_auth_30.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/error_invalid_request_10.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/error_rate_limit_22.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/error_service_unavailable_01.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/error_xml_auth_30.xml +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/success_apt_trade.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/success_empty.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/success_multi_page_1.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/success_multi_page_2.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/success_single_item.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/success_single_page.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/success_string_numerics.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/success_xml.xml +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/datago/success_xml_single_item.xml +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/kosis/error_auth.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/kosis/success_empty.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/kosis/success_single_page.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/lofin/error_auth.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/fixtures/lofin/success_single_page.json +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/integration/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/integration/conftest.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/integration/test_bok_live.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/integration/test_datago_live.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/integration/test_kosis_live.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/integration/test_transport_http.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/core/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/core/test_exceptions.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/core/test_exceptions_coverage.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/core/test_models.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/core/test_models_fixtures.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/providers/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/providers/datago/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/providers/datago/conftest.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/test_catalogue_validation.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/test_config.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/test_config_coverage.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/transport/__init__.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/transport/test_decode.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/transport/test_decode_coverage.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/transport/test_http_coverage.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/transport/test_http_coverage_extra.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/transport/test_logging.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/transport/test_retry.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/transport/test_retry_after.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/transport/test_retry_coverage.py +0 -0
- {kpubdata-0.1.0 → kpubdata-0.2.1}/tests/unit/transport/test_transport_requirements.py +0 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
bump:
|
|
10
|
+
description: "Version bump type"
|
|
11
|
+
required: true
|
|
12
|
+
type: choice
|
|
13
|
+
options:
|
|
14
|
+
- patch
|
|
15
|
+
- minor
|
|
16
|
+
- pre
|
|
17
|
+
default: patch
|
|
18
|
+
dry_run:
|
|
19
|
+
description: "Dry run (skip PyPI publish)"
|
|
20
|
+
required: false
|
|
21
|
+
type: boolean
|
|
22
|
+
default: false
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: write
|
|
26
|
+
id-token: write
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
bump-version:
|
|
30
|
+
name: Bump Version
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
if: github.event_name == 'workflow_dispatch'
|
|
33
|
+
timeout-minutes: 5
|
|
34
|
+
outputs:
|
|
35
|
+
new_version: ${{ steps.bump.outputs.new_version }}
|
|
36
|
+
tag: ${{ steps.bump.outputs.tag }}
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
with:
|
|
40
|
+
fetch-depth: 0
|
|
41
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
42
|
+
|
|
43
|
+
- name: Configure git
|
|
44
|
+
run: |
|
|
45
|
+
git config user.name "github-actions[bot]"
|
|
46
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
47
|
+
|
|
48
|
+
- name: Bump version
|
|
49
|
+
id: bump
|
|
50
|
+
shell: bash
|
|
51
|
+
run: |
|
|
52
|
+
current=$(python3 -c "
|
|
53
|
+
import re, pathlib
|
|
54
|
+
m = re.search(r'version = \"([^\"]+)\"', pathlib.Path('pyproject.toml').read_text())
|
|
55
|
+
print(m.group(1))
|
|
56
|
+
")
|
|
57
|
+
echo "Current version: ${current}"
|
|
58
|
+
|
|
59
|
+
bump="${{ inputs.bump }}"
|
|
60
|
+
new_version=$(python3 -c "
|
|
61
|
+
import re
|
|
62
|
+
v = '${current}'
|
|
63
|
+
parts = v.split('.')
|
|
64
|
+
major, minor = int(parts[0]), int(parts[1])
|
|
65
|
+
patch_raw = parts[2]
|
|
66
|
+
|
|
67
|
+
if '${bump}' == 'patch':
|
|
68
|
+
patch = int(re.match(r'\d+', patch_raw).group())
|
|
69
|
+
print(f'{major}.{minor}.{patch + 1}')
|
|
70
|
+
elif '${bump}' == 'minor':
|
|
71
|
+
print(f'{major}.{minor + 1}.0')
|
|
72
|
+
elif '${bump}' == 'pre':
|
|
73
|
+
if 'a' in patch_raw:
|
|
74
|
+
patch, pre = patch_raw.split('a')
|
|
75
|
+
print(f'{major}.{minor}.{patch}a{int(pre) + 1}')
|
|
76
|
+
else:
|
|
77
|
+
patch = int(patch_raw)
|
|
78
|
+
print(f'{major}.{minor}.{patch + 1}a0')
|
|
79
|
+
")
|
|
80
|
+
|
|
81
|
+
python3 -c "
|
|
82
|
+
import pathlib
|
|
83
|
+
p = pathlib.Path('pyproject.toml')
|
|
84
|
+
p.write_text(p.read_text().replace('version = \"${current}\"', 'version = \"${new_version}\"'))
|
|
85
|
+
"
|
|
86
|
+
|
|
87
|
+
echo "new_version=${new_version}" >> "$GITHUB_OUTPUT"
|
|
88
|
+
echo "tag=v${new_version}" >> "$GITHUB_OUTPUT"
|
|
89
|
+
echo "Bumped: ${current} → ${new_version}"
|
|
90
|
+
|
|
91
|
+
- name: Commit and tag
|
|
92
|
+
run: |
|
|
93
|
+
git add pyproject.toml
|
|
94
|
+
git commit -m "chore: bump version to ${{ steps.bump.outputs.new_version }}"
|
|
95
|
+
git tag "${{ steps.bump.outputs.tag }}"
|
|
96
|
+
git push origin main --tags
|
|
97
|
+
|
|
98
|
+
build:
|
|
99
|
+
name: Build Package
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
needs: [bump-version]
|
|
102
|
+
if: always() && (needs.bump-version.result == 'success' || needs.bump-version.result == 'skipped')
|
|
103
|
+
timeout-minutes: 10
|
|
104
|
+
steps:
|
|
105
|
+
- uses: actions/checkout@v4
|
|
106
|
+
with:
|
|
107
|
+
ref: ${{ github.event_name == 'workflow_dispatch' && 'main' || github.ref }}
|
|
108
|
+
|
|
109
|
+
- uses: astral-sh/setup-uv@v4
|
|
110
|
+
with:
|
|
111
|
+
enable-cache: true
|
|
112
|
+
cache-dependency-glob: "uv.lock"
|
|
113
|
+
|
|
114
|
+
- uses: actions/setup-python@v5
|
|
115
|
+
with:
|
|
116
|
+
python-version: "3.12"
|
|
117
|
+
|
|
118
|
+
- name: Install dependencies
|
|
119
|
+
run: uv sync --frozen --extra dev
|
|
120
|
+
|
|
121
|
+
- name: Run quality gates
|
|
122
|
+
run: |
|
|
123
|
+
uv run ruff check .
|
|
124
|
+
uv run ruff format --check .
|
|
125
|
+
uv run mypy src
|
|
126
|
+
uv run pytest -ra
|
|
127
|
+
|
|
128
|
+
- name: Build sdist and wheel
|
|
129
|
+
run: uv run python -m build
|
|
130
|
+
|
|
131
|
+
- name: Upload artifacts
|
|
132
|
+
uses: actions/upload-artifact@v4
|
|
133
|
+
with:
|
|
134
|
+
name: dist
|
|
135
|
+
path: dist/
|
|
136
|
+
|
|
137
|
+
create-release:
|
|
138
|
+
name: Create GitHub Release
|
|
139
|
+
runs-on: ubuntu-latest
|
|
140
|
+
needs: [bump-version, build]
|
|
141
|
+
if: github.event_name == 'workflow_dispatch' && !inputs.dry_run
|
|
142
|
+
permissions:
|
|
143
|
+
contents: write
|
|
144
|
+
steps:
|
|
145
|
+
- uses: actions/checkout@v4
|
|
146
|
+
with:
|
|
147
|
+
ref: main
|
|
148
|
+
|
|
149
|
+
- name: Create release
|
|
150
|
+
env:
|
|
151
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
152
|
+
run: |
|
|
153
|
+
gh release create "${{ needs.bump-version.outputs.tag }}" \
|
|
154
|
+
--title "${{ needs.bump-version.outputs.tag }}" \
|
|
155
|
+
--generate-notes \
|
|
156
|
+
--target main
|
|
157
|
+
|
|
158
|
+
publish:
|
|
159
|
+
name: Publish to PyPI
|
|
160
|
+
runs-on: ubuntu-latest
|
|
161
|
+
needs: [bump-version, build]
|
|
162
|
+
if: |
|
|
163
|
+
always() &&
|
|
164
|
+
needs.build.result == 'success' &&
|
|
165
|
+
(github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && !inputs.dry_run))
|
|
166
|
+
environment: pypi
|
|
167
|
+
steps:
|
|
168
|
+
- name: Download artifacts
|
|
169
|
+
uses: actions/download-artifact@v4
|
|
170
|
+
with:
|
|
171
|
+
name: dist
|
|
172
|
+
path: dist/
|
|
173
|
+
|
|
174
|
+
- name: Publish to PyPI
|
|
175
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## Scope
|
|
2
|
+
- Remove `get_record` from the provider protocol, registry validation, dataset binding, tests, and docs.
|
|
3
|
+
|
|
4
|
+
## Touched modules
|
|
5
|
+
- `src/kpubdata/core/protocol.py`
|
|
6
|
+
- `src/kpubdata/core/dataset.py`
|
|
7
|
+
- `src/kpubdata/registry.py`
|
|
8
|
+
- affected unit/integration/contract tests
|
|
9
|
+
- API and architecture docs describing removed single-record access
|
|
10
|
+
|
|
11
|
+
## Risks
|
|
12
|
+
- Public `Dataset.get()` removal can leave stale docs/tests behind.
|
|
13
|
+
- Registry validation and protocol changes can break fake adapters in tests.
|
|
14
|
+
|
|
15
|
+
## Validation steps
|
|
16
|
+
- Run LSP diagnostics on changed source and test files.
|
|
17
|
+
- Run `uv run pytest`.
|
|
18
|
+
- Run `uv run mypy src`.
|
|
@@ -43,12 +43,21 @@ dataset = client.dataset("molit.apartment_trades")
|
|
|
43
43
|
result = dataset.list(lawd_code="11680", deal_ym="202503")
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
`list()` returns exactly one page of results. When another page is available,
|
|
47
|
+
the returned `RecordBatch.next_page` is set.
|
|
48
|
+
|
|
49
|
+
### List all pages
|
|
47
50
|
|
|
48
51
|
```python
|
|
49
|
-
|
|
52
|
+
dataset = client.dataset("molit.apartment_trades")
|
|
53
|
+
for batch in dataset.list_all(lawd_code="11680", deal_ym="202503"):
|
|
54
|
+
for item in batch.items:
|
|
55
|
+
print(item)
|
|
50
56
|
```
|
|
51
57
|
|
|
58
|
+
`list_all()` yields one `RecordBatch` per page and follows `next_page`
|
|
59
|
+
automatically until pagination is exhausted.
|
|
60
|
+
|
|
52
61
|
### Schema
|
|
53
62
|
|
|
54
63
|
```python
|
|
@@ -82,9 +91,9 @@ Rules:
|
|
|
82
91
|
|
|
83
92
|
Returns `RecordBatch`.
|
|
84
93
|
|
|
85
|
-
### `
|
|
94
|
+
### `list_all()`
|
|
86
95
|
|
|
87
|
-
Returns
|
|
96
|
+
Returns a generator of `RecordBatch` values, one per page.
|
|
88
97
|
|
|
89
98
|
### `schema()`
|
|
90
99
|
|
|
@@ -101,7 +110,7 @@ KPubData promises stability for:
|
|
|
101
110
|
- `Client`
|
|
102
111
|
- `Client.from_env()`
|
|
103
112
|
- dataset discovery methods
|
|
104
|
-
- `Dataset.list/
|
|
113
|
+
- `Dataset.list/list_all/schema/call_raw`
|
|
105
114
|
- canonical model classes
|
|
106
115
|
- canonical error types
|
|
107
116
|
|
|
@@ -157,4 +166,3 @@ client.call_provider_endpoint("seoul", "SearchSTNTimeTableByIDService", ...)
|
|
|
157
166
|
| :--- | :--- | :--- |
|
|
158
167
|
| [kpubdata-builder](https://github.com/yeongseon/kpubdata-builder) | [API_CONTRACT.md](https://github.com/yeongseon/kpubdata-builder/blob/main/API_CONTRACT.md) | Builder API 규약 |
|
|
159
168
|
| [kpubdata-studio](https://github.com/yeongseon/kpubdata-studio) | [API_CONTRACT.md](https://github.com/yeongseon/kpubdata-studio/blob/main/API_CONTRACT.md) | Studio API 규약 |
|
|
160
|
-
|
|
@@ -144,7 +144,112 @@ KPubData는 이 모든 은행을 대신 처리해주는 **"통합 키오스크"*
|
|
|
144
144
|
5. **5단계: 결과 반환 (Return)**
|
|
145
145
|
- 최종적으로 예쁘게 포장된 `RecordBatch` 객체가 사용자에게 전달됩니다.
|
|
146
146
|
|
|
147
|
-
## 6.
|
|
147
|
+
## 6. 인증 흐름 (Authentication Flow)
|
|
148
|
+
|
|
149
|
+
### 6.1 개요
|
|
150
|
+
|
|
151
|
+
공공데이터 API를 사용하려면 각 기관에서 발급한 **인증키(API Key)**가 필요합니다. KPubData는 이 키를 사용자로부터 받아 각 기관이 요구하는 방식으로 HTTP 요청에 주입합니다.
|
|
152
|
+
|
|
153
|
+
핵심 설계 원칙:
|
|
154
|
+
- **키 저장은 `KPubDataConfig`에 집중**: 모든 기관의 키를 한 곳에서 관리합니다.
|
|
155
|
+
- **키 주입은 각 어댑터에 위임**: 기관마다 키를 넣는 위치와 파라미터 이름이 다르므로, 이 로직은 어댑터가 담당합니다.
|
|
156
|
+
- **환경변수 우선 해석**: 명시적으로 전달된 키 → `KPUBDATA_*` 환경변수 → fallback 환경변수 순으로 탐색합니다.
|
|
157
|
+
|
|
158
|
+
### 6.2 키 해석 우선순위
|
|
159
|
+
|
|
160
|
+
```mermaid
|
|
161
|
+
flowchart TD
|
|
162
|
+
Start[키 요청: require_provider_key] --> E1{명시적으로 전달된 키?}
|
|
163
|
+
E1 -- 있음 --> Use[키 반환]
|
|
164
|
+
E1 -- 없음 --> E2{KPUBDATA_{PROVIDER}_API_KEY 환경변수?}
|
|
165
|
+
E2 -- 있음 --> Use
|
|
166
|
+
E2 -- 없음 --> E3{"{PROVIDER}_API_KEY" 환경변수?}
|
|
167
|
+
E3 -- 있음 --> Use
|
|
168
|
+
E3 -- 없음 --> Err[ConfigError 발생]
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
# 1순위: 명시적 전달
|
|
173
|
+
client = Client(provider_keys={"datago": "MY_KEY"})
|
|
174
|
+
|
|
175
|
+
# 2순위: KPUBDATA_ 접두사 환경변수
|
|
176
|
+
# export KPUBDATA_DATAGO_API_KEY="MY_KEY"
|
|
177
|
+
client = Client.from_env()
|
|
178
|
+
|
|
179
|
+
# 3순위: 접두사 없는 환경변수 (fallback)
|
|
180
|
+
# export DATAGO_API_KEY="MY_KEY"
|
|
181
|
+
client = Client.from_env()
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**관련 코드**: `src/kpubdata/config.py` → `KPubDataConfig.get_provider_key()`, `require_provider_key()`
|
|
185
|
+
|
|
186
|
+
### 6.3 키의 내부 전달 경로
|
|
187
|
+
|
|
188
|
+
```mermaid
|
|
189
|
+
sequenceDiagram
|
|
190
|
+
participant U as 사용자
|
|
191
|
+
participant C as Client
|
|
192
|
+
participant Cfg as KPubDataConfig
|
|
193
|
+
participant A as ProviderAdapter
|
|
194
|
+
participant T as HttpTransport
|
|
195
|
+
participant P as 공공 API
|
|
196
|
+
|
|
197
|
+
U->>C: Client(provider_keys={"datago": "KEY"})
|
|
198
|
+
C->>Cfg: KPubDataConfig(provider_keys={...})
|
|
199
|
+
Note over Cfg: 키를 dict에 보관
|
|
200
|
+
|
|
201
|
+
U->>C: dataset("datago.village_fcst").list(...)
|
|
202
|
+
C->>A: query_records(dataset_ref, query)
|
|
203
|
+
A->>Cfg: require_provider_key("datago")
|
|
204
|
+
Cfg-->>A: "KEY" 반환
|
|
205
|
+
A->>A: _build_params()에서 키를 HTTP 파라미터에 주입
|
|
206
|
+
A->>T: HTTP 요청 (키 포함)
|
|
207
|
+
T->>P: GET https://apis.data.go.kr/...?serviceKey=KEY
|
|
208
|
+
P-->>T: 응답
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**핵심 메서드**: 모든 어댑터는 `_require_api_key()` → `self._config.require_provider_key("slug")` 패턴을 사용합니다.
|
|
212
|
+
|
|
213
|
+
### 6.4 기관별 키 주입 방식
|
|
214
|
+
|
|
215
|
+
기관마다 키를 HTTP 요청에 넣는 위치와 파라미터 이름이 다릅니다.
|
|
216
|
+
|
|
217
|
+
| Provider | 파라미터 이름 | 주입 위치 | 예시 | 특이사항 |
|
|
218
|
+
|---|---|---|---|---|
|
|
219
|
+
| `datago` | `serviceKey` | Query parameter | `?serviceKey=KEY` | dataset별 `service_key_param` 메타데이터로 파라미터명 오버라이드 가능 |
|
|
220
|
+
| `bok` | _(URL 경로)_ | URL path segment | `/{KEY}/json/{operation}/...` | 쿼리 파라미터가 아닌 **URL 경로**에 직접 삽입 |
|
|
221
|
+
| `kosis` | `apiKey` | Query parameter | `?apiKey=KEY` | 표준적인 쿼리 파라미터 방식 |
|
|
222
|
+
| `lofin` | `Key` | Query parameter | `?Key=KEY` | 대문자 `K` 필수. `Type=json`도 대문자 `T` 필수 |
|
|
223
|
+
|
|
224
|
+
```text
|
|
225
|
+
datago: GET https://apis.data.go.kr/.../getVilageFcst?serviceKey=KEY&...
|
|
226
|
+
bok: GET https://ecos.bok.or.kr/api/KEY/json/StatisticSearch/...
|
|
227
|
+
kosis: GET https://kosis.kr/openapi/Idx/indikatorList.do?apiKey=KEY&...
|
|
228
|
+
lofin: GET https://www.lofin365.go.kr/lf/hub/AJGCF?Key=KEY&Type=json&...
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### 6.5 새 어댑터 추가 시 인증 구현 가이드
|
|
232
|
+
|
|
233
|
+
새로운 데이터 기관을 추가할 때, 인증 관련 구현은 다음 3단계를 따릅니다.
|
|
234
|
+
|
|
235
|
+
**1단계**: `_require_api_key()` 메서드를 정의합니다.
|
|
236
|
+
```python
|
|
237
|
+
def _require_api_key(self) -> str:
|
|
238
|
+
return self._config.require_provider_key("your_provider_slug")
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**2단계**: 요청 생성 시 키를 해당 기관의 방식에 맞게 주입합니다.
|
|
242
|
+
```python
|
|
243
|
+
# Query parameter 방식 (가장 일반적)
|
|
244
|
+
params = {"apiKey": self._require_api_key(), ...}
|
|
245
|
+
|
|
246
|
+
# URL path 방식 (bok처럼 경로에 넣는 경우)
|
|
247
|
+
url = f"{base_url}/{self._require_api_key()}/json/{operation}"
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**3단계**: `Client`의 `_BUILTIN_PROVIDERS` 튜플에 등록하면, `from_env()`가 자동으로 `KPUBDATA_{SLUG}_API_KEY` 환경변수를 스캔합니다.
|
|
251
|
+
|
|
252
|
+
## 7. 자주 묻는 질문 (FAQ)
|
|
148
253
|
|
|
149
254
|
**Q: 새 데이터셋을 추가하려면 어디를 수정하나요?**
|
|
150
255
|
A: 해당 기관의 어댑터(`providers/<provider>/adapter.py`)와 데이터 목록 파일(`catalogue.json`)을 수정하면 됩니다.
|
|
@@ -312,7 +312,6 @@ class Dataset:
|
|
|
312
312
|
ref: DatasetRef
|
|
313
313
|
|
|
314
314
|
def list(self, **filters) -> RecordBatch: ...
|
|
315
|
-
def get(self, **key) -> dict[str, object] | None: ...
|
|
316
315
|
def schema(self) -> SchemaDescriptor | None: ...
|
|
317
316
|
def call_raw(self, operation: str, **params) -> object: ...
|
|
318
317
|
```
|
|
@@ -352,4 +351,3 @@ Do not erase provider-native detail.
|
|
|
352
351
|
| [PROVIDER_ADAPTER_CONTRACT.md](./PROVIDER_ADAPTER_CONTRACT.md) | 어댑터 구현 규약 |
|
|
353
352
|
| [API_SPEC.md](./API_SPEC.md) | 파이썬 API 명세 |
|
|
354
353
|
| [VALIDATION.md](./VALIDATION.md) | 아키텍처 타당성 검증 |
|
|
355
|
-
|
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2025-04-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Single-page pagination contract for all adapters (datago, bok, lofin, kosis)
|
|
14
|
+
- `Dataset.list_all()` generator for automatic multi-page iteration
|
|
15
|
+
- `RecordBatch.to_pandas()` for pandas DataFrame conversion (optional `pandas` dependency)
|
|
16
|
+
- Unit tests for BOK and LOFIN adapters
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Default `page_size` increased from 10 to 100
|
|
20
|
+
- **Breaking**: `query_records()` now returns a single page instead of auto-draining all pages
|
|
21
|
+
|
|
22
|
+
### Removed
|
|
23
|
+
- Unreachable single-record adapter stubs
|
|
24
|
+
- Single-record access from the provider and dataset public APIs
|
|
25
|
+
|
|
26
|
+
## [0.1.0] - 2025-04-10
|
|
27
|
+
|
|
10
28
|
### Added
|
|
11
29
|
|
|
12
30
|
- Core framework: `Client`, `Dataset`, `Catalog`, `Query`, `RecordBatch` public API
|
|
@@ -82,11 +82,55 @@ uv run python -m build
|
|
|
82
82
|
|
|
83
83
|
### Release
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
GitHub Actions `Publish to PyPI` workflow를 통해 배포합니다.
|
|
86
|
+
|
|
87
|
+
#### 방법 1: 수동 트리거 (권장)
|
|
88
|
+
|
|
89
|
+
1. GitHub → Actions → **Publish to PyPI** → **Run workflow**
|
|
90
|
+
2. 옵션 선택:
|
|
91
|
+
- **bump**: `patch` (0.1.0 → 0.1.1), `minor` (0.1.0 → 0.2.0), `pre` (0.1.0 → 0.1.1a0)
|
|
92
|
+
- **dry_run**: 체크하면 PyPI 배포 없이 빌드만 확인
|
|
93
|
+
3. workflow가 자동으로 수행하는 작업:
|
|
94
|
+
- `pyproject.toml` 버전 bump + commit + tag
|
|
95
|
+
- quality gates 실행 (ruff, mypy, pytest)
|
|
96
|
+
- sdist + wheel 빌드
|
|
97
|
+
- GitHub Release 생성 (자동 release notes)
|
|
98
|
+
- PyPI 배포 (trusted publisher / OIDC)
|
|
99
|
+
|
|
100
|
+
#### 방법 2: GitHub Release 수동 생성
|
|
101
|
+
|
|
102
|
+
1. 직접 `pyproject.toml` 버전 수정 + commit + push
|
|
103
|
+
2. GitHub → Releases → **Create a new release**
|
|
104
|
+
3. 태그: `v{version}` (예: `v0.2.0`)
|
|
105
|
+
4. Release 생성 시 workflow가 자동으로 빌드 + PyPI 배포
|
|
106
|
+
|
|
107
|
+
#### 배포 전 체크리스트
|
|
108
|
+
|
|
109
|
+
- [ ] main 브랜치에 모든 변경사항 merge 완료
|
|
110
|
+
- [ ] CI 통과 확인 (ruff, mypy, pytest)
|
|
111
|
+
- [ ] `SUPPORTED_DATA.md` 최신 상태
|
|
112
|
+
|
|
113
|
+
#### 배포 흐름도
|
|
114
|
+
|
|
115
|
+
```mermaid
|
|
116
|
+
flowchart TD
|
|
117
|
+
A[변경사항 main에 merge] --> B{배포 방식}
|
|
118
|
+
B -->|수동 트리거| C[Actions > Run workflow]
|
|
119
|
+
C --> D[bump type 선택]
|
|
120
|
+
D --> E[자동: 버전 bump + commit + tag]
|
|
121
|
+
B -->|Release 생성| F[수동: 버전 수정 + Release 생성]
|
|
122
|
+
E --> G[Quality gates]
|
|
123
|
+
F --> G
|
|
124
|
+
G --> H[Build sdist + wheel]
|
|
125
|
+
H --> I[GitHub Release 생성]
|
|
126
|
+
I --> J[PyPI 배포]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### 환경 설정
|
|
130
|
+
|
|
131
|
+
- **PyPI trusted publisher**: GitHub Actions OIDC — 별도 API 토큰 불필요
|
|
132
|
+
- **GitHub environment**: `pypi` (Settings → Environments)
|
|
133
|
+
- **Workflow 파일**: `.github/workflows/publish-pypi.yml`
|
|
90
134
|
|
|
91
135
|
## 7. Versioning policy
|
|
92
136
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kpubdata
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Dialect-inspired Python access framework for Korean public data
|
|
5
5
|
Author: Yeongseon Choe
|
|
6
6
|
License: MIT
|
|
@@ -297,6 +297,37 @@ for item in result.items[:5]:
|
|
|
297
297
|
# ...
|
|
298
298
|
```
|
|
299
299
|
|
|
300
|
+
### 9. 전체 페이지 자동 조회 (list_all)
|
|
301
|
+
|
|
302
|
+
대량의 데이터를 페이지 단위로 자동 순회하려면 `list_all()`을 사용합니다.
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
ds = client.dataset("lofin.expenditure_budget")
|
|
306
|
+
|
|
307
|
+
# 모든 페이지를 자동으로 순회하며 RecordBatch를 반환
|
|
308
|
+
for batch in ds.list_all(fyr="2024"):
|
|
309
|
+
print(f"이번 페이지: {len(batch.items)}건")
|
|
310
|
+
for item in batch.items:
|
|
311
|
+
print(item)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### 10. pandas DataFrame 변환
|
|
315
|
+
|
|
316
|
+
조회 결과를 pandas DataFrame으로 변환하여 분석할 수 있습니다.
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
pip install kpubdata[pandas]
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
ds = client.dataset("bok.base_rate")
|
|
324
|
+
result = ds.list(start_date="202401", end_date="202412")
|
|
325
|
+
|
|
326
|
+
# pandas DataFrame으로 변환
|
|
327
|
+
df = result.to_pandas()
|
|
328
|
+
print(df.head())
|
|
329
|
+
```
|
|
330
|
+
|
|
300
331
|
## 지원 중인 공공데이터
|
|
301
332
|
|
|
302
333
|
현재 지원 현황의 상세 정보와 진행 예정 항목은 [SUPPORTED_DATA.md](./SUPPORTED_DATA.md)에서 확인할 수 있습니다.
|
|
@@ -316,6 +347,9 @@ for item in result.items[:5]:
|
|
|
316
347
|
| 지방재정365 (`lofin`) | 기능별세출 (`expenditure_function`) | 지원 |
|
|
317
348
|
| 지방재정365 (`lofin`) | 채무비율현황 (`debt_ratio`) | 지원 |
|
|
318
349
|
| 지방재정365 (`lofin`) | 재정자립도현황 (`fiscal_independence`) | 지원 |
|
|
350
|
+
| 지방재정365 (`lofin`) | 재원별 세입결산 (`revenue_by_source`) | 지원 |
|
|
351
|
+
|
|
352
|
+
> 검증 수준 및 실API 최종 검증일 등 상세 정보는 [SUPPORTED_DATA.md](./SUPPORTED_DATA.md)를 참고하세요.
|
|
319
353
|
|
|
320
354
|
## 문서 가이드 (Document Guide)
|
|
321
355
|
|
|
@@ -378,3 +412,4 @@ KPubData의 설계 철학과 사용 방법을 안내하는 문서 목록입니
|
|
|
378
412
|
|
|
379
413
|
### v0.3
|
|
380
414
|
- 표준 코어를 기반으로 한 경량 [MCP](https://modelcontextprotocol.io/)(Model Context Protocol — AI 모델이 외부 도구를 사용하는 표준 규약) 어댑터 지원
|
|
415
|
+
|
|
@@ -157,16 +157,7 @@ Acceptance:
|
|
|
157
157
|
- includes `raw`
|
|
158
158
|
- includes metadata about pagination and provider
|
|
159
159
|
|
|
160
|
-
### FR-6.
|
|
161
|
-
|
|
162
|
-
Supported datasets may expose a single-record get operation.
|
|
163
|
-
|
|
164
|
-
Acceptance:
|
|
165
|
-
|
|
166
|
-
- `dataset.get(...)`
|
|
167
|
-
- unsupported datasets raise `UnsupportedCapabilityError`
|
|
168
|
-
|
|
169
|
-
### FR-7. Schema/metadata access
|
|
160
|
+
### FR-6. Schema/metadata access
|
|
170
161
|
|
|
171
162
|
Datasets may expose schema or field metadata.
|
|
172
163
|
|
|
@@ -175,7 +166,7 @@ Acceptance:
|
|
|
175
166
|
- `dataset.schema()`
|
|
176
167
|
- returns `SchemaDescriptor | None`
|
|
177
168
|
|
|
178
|
-
### FR-
|
|
169
|
+
### FR-7. Raw access
|
|
179
170
|
|
|
180
171
|
Every provider adapter must expose a raw-call path.
|
|
181
172
|
|
|
@@ -303,4 +294,3 @@ client.getRTMSDataSvcAptTradeDev(...)
|
|
|
303
294
|
| :--- | :--- | :--- |
|
|
304
295
|
| [kpubdata-builder](https://github.com/yeongseon/kpubdata-builder) | [PRD.md](https://github.com/yeongseon/kpubdata-builder/blob/main/PRD.md) | Builder 제품 요구사항 |
|
|
305
296
|
| [kpubdata-studio](https://github.com/yeongseon/kpubdata-studio) | [PRD.md](https://github.com/yeongseon/kpubdata-studio/blob/main/PRD.md) | Studio 제품 요구사항 |
|
|
306
|
-
|
|
@@ -149,7 +149,6 @@ class ProviderAdapter(Protocol):
|
|
|
149
149
|
def search_datasets(self, text: str) -> list[DatasetRef]: ...
|
|
150
150
|
def get_dataset(self, dataset_id: str) -> DatasetRef: ...
|
|
151
151
|
def query_records(self, dataset: DatasetRef, query: Query) -> RecordBatch: ...
|
|
152
|
-
def get_record(self, dataset: DatasetRef, key: dict[str, object]) -> dict[str, object] | None: ...
|
|
153
152
|
def get_schema(self, dataset: DatasetRef) -> SchemaDescriptor | None: ...
|
|
154
153
|
def call_raw(self, dataset: DatasetRef, operation: str, params: dict[str, object]) -> object: ...
|
|
155
154
|
```
|
|
@@ -163,7 +162,6 @@ classDiagram
|
|
|
163
162
|
+search_datasets(text: str) list~DatasetRef~
|
|
164
163
|
+get_dataset(dataset_id: str) DatasetRef
|
|
165
164
|
+query_records(dataset: DatasetRef, query: Query) RecordBatch
|
|
166
|
-
+get_record(dataset: DatasetRef, key: dict) dict
|
|
167
165
|
+get_schema(dataset: DatasetRef) SchemaDescriptor
|
|
168
166
|
+call_raw(dataset: DatasetRef, op: str, params: dict) object
|
|
169
167
|
}
|
|
@@ -248,4 +246,3 @@ Otherwise, keep the complexity local to the adapter.
|
|
|
248
246
|
| [API_SPEC.md](./API_SPEC.md) | 파이썬 API 명세 |
|
|
249
247
|
| [VALIDATION.md](./VALIDATION.md) | 아키텍처 타당성 검증 |
|
|
250
248
|
| [AGENTS.md](./AGENTS.md) | 어댑터 개발 가이드 및 에이전트 규칙 |
|
|
251
|
-
|
|
@@ -260,6 +260,37 @@ for item in result.items[:5]:
|
|
|
260
260
|
# ...
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
+
### 9. 전체 페이지 자동 조회 (list_all)
|
|
264
|
+
|
|
265
|
+
대량의 데이터를 페이지 단위로 자동 순회하려면 `list_all()`을 사용합니다.
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
ds = client.dataset("lofin.expenditure_budget")
|
|
269
|
+
|
|
270
|
+
# 모든 페이지를 자동으로 순회하며 RecordBatch를 반환
|
|
271
|
+
for batch in ds.list_all(fyr="2024"):
|
|
272
|
+
print(f"이번 페이지: {len(batch.items)}건")
|
|
273
|
+
for item in batch.items:
|
|
274
|
+
print(item)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### 10. pandas DataFrame 변환
|
|
278
|
+
|
|
279
|
+
조회 결과를 pandas DataFrame으로 변환하여 분석할 수 있습니다.
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
pip install kpubdata[pandas]
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
ds = client.dataset("bok.base_rate")
|
|
287
|
+
result = ds.list(start_date="202401", end_date="202412")
|
|
288
|
+
|
|
289
|
+
# pandas DataFrame으로 변환
|
|
290
|
+
df = result.to_pandas()
|
|
291
|
+
print(df.head())
|
|
292
|
+
```
|
|
293
|
+
|
|
263
294
|
## 지원 중인 공공데이터
|
|
264
295
|
|
|
265
296
|
현재 지원 현황의 상세 정보와 진행 예정 항목은 [SUPPORTED_DATA.md](./SUPPORTED_DATA.md)에서 확인할 수 있습니다.
|
|
@@ -279,6 +310,9 @@ for item in result.items[:5]:
|
|
|
279
310
|
| 지방재정365 (`lofin`) | 기능별세출 (`expenditure_function`) | 지원 |
|
|
280
311
|
| 지방재정365 (`lofin`) | 채무비율현황 (`debt_ratio`) | 지원 |
|
|
281
312
|
| 지방재정365 (`lofin`) | 재정자립도현황 (`fiscal_independence`) | 지원 |
|
|
313
|
+
| 지방재정365 (`lofin`) | 재원별 세입결산 (`revenue_by_source`) | 지원 |
|
|
314
|
+
|
|
315
|
+
> 검증 수준 및 실API 최종 검증일 등 상세 정보는 [SUPPORTED_DATA.md](./SUPPORTED_DATA.md)를 참고하세요.
|
|
282
316
|
|
|
283
317
|
## 문서 가이드 (Document Guide)
|
|
284
318
|
|
|
@@ -341,3 +375,4 @@ KPubData의 설계 철학과 사용 방법을 안내하는 문서 목록입니
|
|
|
341
375
|
|
|
342
376
|
### v0.3
|
|
343
377
|
- 표준 코어를 기반으로 한 경량 [MCP](https://modelcontextprotocol.io/)(Model Context Protocol — AI 모델이 외부 도구를 사용하는 표준 규약) 어댑터 지원
|
|
378
|
+
|