Skip to content

Commit

Permalink
إضافة ميزة إدارة معدل الصرف بدون عملية تطبيقها على الزكاة
Browse files Browse the repository at this point in the history
  • Loading branch information
vzool committed Jun 25, 2024
1 parent 4bd8ffe commit c484da0
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 9 deletions.
20 changes: 16 additions & 4 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,26 @@ on:

jobs:
build:

runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
os: [ubuntu-18.04, ubuntu-20.04, ubuntu-latest, macos-11, macos-12, macos-latest, windows-2016, windows-2019, windows-latest]

python-version: [
"3.10",
"3.11",
"3.12",
]
os: [
ubuntu-18.04,
ubuntu-20.04,
ubuntu-latest,
macos-11,
macos-12,
macos-latest,
windows-2016,
windows-2019,
windows-latest,
]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "zakat"
version = "0.2.2"
version = "0.2.3"
authors = [
{ name="Abdelaziz Elrashed Elshaikh Mohamed", email="[email protected]" },
]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
setup(
name='zakat',
packages=find_packages(include=['zakat']),
version='0.2.2',
version='0.2.3',
description='A Python Library for Islamic Financial Management.',
author='Abdelaziz Elrashed Elshaikh Mohamed',
install_requires=[],
Expand Down
128 changes: 125 additions & 3 deletions zakat/zakat_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class Action(Enum):
SUB = auto()
ADD_FILE = auto()
REMOVE_FILE = auto()
EXCHANGE = auto()
REPORT = auto()
ZAKAT = auto()

Expand Down Expand Up @@ -137,7 +138,7 @@ class ZakatTracker:
- history (dict):
- {timestamp} (list): A list of dictionaries storing the history of actions performed.
- {action_dict} (dict):
- action (Action): The type of action (CREATE, TRACK, LOG, SUB, ADD_FILE, REMOVE_FILE, REPORT, ZAKAT).
- action (Action): The type of action (CREATE, TRACK, LOG, SUB, ADD_FILE, REMOVE_FILE, EXCHANGE, REPORT, ZAKAT).
- account (str): The account number associated with the action.
- ref (int): The reference number of the transaction.
- file (int): The reference number of the file (if applicable).
Expand All @@ -152,9 +153,9 @@ class ZakatTracker:

# Hybrid Constants
ZakatCut = lambda x: 0.025*x # Zakat Cut in one Lunar Year
TimeCycle = lambda : int(60*60*24*354.367056*1e9) # Lunar Year in nanoseconds
TimeCycle = lambda days = 355: int(60*60*24*days*1e9) # Lunar Year in nanoseconds
Nisab = lambda x: 595*x # Silver Price in Local currency value
Version = lambda : '0.2.2'
Version = lambda : '0.2.3'

def __init__(self, db_path: str = "zakat.pickle", history_mode: bool = True):
"""
Expand Down Expand Up @@ -212,6 +213,7 @@ def reset(self) -> None:
"""
self._vault = {}
self._vault['account'] = {}
self._vault['exchange'] = {}
self._vault['history'] = {}
self._vault['lock'] = None
self._vault['report'] = {}
Expand Down Expand Up @@ -470,6 +472,14 @@ def recall(self, dry = True, debug = False) -> bool:
continue
self._vault['account'][x['account']]['log'][x['ref']]['file'][x['file']] = x['value']

case Action.EXCHANGE:
if x['account'] is not None:
if x['account'] in self._vault['exchange']:
if x['ref'] in self._vault['exchange'][x['account']]:
if dry:
continue
del self._vault['exchange'][x['account']][x['ref']]

case Action.REPORT:
if x['ref'] in self._vault['report']:
if dry:
Expand Down Expand Up @@ -578,6 +588,27 @@ def _log(self, value: int, desc: str = '', account: str = 1, created: int = None
self._step(Action.LOG, account, ref=created, value=value)
return created

def exchange(self, account, created: int = None, rate: float = None, description: str = None) -> dict:
if rate is not None:
if rate <= 1:
return None
if account not in self._vault['exchange']:
self._vault['exchange'][account] = {}
if created is None:
created = self.time()
self._vault['exchange'][account][created] = {"rate": rate, "description": description}
self._step(Action.EXCHANGE, account, ref=created, value=rate)

if account in self._vault['exchange'] and created is not None:
valid_rates = [(ts, r) for ts, r in self._vault['exchange'][account].items() if ts <= created]
if valid_rates:
latest_rate = max(valid_rates, key=lambda x: x[0])
return latest_rate[1] # إرجاع قاموس يحتوي على المعدل والوصف
return {"rate": 1, "description": None} # إرجاع القيمة الافتراضية مع وصف فارغ

def exchanges(self) -> dict:
return self._vault['exchange'].copy()

def accounts(self) -> dict:
"""
Returns a dictionary containing account numbers as keys and their respective balances as values.
Expand Down Expand Up @@ -1162,6 +1193,10 @@ def DurationFromNanoSeconds(ns: int) -> tuple:
SPOKENTIME = f"{n: 3d} Millennia, {c: 4d} Century, {y: 3d} Years, {d: 4d} Days, {h: 2d} Hours, {m: 2d} Minutes, {s: 2d} Seconds, {ms: 3d} MilliSeconds, {μs: 3d} MicroSeconds, {ns: 3d} NanoSeconds"
return TIMELAPSED, SPOKENTIME

@staticmethod
def day_to_time(day: int, month: int = 6, year: int = 2024) -> int: # افتراض أن الشهر هو يونيو والسنة 2024
return ZakatTracker.time(datetime.datetime(year, month, day))

@staticmethod
def generate_random_date(start_date: datetime.datetime, end_date: datetime.datetime) -> datetime.datetime:
"""
Expand Down Expand Up @@ -1638,6 +1673,93 @@ def test(self, debug: bool = False):
print('valid', valid)
assert self.zakat(report, parts=suite, debug=debug)

# exchange

self.exchange("cash", 25, 3.75, "2024-06-25")
self.exchange("cash", 22, 3.73, "2024-06-22")
self.exchange("cash", 15, 3.69, "2024-06-15")
self.exchange("cash", 10, 3.66)

for i in range(1, 30):
rate, description = self.exchange("cash", i).values()
if debug:
print(i, rate, description)
if i < 10:
assert rate == 1
assert description is None
elif i == 10:
assert rate == 3.66
assert description is None
elif i < 15:
assert rate == 3.66
assert description is None
elif i == 15:
assert rate == 3.69
assert description is not None
elif i < 22:
assert rate == 3.69
assert description is not None
elif i == 22:
assert rate == 3.73
assert description is not None
elif i >= 25:
assert rate == 3.75
assert description is not None
rate, description = self.exchange("bank", i).values()
if debug:
print(i, rate, description)
assert rate == 1
assert description is None

assert len(self._vault['exchange']) > 0
assert len(self.exchanges()) > 0
self._vault['exchange'].clear()
assert len(self._vault['exchange']) == 0
assert len(self.exchanges()) == 0

# حفظ أسعار الصرف باستخدام التواريخ بالنانو ثانية
self.exchange("cash", ZakatTracker.day_to_time(25), 3.75, "2024-06-25")
self.exchange("cash", ZakatTracker.day_to_time(22), 3.73, "2024-06-22")
self.exchange("cash", ZakatTracker.day_to_time(15), 3.69, "2024-06-15")
self.exchange("cash", ZakatTracker.day_to_time(10), 3.66)

for i in [x * 0.12 for x in range(-15, 21)]:
if i <= 1:
assert self.exchange("test", ZakatTracker.time(), i, f"range({i})") is None
else:
assert self.exchange("test", ZakatTracker.time(), i, f"range({i})") is not None

# اختبار النتائج باستخدام التواريخ بالنانو ثانية
for i in range(1, 31):
timestamp_ns = ZakatTracker.day_to_time(i)
rate, description = self.exchange("cash", timestamp_ns).values()
print(i, rate, description)
if i < 10:
assert rate == 1
assert description is None
elif i == 10:
assert rate == 3.66
assert description is None
elif i < 15:
assert rate == 3.66
assert description is None
elif i == 15:
assert rate == 3.69
assert description is not None
elif i < 22:
assert rate == 3.69
assert description is not None
elif i == 22:
assert rate == 3.73
assert description is not None
elif i >= 25:
assert rate == 3.75
assert description is not None
rate, description = self.exchange("bank", i).values()
print(i, rate, description)
assert rate == 1
assert description is None

assert self.export_json("1000-transactions-test.json")
assert self.save("1000-transactions-test.pickle")

Expand Down

0 comments on commit c484da0

Please sign in to comment.