Skip to content

Commit 901fcf4

Browse files
authored
Create Distributed-File-System.py
1 parent 33f3bb0 commit 901fcf4

1 file changed

Lines changed: 196 additions & 0 deletions

File tree

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import threading
2+
import tkinter as tk
3+
from tkinter import messagebox, filedialog
4+
from dataclasses import dataclass
5+
from typing import List, Dict
6+
import os
7+
import shutil
8+
import csv
9+
10+
import ttkbootstrap as tb
11+
from ttkbootstrap.constants import *
12+
from tkinterdnd2 import TkinterDnD, DND_FILES
13+
14+
# ---------------- GLOBAL STATE ---------------- #
15+
storage_nodes = ["Node_A", "Node_B", "Node_C"]
16+
file_registry: List["DistributedFile"] = []
17+
18+
BASE_DIR = "dfs_nodes"
19+
os.makedirs(BASE_DIR, exist_ok=True)
20+
for n in storage_nodes:
21+
os.makedirs(os.path.join(BASE_DIR, n), exist_ok=True)
22+
23+
# ---------------- DATA STRUCTURE ---------------- #
24+
@dataclass
25+
class DistributedFile:
26+
filename: str
27+
size_kb: float
28+
replicas: List[str]
29+
30+
# ---------------- HELPERS ---------------- #
31+
def kb(size):
32+
return round(size / 1024, 2)
33+
34+
def reset_nodes():
35+
for n in storage_nodes:
36+
path = os.path.join(BASE_DIR, n)
37+
shutil.rmtree(path)
38+
os.makedirs(path)
39+
file_registry.clear()
40+
41+
# ---------------- DRAG & DROP ---------------- #
42+
def drop_file(event):
43+
files = app.tk.splitlist(event.data)
44+
for file in files:
45+
if os.path.isfile(file):
46+
threading.Thread(
47+
target=store_file,
48+
args=(file,),
49+
daemon=True
50+
).start()
51+
52+
# ---------------- FILE STORAGE ---------------- #
53+
def store_file(path):
54+
filename = os.path.basename(path)
55+
size = kb(os.path.getsize(path))
56+
replicas = []
57+
58+
try:
59+
for node in storage_nodes:
60+
dest = os.path.join(BASE_DIR, node, filename)
61+
shutil.copy(path, dest)
62+
replicas.append(node)
63+
64+
file_registry.append(
65+
DistributedFile(filename, size, replicas)
66+
)
67+
app.after(0, refresh_files)
68+
69+
except Exception as e:
70+
messagebox.showerror("Storage Error", str(e))
71+
72+
# ---------------- DOWNLOAD ---------------- #
73+
def download_file():
74+
sel = file_listbox.curselection()
75+
if not sel:
76+
return
77+
78+
file = file_registry[sel[0]]
79+
node = file.replicas[0]
80+
81+
src = os.path.join(BASE_DIR, node, file.filename)
82+
dest = filedialog.asksaveasfilename(
83+
initialfile=file.filename
84+
)
85+
86+
if dest:
87+
shutil.copy(src, dest)
88+
messagebox.showinfo("Downloaded", "File retrieved successfully")
89+
90+
# ---------------- EXPORT METADATA ---------------- #
91+
def export_metadata():
92+
if not file_registry:
93+
messagebox.showwarning("No Data", "No files to export")
94+
return
95+
96+
path = filedialog.asksaveasfilename(
97+
defaultextension=".csv",
98+
filetypes=[("CSV", "*.csv")]
99+
)
100+
if not path:
101+
return
102+
103+
with open(path, "w", newline="", encoding="utf-8") as f:
104+
writer = csv.writer(f)
105+
writer.writerow(["Filename", "Size (KB)", "Replicas"])
106+
for fdata in file_registry:
107+
writer.writerow([
108+
fdata.filename,
109+
fdata.size_kb,
110+
", ".join(fdata.replicas)
111+
])
112+
113+
messagebox.showinfo("Exported", "Metadata exported successfully")
114+
115+
# ---------------- DISPLAY ---------------- #
116+
def refresh_files():
117+
file_listbox.delete(0, END)
118+
for f in file_registry:
119+
file_listbox.insert(
120+
END,
121+
f"{f.filename} ({f.size_kb} KB) → {len(f.replicas)} replicas"
122+
)
123+
124+
# ---------------- UI ---------------- #
125+
app = TkinterDnD.Tk()
126+
app.title("🗄️ Distributed File System")
127+
app.geometry("950x650")
128+
129+
style = tb.Style("flatly")
130+
131+
top = tb.Frame(app, padding=15)
132+
top.pack(fill=X)
133+
134+
tb.Label(
135+
top,
136+
text="🗄️ Distributed File System (Simulated)",
137+
font=("Segoe UI", 18, "bold")
138+
).pack(anchor=W)
139+
140+
tb.Label(
141+
top,
142+
text="Drag & drop files below to distribute across nodes",
143+
font=("Segoe UI", 11)
144+
).pack(anchor=W, pady=5)
145+
146+
drop_zone = tb.Frame(top, height=80, bootstyle="secondary")
147+
drop_zone.pack(fill=X, pady=10)
148+
149+
tb.Label(
150+
drop_zone,
151+
text="📁 DROP FILES HERE",
152+
font=("Segoe UI", 14)
153+
).place(relx=0.5, rely=0.5, anchor=CENTER)
154+
155+
drop_zone.drop_target_register(DND_FILES)
156+
drop_zone.dnd_bind("<<Drop>>", drop_file)
157+
158+
tb.Label(top, text="Stored Files:", font=("Segoe UI", 12)).pack(anchor=W)
159+
160+
file_listbox = tk.Listbox(top, height=10)
161+
file_listbox.pack(fill=X, pady=5)
162+
163+
btn_row = tb.Frame(top)
164+
btn_row.pack(fill=X, pady=5)
165+
166+
tb.Button(
167+
btn_row,
168+
text="Download",
169+
bootstyle="success",
170+
command=download_file
171+
).pack(side=LEFT)
172+
173+
tb.Button(
174+
btn_row,
175+
text="Export Metadata",
176+
command=export_metadata
177+
).pack(side=LEFT, padx=5)
178+
179+
tb.Button(
180+
btn_row,
181+
text="Reset Nodes",
182+
bootstyle="danger",
183+
command=lambda: [reset_nodes(), refresh_files()]
184+
).pack(side=LEFT, padx=5)
185+
186+
# ---------------- FOOTER ---------------- #
187+
footer = tb.Frame(app, padding=10)
188+
footer.pack(fill=X, side=BOTTOM)
189+
190+
tb.Label(
191+
footer,
192+
text="Nodes: " + " | ".join(storage_nodes),
193+
font=("Segoe UI", 10)
194+
).pack(anchor=W)
195+
196+
app.mainloop()

0 commit comments

Comments
 (0)