20-bo‘lim
Amaliy loyiha - Xarajatlar hisoblagichi
Barcha o'rganilgan bilimlarni birlashtirib to'liq ishlaydigan dastur yaratamiz: OOP, SQLite, xatolarni boshqarish, hisobotlar va CSV eksport.
Ushbu bo‘lim mundarijasi
Tabriklaymiz - siz 19 ta bo'limni yakunladingiz! Endi barcha bilimlarni bitta to'liq ishlaydigan dasturga birlashtiramiz.
Loyiha haqida #
Xarajatlar hisoblagichi - shaxsiy moliyani kuzatish dasturi. U quyidagilarni bajaradi:
- Xarajat va daromadlarni SQLite bazasiga yozadi
- Toifalar bo'yicha guruhlaydi
- Oylik hisobot va matnli diagramma chiqaradi
- Ma'lumotni CSV faylga eksport qiladi
- Barcha xatolarni to'g'ri boshqaradi
1-qadam: modellar.py #
# modellar.py
"""Loyiha ma'lumot modellari."""
from dataclasses import dataclass, field
from datetime import date
from enum import Enum
class AmalTuri(Enum):
"""Amal turi: daromad yoki xarajat."""
DAROMAD = "daromad"
XARAJAT = "xarajat"
TOIFALAR = {
AmalTuri.XARAJAT: [
"Oziq-ovqat", "Transport", "Kommunal", "Ta'lim",
"Sog'liq", "Kiyim", "Ko'ngilochar", "Boshqa",
],
AmalTuri.DAROMAD: ["Oylik", "Qo'shimcha ish", "Sovg'a", "Boshqa"],
}
@dataclass
class Amal:
"""Bitta moliyaviy amal."""
turi: AmalTuri
toifa: str
summa: int
izoh: str = ""
sana: date = field(default_factory=date.today)
id: int | None = None
def __post_init__(self):
"""Obyekt yaratilgandan keyin tekshiruv."""
if self.summa <= 0:
raise ValueError("Summa musbat son bo'lishi kerak")
if self.toifa not in TOIFALAR[self.turi]:
ruxsat = ", ".join(TOIFALAR[self.turi])
raise ValueError(f"Noto'g'ri toifa. Ruxsat etilgan: {ruxsat}")
@property
def belgili_summa(self) -> int:
"""Daromad musbat, xarajat manfiy ko'rinishda."""
return self.summa if self.turi == AmalTuri.DAROMAD else -self.summa
def __str__(self) -> str:
belgi = "+" if self.turi == AmalTuri.DAROMAD else "-"
return (f"{self.sana.strftime('%d.%m.%Y')} {self.toifa:<14}"
f"{belgi}{self.summa:>12,} so'm {self.izoh}")
Enum nima?Enum - cheklangan qiymatlar to'plami. AmalTuri.XARAJAT yozganingizda muharrir sizga variantlarni ko'rsatadi va "xarjat" kabi imlo xatosi darhol xato beradi. Oddiy satrlar bilan bunday himoya yo'q.
2-qadam: baza.py #
# baza.py
"""SQLite bazasi bilan ishlash qatlami."""
import sqlite3
from contextlib import contextmanager
from datetime import date
from pathlib import Path
from modellar import Amal, AmalTuri
BAZA_YOLI = Path("xarajatlar.db")
@contextmanager
def ulanish():
"""Bazaga ulanadi va yakunda albatta yopadi."""
aloqa = sqlite3.connect(BAZA_YOLI)
aloqa.row_factory = sqlite3.Row
try:
yield aloqa
aloqa.commit()
except Exception:
aloqa.rollback()
raise
finally:
aloqa.close()
def bazani_tayyorla() -> None:
"""Jadval mavjud bo'lmasa yaratadi."""
with ulanish() as aloqa:
aloqa.execute("""
CREATE TABLE IF NOT EXISTS amallar (
id INTEGER PRIMARY KEY AUTOINCREMENT,
turi TEXT NOT NULL,
toifa TEXT NOT NULL,
summa INTEGER NOT NULL,
izoh TEXT DEFAULT '',
sana TEXT NOT NULL
)
""")
aloqa.execute("CREATE INDEX IF NOT EXISTS idx_sana ON amallar (sana)")
def amal_qosh(amal: Amal) -> int:
"""Yangi amalni bazaga yozadi va uning id sini qaytaradi."""
with ulanish() as aloqa:
kursor = aloqa.execute(
"INSERT INTO amallar (turi, toifa, summa, izoh, sana) VALUES (?, ?, ?, ?, ?)",
(amal.turi.value, amal.toifa, amal.summa, amal.izoh, amal.sana.isoformat()),
)
return kursor.lastrowid
def amallarni_ol(oy: int | None = None, yil: int | None = None) -> list[Amal]:
"""Amallarni o'qiydi, oy va yil bo'yicha filtrlash mumkin."""
sorov = "SELECT * FROM amallar"
parametrlar = []
if oy and yil:
sorov += " WHERE strftime('%m', sana) = ? AND strftime('%Y', sana) = ?"
parametrlar = [f"{oy:02d}", str(yil)]
sorov += " ORDER BY sana DESC, id DESC"
with ulanish() as aloqa:
qatorlar = aloqa.execute(sorov, parametrlar).fetchall()
return [
Amal(
id=qator["id"],
turi=AmalTuri(qator["turi"]),
toifa=qator["toifa"],
summa=qator["summa"],
izoh=qator["izoh"],
sana=date.fromisoformat(qator["sana"]),
)
for qator in qatorlar
]
def amalni_ochir(amal_id: int) -> bool:
"""Amalni o'chiradi, muvaffaqiyat holatini qaytaradi."""
with ulanish() as aloqa:
kursor = aloqa.execute("DELETE FROM amallar WHERE id = ?", (amal_id,))
return kursor.rowcount > 0
3-qadam: hisobot.py #
# hisobot.py
"""Statistika, diagramma va eksport."""
import csv
from collections import defaultdict
from pathlib import Path
from modellar import Amal, AmalTuri
KENGLIK = 58
def sarlavha(matn: str) -> None:
print("=" * KENGLIK)
print(f"{matn:^{KENGLIK}}")
print("=" * KENGLIK)
def umumiy_hisobot(amallar: list[Amal]) -> None:
"""Daromad, xarajat va qoldiqni chiqaradi."""
daromad = sum(a.summa for a in amallar if a.turi == AmalTuri.DAROMAD)
xarajat = sum(a.summa for a in amallar if a.turi == AmalTuri.XARAJAT)
qoldiq = daromad - xarajat
sarlavha("UMUMIY HISOBOT")
print(f"{'Jami daromad:':<32}{daromad:>18,} so'm")
print(f"{'Jami xarajat:':<32}{xarajat:>18,} so'm")
print("-" * KENGLIK)
print(f"{'QOLDIQ:':<32}{qoldiq:>18,} so'm")
if daromad > 0:
foiz = xarajat / daromad * 100
print(f"{'Xarajat ulushi:':<32}{foiz:>17.1f} %")
if foiz > 90:
print("\n ! Diqqat: daromadingizning deyarli hammasi sarflanmoqda.")
elif foiz < 60:
print("\n + Ajoyib! Daromadingizning katta qismini tejayapsiz.")
print("=" * KENGLIK)
def toifalar_diagrammasi(amallar: list[Amal]) -> None:
"""Toifalar bo'yicha matnli ustunli diagramma chizadi."""
xarajatlar = defaultdict(int)
for amal in amallar:
if amal.turi == AmalTuri.XARAJAT:
xarajatlar[amal.toifa] += amal.summa
if not xarajatlar:
print("\nXarajatlar mavjud emas.")
return
jami = sum(xarajatlar.values())
eng_katta = max(xarajatlar.values())
print()
sarlavha("TOIFALAR BO'YICHA XARAJATLAR")
for toifa, summa in sorted(xarajatlar.items(), key=lambda j: j[1], reverse=True):
foiz = summa / jami * 100
ustun = "#" * int(summa / eng_katta * 22)
print(f"{toifa:<14}{summa:>12,}{foiz:>7.1f}% {ustun}")
print("-" * KENGLIK)
print(f"{'JAMI':<14}{jami:>12,}{100.0:>7.1f}%")
print("=" * KENGLIK)
def csv_eksport(amallar: list[Amal], yol: str = "hisobot.csv") -> Path:
"""Amallarni CSV faylga yozadi."""
fayl_yoli = Path(yol)
with open(fayl_yoli, "w", encoding="utf-8", newline="") as fayl:
yozuvchi = csv.writer(fayl)
yozuvchi.writerow(["Sana", "Turi", "Toifa", "Summa", "Izoh"])
for amal in amallar:
yozuvchi.writerow([
amal.sana.isoformat(),
amal.turi.value,
amal.toifa,
amal.summa,
amal.izoh,
])
return fayl_yoli.resolve()
4-qadam: asosiy.py #
# asosiy.py
"""Xarajatlar hisoblagichi - asosiy menyu."""
from datetime import date
import baza
import hisobot
from modellar import Amal, AmalTuri, TOIFALAR
def son_sora(xabar: str, eng_kam: int = 1) -> int:
"""Foydalanuvchidan to'g'ri son kiritilgunicha so'raydi."""
while True:
try:
qiymat = int(input(xabar).replace(" ", "").replace(",", ""))
if qiymat < eng_kam:
print(f" ! Qiymat {eng_kam} dan kichik bo'lmasin.")
continue
return qiymat
except ValueError:
print(" ! Faqat butun son kiriting.")
def toifa_tanla(turi: AmalTuri) -> str:
"""Toifalar ro'yxatidan bittasini tanlatadi."""
toifalar = TOIFALAR[turi]
print("\nToifani tanlang:")
for tartib, toifa in enumerate(toifalar, start=1):
print(f" {tartib}. {toifa}")
tanlov = son_sora("Raqam: ", eng_kam=1)
while tanlov > len(toifalar):
print(f" ! 1 dan {len(toifalar)} gacha son kiriting.")
tanlov = son_sora("Raqam: ", eng_kam=1)
return toifalar[tanlov - 1]
def amal_qoshish(turi: AmalTuri) -> None:
"""Yangi daromad yoki xarajat qo'shadi."""
nomi = "daromad" if turi == AmalTuri.DAROMAD else "xarajat"
print(f"\n--- Yangi {nomi} ---")
toifa = toifa_tanla(turi)
summa = son_sora("Summa (so'm): ")
izoh = input("Izoh (ixtiyoriy): ").strip()
try:
amal = Amal(turi=turi, toifa=toifa, summa=summa, izoh=izoh)
yangi_id = baza.amal_qosh(amal)
print(f"\n + Saqlandi (#{yangi_id}): {amal}")
except ValueError as xato:
print(f"\n ! Xato: {xato}")
def amallarni_korish() -> None:
"""Barcha amallarni jadval ko'rinishida chiqaradi."""
amallar = baza.amallarni_ol()
if not amallar:
print("\nHali birorta amal kiritilmagan.")
return
print()
hisobot.sarlavha("BARCHA AMALLAR")
for amal in amallar[:20]:
print(f"#{amal.id:<4}{amal}")
if len(amallar) > 20:
print(f"\n ... va yana {len(amallar) - 20} ta amal")
print("=" * hisobot.KENGLIK)
def oylik_hisobot() -> None:
"""Tanlangan oy bo'yicha hisobot."""
bugun = date.today()
oy = son_sora(f"Oy raqami (1-12, standart {bugun.month}): ", eng_kam=1)
yil = son_sora(f"Yil (standart {bugun.year}): ", eng_kam=2000)
amallar = baza.amallarni_ol(oy=oy, yil=yil)
if not amallar:
print(f"\n{oy:02d}.{yil} uchun ma'lumot topilmadi.")
return
print(f"\n>>> {oy:02d}.{yil} hisoboti ({len(amallar)} ta amal)\n")
hisobot.umumiy_hisobot(amallar)
hisobot.toifalar_diagrammasi(amallar)
def eksport() -> None:
"""Ma'lumotni CSV ga chiqaradi."""
amallar = baza.amallarni_ol()
if not amallar:
print("\nEksport qilish uchun ma'lumot yo'q.")
return
yol = hisobot.csv_eksport(amallar)
print(f"\n + {len(amallar)} ta amal eksport qilindi:\n {yol}")
def menyu() -> None:
"""Asosiy menyu sikli."""
baza.bazani_tayyorla()
amallar_xaritasi = {
"1": lambda: amal_qoshish(AmalTuri.XARAJAT),
"2": lambda: amal_qoshish(AmalTuri.DAROMAD),
"3": amallarni_korish,
"4": lambda: hisobot.umumiy_hisobot(baza.amallarni_ol()),
"5": lambda: hisobot.toifalar_diagrammasi(baza.amallarni_ol()),
"6": oylik_hisobot,
"7": eksport,
}
while True:
print("\n" + "=" * hisobot.KENGLIK)
print(f"{'XARAJATLAR HISOBLAGICHI':^{hisobot.KENGLIK}}")
print("=" * hisobot.KENGLIK)
print(" 1 - Xarajat qo'shish")
print(" 2 - Daromad qo'shish")
print(" 3 - Barcha amallar")
print(" 4 - Umumiy hisobot")
print(" 5 - Toifalar diagrammasi")
print(" 6 - Oylik hisobot")
print(" 7 - CSV ga eksport")
print(" 0 - Chiqish")
tanlov = input("\nTanlovingiz: ").strip()
if tanlov == "0":
print("\nXayr! Moliyangiz nazoratda bo'lsin.")
break
amal = amallar_xaritasi.get(tanlov)
if amal is None:
print(" ! Noto'g'ri tanlov.")
continue
try:
amal()
except Exception as xato:
print(f"\n ! Kutilmagan xato: {xato}")
if __name__ == "__main__":
try:
menyu()
except KeyboardInterrupt:
print("\n\nDastur foydalanuvchi tomonidan to'xtatildi.")
Dasturni ishga tushirish #
python asosiy.py
==========================================================
XARAJATLAR HISOBLAGICHI
==========================================================
1 - Xarajat qo'shish
2 - Daromad qo'shish
3 - Barcha amallar
4 - Umumiy hisobot
5 - Toifalar diagrammasi
6 - Oylik hisobot
7 - CSV ga eksport
0 - Chiqish
Tanlovingiz: 5
==========================================================
TOIFALAR BO'YICHA XARAJATLAR
==========================================================
Oziq-ovqat 1,850,000 41.1% ######################
Transport 720,000 16.0% ########
Kommunal 640,000 14.2% #######
Ta'lim 500,000 11.1% ######
Ko'ngilochar 380,000 8.4% ####
Kiyim 260,000 5.8% ###
Sog'liq 150,000 3.3% #
----------------------------------------------------------
JAMI 4,500,000 100.0%
==========================================================
==========================================================
UMUMIY HISOBOT
==========================================================
Jami daromad: 7,000,000 so'm
Jami xarajat: 4,500,000 so'm
----------------------------------------------------------
QOLDIQ: 2,500,000 so'm
Xarajat ulushi: 64.3 %
==========================================================
Loyihada nima ishlatildi? #
| Bo'lim | Mavzu | Loyihada qayerda |
|---|---|---|
| 3-4 | Sintaksis, turlar | Butun loyiha bo'ylab |
| 5 | Operatorlar | Foiz va qoldiq hisoblash |
| 6 | Satrlar, f-satrlar | Jadval formatlash |
| 7-8 | Ro'yxat, lug'at | TOIFALAR, defaultdict |
| 9-10 | Shartlar, sikllar | Menyu sikli, validatsiya |
| 11 | Funksiyalar | Barcha modullar |
| 12 | Modullar | 4 ta faylga bo'lish |
| 13 | Fayllar, CSV | Eksport funksiyasi |
| 14 | Xatoliklar | try/except, __post_init__ |
| 15-16 | OOP | Amal klassi, Enum |
| 17 | Ilg'or | @contextmanager, @property, comprehension |
| 18 | Standart kutubxona | collections, pathlib, datetime |
| 19 | Ma'lumotlar bazasi | SQLite qatlami |
Dasturni o'zingiz rivojlantiring:
- Amalni tahrirlash - mavjud yozuvni o'zgartirish imkoniyati.
- Byudjet limiti - har bir toifa uchun oylik limit belgilash va oshib ketganda ogohlantirish.
- Qidiruv - izoh bo'yicha matnli qidiruv (
LIKE ?SQL bilan). - Grafik -
matplotlibbilan haqiqiy doiraviy diagramma chizish. - Testlar -
unittestbilanmodellar.pyvahisobot.pyuchun testlar yozing. - Valyuta - USD kursini API dan olib, summani ikkala valyutada ko'rsatish.
Bundan keyin nima qilish kerak? #
Darslik o'qish - bu boshlanishi. Haqiqiy o'rganish o'z loyihangizni yozganingizda boshlanadi.
Kichik boshlang: telefon kitobi, ob-havo boti, fayllarni saralovchi skript. Har bir loyihada yangi narsa duch keladi va aynan shu paytda bilim mustahkamlanadi. Kod yozmasdan o'qilgan 100 ta darslik - yozilgan 1 ta loyihadan kamroq foyda beradi.
Yakuniy xulosa #
Siz 20 ta bo'limda quyidagilarni o'rgandingiz:
- Python sintaksisi, ma'lumot turlari va operatorlar
- Ro'yxat, kortej, lug'at va to'plamlar bilan ishlash
- Shartlar, sikllar va funksiyalar
- Modullar, paketlar va virtual muhit
- Fayllar, CSV va JSON formatlari
- Xatoliklarni professional boshqarish
- Obyektga yo'naltirilgan dasturlashning barcha tamoyillari
- Generatorlar, dekoratorlar va kontekst menejerlar
- Standart kutubxonaning eng kerakli modullari
- Ma'lumotlar bazasi, API va testlar
Bu - boshlang'ich dasturchi uchun to'liq bilimlar to'plami. Endi siz haqiqiy loyihalar yozishga tayyorsiz.
Omad tilaymiz! Savol yoki taklifingiz bo'lsa, har bir sahifa pastidagi tugma orqali bizga yozing.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.