Asosiy qismga o'tish

Loyiha: to'liq ko'rinish

17-bob 18 kod bloki 3865 so'z

Nima qurdik

Birinchi bobda 9 qatorli skript bor edi va u birinchi buzuq qatorda yiqildi. Endi to'liq loyiha bor.

matn4 qator
find src tests -name "*.py" | sort
echo ""
echo "--- konfiguratsiya fayllari ---"
ls -1 pyproject.toml ruff.toml mypy.ini .gitignore .env.namuna tekshir.sh 2>/dev/null
Chiqish (qaytish kodi 0, 144 ms)
src/hisobot/__init__.py
src/hisobot/__main__.py
src/hisobot/chiqarish.py
src/hisobot/cli.py
src/hisobot/hisob.py
src/hisobot/ko_p.py
src/hisobot/modellar.py
src/hisobot/muhit.py
src/hisobot/oqish.py
src/hisobot/sozlama.py
src/hisobot/tarmoq.py
src/hisobot/tekshiruv.py
src/hisobot/xatolar.py
tests/conftest.py
tests/test_chiqarish.py
tests/test_cli.py
tests/test_hisob.py
tests/test_izolyatsiya.py
tests/test_jurnal.py
tests/test_ko_p.py
tests/test_oqish.py
tests/test_sana.py
tests/test_scope.py
tests/test_sozlama.py
tests/test_tarmoq.py
tests/test_tekshiruv.py
tests/test_xatolar.py

--- konfiguratsiya fayllari ---
.env.namuna
.gitignore
mypy.ini
pyproject.toml
ruff.toml
tekshir.sh

Har bob nimani qo'shganini ko'rib chiqamiz:

python19 qator
import pandas as pd
show(pd.DataFrame([
    {"bob": 1, "qo'shgan narsasi": "To'rt ishni ajratish: o'qish, tekshirish, hisoblash, ko'rsatish"},
    {"bob": 2, "qo'shgan narsasi": "venv, pyproject.toml, src/ tartibi, tahrirlanadigan o'rnatish"},
    {"bob": 3, "qo'shgan narsasi": "Paket tuzilishi, `python3 -m hisobot`, __init__.py"},
    {"bob": 4, "qo'shgan narsasi": "Talaba/Natija dataclass lari, Daraja enum i"},
    {"bob": 5, "qo'shgan narsasi": "Tur ko'rsatkichlari, mypy strict"},
    {"bob": 6, "qo'shgan narsasi": "Birinchi testlar, TDD bilan sanani_tekshir"},
    {"bob": 7, "qo'shgan narsasi": "parametrize, fixture, conftest.py, qamrov"},
    {"bob": 8, "qo'shgan narsasi": "Istisno oilasi, to'plash vs otish, tushunarli xabarlar"},
    {"bob": 9, "qo'shgan narsasi": "logging, -v/-vv darajalari"},
    {"bob": 10, "qo'shgan narsasi": "chiqarish.py: JSON va CSV yozish"},
    {"bob": 11, "qo'shgan narsasi": "Sozlama muhit o'zgaruvchilaridan, .env, sirlar"},
    {"bob": 12, "qo'shgan narsasi": "tarmoq.py: timeout, qayta urinish, backoff"},
    {"bob": 13, "qo'shgan narsasi": "ko_p.py: bir nechta faylni parallel qayta ishlash"},
    {"bob": 14, "qo'shgan narsasi": "Profiling, kesh, generator bilan xotira tejash"},
    {"bob": 15, "qo'shgan narsasi": "cli.py: to'liq argparse, qaytish kodlari, o'rnatiladigan buyruq"},
    {"bob": 16, "qo'shgan narsasi": "ruff, tekshir.sh, CI, pre-commit"},
]))
bobqo'shgan narsasi
1To'rt ishni ajratish: o'qish, tekshirish, hisoblash, ko'rsatish
2venv, pyproject.toml, src/ tartibi, tahrirlanadigan o'rnatish
3Paket tuzilishi, `python3 -m hisobot`, __init__.py
4Talaba/Natija dataclass lari, Daraja enum i
5Tur ko'rsatkichlari, mypy strict
6Birinchi testlar, TDD bilan sanani_tekshir
7parametrize, fixture, conftest.py, qamrov
8Istisno oilasi, to'plash vs otish, tushunarli xabarlar
9logging, -v/-vv darajalari
10chiqarish.py: JSON va CSV yozish
11Sozlama muhit o'zgaruvchilaridan, .env, sirlar
12tarmoq.py: timeout, qayta urinish, backoff
13ko_p.py: bir nechta faylni parallel qayta ishlash
14Profiling, kesh, generator bilan xotira tejash
15cli.py: to'liq argparse, qaytish kodlari, o'rnatiladigan buyruq

