Metadata-Version: 2.4
Name: tr-acikveri
Version: 0.1.1
Summary: Türkiye açık veri portalları (CKAN) için tek istemci: çoklu portal arama, veri seti bilgisi, kibar ve sürdürülebilir indirme, cp1254/UTF-8 BOM ve ; ayraçlı CSV ile GeoJSON okuma, Türkçe CLI.
Author-email: Yusuf Eminoğlu <geospacephilo@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/YusufEminoglu/tr_acikveri_sdk
Project-URL: Repository, https://github.com/YusufEminoglu/tr_acikveri_sdk
Project-URL: Issues, https://github.com/YusufEminoglu/tr_acikveri_sdk/issues
Project-URL: Changelog, https://github.com/YusufEminoglu/tr_acikveri_sdk/blob/main/CHANGELOG.md
Keywords: open-data,acik-veri,ckan,turkey,turkiye,istanbul,izmir,municipality,csv,geojson,gis,urban-planning
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Environment :: Console
Classifier: Natural Language :: Turkish
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: pandas
Requires-Dist: pandas>=1.3.0; extra == "pandas"
Provides-Extra: excel
Requires-Dist: pandas>=1.3.0; extra == "excel"
Requires-Dist: openpyxl>=3.0.0; extra == "excel"
Provides-Extra: geo
Requires-Dist: geopandas>=0.10.0; extra == "geo"
Provides-Extra: certifi
Requires-Dist: certifi; extra == "certifi"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Dynamic: license-file

# tr-acikveri

