Metadata-Version: 2.5
Name: rowfuse
Version: 0.1.0
Summary: Stream CSV merges with deterministic row hashes and disk-backed deduplication.
Project-URL: Homepage, https://github.com/pyaidev/rowfuse
Project-URL: Documentation, https://github.com/pyaidev/rowfuse#readme
Project-URL: Repository, https://github.com/pyaidev/rowfuse
Project-URL: Issues, https://github.com/pyaidev/rowfuse/issues
Author: Nurmuhammad Mashrapov
Keywords: csv,deduplication,hashing,merge,parquet,streaming
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.11
Provides-Extra: parquet
Requires-Dist: pyarrow>=21.0.0; extra == 'parquet'
Description-Content-Type: text/markdown

# RowFuse

Katta CSV fayllarni oqim tarzida birlashtirish, qatorlarni hashlash va dublikatlarni diskdagi indeks yordamida ajratish uchun Python kutubxonasi.

Birinchi versiya: **0.1.0**. [Manba kodi](https://github.com/pyaidev/rowfuse) · [Xato va takliflar](https://github.com/pyaidev/rowfuse/issues)

## O‘rnatish

Python 3.11 yoki undan yangi versiya kerak:

```bash
pip install rowfuse

# Parquet natijalari uchun:
pip install 'rowfuse[parquet]'
```

CSV funksiyalari Python standart kutubxonasi bilan ishlaydi. PyArrow faqat Parquet natijasi so‘ralganda yuklanadi.

## Tez boshlash

```python
from rowfuse import merge

result = merge(
    ["data/yanvar.csv", "data/fevral.csv"],
    output="jami.parquet",
    hash_algorithm="sha256",
    hash_by="all",
    hash_column="_row_hash",
    deduplicate=True,
    keep="first",
)

print(result.files_merged)
print(result.rows_written)
print(result.duplicates_removed)
print(result.file_hash)
result.write_report("report.json")
```

CSV natijasi uchun `output='jami.csv'` deb yozing. Fayllar ro‘yxati o‘rniga `sources='data/*.csv'` yoki bir nechta glob berish mumkin.

## Birlashtirish qoidalari

- Qatorlar ketma-ket qo‘shiladi. Ro‘yxatdagi fayllar ko‘rsatilgan tartibda, har bir glob natijalari esa fayl yo‘li bo‘yicha saralangan holda o‘qiladi.
- Bir fizik fayl takror berilsa, bir marta o‘qiladi. Bu symlink va hardlink orqali qayta berilgan fayllarga ham tegishli.
- Sarlavha bir marta yoziladi. Ustunlar birinchi fayldagi tartibda joylashadi; keyingi fayllar nom bo‘yicha moslanadi.
- `schema_mode='strict'` standart rejim: barcha fayllarda ustun nomlari to‘plami bir xil bo‘lishi kerak. Ustunlar tartibi farq qilishi mumkin.
- `schema_mode='union'`: yangi ustunlar birinchi uchragan tartibida qo‘shiladi. Yetishmagan maydon CSV’da bo‘sh, Parquet’da `null` bo‘ladi.
- CSV’dagi qiymatlar satr sifatida saqlanadi, shu jumladan Parquet natijasida ham. `00123`, `NA` va `1.00` kabi qiymatlar avtomatik o‘zgartirilmaydi.
- UTF-8 va UTF-8 BOM standart holatda o‘qiladi. Boshqa kodlash uchun `encoding`, ajratgich uchun `delimiter` bering. Natija CSV UTF-8’da yoziladi.
- Bo‘sh fizik qatorlar tashlab ketiladi va `blank_rows_skipped` orqali hisoblanadi. Qo‘shtirnoq ichidagi bo‘sh qiymat yozuv sifatida saqlanadi.
- Takror yoki bo‘sh ustun nomlari, noto‘g‘ri maydon soni, buzilgan qo‘shtirnoqlar va kodlash xatolari fayl konteksti bilan xato qaytaradi. Xatoli yozuvlarni alohida saqlash keyingi versiya uchun rejalashtirilgan.

```python
result = merge(
    "data/*.csv",
    output="jami.csv",
    schema_mode="union",
    delimiter=";",
    output_delimiter=",",
)
```

## Hashlash va dublikatlar

`hash_column='_row_hash'` qator hashini natijaga qo‘shadi. Parametr berilmasa, yangi hash ustuni yaratilmaydi. Yakuniy faylning baytlar bo‘yicha hashi har doim `result.file_hash` orqali qaytadi.

```python
from rowfuse import hash_file, hash_row, merge

digest = hash_row({"id": "001", "name": "Ali"})
checksum = hash_file("jami.csv")

result = merge(
    "data/*.csv",
    output="users.csv",
    hash_by=["user_id"],
    hash_column="_row_hash",
    deduplicate=True,
)
```

- Standart algoritm SHA-256. SHA-512 va BLAKE2b ham qo‘llanadi.
- `hash_by='all'` barcha ma’lumot ustunlarini oladi. Tanlangan ustunlar har bir kirish faylida mavjud bo‘lishi shart, hatto `union` rejimida ham.
- Hash formati `rowfuse-row-v1`: nom bo‘yicha saralangan `[ustun, qiymat]` juftliklari ixcham UTF-8 JSON’da kodlanadi va `rowfuse:row:v1` hamda NUL bayti prefiksi bilan hashlanadi.
- Qiymatlar kesilmaydi, kichik harfga o‘tkazilmaydi yoki songa aylantirilmaydi. Ustunlar tartibi hashga ta’sir qilmaydi; ustun nomlari va qiymatlari ta’sir qiladi.
- Yetishmagan maydon (`None`) va mavjud bo‘sh satr (`''`) hashda farqlanadi. CSV yozuvida ikkalasi ham bo‘sh ko‘rinadi; bu farqni saqlash uchun Parquet’dan foydalaning. `hash_by='all'` hashi birlashtirilgan sxemaga bog‘liq.
- `deduplicate=False` standart holat: barcha yozuvlar saqlanadi. `True` bo‘lsa, tanlangan ustunlar bir xil bo‘lgan yozuvlarning birinchisi qoladi. Faqat `keep='first'` qo‘llanadi.
- Hash mos tushganda to‘liq tanlangan qiymatlar ham diskdagi indeksda tekshiriladi. Hash to‘qnashuvi turli yozuvlarni birlashtirib yubormaydi.

## Katta fayllar va natijani saqlash

CSV natijasi qatorlab yoziladi. Parquet buferi `batch_size` (standart 50 000 qator) yoki `batch_bytes` (standart 8 MiB qiymat baytlari) chegarasiga yetganda yoziladi. Bu qiymatlar butun jarayon RAM’iga qat’iy limit emas: keng yozuvlar, Python obyektlari va Arrow qo‘shimcha xotira ishlatadi.

Dublikatlar indeksi vaqtinchalik SQLite faylida saqlanadi; uning keshi taxminan 8 MiB etib sozlangan. Indeks to‘liq tanlangan qiymatlarni ham saqlagani uchun disk sarfi sezilarli bo‘lishi mumkin. `temp_dir` orqali ish papkasini tanlash mumkin. Ish tugaganda yoki Python xatosi bilan to‘xtaganda indeks tozalanadi.

```python
merge(
    "data/*.csv",
    output="jami.parquet",
    deduplicate=True,
    batch_size=10_000,
    batch_bytes=4 * 1024 * 1024,
    temp_dir="/path/to/scratch",
)
```

Natija avval chiqish papkasidagi vaqtinchalik faylga yoziladi va to‘liq tugagach atomik e’lon qilinadi. `overwrite=False` standart holatda mavjud natijani himoya qiladi. `overwrite=True` bilan ham xatoli import oldingi natijani almashtirmaydi. Natija glob ichiga tushsa, kirishdan chiqariladi; natijani kirish fayli sifatida aniq berish rad etiladi.

Manba fayllari jarayon davomida o‘zgarmasligi kerak. Fayl metama’lumotlari o‘zgarishi tekshiriladi; bu tashqi yozuvchilarga qarshi fayl qulfi emas. Qattiq to‘xtatish yoki elektr uzilishi vaqtinchalik fayllarni qoldirishi mumkin. Ushbu versiya mahalliy fayllar bilan ishlaydi va uzilgan vazifani davom ettirmaydi. CSV maydon uzunligi Python’ning `csv.field_size_limit()` sozlamasiga amal qiladi.

## Terminal

```bash
rowfuse merge 'data/*.csv' \
    --output jami.parquet \
    --hash-column _row_hash \
    --deduplicate \
    --report report.json

# Tanlangan ustunlar:
python -m rowfuse merge a.csv b.csv \
    -o jami.csv --hash-by user_id --hash-by order_id --deduplicate
```

Terminalga JSON hisobot chiqariladi. `--report` yangi hisobot faylini ham yaratadi. Hisobot yozish xatosi undan avval tugallangan merge natijasini bekor qilmaydi.

## Ishlab chiqish

```bash
git clone https://github.com/pyaidev/rowfuse.git
cd rowfuse
uv sync --extra parquet
uv run --extra parquet pytest
uv run ruff check .
uv run ruff format --check .
uv build
```

Faqat standart kutubxona yo‘lini tekshirish uchun PyArrow o‘rnatilmagan muhitda ham CSV testlarini bajarish mumkin; Parquet testlari bunday muhitda o‘tkazib yuboriladi.

GitHub Actions Python 3.11–3.14 muhitlarida testlarni bajaradi. `v` bilan boshlangan versiya tegi nashr workflow’ini ishga tushiradi. PyPI uchun bir martalik sozlash va keyingi relizlar [PUBLISHING.md](PUBLISHING.md) faylida yozilgan.

## O‘lchangan sinov

Mahalliy macOS / Python 3.12.10 muhitida ikki sintetik CSV fayl bilan tekshirildi. Jami 300 000 yozuv va 83.7 MB kirish ma’lumoti; hashlash hamda disk orqali dublikatlarni ajratish yoqilgan. Ikkala natijada 225 000 yozuv qoldi, 75 000 dublikat olib tashlandi.

| Natija | Merge vaqti | Jarayonning eng yuqori RSS xotirasi |
| --- | --- | --- |
| CSV | 8.048 soniya | 32.2 MB |
| Parquet | 6.681 soniya | 79.4 MB |

Bu bir martalik sintetik sinov; fayllarda takrorlanuvchi 256 baytli matn bor. Natijalar qurilma, disk, ustunlar va qiymatlarga bog‘liq. RSS butun jarayon uchun, vaqt esa kirish fayllarini yaratishdan keyingi merge va checksum uchun o‘lchangan.

```bash
uv run --extra parquet python benchmarks/merge_memory.py --rows-per-file 150000 --format csv
uv run --extra parquet python benchmarks/merge_memory.py --rows-per-file 150000 --format parquet
```

Aniq o‘lchovlar [benchmarks/results.json](benchmarks/results.json) faylida.

Keyingi rejalashtirilgan imkoniyatlar: `inspect()`, `validate()`, xatoli yozuvlar fayli va tugallangan fayllar bo‘yicha checkpoint. Ular ushbu versiyaning API’siga hali kirmaydi.
