Skip to main content

Typed Python client for the National Thesis Center of Turkey.

Project description

yoktez

PyPI - Python Version PyPI - License PyPI - Status PyPI - Downloads

yoktez mascot generated by Google's Nano Banana 2

Typed Python client for the National Thesis Center of Turkey.

yoktez wraps the YOK NTC JSP/AJAX surface behind a single synchronous Client with frozen-dataclass return types, a deterministic exception hierarchy, and bilingual-aware fields. Built for application and CLI developers who need a typed surface and a small install footprint without writing bespoke scraping code for each project.

Installation

pip install yoktez

Requires Python 3.14+.

Quickstart

"""End-to-end yoktez quickstart: search -> metadata -> assets.

Demonstrates the typical three-call flow without writing files to disk.

Run with: `python examples/quickstart.py`
"""

from yoktez import AssetStatus, Client

_QUERY = "yapay zeka"


with Client() as client:
    results = client.search.simple(_QUERY)
    print(f"{results.total} matches for {_QUERY!r}")

    thesis = results[0]
    print(f"  title:   {thesis.title}")
    print(f"  author:  {thesis.author}")
    print(f"  year:    {thesis.year}")
    print(f"  keys:    {thesis.registration_no} / {thesis.thesis_no}")

    metadata = client.metadata.get(thesis)
    print(f"  advisor: {metadata.supervisor}")
    if metadata.affiliation is not None:
        print(f"  uni:     {metadata.affiliation.university}")
    if metadata.keywords is not None:
        print(f"  tags:    {len(metadata.keywords)} keywords")

    assets = client.assets.get(thesis)
    print(f"  status:  {assets.status.name}")
    if assets.status is AssetStatus.AVAILABLE:
        print(f"  pdf_key: {assets.pdf_key}")

Sample output:

6841 matches for 'yapay zeka'
  title:   Kimya eğitiminde yapay zekâ araştırmalarına ilişkin bir meta-sentez çalışması
  author:  MURAT EBUBEKİR YAYLA
  year:    2026
  keys:    nslbSyAODG1_FIruL8qUAA / THvIvDpZXvJIiHZpuqpKVw
  advisor: PROF. DR. MUSA ÜCE
  uni:     MARMARA ÜNİVERSİTESİ
  tags:    5 keywords
  status:  AVAILABLE
  pdf_key: 5T1_CZ5-UGb9QCmoURec4AbpuuyvqUeed_1PcCh_6DVZ4b1fbX7Gcu-DQFLIcE11

Features

  • Four search modes: simple, advanced, detail, and recent from a single client.search namespace, all returning a sliceable SearchResults carrying the database-wide match total alongside the result window.
  • Structured metadata: client.metadata.get(thesis) returns a typed ThesisMetadata with bilingual keywords (Bilingual(raw, tr, en)), a tiered Affiliation, and pre-formatted citation strings (APA / IEEE / MLA / Chicago / Harvard).
  • Two-step asset download: client.assets.get(thesis) resolves to one of AVAILABLE / UNDER_EMBARGO / NO_PERMIT / PREPARING before any bytes move; the available branch exposes a pdf_key (and optional appendix_key) to feed download_pdf / download_appendix.
  • Catalog lookups: client.lookups covers universities (TR / INT), institutes, divisions, subjects, departments, sections, and keywords, with per-instance memoization and an explicit refresh().
  • Typed value objects: every returned record is a @dataclass(frozen=True, slots=True); values are immutable, hashable where field types allow, and ship with py.typed for downstream type checkers.
  • Sync-only, thread-friendly: no async/await surface; the recommended concurrency pattern is one Client per thread.
  • Small dependency surface: httpx, beautifulsoup4, and lxml. No Rust core, no auth, no hidden state.

Usage

All snippets assume with Client() as client: for deterministic cleanup of the underlying HTTP connection pool.

Search

Simple search by free text, optionally narrowed to a single field:

from yoktez import Client, SearchField

with Client() as client:
    results = client.search.simple("yapay zeka", field=SearchField.ABSTRACT)

    print(f"{results.total} matches")
    for thesis in results[:5]:
        print(thesis.year, thesis.title)

Advanced search joins up to three terms with boolean operators:

from yoktez import AdvancedOperator, Client, MatchType

with Client() as client:
    results = client.search.advanced(
        "sosyal",
        term2="medya",
        op1=AdvancedOperator.AND,
        match=MatchType.INCLUDES,
    )

Detail search accepts the full filter surface; enum-shaped parameters also accept the member name as a string or the raw int code:

from yoktez import Client, ThesisType

with Client() as client:
    unis = client.lookups.universities()
    results = client.search.detail(
        university=unis[0],
        year_min=2020,
        year_max=2025,
        degree_type=ThesisType.MASTER,  # also accepts "MASTER" or 1
    )

Recently added theses (server-fixed 15-day window):

from yoktez import Client

with Client() as client:
    results = client.search.recent()

Metadata

from yoktez import Client

with Client() as client:
    thesis = client.search.simple("makine öğrenmesi")[0]
    metadata = client.metadata.get(thesis)

    if metadata.affiliation is not None:
        print(metadata.affiliation.university)
    if metadata.keywords:
        print(metadata.keywords[0].tr, "=", metadata.keywords[0].en)
    if metadata.references is not None:
        print(metadata.references.apa)

