Files

117 lines
4.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Bellek içi (in-memory) görev deposu.
Veritabanı yok; görevler süreç belleğinde bir dict içinde tutulur.
Sunucu yeniden başlarsa veriler sıfırlanır — bu proje için beklenen
davranış (bilinçli tercih, README'de de belirtildi).
Neden dict + Lock?
- dict ile id -> Task eşlemesi O(1) arama sağlar (listede O(n) tarama gerekirdi).
- WebSocket log döngüsü ile HTTP istekleri AYNI ANDA çalışabildiği için
paylaşılan listeye eşzamanlı erişim olur; threading.Lock bunu güvene alır.
"""
from __future__ import annotations
import threading
from datetime import datetime, timezone
from typing import Dict, List, Optional
from .models import Task, TaskCreate, TaskStatus, TaskUpdate
def _utcnow() -> datetime:
"""Zaman damgası için tek merkez: hep UTC döner."""
return datetime.now(timezone.utc)
class TaskStore:
"""Görevlerin tutulduğu bellek içi depo (CRUD operasyonları)."""
def __init__(self) -> None:
# id -> Task eşlemesi. Sıralamayı ayrıca tutmuyoruz; listelerken
# created_at'e göre sıralıyoruz (en yeni en üstte).
self._items: Dict[str, Task] = {}
self._lock = threading.Lock()
# ------------------------------------------------------------------ #
# Okuma işlemleri
# ------------------------------------------------------------------ #
def list_all(self) -> List[Task]:
"""Tüm görevleri en yeni en üstte olacak şekilde döndürür."""
with self._lock:
gorevler = list(self._items.values())
# created_at ISO/aware datetime olduğu için doğrudan karşılaştırılabilir
gorevler.sort(key=lambda t: t.created_at, reverse=True)
return gorevler
def get(self, task_id: str) -> Optional[Task]:
"""Verilen id'ye ait görevi döndürür; yoksa None."""
with self._lock:
return self._items.get(task_id)
def count(self) -> int:
"""Toplam görev sayısı (WebSocket loglarında kullanılıyor)."""
with self._lock:
return len(self._items)
def count_by_status(self) -> Dict[str, int]:
"""Durum bazında görev sayısı — paneldeki istatistik için."""
with self._lock:
degerler = list(self._items.values())
sonuc = {s.value: 0 for s in TaskStatus}
for gorev in degerler:
sonuc[gorev.status.value] += 1
return sonuc
# ------------------------------------------------------------------ #
# Yazma işlemleri
# ------------------------------------------------------------------ #
def create(self, payload: TaskCreate) -> Task:
"""Yeni görev oluşturur (id ve created_at otomatik üretilir)."""
gorev = Task(title=payload.title, status=payload.status)
with self._lock:
self._items[gorev.id] = gorev
return gorev
def update(self, task_id: str, payload: TaskUpdate) -> Optional[Task]:
"""Var olan görevi günceller.
Dönen değer:
- Task : güncelleme başarılı
- None : bu id'de görev yok (router bunu 404'e çevirir)
"""
with self._lock:
mevcut = self._items.get(task_id)
if mevcut is None:
return None
# Yalnızca gönderilen alanları değiştir (PATCH benzeri davranış)
guncel = mevcut.model_copy(deep=True)
if payload.title is not None:
guncel.title = payload.title
if payload.status is not None:
guncel.status = payload.status
self._items[task_id] = guncel
return guncel
def delete(self, task_id: str) -> Optional[Task]:
"""Görevi siler.
Dönen değer:
- Task : silinen görev (istemciye bilgi amaçlı döner)
- None : bu id'de görev yok -> 404
"""
with self._lock:
return self._items.pop(task_id, None)
def clear(self) -> None:
"""Tüm görevleri temizler — testler arası izolasyon için."""
with self._lock:
self._items.clear()
# Uygulama genelinde TEK depo örneği. Router'lar ve WebSocket döngüsü
# hep bu nesneyi paylaşır; bu yüzden Lock kullanımı zorunluydu.
store = TaskStore()