… 16 qatordan birinchi 15 tasi

Oxirgi bo'lak: hisobot matni

Bitta narsa yetishmayapti — odam o'qiydigan hisobot. Uni qo'shamiz.

shu faylni yaratingsrc/hisobot/matn.py

python42 qator
"""Odam o'qiydigan hisobot matni."""
from .hisob import daraja_taqsimoti, eng_yaxshi, guruh_ortachasi
from .modellar import Natija


def hisobot_matni(natija: Natija, kenglik: int = 46) -> str:
    """Natijadan formatlangan matnli hisobot yasaydi."""
    chiziq = "=" * kenglik
    qatorlar = [chiziq, "TALABALAR HISOBOTI".center(kenglik), chiziq, ""]

    jami = natija.jami
    ulush = 100 * len(natija.talabalar) / jami if jami else 0.0
    qatorlar.append(f"Jami qator      : {jami}")
    qatorlar.append(f"To'g'ri         : {len(natija.talabalar)} ({ulush:.1f}%)")
    qatorlar.append(f"Rad etilgan     : {len(natija.xatolar)}")

    if natija.talabalar:
        qatorlar += ["", "-" * kenglik, "GURUH BO'YICHA O'RTACHA", "-" * kenglik]
        ortachalar = guruh_ortachasi(natija.talabalar)
        eng_katta = max(ortachalar.values())
        for guruh, ortacha in ortachalar.items():
            ustun = "#" * int(20 * ortacha / eng_katta) if eng_katta else ""
            qatorlar.append(f"  {guruh:<4} {ortacha:6.2f}  {ustun}")

        qatorlar += ["", "-" * kenglik, "DARAJALAR", "-" * kenglik]
        for daraja, soni in daraja_taqsimoti(natija.talabalar).items():
            foiz = 100 * soni / len(natija.talabalar)
            qatorlar.append(f"  {daraja:<12} {soni:3}  ({foiz:5.1f}%)")

        eng = eng_yaxshi(natija.talabalar)
        if eng is not None:
            qatorlar += ["", f"Eng yuqori: {eng.ism} — {eng.baho} ({eng.guruh} guruhi)"]

    if natija.xatolar:
        qatorlar += ["", "-" * kenglik, f"XATOLAR ({len(natija.xatolar)})", "-" * kenglik]
        for qator, ism, sabab in natija.xatolar[:10]:
            qatorlar.append(f"  {qator:>4}-qator  {ism:<12} {sabab}")
        if len(natija.xatolar) > 10:
            qatorlar.append(f"  ... va yana {len(natija.xatolar) - 10} ta")

    qatorlar += ["", chiziq]
    return "\n".join(qatorlar)

shu faylni yaratingsrc/hisobot/__init__.py

python34 qator
"""CSV ma'lumotini tekshirib hisobot chiqaradigan asbob."""

from .chiqarish import csv_yoz, json_yoz, xulosa
from .hisob import daraja_taqsimoti, eng_yaxshi, guruh_ortachasi
from .matn import hisobot_matni
from .modellar import Daraja, Natija, Talaba
from .oqish import oqi, tozala
from .sozlama import Sozlama
from .tekshiruv import bahoni_tekshir, sanani_tekshir
from .xatolar import FaylXato, HisobotXato, QatorXato, SozlamaXato

