Skip to main content

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.

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.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

0.1.2

2 files

0.1.1

2 files

Supported by

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