[![PyPI version](https://img.shields.io/pypi/v/tr-acikveri.svg)](https://pypi.org/project/tr-acikveri/)
[![Python versions](https://img.shields.io/pypi/pyversions/tr-acikveri.svg)](https://pypi.org/project/tr-acikveri/)
[![CI](https://github.com/YusufEminoglu/tr_acikveri_sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/YusufEminoglu/tr_acikveri_sdk/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

**English summary.** `tr-acikveri` is one Python client for Turkish public open-data
portals that run CKAN (Istanbul İBB, İzmir, Konya, Gaziantep, Manisa, Trabzon and
others): search datasets across many portals, inspect metadata, list resources by
format, and download them politely with caching, rate limiting, retries and resume.
It reads the CSV files these portals actually publish (Windows-1254 or UTF-8 with BOM,
`;` delimiters, decimal commas) and GeoJSON, ships a Turkish CLI, and reminds you that
every dataset's licence belongs to the publishing portal.

---

**Türkiye'deki açık veri portalları için tek istemci.**

Belediyelerin ve kurumların açık veri portallarının çoğu
[CKAN](https://ckan.org) üzerinde çalışır ve aynı *CKAN Action API*'sini sunar.
`tr-acikveri` bu portalları tek bir arayüzün arkasına koyar:

- Bir veya birden çok portalda **veri seti arama** (portallar paralel sorgulanır,
  hata veren portal aramayı durdurmaz).
- **Kurum, etiket, veri seti bilgisi** ve **biçime göre kaynak listeleme**
  (CSV, GeoJSON, SHP, XLSX, KML ...; portalların tutarsız biçim adları normalleştirilir).
- **Kibar indirme:** `tr-acikveri/<sürüm>` User-Agent'ı, sunucu başına hız sınırı,
  zaman aşımı, `Retry-After` dikkate alan yeniden deneme, diskte önbellek ve
  yarıda kalan indirmeyi **kaldığı yerden sürdürme** (HTTP `Range`).
- **Türkçe dosyalar:** cp1254 / UTF-8 BOM kodlama tespiti, `;` ayraç tespiti,
  `1.234,56` gibi sayıların okunması, eski `crs` alanlı GeoJSON (ör. EPSG:5254).
- İsteğe bağlı **pandas / geopandas** dönüşümleri ve Türkçe **komut satırı** (`ara`,
  `bilgi`, `indir`, `portallar`, `kurumlar`, `etiketler`).

Yalnızca Python standart kütüphanesiyle çalışır; Python 3.9 ve üstü gerekir.

> **Lisans uyarısı.** Bu paket MIT lisanslıdır, ancak **indirdiğiniz verilerin lisansı
> her portala ve veriyi yayımlayan kuruma aittir** (İBB Açık Veri Lisansı, İzmir BB Açık
> Veri Lisansı, CC BY 4.0, CC BY-NC ...). Paket size veriler üzerinde hiçbir hak
> vermez. Her veri setinin lisansını (`bilgi` komutu veya `Dataset.license_title`)
> okuyun, koşullarına uyun ve kaynak gösterin. Ticari olmayan (NC) lisanslara özellikle
> dikkat edin.

## Kurulum

```bash
pip install tr-acikveri               # yalnızca standart kütüphane
pip install "tr-acikveri[pandas]"     # + DataFrame dönüşümü
pip install "tr-acikveri[excel]"      # + XLSX/XLS okuma (pandas + openpyxl)
pip install "tr-acikveri[geo]"        # + GeoDataFrame (geopandas)
pip install "tr-acikveri[certifi]"    # + Mozilla CA paketi (bkz. "TLS sertifikaları")
```

## Hızlı başlangıç (komut satırı)

```bash
tr-acikveri portallar                       # CKAN portalları
tr-acikveri portallar --hepsi               # CKAN olmayanlar dahil
tr-acikveri ara otopark -p ibb -p izmir -f csv -n 5
tr-acikveri bilgi ibb ispark-otopark-bilgileri
tr-acikveri indir konya otopark-bilgileri -f csv -o veri/
tr-acikveri kurumlar izmir --json
```

Örnek çıktı (kısaltılmış, 2026-11-09):

```
[konya] otopark-bilgileri
    Otopark Bilgileri (CSV) · Destek Hizmetleri Dairesi Başkanlığı
[izmir] otopark-ucretleri
    Otopark Ücretleri (CSV, XLSX) · İZELMAN A.Ş.

Toplam eşleşme 3 (izmir: 2; konya: 1); gösterilen 3.
```

Ortak seçenekler: `--onbellek KLASÖR`, `--onbellek-suresi SN`, `--onbelleksiz`,
`--zaman-asimi SN`, `--aralik SN` (aynı sunucuya iki istek arası, varsayılan 1 sn),
`--yeniden-deneme N`, `--iletisim E-POSTA` (User-Agent'a eklenir), `--json`.
`-p` portal anahtarı, şehir adı (`İzmir`, `istanbul`) veya herhangi bir CKAN adresi
olabilir. Yönlendirilen çıktı (ör. `> sonuc.json`) her zaman UTF-8 yazılır.

## Hızlı başlangıç (Python)

```python
from tr_acikveri import AcikVeri

av = AcikVeri(contact="ad.soyad@kurum.edu.tr")      # iletişim adresi önerilir

sonuc = av.search("otopark", portals=["ibb", "izmir", "konya"], formats=["csv"], rows=5)
for ds in sonuc:
    print(ds.portal, ds.name, ds.formats, ds.license_title)
print(sonuc.counts, sonuc.errors)

ds = av.dataset("konya", "otopark-bilgileri")
kaynak = ds.resources_by_format(["csv"])[0]
tablo = av.read_csv(kaynak)                   # indirir (önbellek) ve okur
print(tablo.encoding, tablo.delimiter, tablo.columns)
kapasite = tablo.numeric("bolgekapasite")      # "1.250" -> 1250.0

df = av.to_dataframe(kaynak, numeric=True)     # [pandas]

parklar = av.search("park", portals=["trabzon"], formats=["geojson"]).datasets[0]
geo_kaynak = parklar.resources_by_format(["geojson"])[0]
geo = av.read_geojson(geo_kaynak)              # FeatureCollection dict
gdf = av.to_geodataframe(geo_kaynak)           # [geo]
```

Tek portal için alt düzey istemci:

```python
from tr_acikveri import CkanClient

ibb = CkanClient("ibb")
sayi, sayfa = ibb.search("bisiklet", formats=["geojson"], sort="metadata_modified desc")
for ds in ibb.iter_search("hava kalitesi", limit=200):
    ...
kayitlar = ibb.datastore_search(resource_id, limit=100, filters={"ilce": "Kadıköy"})
```

## Portal kaydı

Her kayıt 2026-11-09 tarihinde `<adres>/api/3/action/status_show` adresine yapılan tek
bir salt okunur `GET` isteğiyle doğrulandı; lisans notları portalın `license_list`
ve `package_search` yanıtlarından alındı. Veri seti sayıları aynı tarihe aittir.

| Anahtar | Portal | Adres | Veri seti | Lisans (portalın bildirdiği) |
|---|---|---|---:|---|
| `ibb` | İstanbul BB | https://data.ibb.gov.tr | 565 | İBB Açık Veri Lisansı |
| `izmir` | İzmir BB | https://acikveri.bizizmir.com | 251 | İzmir BB Açık Veri Lisansı; bazıları CC BY / belirtilmemiş |
| `konya` | Konya BB | https://acikveri.konya.bel.tr | 233 | CC BY 4.0 |
| `gaziantep` | Gaziantep | https://acikveri.gaziantep.bel.tr | 252 | Gaziantep Açık Veri Lisansı |
| `balikesir` | Balıkesir BB | https://acikveri.balikesir.bel.tr | 252 | Balıkesir Açık Veri Lisansı |
| `denizli` | Denizli | https://acikveri.denizli.bel.tr | 39 | Denizli Açık Veri Lisansı |
| `trabzon` | Trabzon BB | https://acikveri.trabzon.bel.tr | 206 | Trabzon Açık Veri Lisansı |
| `manisa` | Manisa BB | https://acikveri.manisa.bel.tr | 1389 | Manisa BB Açık Veri Lisansı (mbb-odl-1.0) |
| `canakkale` | Çanakkale | https://acikveri.canakkale.bel.tr | 123 | Çanakkale Açık Veri Lisansı |
| `bursa-yesil` | Bursa Açık Yeşil | https://acikyesil.bursa.bel.tr | 49 | kurum lisansı (yalnızca yeşil alan verisi) |
| `kadikoy` | Kadıköy (ilçe) | https://acikveri.kadikoy.bel.tr | 161 | karışık: CC BY, CC BY-NC, CC BY-ND |
| `tuzla` | Tuzla (ilçe) | https://veri.tuzla.bel.tr | 91 | CC BY |
| `nilufer` | Nilüfer (ilçe) | https://acikveri.nilufer.bel.tr | 65 | çoğunda belirtilmemiş; bazıları CC BY / CC BY-NC |

Desteklenmeyen (CKAN API'si yok veya kontrol sırasında erişilemedi) ve
`portallar --hepsi` ile listelenenler: Ankara (seffaf.ankara.bel.tr), Kocaeli
(veri.kocaeli.bel.tr), Kayseri, Küçükçekmece, Sağlık Bakanlığı (özel yazılım);
Antalya, Sakarya ve ULASAV (ulasav.csb.gov.tr; erişilemedi, API türü doğrulanamadı).
Gaziantep, Denizli, Trabzon, Çanakkale, Balıkesir ve Bursa Açık Yeşil portalları
`ulasav-license` kimlikli ortak CKAN şablonunu kullanır; lisans metni her portalın
`/license` sayfasındadır.

Kayıtta olmayan bir CKAN portalı adresle kullanılabilir veya kaydedilebilir:

```python
from tr_acikveri import Portal, register_portal, CkanClient

CkanClient("https://acikveri.ornek.bel.tr").search("imar")
register_portal(Portal("ornek", "Örnek Belediyesi", "https://acikveri.ornek.bel.tr",
                       city="Örnek", license_note="CC BY 4.0"))
```

Portallar değişir: adres taşınabilir, API kapanabilir. Kayıttaki bilgiler kontrol
tarihindeki durumu yansıtır.

## API özeti

| Nesne | Açıklama |
|---|---|
| `AcikVeri(portals=None, cache_dir=None, cache_ttl=86400, contact=None, timeout=30, min_interval=1.0, retries=3, max_workers=4)` | Çok portallı istemci |
| `.search(query, portals, formats, rows, organization, tags, sort)` → `SearchResult` | `datasets`, `counts`, `errors`, `total` |
| `.dataset(portal, ad)` → `Dataset`; `.resources(portal, ad, formats)` → `[Resource]` | Meta veri ve kaynaklar |
| `.organizations(portal)`, `.tags(portal, query)` | Kurumlar, etiketler |
| `.download(resource, dest=None, overwrite, resume, max_bytes, progress)` → `Path` | Önbelleğe veya hedefe indirme; `.meta.json` kaynak bilgisi |
| `.download_dataset(portal, ad, formats, dest_dir)` | Veri setinin tüm dosyaları |
| `.read_csv`, `.read_geojson`, `.load`, `.to_dataframe`, `.to_geodataframe` | Okuma |
| `CkanClient(portal)` | `search`, `iter_search`, `dataset`, `resource`, `organizations`, `tags`, `licenses`, `datastore_search`, `action` |
| `list_portals`, `get_portal`, `register_portal`, `Portal` | Portal kaydı |
| `read_csv`, `CsvTable`, `detect_encoding`, `sniff_delimiter`, `parse_number`, `read_geojson`, `geojson_crs` | Dosya okuyucular |
| `HttpClient`, `RetryPolicy`, `RateLimiter`, `Cache` | Taşıma katmanı |
| `AcikVeriError` → `HttpError`, `TransportError`, `CkanError` → `NotFoundError`, `UnsupportedPortalError`, `PortalNotFoundError`, `FormatError`, `MissingDependencyError` | Hatalar |

## Yöntem ve kaynaklar

- **CKAN Action API v3** — yalnızca okuma eylemleri (`status_show`, `package_search`,
  `package_show`, `resource_show`, `organization_list`, `tag_list`, `license_list`,
  `datastore_search`), hepsi `GET` ile. Kaynak: CKAN API belgeleri,
  https://docs.ckan.org/en/2.9/api/. `package_search` için `rows` en fazla 1000'e
  sınırlanır (CKAN'ın `ckan.search.rows_max` varsayılanı).
- **Biçim filtresi** — `res_format` Solr'da birebir eşleşen bir alan olduğundan,
  portallarda görülen yazımların tamamı (`CSV`, `csv`, `.csv`, `GeoJSON`, `.shp`,
  `Excel` ...) sunucuya gönderilir; sonuç ayrıca istemci tarafında normalleştirilmiş
  biçimle süzülür.
- **HTTP** — `Retry-After` (saniye veya HTTP tarihi) ve `Range`/`Content-Range`
  ile sürdürme: RFC 9110 §10.2.3 ve §14. Yeniden deneme 408/425/429/5xx ve ağ
  hatalarında, üstel bekleme (+%10 rastgele) ile yapılır. Hız sınırı sunucu (host)
  başınadır ve iş parçacıkları arasında paylaşılır.
- **Kodlama** — sıra: UTF-8 BOM → UTF-16 BOM → katı UTF-8 → Windows-1254 (cp1254,
  Türkçe Windows kod sayfası) → ISO-8859-9. Ayraç, ilk 50 satırda en tutarlı sütun
  sayısını veren adaydır (`;`, `,`, sekme, `|`; eşitlikte `;`).
- **Sayılar** — `parse_number` Türkçe yazımı (ondalık virgül, binlik nokta) varsayar;
  yalnızca nokta içeren ve binlik gruplamaya uymayan değerler (`12.5`) ondalık
  okunur. `%`, `TL`, `₺` ve boşluklar yok sayılır.
- **GeoJSON** — RFC 7946 (WGS 84, `EPSG:4326`). 2008 GeoJSON belirtimindeki `crs`
  alanı varsa (`urn:ogc:def:crs:EPSG::5254` gibi) `geojson_crs` bu kodu döndürür ve
  `to_geodataframe` onu kullanır.

## TLS sertifikaları

Sertifika doğrulaması her zaman açıktır. Bazı portallar (kontrol tarihinde
`data.ibb.gov.tr`) Windows'un yalnızca gerektiğinde indirdiği bir kök sertifikaya
zincirlenir; Python bu durumda `CERTIFICATE_VERIFY_FAILED` hatası verir.
`certifi` kuruluysa (`pip install "tr-acikveri[certifi]"`) paket otomatik olarak
Mozilla CA paketini kullanır. Alternatifler: `SSL_CERT_FILE` ortam değişkeni,
`HttpClient(ca_file=...)` veya `HttpClient(ssl_context=...)`.

## Önbellek

Varsayılan klasör `TR_ACIKVERI_CACHE` ortam değişkeni, yoksa Windows'ta
`%LOCALAPPDATA%\tr-acikveri`, diğer sistemlerde `~/.cache/tr-acikveri`'dir.
API yanıtları `api/` altında (varsayılan 1 gün), indirilen dosyalar
`files/<portal>/<kaynak kimliği>/` altında tutulur. Her dosyanın yanında kaynak
adresi, portal, veri seti, indirme zamanı ve HTTP doğrulayıcılarını içeren bir
`.meta.json` bulunur. İndirilen verileri bir git deposuna eklemeyin.

## Sınırlamalar

- Yalnızca CKAN portalları desteklenir; özel yazılımlı portallar (Ankara, Kocaeli,
  Kayseri, Sağlık Bakanlığı ...) kayıtta yalnızca bilgi amaçlıdır.
- Portal listesi, veri seti sayıları ve lisans notları 2026-11-09 tarihli kontrollere
  dayanır; değişebilir. Bağlayıcı olan, veri setinin portaldaki lisansıdır.
- Kodlama tespiti sezgiseldir: UTF-8 olarak geçerli olan cp1254 dosyaları (çok nadir)
  UTF-8 sayılır; gerekirse `encoding="cp1254"` verin.
- `API`/`HTML` türündeki kaynaklar dosya değildir ve varsayılan olarak indirilmez.
- Çok portallı aramada `rows` portal başına uygulanır; sonuçlar portallar arasında
  yeniden sıralanmaz.
- Önbellekteki dosya, sunucuda güncellense bile `overwrite=True` (CLI: `--yeniden`)
  verilmedikçe yeniden indirilmez.

## Geliştirme

```bash
pip install -e ".[dev,pandas]"
pytest --cov=tr_acikveri
ruff check . && ruff format --check src tests && mypy src
```

Testler gerçek ağa hiç çıkmaz: `tests/mockserver.py` iki CKAN portalını, hatalı
portalları (500, HTML), 429/503 yanıtlarını, bağlantısı yarıda kopan indirmeleri ve
`Range` desteği olmayan sunucuları taklit eden yerel bir HTTP sunucusudur.

## Lisans

MIT © 2026 Yusuf Eminoğlu. Bkz. [LICENSE](LICENSE). Bu lisans yalnızca yazılımı
kapsar; portallardan alınan veriler kendi lisanslarına tabidir.

## Academic citation

```bibtex
@software{eminoglu_tr_acikveri,
  author  = {Eminoğlu, Yusuf},
  title   = {tr-acikveri},
  year    = {2026},
  version = {0.1.1},
  url     = {https://github.com/YusufEminoglu/tr_acikveri_sdk}
}
```

Ayrıca bkz. `CITATION.cff`.