__version__ = "1.0.0"
__all__ = [
    "Daraja",
    "FaylXato",
    "HisobotXato",
    "Natija",
    "QatorXato",
    "Sozlama",
    "SozlamaXato",
    "Talaba",
    "__version__",
    "bahoni_tekshir",
    "csv_yoz",
    "daraja_taqsimoti",
    "eng_yaxshi",
    "guruh_ortachasi",
    "hisobot_matni",
    "json_yoz",
    "oqi",
    "sanani_tekshir",
    "tozala",
    "xulosa",
]

CLI ni yangi funksiyaga ulaymiz:

shu faylni yaratingsrc/hisobot/cli.py

python117 qator
"""Buyruq qatori interfeysi."""
import argparse
import json
import logging
import sys
from collections.abc import Mapping
from pathlib import Path

from . import __version__
from .chiqarish import csv_yoz, json_yoz, xulosa
from .ko_p import hammasi
from .matn import hisobot_matni
from .modellar import Natija
from .sozlama import Sozlama
from .xatolar import SozlamaXato

log = logging.getLogger(__name__)


def jurnalni_sozla(batafsil: int, jim: bool) -> None:
    daraja = logging.ERROR if jim else {0: logging.WARNING, 1: logging.INFO}.get(
        batafsil, logging.DEBUG)
    logging.basicConfig(level=daraja, stream=sys.stderr,
                        format="%(levelname)-8s %(name)s: %(message)s")


def tahlilchi() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(
        prog="hisobot",
        description="CSV fayldan talabalar hisobotini tayyorlaydi.",
        epilog="Misol:  hisobot talabalar.csv --format json --chiqish natija.json",
    )
    p.add_argument("fayllar", nargs="+", type=Path, metavar="FAYL")
    p.add_argument("--format", choices=["matn", "json", "csv"], default="matn")
    p.add_argument("--chiqish", type=Path, metavar="YO'L")
    p.add_argument("--max-baho", type=int, metavar="N")
    p.add_argument("--qatiy", action="store_true")
    p.add_argument("-j", "--ishchilar", type=int, default=4, metavar="N")
    p.add_argument("-v", "--batafsil", action="count", default=0)
    p.add_argument("-q", "--jim", action="store_true")
    p.add_argument("--version", action="version", version=f"hisobot {__version__}")
    return p


def _sozlamani_tayyorla(args: argparse.Namespace) -> Sozlama:
    s = Sozlama.muhitdan()
    if args.max_baho is None and not args.qatiy:
        return s
    s = Sozlama(max_baho=args.max_baho if args.max_baho is not None else s.max_baho,
                min_baho=s.min_baho, chiqish_papka=s.chiqish_papka,
                qatiy=args.qatiy or s.qatiy)
    s.tekshir()
    return s


def _birlashtir(natijalar: Mapping[str, Natija | str]) -> tuple[Natija | None, bool]:
    birlashgan: Natija | None = None
    xatolar_bor = False
    for nom in sorted(natijalar):
        n = natijalar[nom]
        if not isinstance(n, Natija):
            log.error("%s: %s", nom, n)
            xatolar_bor = True
            continue
        if birlashgan is None:
            birlashgan = Natija(talabalar=list(n.talabalar), xatolar=list(n.xatolar))
        else:
            birlashgan.talabalar.extend(n.talabalar)
            birlashgan.xatolar.extend(n.xatolar)
    return birlashgan, xatolar_bor