Assets (two-step download)

from yoktez import AssetStatus, Client

with Client() as client:
    thesis = client.search.simple("yapay zeka")[0]
    assets = client.assets.get(thesis)

    if assets.status is AssetStatus.AVAILABLE and assets.pdf_key is not None:
        client.assets.download_pdf(assets.pdf_key, "thesis.pdf")

        if assets.appendix_key is not None:
            client.assets.download_appendix(assets.appendix_key, "thesis-ek.rar")

download_pdf and download_appendix accept a filesystem path (Path or str, opened and closed for you) or a pre-opened binary file-like (written to but not closed — ownership stays with the caller).

Lookups

from yoktez import Client, UniversitySource

with Client() as client:
    unis = client.lookups.universities(UniversitySource.TR)
    institutes = client.lookups.institutes(unis[0])
    divisions = client.lookups.divisions(unis[0], institutes[0])

    # Bulk catalogs; keywords() also accepts group / language / first_letter / search.
    keywords = client.lookups.all_keywords()

Every client.lookups.* call is memoized on the Client instance. Call client.lookups.refresh() to clear the cache if YOKSIS IDs are suspected to have rotated.

HTTP client configuration

Client accepts keyword-only overrides for the underlying httpx.Client:

from yoktez import Client

with Client(timeout=60, retries=5, user_agent="my-app/1.0") as client:
    ...

For full control, inject a pre-built httpx.Client via http_client=. Ownership stays with the caller; Client.close() is a no-op for an injected client:

import httpx
from yoktez import Client

http = httpx.Client(timeout=30.0, follow_redirects=True)
try:
    with Client(http_client=http) as client:
        ...
finally:
    http.close()

Concurrency

yoktez.Client is single-threaded by design — share one per thread, never across threads. The library ships no concurrency primitives; threading strategy is the caller's choice.

Design principles

  • Synchronous-only API: Sync is sufficient for YOK NTC's IO patterns; an async surface would double the API and complicate testing for no proven benefit. Concurrency strategy belongs to the caller, and examples/multithreaded_pool.py demonstrates the one-Client-per-thread pattern.
  • Frozen-dataclass value objects: Every returned record is @dataclass(frozen=True, slots=True). Stdlib-only, immutable, hashable, and very fast.
  • Coerce-on-input enum handling: Enum-shaped parameters accept the matching Enum member, its name (e.g., "MASTER"), or its raw int code; the raw-int passthrough tolerates new YOK NTC codes the library hasn't yet enumerated, so wire-side additions don't gate a release.
  • Two-step download flow: client.assets.get(...) resolves status first; download_pdf and download_appendix run only on the available branch. Honest to the underlying YOK NTC flow, and lets callers inspect embargo dates and appendix availability before committing to a second request.
  • Hierarchical logger naming: Every sub-package logs under yoktez.<concern> (yoktez.http, yoktez.search, yoktez.lookups, yoktez.assets). Operators can silence the high-volume HTTP DEBUG channel while preserving the rarer parser WARNING channels; a single logging.getLogger("yoktez").setLevel(...) still catches every child through parent propagation.

Limitations

yoktez is intentionally narrow. The following are out of scope and will not land in this package:

  • No async API: Synchronous code throughout; no async def, no asyncio surface.
  • No multi-threaded helper functions: Concurrency strategy is the caller's choice.
  • No authentication or login flows (e-Devlet): Anonymous public-data access only; features requiring login (favorites, history) are excluded.
  • No bypassing access restrictions: Embargoed and no-permit theses surface their state via AssetStatus and the matching exception types; the library does not attempt to circumvent these.
  • No data hosting or mirroring: The library fetches on demand; no bundled snapshots of the YOK NTC database.
  • No CLI shipped from this package: A separate package may add one later — out of scope here.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

yoktez-0.2.0.tar.gz (379.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

yoktez-0.2.0-py3-none-any.whl (38.6 kB view details)

Uploaded Python 3

File details

Details for the file yoktez-0.2.0.tar.gz.

File metadata

  • Download URL: yoktez-0.2.0.tar.gz
  • Upload date:
  • Size: 379.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for yoktez-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b07af113cf928dfd4a931c343d2e8336b3ca8d4bf3cfb6bfd37ac54ab6a3f0e8
MD5 e6b8cdee78fe7d93498dc5bd656adf13
BLAKE2b-256 9e6113974b07f95418cdfdf3a712560d2259030baebf94ef21c11f77ebd47269

See more details on using hashes here.

Provenance

The following attestation bundles were made for yoktez-0.2.0.tar.gz:

Publisher: release.yml on ozefe/yoktez

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file yoktez-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: yoktez-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 38.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for yoktez-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 90376d452e1e805cda30afaba9b930a5418971b3c6b0bf04c04b4531a3be100c
MD5 0a29c3be371d9fa6588369e9d86cb1f8
BLAKE2b-256 f0f4f87499db3e87974f8293e85d74c866df136dd9a816119e3436f3181b0c20

See more details on using hashes here.

Provenance

The following attestation bundles were made for yoktez-0.2.0-py3-none-any.whl:

Publisher: release.yml on ozefe/yoktez

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page