Archives
Trending
Support
Login
clear text
XML
Django
JavaScript
MATLAB
C
C++
C#
Python
SQL
Shell
Bash
Markdown
YAML
JSON
HTML
CSS
PHP
Java
Ruby
Go
Rust
Swift
Kotlin
Arduino
TypeScript
Perl
Autohotkey
Lua
SQF
R
Scala
Haskell
Groovy
Dart
Clojure
VB.NET
Objective-C
PowerShell
Bash
CoffeeScript
Verilog
from abc import ABC, abstractmethod from datetime import datetime, timedelta from enum import Enum import uuid import pickle import os from observer import Observable # ============================================================================== # 1. FINITE STATE MACHINE (FSM) IMPLEMENTATION # ============================================================================== class TransactionContext: """ Context untuk FSM Transaksi. Objek ini akan mengubah perilakunya berdasarkan state saat ini. """ def __init__(self, member, item): self.transaksi_id = "TRX-" + str(uuid.uuid4())[:8].upper() self.member = member self.item = item self.tgl_pinjam = datetime.now() self.tgl_kembali_rencana = self.tgl_pinjam + timedelta(days=14) self.denda = 0.0 self._state = ActiveState() def set_state(self, state): self._state = state def get_status_name(self): return self._state.get_name() def hitung_denda_saat_ini(self): return self._state.calculate_fine(self) def proses_pengembalian(self): self._state.return_item(self) def to_dict(self): return { "id": self.transaksi_id, "item": self.item.judul, "nama_member": self.member.nama, "tgl_pinjam": self.tgl_pinjam.strftime('%Y-%m-%d'), "tgl_kembali": self.tgl_kembali_rencana.strftime('%Y-%m-%d'), "status": self.get_status_name(), "denda": f"Rp{self.hitung_denda_saat_ini():,.2f}" } class TransactionState(ABC): @abstractmethod def get_name(self): pass @abstractmethod def calculate_fine(self, context): pass @abstractmethod def return_item(self, context): pass class ActiveState(TransactionState): def get_name(self): return "Aktif" def calculate_fine(self, context): if datetime.now() > context.tgl_kembali_rencana: print(f"INFO FSM: Transisi State {context.transaksi_id} [Active -> Overdue]") context.set_state(OverdueState()) return context.hitung_denda_saat_ini() return 0.0 def return_item(self, context): print(f"INFO FSM: Transisi State {context.transaksi_id} [Active -> Completed]") context.item.ubah_status(StatusItem.TERSEDIA) context.set_state(CompletedState()) class OverdueState(TransactionState): def get_name(self): return "Terlambat" def calculate_fine(self, context): selisih_hari = (datetime.now() - context.tgl_kembali_rencana).days return float(max(selisih_hari, 0) * 1000) def return_item(self, context): final_denda = self.calculate_fine(context) context.denda = final_denda print(f"INFO FSM: Transisi State {context.transaksi_id} [Overdue -> Completed] dengan denda {final_denda}") context.item.ubah_status(StatusItem.TERSEDIA) context.set_state(CompletedState()) class CompletedState(TransactionState): def get_name(self): return "Selesai" def calculate_fine(self, context): return context.denda def return_item(self, context): raise Exception("Item sudah dikembalikan sebelumnya.") class StatusItem(Enum): TERSEDIA = "Tersedia" DIPINJAM = "Dipinjam" class LoanableItem(ABC): def __init__(self, item_id, judul): self.item_id = item_id self.judul = judul self.status = StatusItem.TERSEDIA @abstractmethod def get_details(self): pass def ubah_status(self, status: StatusItem): self.status = status class Buku(LoanableItem): def __init__(self, item_id, judul, pengarang): super().__init__(item_id, judul) self.pengarang = pengarang def get_details(self): return {"Tipe": "Buku", "Pengarang": self.pengarang} class Majalah(LoanableItem): def __init__(self, item_id, judul, edisi): super().__init__(item_id, judul) self.edisi = edisi def get_details(self): return {"Tipe": "Majalah", "Edisi": self.edisi} class ItemFactory: @staticmethod def create_item(tipe, item_id, judul, **kwargs): if tipe.lower() == 'buku': return Buku(item_id, judul, kwargs.get('pengarang', '-')) elif tipe.lower() == 'majalah': return Majalah(item_id, judul, kwargs.get('edisi', '-')) raise ValueError("Tipe item tidak dikenal") class Member: def __init__(self, nama, alamat): self.member_id = "MEM-" + str(uuid.uuid4())[:8].upper() self.nama = nama self.alamat = alamat # --- OBSERVER PATTERN --- class Perpustakaan(Observable): # Warisi Observable def __init__(self, filepath="perpus_data.pkl"): super().__init__() # Init Observable self.filepath = filepath self.items = [] self.members = [] self.transactions = [] self.load_data() def add_item(self, item): self.items.append(item) # Notify View: Item bertambah self.notify_observers(event="items_changed", data=self.items) def add_member(self, member): self.members.append(member) # Notify View: Member bertambah self.notify_observers(event="members_changed", data=self.members) def create_transaction(self, member_id, item_id): member = next((m for m in self.members if m.member_id == member_id), None) item = next((i for i in self.items if i.item_id == item_id), None) if not member or not item: raise ValueError("Member atau Item tidak ditemukan") if item.status != StatusItem.TERSEDIA: raise ValueError("Item sedang dipinjam") trx = TransactionContext(member, item) self.transactions.append(trx) item.ubah_status(StatusItem.DIPINJAM) # Notify View: Transaksi baru & status item berubah self.notify_observers(event="transactions_changed", data=self.transactions) self.notify_observers(event="items_changed", data=self.items) return trx def return_transaction(self, trx_id): trx = next((t for t in self.transactions if t.transaksi_id == trx_id), None) if not trx: raise ValueError("Transaksi tidak ditemukan") trx.proses_pengembalian() # Notify View: Transaksi update & status item berubah self.notify_observers(event="transactions_changed", data=self.transactions) self.notify_observers(event="items_changed", data=self.items) return trx def refresh_data(self): """Method manual untuk memicu notifikasi semua data (misal saat startup).""" self.notify_observers(event="members_changed", data=self.members) self.notify_observers(event="items_changed", data=self.items) # Pastikan hitung denda terbaru sebelum notify for t in self.transactions: t.hitung_denda_saat_ini() self.notify_observers(event="transactions_changed", data=self.transactions) def save_data(self): data = {"items": self.items, "members": self.members, "transactions": self.transactions} with open(self.filepath, 'wb') as f: pickle.dump(data, f) def load_data(self): if os.path.exists(self.filepath): try: with open(self.filepath, 'rb') as f: data = pickle.load(f) self.items = data.get("items", []) self.members = data.get("members", []) self.transactions = data.get("transactions", []) except Exception as e: print(f"Error loading data: {e}")
Mark as private (unlisted)
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
everlasting (like CCCP)