def main(argv: list[str] | None = None) -> int:
    args = tahlilchi().parse_args(argv)
    jurnalni_sozla(args.batafsil, args.jim)

    try:
        sozlama = _sozlamani_tayyorla(args)
    except SozlamaXato as e:
        log.error("sozlama xatosi: %s", e)
        return 2

    birlashgan, xatolar_bor = _birlashtir(
        hammasi(list(args.fayllar), ishchilar=args.ishchilar, sozlama=sozlama)
    )
    if birlashgan is None:
        log.error("hech qaysi fayl o'qilmadi")
        return 1
    if birlashgan.xatolar:
        xatolar_bor = True
        if sozlama.qatiy:
            log.error("qat'iy rejim: %d xato qator topildi", len(birlashgan.xatolar))
            return 1

    if args.format == "json":
        if args.chiqish:
            json_yoz(birlashgan, args.chiqish)
        else:
            print(json.dumps(xulosa(birlashgan), ensure_ascii=False, indent=2))
    elif args.format == "csv":
        if not args.chiqish:
            log.error("--format csv uchun --chiqish kerak")
            return 2
        csv_yoz(birlashgan, args.chiqish)
    else:
        matn = hisobot_matni(birlashgan)
        if args.chiqish:
            args.chiqish.write_text(matn + "\n", encoding="utf-8")
            log.info("hisobot yozildi: %s", args.chiqish)
        else:
            print(matn)

    return 1 if xatolar_bor else 0


if __name__ == "__main__":
    sys.exit(main())
matn2 qator
.venv/bin/python3 -m pip install --quiet -e .
.venv/bin/hisobot sinov_malumot.csv --jim
Chiqish (qaytish kodi 1, 37120 ms)
==============================================
              TALABALAR HISOBOTI              
==============================================

Jami qator      : 8
To'g'ri         : 6 (75.0%)
Rad etilgan     : 2

----------------------------------------------
GURUH BO'YICHA O'RTACHA
----------------------------------------------
  A     90.67  ####################
  B     83.00  ##################
  C     61.00  #############

----------------------------------------------
DARAJALAR
----------------------------------------------
  yaxshi         3  ( 50.0%)
  a'lo           2  ( 33.3%)
  qoniqarli      1  ( 16.7%)

Eng yuqori: Feruza — 95 (A guruhi)

----------------------------------------------
XATOLAR (2)
----------------------------------------------
     5-qator  Elyor        baho son emas: ''
     8-qator  Hasan        baho son emas: 'abc'

==============================================

Chiqish formati o'zgardi — demak 15-bobdagi testlar ham yangilanishi kerak. 8-bobdagi bilan bir xil holat: xulq ongli o'zgardi, test unga ergashadi.

matn1 qator
.venv/bin/python3 -m pytest tests/test_cli.py -q 2>&1 | tail -6
Chiqish (qaytish kodi 0, 5075 ms)
E        +      where readouterr = <_pytest.capture.CaptureFixture object at 0x7fb028126590>.readouterr

tests/test_cli.py:60: AssertionError
=========================== short test summary info ============================
FAILED tests/test_cli.py::test_toza_fayl_nol_qaytaradi - assert "to'g'ri: 2" ...
FAILED tests/test_cli.py::test_bir_nechta_fayl - assert 'jami: 4' in "=======...

shu faylni yaratingtests/test_cli.py

python75 qator
import json

import pytest

from hisobot.cli import main

TOGRI = "ism,guruh,baho,sana\nAziz,A,85,2026-09-01\nBobur,B,95,2026-09-02\n"
XATOLI = "ism,guruh,baho,sana\nAziz,A,abc,2026-09-01\n"


def _fayl(papka, nom, matn):
    y = papka / nom
    y.write_text(matn, encoding="utf-8")
    return str(y)


def test_toza_fayl_nol_qaytaradi(tmp_path, capsys):
    kod = main([_fayl(tmp_path, "a.csv", TOGRI), "--jim"])
    assert kod == 0
    chiqish = capsys.readouterr().out
    assert "To'g'ri         : 2" in chiqish
    assert "TALABALAR HISOBOTI" in chiqish


def test_xatoli_fayl_bir_qaytaradi(tmp_path):
    assert main([_fayl(tmp_path, "b.csv", XATOLI), "--jim"]) == 1


