doc3gpp 0.1.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.
- doc3gpp-0.1.1/.env.example +26 -0
- doc3gpp-0.1.1/.github/workflows/publish.yml +45 -0
- doc3gpp-0.1.1/.gitignore +32 -0
- doc3gpp-0.1.1/AGENTS.md +313 -0
- doc3gpp-0.1.1/LICENSE +21 -0
- doc3gpp-0.1.1/PKG-INFO +510 -0
- doc3gpp-0.1.1/README.md +457 -0
- doc3gpp-0.1.1/doc3gpp.toml.example +1 -0
- doc3gpp-0.1.1/docs/3gpp-knowledge.md +548 -0
- doc3gpp-0.1.1/docs/architecture.md +615 -0
- doc3gpp-0.1.1/docs/cli.md +1599 -0
- doc3gpp-0.1.1/docs/code-map.md +148 -0
- doc3gpp-0.1.1/docs/conventions.md +356 -0
- doc3gpp-0.1.1/docs/known-constraints.md +141 -0
- doc3gpp-0.1.1/pyproject.toml +101 -0
- doc3gpp-0.1.1/scripts/dev_run.sh +6 -0
- doc3gpp-0.1.1/scripts/test_sqlite.sh +8 -0
- doc3gpp-0.1.1/src/doc3gpp/__init__.py +5 -0
- doc3gpp-0.1.1/src/doc3gpp/cli.py +3635 -0
- doc3gpp-0.1.1/src/doc3gpp/cli_auto_sync.py +333 -0
- doc3gpp-0.1.1/src/doc3gpp/cli_filters.py +155 -0
- doc3gpp-0.1.1/src/doc3gpp/cli_url_helpers.py +41 -0
- doc3gpp-0.1.1/src/doc3gpp/config.py +6 -0
- doc3gpp-0.1.1/src/doc3gpp/data/doc3gpp.toml.example +101 -0
- doc3gpp-0.1.1/src/doc3gpp/models/__init__.py +1 -0
- doc3gpp-0.1.1/src/doc3gpp/models/meeting.py +42 -0
- doc3gpp-0.1.1/src/doc3gpp/models/sync.py +90 -0
- doc3gpp-0.1.1/src/doc3gpp/models/tdoc.py +62 -0
- doc3gpp-0.1.1/src/doc3gpp/models/tdoc_cr.py +349 -0
- doc3gpp-0.1.1/src/doc3gpp/models/tdoc_file.py +73 -0
- doc3gpp-0.1.1/src/doc3gpp/models/tsg.py +27 -0
- doc3gpp-0.1.1/src/doc3gpp/models/wi.py +31 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/__init__.py +19 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/calendar_parser.py +209 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/cr/__init__.py +29 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/cr/cover_page.py +161 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/cr/cr_parsers.py +196 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/cr/header.py +60 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/cr/helpers.py +203 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/cr/ttcn_sections.py +400 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/cr_parser.py +75 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/direct_extractor.py +490 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/docx_converter.py +325 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/html_parsers.py +12 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/normalizers.py +45 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/tdoc_file_parser.py +191 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/tdoc_parser.py +391 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/tdoc_parsers.py +89 -0
- doc3gpp-0.1.1/src/doc3gpp/parsers/wi_parser.py +90 -0
- doc3gpp-0.1.1/src/doc3gpp/repository/__init__.py +1 -0
- doc3gpp-0.1.1/src/doc3gpp/repository/protocols.py +457 -0
- doc3gpp-0.1.1/src/doc3gpp/scraping/__init__.py +1 -0
- doc3gpp-0.1.1/src/doc3gpp/scraping/cache.py +377 -0
- doc3gpp-0.1.1/src/doc3gpp/scraping/cache_keys.py +60 -0
- doc3gpp-0.1.1/src/doc3gpp/scraping/calendar_source.py +17 -0
- doc3gpp-0.1.1/src/doc3gpp/scraping/client.py +189 -0
- doc3gpp-0.1.1/src/doc3gpp/scraping/ftp_source.py +116 -0
- doc3gpp-0.1.1/src/doc3gpp/scraping/portal_source.py +96 -0
- doc3gpp-0.1.1/src/doc3gpp/scraping/tdoc_zip_source.py +327 -0
- doc3gpp-0.1.1/src/doc3gpp/scraping/wi_source.py +43 -0
- doc3gpp-0.1.1/src/doc3gpp/services/__init__.py +1 -0
- doc3gpp-0.1.1/src/doc3gpp/services/factory.py +159 -0
- doc3gpp-0.1.1/src/doc3gpp/services/meetings_service.py +165 -0
- doc3gpp-0.1.1/src/doc3gpp/services/tdoc_cr_service.py +1086 -0
- doc3gpp-0.1.1/src/doc3gpp/services/tdoc_file_service.py +79 -0
- doc3gpp-0.1.1/src/doc3gpp/services/tdoc_service.py +96 -0
- doc3gpp-0.1.1/src/doc3gpp/services/tdoc_sync_coordinator.py +221 -0
- doc3gpp-0.1.1/src/doc3gpp/services/tsg_service.py +249 -0
- doc3gpp-0.1.1/src/doc3gpp/services/wi_service.py +69 -0
- doc3gpp-0.1.1/src/doc3gpp/settings/__init__.py +1 -0
- doc3gpp-0.1.1/src/doc3gpp/settings/config_source.py +113 -0
- doc3gpp-0.1.1/src/doc3gpp/settings/config_writer.py +326 -0
- doc3gpp-0.1.1/src/doc3gpp/settings/loader.py +33 -0
- doc3gpp-0.1.1/src/doc3gpp/settings/schema.py +387 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/__init__.py +1 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/backends/__init__.py +11 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/backends/mysql.py +14 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/backends/postgres.py +13 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/backends/sqlite.py +43 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/cache.py +15 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/compression.py +49 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/db/__init__.py +1 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/db/base.py +7 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/db/migrate.py +19 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/db/migrations/README.md +8 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/db/models.py +288 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/db/session.py +37 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/export.py +21 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/repositories/__init__.py +1 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/repositories/meeting_sql.py +264 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/repositories/tdoc_cr_sql.py +263 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/repositories/tdoc_cr_ttcn_sql.py +156 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/repositories/tdoc_file_sql.py +173 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/repositories/tdoc_sql.py +374 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/repositories/tsg_sql.py +120 -0
- doc3gpp-0.1.1/src/doc3gpp/storage/repositories/wi_sql.py +120 -0
- doc3gpp-0.1.1/tests/conftest.py +17 -0
- doc3gpp-0.1.1/tests/fixtures/sample_pages/3GPP-meeting-R5.html +26 -0
- doc3gpp-0.1.1/tests/fixtures/tdoc_cr_doc/C6-250028.zip +0 -0
- doc3gpp-0.1.1/tests/fixtures/tdoc_cr_doc/R5-227476.zip +0 -0
- doc3gpp-0.1.1/tests/fixtures/tdoc_cr_doc/R5-253079.zip +0 -0
- doc3gpp-0.1.1/tests/fixtures/tdoc_cr_doc/R5s260009.zip +0 -0
- doc3gpp-0.1.1/tests/fixtures/tdoc_cr_doc/R5s260051.zip +0 -0
- doc3gpp-0.1.1/tests/fixtures/tdoc_cr_doc/R5s260135.zip +0 -0
- doc3gpp-0.1.1/tests/fixtures/tdoc_cr_doc/R5s260176.zip +0 -0
- doc3gpp-0.1.1/tests/fixtures/tdoc_xlsx/TDoc_List_Meeting_85434_portal.xlsx +0 -0
- doc3gpp-0.1.1/tests/fixtures/tdoc_xlsx/TDoc_List_Meeting_RAN5#111.xlsx +0 -0
- doc3gpp-0.1.1/tests/fixtures/wi_pages/R5.html +1 -0
- doc3gpp-0.1.1/tests/integration/test_auto_sync_sqlite.py +259 -0
- doc3gpp-0.1.1/tests/integration/test_cli_sqlite.py +79 -0
- doc3gpp-0.1.1/tests/integration/test_config_init_cli_sqlite.py +209 -0
- doc3gpp-0.1.1/tests/integration/test_config_set_cli_sqlite.py +122 -0
- doc3gpp-0.1.1/tests/integration/test_db_reset_sqlite.py +186 -0
- doc3gpp-0.1.1/tests/integration/test_meeting_service_sqlite.py +213 -0
- doc3gpp-0.1.1/tests/integration/test_mysql_backend.py +16 -0
- doc3gpp-0.1.1/tests/integration/test_online_3gpp_calendar.py +38 -0
- doc3gpp-0.1.1/tests/integration/test_online_tdoc_fetch_r5.py +63 -0
- doc3gpp-0.1.1/tests/integration/test_online_tdoc_parse.py +176 -0
- doc3gpp-0.1.1/tests/integration/test_sdk_integration.py +336 -0
- doc3gpp-0.1.1/tests/integration/test_sqlite_backend.py +14 -0
- doc3gpp-0.1.1/tests/integration/test_tdoc_cr_sqlite.py +1424 -0
- doc3gpp-0.1.1/tests/integration/test_tdoc_cr_ttcn_sqlite.py +601 -0
- doc3gpp-0.1.1/tests/integration/test_tdoc_file_sqlite.py +544 -0
- doc3gpp-0.1.1/tests/integration/test_tdoc_parse_direct_sqlite.py +412 -0
- doc3gpp-0.1.1/tests/integration/test_tdoc_parse_ftp_batch_sqlite.py +150 -0
- doc3gpp-0.1.1/tests/integration/test_tdoc_sqlite.py +548 -0
- doc3gpp-0.1.1/tests/integration/test_tsg_sqlite.py +110 -0
- doc3gpp-0.1.1/tests/integration/test_wi_sqlite.py +123 -0
- doc3gpp-0.1.1/tests/unit/test_cache_cli.py +298 -0
- doc3gpp-0.1.1/tests/unit/test_cache_keys.py +111 -0
- doc3gpp-0.1.1/tests/unit/test_calendar_parser.py +91 -0
- doc3gpp-0.1.1/tests/unit/test_calendar_parser_defensive.py +172 -0
- doc3gpp-0.1.1/tests/unit/test_cli_auto_sync.py +719 -0
- doc3gpp-0.1.1/tests/unit/test_cli_filters.py +243 -0
- doc3gpp-0.1.1/tests/unit/test_config_init_cli.py +320 -0
- doc3gpp-0.1.1/tests/unit/test_config_set_cli.py +311 -0
- doc3gpp-0.1.1/tests/unit/test_config_writer.py +525 -0
- doc3gpp-0.1.1/tests/unit/test_cr_parser.py +938 -0
- doc3gpp-0.1.1/tests/unit/test_db_reset_cli.py +255 -0
- doc3gpp-0.1.1/tests/unit/test_docx_converter.py +195 -0
- doc3gpp-0.1.1/tests/unit/test_ftp_listing.py +182 -0
- doc3gpp-0.1.1/tests/unit/test_list_output_format.py +405 -0
- doc3gpp-0.1.1/tests/unit/test_meeting_cli.py +133 -0
- doc3gpp-0.1.1/tests/unit/test_meeting_cli_filters_combined.py +27 -0
- doc3gpp-0.1.1/tests/unit/test_meeting_cli_invalid_fields.py +16 -0
- doc3gpp-0.1.1/tests/unit/test_meeting_cli_location_filter.py +19 -0
- doc3gpp-0.1.1/tests/unit/test_meeting_repository_combined_filters.py +98 -0
- doc3gpp-0.1.1/tests/unit/test_meeting_repository_filters.py +372 -0
- doc3gpp-0.1.1/tests/unit/test_meeting_repository_location_filter.py +52 -0
- doc3gpp-0.1.1/tests/unit/test_meeting_repository_upsert.py +197 -0
- doc3gpp-0.1.1/tests/unit/test_meeting_sync_tsg_validation.py +227 -0
- doc3gpp-0.1.1/tests/unit/test_parsers_utils.py +18 -0
- doc3gpp-0.1.1/tests/unit/test_portal_source.py +76 -0
- doc3gpp-0.1.1/tests/unit/test_scraper_client.py +296 -0
- doc3gpp-0.1.1/tests/unit/test_settings.py +8 -0
- doc3gpp-0.1.1/tests/unit/test_settings_config_file.py +641 -0
- doc3gpp-0.1.1/tests/unit/test_storage_utils.py +31 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_cache.py +325 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_cli_fields.py +366 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_cr_model.py +559 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_cr_service.py +641 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_cr_service_ftp_batch.py +371 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_file_parser.py +375 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_file_service.py +114 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_file_source.py +174 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_parse_cli.py +3162 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_parse_direct.py +1508 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_parse_url_batch.py +224 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_parser.py +531 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_repository_crud.py +143 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_repository_filters.py +608 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_service_sync.py +118 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_sync_cli.py +232 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_sync_coordinator.py +641 -0
- doc3gpp-0.1.1/tests/unit/test_tdoc_zip_source.py +543 -0
- doc3gpp-0.1.1/tests/unit/test_tsg_cli.py +189 -0
- doc3gpp-0.1.1/tests/unit/test_tsg_repository.py +105 -0
- doc3gpp-0.1.1/tests/unit/test_tsg_service.py +180 -0
- doc3gpp-0.1.1/tests/unit/test_tsg_url_pattern.py +47 -0
- doc3gpp-0.1.1/tests/unit/test_wi_cli.py +139 -0
- doc3gpp-0.1.1/tests/unit/test_wi_parser.py +64 -0
- doc3gpp-0.1.1/tests/unit/test_wi_repository_filters.py +159 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Copy to .env and adjust values as needed.
|
|
2
|
+
#
|
|
3
|
+
# Only the env vars listed below are honoured by doc3gpp — every other
|
|
4
|
+
# DOC3GPP_* variable is silently ignored. Configure everything else via
|
|
5
|
+
# the TOML config file (`doc3gpp config set <key> <value>` or a hand-
|
|
6
|
+
# edited `doc3gpp.toml`); see doc3gpp.toml.example for the full schema.
|
|
7
|
+
#
|
|
8
|
+
# Recognised vars (see doc3gpp.settings.schema.ALLOWED_ENV_VARS):
|
|
9
|
+
# DOC3GPP_DATABASE_URL, DOC3GPP_DB_ECHO, DOC3GPP_LOG_LEVEL,
|
|
10
|
+
# DOC3GPP_HTTP_VERIFY, DOC3GPP_CACHE__DIR, DOC3GPP_SYNC__AUTO_SYNC
|
|
11
|
+
|
|
12
|
+
# DOC3GPP_DATABASE_URL: leave commented to use the default
|
|
13
|
+
# sqlite+pysqlite:///<Path.home()>/.local/share/doc3gpp/doc3gpp.db
|
|
14
|
+
# DOC3GPP_DATABASE_URL=sqlite+pysqlite:////absolute/path/to/doc3gpp.db
|
|
15
|
+
DOC3GPP_DB_ECHO=false
|
|
16
|
+
DOC3GPP_HTTP_VERIFY=false
|
|
17
|
+
DOC3GPP_LOG_LEVEL=INFO
|
|
18
|
+
|
|
19
|
+
# Cache directory for the TDoc extraction pipeline
|
|
20
|
+
# (overrides the XDG default ~/.cache/doc3gpp/tdocs).
|
|
21
|
+
# DOC3GPP_CACHE__DIR=/path/to/cache
|
|
22
|
+
|
|
23
|
+
# When true, `meeting list`, `tdoc list`, `tdoc show`, and DB-mode
|
|
24
|
+
# `tdoc parse` internally trigger the same sync paths used by explicit
|
|
25
|
+
# `meeting sync` / `tdoc sync`. Same skip rules apply.
|
|
26
|
+
DOC3GPP_SYNC__AUTO_SYNC=false
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build-n-publish:
|
|
9
|
+
name: Build and publish Python distro to PyPI
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
# environment + id-token: write are mandatory for Trusted Publishing (OIDC).
|
|
13
|
+
# The "testpypi" environment must match what's registered at
|
|
14
|
+
# https://test.pypi.org/manage/account/publishing/.
|
|
15
|
+
environment:
|
|
16
|
+
name: pypi
|
|
17
|
+
url: https://pypi.org/p/doc3gpp
|
|
18
|
+
permissions:
|
|
19
|
+
id-token: write # Required for OIDC authentication
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout code
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
|
|
31
|
+
- name: Install build tools
|
|
32
|
+
run: |
|
|
33
|
+
python -m pip install --upgrade pip
|
|
34
|
+
python -m pip install build
|
|
35
|
+
|
|
36
|
+
- name: Build sdist and wheel
|
|
37
|
+
run: python -m build
|
|
38
|
+
|
|
39
|
+
- name: Inspect built artifacts
|
|
40
|
+
run: ls -lh dist/
|
|
41
|
+
|
|
42
|
+
- name: Publish to PyPI
|
|
43
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
44
|
+
# with:
|
|
45
|
+
# repository-url: https://test.pypi.org/legacy/
|
doc3gpp-0.1.1/.gitignore
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
|
|
4
|
+
.venv/
|
|
5
|
+
venv/
|
|
6
|
+
env/
|
|
7
|
+
|
|
8
|
+
.env
|
|
9
|
+
.envrc
|
|
10
|
+
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
*.egg-info/
|
|
14
|
+
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
.coverage.*
|
|
18
|
+
htmlcov/
|
|
19
|
+
|
|
20
|
+
.ruff_cache/
|
|
21
|
+
|
|
22
|
+
uv.lock
|
|
23
|
+
|
|
24
|
+
docs/_build/
|
|
25
|
+
|
|
26
|
+
tempCodeRunnerFile.py
|
|
27
|
+
|
|
28
|
+
.omo/
|
|
29
|
+
.opencode/
|
|
30
|
+
.serena/
|
|
31
|
+
.logs/
|
|
32
|
+
.codegraph/
|
doc3gpp-0.1.1/AGENTS.md
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# doc3gpp Agent Guide
|
|
2
|
+
|
|
3
|
+
**Generated:** 2026-07-10
|
|
4
|
+
**Branch:** main
|
|
5
|
+
|
|
6
|
+
A Python CLI/library that scrapes 3GPP meeting calendars, TDoc lists, auxiliary TDoc files, CR cover pages, and WIs into SQL.
|
|
7
|
+
|
|
8
|
+
This guide stays lean on purpose: it covers the **shape** of the
|
|
9
|
+
codebase — layout, where to look for a change, architecture rules, and
|
|
10
|
+
common commands. Everything that drifts easily (symbol tables, settings
|
|
11
|
+
caching details, filter grammar, anti-patterns, known constraints) lives
|
|
12
|
+
in [`docs/`](docs/) and is linked from the "Doc pointers" section below.
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
python -m venv .venv && source .venv/bin/activate
|
|
18
|
+
pip install -e ".[dev]"
|
|
19
|
+
cp .env.example .env
|
|
20
|
+
doc3gpp db init
|
|
21
|
+
doc3gpp db check
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Build system: **hatchling**. Stack: Python 3.10+, SQLAlchemy 2.0,
|
|
25
|
+
Pydantic v2 + pydantic-settings, httpx, BeautifulSoup4 + lxml, openpyxl,
|
|
26
|
+
alembic (installed but not wired). Optional extras:
|
|
27
|
+
|
|
28
|
+
- `.[cli]` — Typer CLI (also in `[dev]`).
|
|
29
|
+
- `.[mysql]` / `.[postgres]` — DB drivers.
|
|
30
|
+
- `.[extract]` — `python-docx` for the TDoc extraction pipeline.
|
|
31
|
+
|
|
32
|
+
`pip install doc3gpp` installs the SDK only; `pip install "doc3gpp[cli]"`
|
|
33
|
+
or `pipx install "doc3gpp[cli]"` adds the `doc3gpp` CLI command.
|
|
34
|
+
|
|
35
|
+
## Structure (high level)
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
doc3gpp/
|
|
39
|
+
├── src/doc3gpp/ # package root
|
|
40
|
+
│ ├── cli.py # Typer commands (7 groups, 20 commands)
|
|
41
|
+
│ ├── models/ # domain dataclasses — never leak ORM attrs out
|
|
42
|
+
│ ├── repository/ # abstract repo contracts (Protocols)
|
|
43
|
+
│ ├── services/ # orchestration; CLI-injected via factory
|
|
44
|
+
│ ├── scraping/ # HTTP / FTP transport — no parsing
|
|
45
|
+
│ ├── parsers/ # HTML / Excel → domain objects — no network
|
|
46
|
+
│ ├── settings/ # pydantic-settings + TOML discovery
|
|
47
|
+
│ ├── storage/ # persistence umbrella
|
|
48
|
+
│ │ ├── compression.py # shared gzip JSON helpers (cover + TTCN sidecars)
|
|
49
|
+
│ │ ├── db/ # ORM models, engine, create_schema bootstrap
|
|
50
|
+
│ │ ├── backends/ # per-dialect engine kwargs
|
|
51
|
+
│ │ └── repositories/ # SQL impls of the repository Protocols
|
|
52
|
+
│ │ └── tdoc_cr_ttcn_sql.py # SQL impl of TDocCrTTCNDetailRepository
|
|
53
|
+
│ └── cli_filters.py # shared filter / TDoc-id grammar
|
|
54
|
+
├── tests/
|
|
55
|
+
│ ├── unit/ # mock external calls
|
|
56
|
+
│ ├── integration/ # sqlite by default; online + mysql opt-in
|
|
57
|
+
│ └── fixtures/ # sample HTML + XLSX + zip docs
|
|
58
|
+
├── docs/ # architecture, CLI ref, conventions, code map, constraints
|
|
59
|
+
└── scripts/ # test_sqlite.sh, dev_run.sh
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
For the full symbol-to-file table, see
|
|
63
|
+
[`docs/code-map.md`](docs/code-map.md).
|
|
64
|
+
|
|
65
|
+
## Where to look
|
|
66
|
+
|
|
67
|
+
| Task | Location | Notes |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| Add a CLI command | `src/doc3gpp/cli.py` | Follow pattern: service → repo → CLI. |
|
|
70
|
+
| Add a config writer / CLI set command | `src/doc3gpp/settings/config_writer.py` + `src/doc3gpp/cli.py` (`config_app`) | TOML read-modify-write helpers; Typer `config set` command. |
|
|
71
|
+
| Add a data source | `src/doc3gpp/scraping/` + `src/doc3gpp/parsers/` | Network in `scraping/`, parsing in `parsers/`. |
|
|
72
|
+
| Add a domain model | `src/doc3gpp/models/` | `@dataclass(slots=True)`; never expose ORM attrs. |
|
|
73
|
+
| Add a storage backend | `src/doc3gpp/storage/backends/` | Engine kwargs per dialect. |
|
|
74
|
+
| Change filters for a list | `src/doc3gpp/repository/protocols.py` + `src/doc3gpp/storage/repositories/` | Update **both** the Protocol and the impl. |
|
|
75
|
+
| Run all tests | `./scripts/test_sqlite.sh` | Unit + integration, sqlite-only. |
|
|
76
|
+
| Run online tests | `python -m pytest -m online -rs` | Hits live 3gpp.org + FTP. |
|
|
77
|
+
|
|
78
|
+
For deeper conventions (filter grammar, settings caching, anti-patterns,
|
|
79
|
+
commit policy), see [`docs/conventions.md`](docs/conventions.md).
|
|
80
|
+
|
|
81
|
+
## Architecture boundaries
|
|
82
|
+
|
|
83
|
+
Strict layered separation in `src/doc3gpp/`. Each layer depends only on
|
|
84
|
+
the layer below it; services reach down into storage through the
|
|
85
|
+
`repository/` Protocols rather than touching the concrete ORM. See
|
|
86
|
+
[`docs/architecture.md`](docs/architecture.md) for the layered diagram,
|
|
87
|
+
runtime data flow, and ORM schema.
|
|
88
|
+
|
|
89
|
+
| Layer | Rule |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `models/` | pass between layers; **never leak ORM attributes** |
|
|
92
|
+
| `repository/` (abstract) | Protocol contracts only |
|
|
93
|
+
| `services/` | orchestration; injected with a repo impl via `services/factory.build_*` |
|
|
94
|
+
| `scraping/` | HTTP / FTP transport only — **no HTML parsing** |
|
|
95
|
+
| `parsers/` | HTML / Excel → domain only — **no network** |
|
|
96
|
+
| `storage/db/` | ORM models, engine factory, `create_schema` bootstrap |
|
|
97
|
+
| `storage/compression.py` | shared gzip JSON helpers for binary detail columns |
|
|
98
|
+
| `storage/repositories/` | SQL impls of the `repository/` Protocols |
|
|
99
|
+
| `settings/` | env + TOML config (pydantic-settings; precedence: CLI > env > file > defaults) |
|
|
100
|
+
| `cli.py` | thin Typer commands; never instantiate SQL repos directly |
|
|
101
|
+
|
|
102
|
+
Workflows in one line (full prose in `docs/architecture.md`):
|
|
103
|
+
|
|
104
|
+
- `doc3gpp meeting sync --tsg <s>` → `MeetingService.sync` → DynaReport
|
|
105
|
+
HTML → `parse_3gpp_calendar` → stamp `Meeting.tsg` →
|
|
106
|
+
`SQLAlchemyMeetingRepository.upsert_many`. Skips when the TSG was synced
|
|
107
|
+
within `Settings.sync.meeting_sync_interval` unless `--force` is used.
|
|
108
|
+
- `doc3gpp tdoc sync --meeting-id <id>` / `--meeting <name>` →
|
|
109
|
+
`TDocSyncCoordinator.sync_for_meeting_id` /
|
|
110
|
+
`sync_for_meeting_name` → `TDocService.sync_tdoc_list` +
|
|
111
|
+
`TDocFileService.sync_from_meeting_ftp`. The TDoc list comes from
|
|
112
|
+
`Settings.sync.tdoc_list_url_template` (default
|
|
113
|
+
`https://portal.3gpp.org/ngppapp/GenerateDocumentList.aspx?meetingId={meeting_id}`);
|
|
114
|
+
the auxiliary TDoc file scan still uses the stored meeting row's
|
|
115
|
+
FTP URL. **No meeting row → no TDoc sync.** Skips when the meeting
|
|
116
|
+
is outside `Settings.sync.tdoc_list_closed_window` or was synced
|
|
117
|
+
within `Settings.sync.tdoc_list_sync_interval`; `--force` bypasses
|
|
118
|
+
both checks.
|
|
119
|
+
- `doc3gpp tdoc sync` (no selector) →
|
|
120
|
+
`TDocSyncCoordinator.sync_all_tracked_meetings` → every distinct
|
|
121
|
+
``meeting_id`` in the ``tdocs`` table is synced individually with the
|
|
122
|
+
same two skip rules applied per meeting.
|
|
123
|
+
- `doc3gpp wi sync --tsg <s>` → `WiService.sync` → `fetch_wis` →
|
|
124
|
+
`parse_3gpp_wis` → `SQLAlchemyWiRepository.upsert_many` (composite
|
|
125
|
+
PK `(wi_id, tsg_short)`; `tsgs` table is auto-seeded so the FK
|
|
126
|
+
validates).
|
|
127
|
+
- `doc3gpp tdoc parse <filters>` is end-to-end filter-driven — every
|
|
128
|
+
flag is a filter, capped by `Settings.tdoc_parse.max_batch`. In normal
|
|
129
|
+
mode the SQL query excludes rows already present in `tdoc_cr_details`,
|
|
130
|
+
so the batch cap applies only to pending TDocs; the preview and
|
|
131
|
+
confirmation list only pending rows. `--force` explicitly includes and
|
|
132
|
+
re-parses already-parsed matches. The parser returns
|
|
133
|
+
`TDocCRParseResult(cover, ttcn)` and `TDocCrService` fans the result
|
|
134
|
+
out across THREE independent upserts: the slim cover-page row in
|
|
135
|
+
`tdoc_cr_details`, the optional `tdoc_cr_ttcn_details` sidecar (only
|
|
136
|
+
when the parser recognised a TTCN CR), and the `tdoc_extracts`
|
|
137
|
+
metadata row. Full grammar and prompt-completion semantics in
|
|
138
|
+
[`docs/conventions.md`](docs/conventions.md) and
|
|
139
|
+
[`docs/cli.md`](docs/cli.md).
|
|
140
|
+
- `doc3gpp tdoc parse --from-path PATH` / `--from-url URL` is a
|
|
141
|
+
direct-mode alternative that bypasses the database filters. Local
|
|
142
|
+
files parse in-memory only; 3GPP-URL downloads follow the
|
|
143
|
+
FK-aware behaviour matrix in `docs/cli.md` (cache + DB writes
|
|
144
|
+
land when the filename's tdoc_id is present in `tdocs`; otherwise
|
|
145
|
+
the result is still emitted with a warning). The zip cache is
|
|
146
|
+
keyed on the **original (sanitized) filename** (D10 fix) so
|
|
147
|
+
multiple revisions of the same tdoc_id never collide. When
|
|
148
|
+
`Settings.sync.auto_sync` is enabled, `tdoc parse --from-url`
|
|
149
|
+
on a 3GPP FTP URL extracts tdoc_id candidates from the URL
|
|
150
|
+
(basename for file URLs; BFS up to `--max-depth` / `--recursive`
|
|
151
|
+
for folder URLs) and runs `trigger_auto_sync(...)` **before**
|
|
152
|
+
dispatching to the per-file parse helpers — see
|
|
153
|
+
[`src/doc3gpp/cli.py:1353-1366`](src/doc3gpp/cli.py) and the
|
|
154
|
+
"Auto-sync from URL candidates" section in `docs/cli.md`. The
|
|
155
|
+
ordering is TSG sync → meeting sync → parse, so the meeting_id
|
|
156
|
+
resolution can usually find the parent row by the time the parse
|
|
157
|
+
fires. Same skip rules as DB-mode apply; non-3GPP URLs never
|
|
158
|
+
trigger auto-sync; failures stay warnings.
|
|
159
|
+
- `doc3gpp tdoc show --tdoc <id>` resolves the parent `tdoc` row, then
|
|
160
|
+
looks up the slim cover-page details and the extract metadata by the
|
|
161
|
+
row's immutable `tdoc.ftp_url` (one row per URL — the URL is the row
|
|
162
|
+
identity for both `tdoc_cr_details` and `tdoc_extracts`). The TTCN
|
|
163
|
+
sidecar is joined in via `cr_ttcn_repo.get_by_url(tdoc.ftp_url)` only
|
|
164
|
+
when `is_ttcn_tdoc(tdoc.tdoc_id)` is `True`. Auxiliary files are
|
|
165
|
+
read once via `file_repo.get_for_tdoc_id(tdoc.tdoc_id)` and match
|
|
166
|
+
by `tdoc_id` (not URL) so all revisions / reviews / support files
|
|
167
|
+
surface in a single pass. The CLI's renderers emit `cover`, the
|
|
168
|
+
optional `ttcn` block, `extracted_at` (sourced from the
|
|
169
|
+
`tdoc_extracts` row via PK JOIN), and `files` (the auxiliary files
|
|
170
|
+
block / placeholder) as separate sections — the legacy `details` /
|
|
171
|
+
`parser_version` fields no longer appear in the output. The `tdoc_extracts` row carries a single `cache_file` column
|
|
172
|
+
(basename, derived from `tdoc.ftp_url` via `derive_cache_file()`);
|
|
173
|
+
the CLI reconstructs paths as `{cache.dir}/zips/<cache_file>` and
|
|
174
|
+
`{cache.dir}/markdown/<cache_file>` via `_build_cache().root` +
|
|
175
|
+
`derive_cache_file(ftp_url)`. **Both subtrees write real ZIPs** so
|
|
176
|
+
the `.zip` extension maps to a format `unzip` / 7z / WinZip can open
|
|
177
|
+
straight from disk — the zip subtree holds the 3GPP-served zip bytes;
|
|
178
|
+
the markdown subtree holds a `zipfile.ZipFile` wrapper produced by
|
|
179
|
+
`_wrap_markdown_zip` (single entry named `<docx stem>.md`,
|
|
180
|
+
`ZIP_DEFLATED`).
|
|
181
|
+
- `doc3gpp tdoc show --ftp-url <url>` resolves the URL into matching
|
|
182
|
+
rows across four tables (`tdocs`, `tdoc_cr_details`,
|
|
183
|
+
`tdoc_cr_ttcn_details`, `tdoc_files`) directly — `tdocs` and
|
|
184
|
+
`tdoc_files` use the new `get_by_ftp_url` lookups
|
|
185
|
+
(`SQLAlchemyTDocRepository.get_by_ftp_url` /
|
|
186
|
+
`SQLAlchemyTDocFileRepository.get_by_ftp_url`); the cover / TTCN
|
|
187
|
+
tables use the existing URL-PK lookups. The `--ftp-url` path is
|
|
188
|
+
**mutually exclusive** with `--tdoc` (an XOR validator raises
|
|
189
|
+
`BadParameter` when neither or both are supplied) and **does NOT
|
|
190
|
+
trigger** `trigger_auto_sync` — there's no parent TDoc to anchor a
|
|
191
|
+
meeting sync on, and a URL-keyed read should be a deterministic
|
|
192
|
+
snapshot of whatever is already in the DB. URL is normalised via
|
|
193
|
+
`normalize_ftp_path` so both full URLs
|
|
194
|
+
(`https://www.3gpp.org/ftp/...`) and bare relative paths resolve
|
|
195
|
+
the same row. `tdocs.ftp_url` is maintained as a 1:1 invariant by
|
|
196
|
+
the upload pipeline (no DB-level `UNIQUE` constraint); the lookup
|
|
197
|
+
returns a single row via `ORDER BY tdoc_id ASC LIMIT 1` as a
|
|
198
|
+
deterministic fallback if the invariant is ever violated.
|
|
199
|
+
`--format raw` on the URL path reads the cache file directly via
|
|
200
|
+
`derive_cache_file(url)` (no `TDocCrService.extract` detour)
|
|
201
|
+
because the URL is the row identity — a cache miss raises
|
|
202
|
+
`BadParameter` pointing at `doc3gpp tdoc parse --from-url <url>`
|
|
203
|
+
or `doc3gpp tdoc parse --tdoc <id>`. The CLI bundles the result
|
|
204
|
+
into a new `TDocShowRecordByUrl(ftp_url, tdoc, cover, ttcn,
|
|
205
|
+
extracted_at, files)` DTO and renders it under a
|
|
206
|
+
`# FTP URL` / `[FTP URL]` anchor; the renderer contract follows
|
|
207
|
+
the same omit-when-null convention as the `--tdoc` path's
|
|
208
|
+
`TDocShowRecord`.
|
|
209
|
+
- `doc3gpp config path` / `doc3gpp config show` dump the resolved
|
|
210
|
+
TOML + env settings for diffing against `doc3gpp.toml.example`.
|
|
211
|
+
- `doc3gpp config init --target <auto|project|user> [--force]` writes
|
|
212
|
+
the packaged default TOML template (full defaults) at the bootstrap
|
|
213
|
+
target — `auto` (default) picks `./doc3gpp.toml` when run from a
|
|
214
|
+
project root, otherwise `~/.config/doc3gpp/config.toml`. Refuses
|
|
215
|
+
while `DOC3GPP_CONFIG` is set; `--force` overwrites an existing file.
|
|
216
|
+
- `doc3gpp config set <key> <value>` writes one setting into the active
|
|
217
|
+
TOML config file (refuses when none is in use; run `config init` to
|
|
218
|
+
bootstrap one); the previous `--init` / `--target` / `--force` flags
|
|
219
|
+
are removed — see the plan at
|
|
220
|
+
`.omo/plans/config-set-command.md` for the full command contract.
|
|
221
|
+
- When `Settings.sync.auto_sync` is enabled, `meeting list`, `tdoc list`,
|
|
222
|
+
`tdoc show`, and database-mode `tdoc parse` internally trigger the
|
|
223
|
+
same meeting-calendar and TDoc-list sync paths used by explicit
|
|
224
|
+
`meeting sync` / `tdoc sync`. The same skip rules apply and are never
|
|
225
|
+
bypassed; failures are logged as warnings and do not abort the read
|
|
226
|
+
command. Direct-mode `tdoc parse --from-path` / `--from-url` never
|
|
227
|
+
triggers auto-sync. The `tdoc show --ftp-url` selector also never
|
|
228
|
+
triggers auto-sync — the URL is the row identity and there's no
|
|
229
|
+
parent TDoc / meeting to anchor a sync on; users wanting a fresh
|
|
230
|
+
extract at the URL must run `tdoc parse --from-url <url>` or
|
|
231
|
+
`tdoc parse --tdoc <id>` explicitly.
|
|
232
|
+
|
|
233
|
+
## Common commands
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
# Lint (ruff is the only configured tool)
|
|
237
|
+
ruff check .
|
|
238
|
+
|
|
239
|
+
# Full sqlite test suite (unit + integration, excludes online + mysql)
|
|
240
|
+
./scripts/test_sqlite.sh
|
|
241
|
+
|
|
242
|
+
# Online tests (opt-in, hits live 3gpp.org and FTP)
|
|
243
|
+
python -m pytest -m online -rs
|
|
244
|
+
|
|
245
|
+
# MySQL tests (needs DOC3GPP_TEST_MYSQL_URL)
|
|
246
|
+
python -m pytest -m mysql
|
|
247
|
+
|
|
248
|
+
# Bootstrap dev environment
|
|
249
|
+
./scripts/dev_run.sh
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
`pyproject.toml [tool.pytest.ini_options]` sets `pythonpath = ["src"]`,
|
|
253
|
+
so tests resolve `doc3gpp.*` without an editable install.
|
|
254
|
+
|
|
255
|
+
## Doc pointers
|
|
256
|
+
|
|
257
|
+
| Topic | Doc |
|
|
258
|
+
| --- | --- |
|
|
259
|
+
| Layered diagram, runtime data flow, ORM schema, CLI inventory, testing layout, design rules | [`docs/architecture.md`](docs/architecture.md) |
|
|
260
|
+
| Per-command CLI reference (every flag, default, example) | [`docs/cli.md`](docs/cli.md) |
|
|
261
|
+
| 3GPP URL conventions, naming conventions, parser field semantics | [`docs/3gpp-knowledge.md`](docs/3gpp-knowledge.md) |
|
|
262
|
+
| Where each public symbol lives (symbol → file reference) | [`docs/code-map.md`](docs/code-map.md) |
|
|
263
|
+
| Filter grammar, settings caching, commit policy, anti-patterns, unique styles | [`docs/conventions.md`](docs/conventions.md) |
|
|
264
|
+
| Open limitations (schema bootstrap, hardcoded FTP URL, R5-/C6- URL templates, test surface, …) | [`docs/known-constraints.md`](docs/known-constraints.md) |
|
|
265
|
+
| Per-knob TOML schema reference | [`doc3gpp.toml.example`](doc3gpp.toml.example) |
|
|
266
|
+
|
|
267
|
+
Update `README.md`, `AGENTS.md`, and the relevant `docs/*.md` in the
|
|
268
|
+
same change set when CLI surface or behaviour changes — see
|
|
269
|
+
[`docs/conventions.md`](docs/conventions.md) §"Documentation sync" for
|
|
270
|
+
the convention.
|
|
271
|
+
|
|
272
|
+
<!-- headroom:rtk-instructions -->
|
|
273
|
+
# RTK (Rust Token Killer) - Token-Optimized Commands
|
|
274
|
+
|
|
275
|
+
When running shell commands, **always prefix with `rtk`**. This reduces context
|
|
276
|
+
usage by 60-90% with zero behavior change. If rtk has no filter for a command,
|
|
277
|
+
it passes through unchanged — so it is always safe to use.
|
|
278
|
+
|
|
279
|
+
## Key Commands
|
|
280
|
+
```bash
|
|
281
|
+
# Git (59-80% savings)
|
|
282
|
+
rtk git status rtk git diff rtk git log
|
|
283
|
+
|
|
284
|
+
# Files & Search (60-75% savings)
|
|
285
|
+
rtk ls <path> rtk read <file> rtk grep <pattern>
|
|
286
|
+
rtk find <pattern> rtk diff <file>
|
|
287
|
+
|
|
288
|
+
# Test (90-99% savings) — shows failures only
|
|
289
|
+
rtk pytest tests/ rtk cargo test rtk test <cmd>
|
|
290
|
+
|
|
291
|
+
# Build & Lint (80-90% savings) — shows errors only
|
|
292
|
+
rtk tsc rtk lint rtk cargo build
|
|
293
|
+
rtk prettier --check rtk mypy rtk ruff check
|
|
294
|
+
|
|
295
|
+
# Analysis (70-90% savings)
|
|
296
|
+
rtk err <cmd> rtk log <file> rtk json <file>
|
|
297
|
+
rtk summary <cmd> rtk deps rtk env
|
|
298
|
+
|
|
299
|
+
# GitHub (26-87% savings)
|
|
300
|
+
rtk gh pr view <n> rtk gh run list rtk gh issue list
|
|
301
|
+
|
|
302
|
+
# Infrastructure (85% savings)
|
|
303
|
+
rtk docker ps rtk kubectl get rtk docker logs <c>
|
|
304
|
+
|
|
305
|
+
# Package managers (70-90% savings)
|
|
306
|
+
rtk pip list rtk pnpm install rtk npm run <script>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Rules
|
|
310
|
+
- In command chains, prefix each segment: `rtk git add . && rtk git commit -m "msg"`
|
|
311
|
+
- For debugging, use raw command without rtk prefix
|
|
312
|
+
- `rtk proxy <cmd>` runs command without filtering but tracks usage
|
|
313
|
+
<!-- /headroom:rtk-instructions -->
|
doc3gpp-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jerry wang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|