def test_fayl_yoq_bir_qaytaradi(tmp_path):
    assert main([str(tmp_path / "yoq.csv"), "--jim"]) == 1


def test_json_format(tmp_path, capsys):
    main([_fayl(tmp_path, "c.csv", TOGRI), "--format", "json", "--jim"])
    malumot = json.loads(capsys.readouterr().out)
    assert malumot["togri"] == 2
    assert malumot["guruh_ortachasi"] == {"A": 85.0, "B": 95.0}


def test_json_faylga(tmp_path):
    chiqish = tmp_path / "n.json"
    main([_fayl(tmp_path, "d.csv", TOGRI), "--format", "json", "--chiqish", str(chiqish), "--jim"])
    assert json.loads(chiqish.read_text(encoding="utf-8"))["togri"] == 2


def test_matn_faylga(tmp_path):
    """Yangi xulq: --format matn ham faylga yozila oladi."""
    chiqish = tmp_path / "n.txt"
    main([_fayl(tmp_path, "d2.csv", TOGRI), "--chiqish", str(chiqish), "--jim"])
    assert "TALABALAR HISOBOTI" in chiqish.read_text(encoding="utf-8")


def test_csv_chiqishsiz_ikki_qaytaradi(tmp_path):
    assert main([_fayl(tmp_path, "e.csv", TOGRI), "--format", "csv", "--jim"]) == 2


def test_max_baho_cheklovi(tmp_path):
    """85 va 95 — 5 balli tizimda ikkalasi ham xato."""
    assert main([_fayl(tmp_path, "f.csv", TOGRI), "--max-baho", "5", "--jim"]) == 1


def test_notogri_max_baho(tmp_path):
    assert main([_fayl(tmp_path, "g.csv", TOGRI), "--max-baho", "-5", "--jim"]) == 2


def test_bir_nechta_fayl(tmp_path, capsys):
    kod = main([_fayl(tmp_path, "h1.csv", TOGRI), _fayl(tmp_path, "h2.csv", TOGRI), "--jim"])
    assert kod == 0
    assert "Jami qator      : 4" in capsys.readouterr().out


def test_notogri_argument_sistemexit(tmp_path):
    with pytest.raises(SystemExit) as e:
        main([_fayl(tmp_path, "i.csv", TOGRI), "--format", "xml"])
    assert e.value.code == 2
matn1 qator
.venv/bin/python3 -m pytest tests/test_cli.py -q
Chiqish (qaytish kodi 0, 2157 ms)
...........                                                              [100%]

Katta ma'lumotda sinaymiz

python14 qator
.venv/bin/python3 -c "
import csv, random
random.seed(7)
guruhlar = ['A', 'B', 'C', 'D']
with open('katta_sinov.csv', 'w', encoding='utf-8', newline='') as f:
    w = csv.writer(f)
    w.writerow(['ism', 'guruh', 'baho', 'sana'])
    for i in range(5000):
        baho = random.choice([str(random.randint(40, 100))] * 18 + ['abc', '', '150'])
        sana = random.choice(['2026-09-01'] * 19 + ['01.09.2026'])
        w.writerow([f'Talaba{i:04d}', random.choice(guruhlar), baho, sana])
print('yasaldi: katta_sinov.csv')
"
wc -l katta_sinov.csv
Chiqish (qaytish kodi 0, 83 ms)
yasaldi: katta_sinov.csv
5001 katta_sinov.csv
matn1 qator
time .venv/bin/hisobot katta_sinov.csv --jim
Chiqish (qaytish kodi 1, 270 ms)
==============================================
              TALABALAR HISOBOTI              
==============================================

Jami qator      : 5000
To'g'ri         : 4107 (82.1%)
Rad etilgan     : 893

----------------------------------------------
GURUH BO'YICHA O'RTACHA
----------------------------------------------
  A     69.78  ###################
  B     69.22  ###################
  C     69.98  ####################
  D     69.49  ###################

----------------------------------------------
DARAJALAR
----------------------------------------------
  qoniqarsiz   1381  ( 33.6%)
  qoniqarli    1049  ( 25.5%)
  yaxshi       962  ( 23.4%)
  a'lo         715  ( 17.4%)

Eng yuqori: Talaba0024 — 100 (A guruhi)

----------------------------------------------
XATOLAR (893)
----------------------------------------------
     7-qator  Talaba0005   baho son emas: 'abc'
     8-qator  Talaba0006   baho 0..100 oralig'ida bo'lishi kerak, berilgan: 150
     9-qator  Talaba0007   baho son emas: 'abc'
    13-qator  Talaba0011   baho son emas: 'abc'
    15-qator  Talaba0013   sana formati noto'g'ri (YYYY-MM-DD kutilgan): '01.09.2026'
    17-qator  Talaba0015   baho son emas: 'abc'
    19-qator  Talaba0017   baho son emas: 'abc'
    24-qator  Talaba0022   sana formati noto'g'ri (YYYY-MM-DD kutilgan): '01.09.2026'
    38-qator  Talaba0036   baho son emas: 'abc'
    40-qator  Talaba0038   sana formati noto'g'ri (YYYY-MM-DD kutilgan): '01.09.2026'
  ... va yana 883 ta

==============================================

real	0m0.267s
user	0m0.035s
sys	0m0.012s

5000 qator, jumladan ataylab buzilganlari. Dastur hammasini qayta ishladi, xatolarni sanadi va hisobot berdi.

Barcha formatlar

python9 qator
echo "=== JSON ==="
.venv/bin/hisobot katta_sinov.csv --format json --chiqish natija.json --jim
head -14 natija.json
echo ""
echo "=== CSV (birinchi 3 qator) ==="
.venv/bin/hisobot katta_sinov.csv --format csv --chiqish natija.csv --jim
head -3 natija.csv
wc -l natija.csv
rm -f natija.csv natija.json
Chiqish (qaytish kodi 0, 621 ms)
=== JSON ===
{
  "jami": 5000,
  "togri": 4107,
  "xato": 893,
  "guruh_ortachasi": {
    "A": 69.78,
    "B": 69.22,
    "C": 69.98,
    "D": 69.49
  },
  "darajalar": {
    "qoniqarsiz": 1381,
    "qoniqarli": 1049,
    "yaxshi": 962,

=== CSV (birinchi 3 qator) ===
ism,guruh,baho,sana,daraja
Talaba0000,A,60,2026-09-01,qoniqarli
Talaba0001,C,44,2026-09-01,qoniqarsiz
4108 natija.csv

Yakuniy sifat tekshiruvi

Bu bobda yangi kod yozdik (matn.py, yangilangan cli.py), shuning uchun avval uni formatlaymiz — 16-bobdagi qoida bo'yicha:

matn2 qator
ruff format src tests
ruff check --fix src tests
Chiqish (qaytish kodi 0, 84 ms)
2 files reformatted, 26 files left unchanged
All checks passed!
matn1 qator
./tekshir.sh
Chiqish (qaytish kodi 0, 5473 ms)
=== format ===
28 files already formatted
OK

=== linter ===
All checks passed!
OK

=== turlar ===
Success: no issues found in 14 source files
OK

=== testlar ===
........................................................................ [ 85%]
............                                                             [100%]
OK

HAMMASI O'TDI
python1 qator
.venv/bin/python3 -m pytest --cov=hisobot --cov-report=term-missing -q 2>&1 | tail -20
Chiqish (qaytish kodi 0, 7961 ms)
_______________ coverage: platform linux, python 3.10.12-final-0 _______________

Name                       Stmts   Miss  Cover   Missing
--------------------------------------------------------
src/hisobot/__init__.py       10      0   100%
src/hisobot/__main__.py        2      2     0%   3-5
src/hisobot/chiqarish.py      22      0   100%
src/hisobot/cli.py            82      3    96%   98-99, 110
src/hisobot/hisob.py          12      0   100%
src/hisobot/ko_p.py           22      0   100%
src/hisobot/matn.py           32      1    97%   40
src/hisobot/modellar.py       33      1    97%   21
src/hisobot/muhit.py          17     17     0%   3-26
src/hisobot/oqish.py          42      4    90%   25-28
src/hisobot/sozlama.py        39      1    97%   59
src/hisobot/tarmoq.py         30      4    87%   41-44
src/hisobot/tekshiruv.py      22      0   100%
src/hisobot/xatolar.py        13      4    69%   24-27
--------------------------------------------------------
TOTAL                        378     37    90%

Loyiha raqamlarda

python25 qator
import subprocess, sys
from pathlib import Path

ish = ISH
src = list((ish / "src").rglob("*.py"))
testlar = list((ish / "tests").rglob("*.py"))

def qatorlar(fayllar):
    return sum(len(p.read_text(encoding="utf-8").splitlines()) for p in fayllar)

import pandas as pd
show(pd.DataFrame([
    {"o'lchov": "Manba fayllari", "qiymat": len(src)},
    {"o'lchov": "Manba qatorlari", "qiymat": qatorlar(src)},
    {"o'lchov": "Test fayllari", "qiymat": len(testlar)},
    {"o'lchov": "Test qatorlari", "qiymat": qatorlar(testlar)},
    {"o'lchov": "Test / manba nisbati", "qiymat": f"{qatorlar(testlar) / max(qatorlar(src), 1):.2f}"},
    {"o'lchov": "Eng katta fayl, qator", "qiymat": max(len(p.read_text(encoding='utf-8').splitlines()) for p in src)},
]))
print("\nmodullar va vazifalari:")
for p in sorted(src):
    if p.name == "__init__.py":
        continue
    birinchi = p.read_text(encoding="utf-8").splitlines()[0].strip('"')
    print(f"  {p.name:16} {birinchi[:58]}")
Chiqish
modullar va vazifalari:
  __main__.py      python3 -m hisobot bilan ishga tushadi.
  chiqarish.py     Natijani turli formatlarda yozish.
  cli.py           Buyruq qatori interfeysi.
  hisob.py         Statistika: kiritish/chiqarishsiz, faqat hisob.
  ko_p.py          Bir nechta CSV faylni parallel qayta ishlash.
  matn.py          Odam o'qiydigan hisobot matni.
  modellar.py      Ma'lumot tuzilmalari.
  muhit.py         Ixtiyoriy .env faylini o'qish. Faqat lokal qulaylik uchun.
  oqish.py         CSV o'qish va qatorlarni tekshirib modelga aylantirish.
  sozlama.py       Konfiguratsiya: muhit o'zgaruvchilaridan o'qiladi, sukut q
  tarmoq.py        HTTP so'rovlari: timeout, qayta urinish, backoff.
  tekshiruv.py     Qoidalar: qator to'g'rimi yoki yo'q.
  xatolar.py       Loyihaning istisno oilasi.
o'lchovqiymat
Manba fayllari14
Manba qatorlari620
Test fayllari14
Test qatorlari523
Test / manba nisbati0.84
Eng katta fayl, qator123

Test kodi manba kodidan ko'proq — bu normal va yaxshi belgi.

Eng katta fayl ham 150 qatordan oshmadi. Bu tasodif emas: har modul bitta ish qiladi.

Nimani boshqacha qilish mumkin edi

Halol bo'lish kerak — loyihada yechilmagan narsalar bor:

python13 qator
import pandas as pd
show(pd.DataFrame([
    {"cheklov": "oqi() butun faylni xotiraga o'qiydi",
     "qachon muammo": "1 GB dan katta fayl", "yechim": "generator (14-bob mashqi)"},
    {"cheklov": "Faqat CSV formati",
     "qachon muammo": "Excel yoki Parquet kerak bo'lsa", "yechim": "o'qish qatlamini interfeysga ajratish"},
    {"cheklov": "tarmoq.py ishlatilmayapti",
     "qachon muammo": "Ma'lumot API dan kelsa", "yechim": "oqish.py ga manba tanlash qo'shish"},
    {"cheklov": "Sana faqat YYYY-MM-DD",
     "qachon muammo": "Boshqa formatdagi fayl", "yechim": "sozlamaga format qo'shish"},
    {"cheklov": "Til qattiq yozilgan (o'zbekcha)",
     "qachon muammo": "Boshqa til kerak bo'lsa", "yechim": "gettext yoki xabarlar lug'ati"},
]))
cheklovqachon muammoyechim
oqi() butun faylni xotiraga o'qiydi1 GB dan katta faylgenerator (14-bob mashqi)
Faqat CSV formatiExcel yoki Parquet kerak bo'lsao'qish qatlamini interfeysga ajratish
tarmoq.py ishlatilmayaptiMa'lumot API dan kelsaoqish.py ga manba tanlash qo'shish
Sana faqat YYYY-MM-DDBoshqa formatdagi faylsozlamaga format qo'shish
Til qattiq yozilgan (o'zbekcha)Boshqa til kerak bo'lsagettext yoki xabarlar lug'ati

Bularning hech biri hozir muammo emas. Ularni oldindan yechish 1-bobdagi qoidaga zid bo'lardi: "kelajakda kerak bo'lishi mumkin" — bu sabab emas.

Muhimi shundaki, ular yechilganda qayerga tegish kerakligi ma'lum. Tuzilish shu uchun kerak.

Keyingi qadam

python9 qator
import pandas as pd
show(pd.DataFrame([
    {"mavzu": "Ma'lumotlar bazasi", "nima o'rganish": "SQLAlchemy, migratsiyalar (alembic)"},
    {"mavzu": "Veb API", "nima o'rganish": "FastAPI, pydantic, async"},
    {"mavzu": "Konteyner", "nima o'rganish": "Docker, ko'p bosqichli qurish"},
    {"mavzu": "Fon vazifalari", "nima o'rganish": "Celery yoki RQ, navbatlar"},
    {"mavzu": "Kuzatuv", "nima o'rganish": "Tuzilgan jurnal, metrikalar, Sentry"},
    {"mavzu": "MLOps", "nima o'rganish": "Model versiyalash, tajribalarni kuzatish (keyingi kitob)"},
]))
mavzunima o'rganish
Ma'lumotlar bazasiSQLAlchemy, migratsiyalar (alembic)
Veb APIFastAPI, pydantic, async
KonteynerDocker, ko'p bosqichli qurish
Fon vazifalariCelery yoki RQ, navbatlar
KuzatuvTuzilgan jurnal, metrikalar, Sentry
MLOpsModel versiyalash, tajribalarni kuzatish (keyingi kitob)

Xulosa

Fikr Mazmuni
Ajratish Har modul bitta ish qiladi; eng katta fayl 150 qatordan kam
Tekshiruv Fayl chegarasida qat'iy, ichkarida ishonch
Xatolar To'planadi yoki otiladi — ongli tanlov
Sozlama Muhitdan, erta tekshiriladi
Testlar Manba kodidan ko'proq — bu normal
Avtomatlashtirish Bitta ./tekshir.sh hammasini tekshiradi
Halollik Yechilmagan cheklovlarni biling va yozib qo'ying

Talaba ishi

  1. hisobot_matni uchun test yozing: bo'sh Natija bilan yiqilmasligini tekshiring.
  2. --format markdown qo'shing va matn.py ga markdown_matni yozing.
  3. katta_sinov.csv ni 500 000 qatorga kattalashtiring. Qaysi qadam eng sekin bo'ladi? tracemalloc bilan xotirani o'lchang.
  4. Loyihani o'zingizning ma'lumotingizga moslang: ustun nomlarini va qoidalarni o'zgartiring. Nechta faylga tegish kerak bo'ldi? Bu yaxshi ko'rsatkichmi?

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar