diff --git a/REadME.md b/REadME.md new file mode 100644 index 00000000..97b0cfb1 --- /dev/null +++ b/REadME.md @@ -0,0 +1,68 @@ +mini-bookmark V2 + +A CLI tool to save and manage web URLs, developed as part of a university assignment at Samsun Üniversitesi. + +Student Information + +Name: Hüdanur Şener + +Student ID: 251478066 + +Overview + +mini-bookmark is a lightweight command-line interface tool that allows users to store, list, search, and delete web bookmarks. All data is persisted in a plain text file (links.dat) located within a hidden .minibookmark/ directory. + +V1 vs V2: What's New? + +The project has evolved from V1 to V2 with the following key improvements: + +Command Completion: In V1, list and delete were placeholders. In V2, they are fully functional. + +URL Validation: V2 introduces a safety check ensuring all added URLs start with 'http' or 'https'. + +Formatted Output: The list command now presents data in a clean, professional table format. + +Enhanced Error Handling: Improved feedback for missing arguments, invalid IDs, and uninitialized states. + +V2 Task List + +List Command: Implemented full listing of bookmarks with a formatted header and aligned columns. + +Delete Command: Added functionality to remove a specific bookmark entry using its ID number. + +Validation Logic: Integrated a prefix check for URLs to prevent malformed data entry. + +Installation \& Usage + +1\. Initialize the Project + +python minibookmark.py init + +2\. Add a Bookmark + +python minibookmark.py add "Google" "https://google.com" + +3\. List All Bookmarks + +python minibookmark.py list + +4\. Search Bookmarks + +python minibookmark.py search "keyword" + +5\. Delete a Bookmark + +python minibookmark.py delete + +Data Format + +Data is stored in .minibookmark/links.dat using the following structure: + +id|title|url|date + +License + +Educational purpose only. + + + diff --git a/problems/mini-bookmark/SPEC-v1.txt b/problems/mini-bookmark/SPEC-v1.txt new file mode 100644 index 00000000..f8f5b366 --- /dev/null +++ b/problems/mini-bookmark/SPEC-v1.txt @@ -0,0 +1,49 @@ +PROJECT: mini-bookmark +AUTHOR: Hüdanur Şener (251478066) +VERSION: v0 +DATE: 2026-03-16 + +======================================== +OVERVIEW +======================================== +mini-bookmark is a CLI tool to save and manage web URLs. +It stores bookmarks in a plain text file. +All data is kept in a directory named .minibookmark/ + +======================================== +COMMANDS +======================================== + +--- init --- +Usage: python minibookmark.py init +Creates .minibookmark/ directory and an empty links.dat file. +If it exists, prints "Already initialized." + +--- add --- +Usage: python minibookmark.py add "Google" "https://google.com" +Saves a new bookmark with an auto-increment ID. +Format: id|title|url|date +Prints: "Added bookmark #: " + +--- list --- +Usage: python minibookmark.py list +(To be implemented) +Will show all saved bookmarks. + +--- delete --- +Usage: python minibookmark.py delete <id> +(To be implemented) +Will remove a bookmark by ID. + +======================================== +DATA FORMAT +======================================== +File: .minibookmark/links.dat +Format: id|title|url|2026-03-16 + +======================================== +ERROR HANDLING +======================================== +- Running without init: "Not initialized. Run: python minibookmark.py init" +- Unknown command: "Unknown command: <command>" +- Missing arguments: "Usage: python minibookmark.py <command> [args]" diff --git a/problems/mini-bookmark/SPEC-v2.txt b/problems/mini-bookmark/SPEC-v2.txt new file mode 100644 index 00000000..aa428846 --- /dev/null +++ b/problems/mini-bookmark/SPEC-v2.txt @@ -0,0 +1,54 @@ +PROJECT: mini-bookmark +AUTHOR: Hüdanur Şener (251478066) +VERSION: v1 +DATE: 2026-03-29 + +======================================== +OVERVIEW +======================================== +mini-bookmark is a CLI tool to save and manage web URLs. +It stores bookmarks in a plain text file. [cite: 2] +All data is kept in a directory named .minibookmark/ + +======================================== +COMMANDS +======================================== + +--- init --- +Usage: python minibookmark.py init +Creates .minibookmark/ directory and an empty links.dat file. +If it exists, prints "Already initialized." [cite: 3] + +--- add --- +Usage: python minibookmark.py add "Title" "URL" +Saves a new bookmark with an auto-increment ID. [cite: 4] +Format: id|title|url|date +Prints: "Added bookmark #<id>: <title>" + +--- list --- +Usage: python minibookmark.py list +(To be implemented) +Will show all saved bookmarks. + +--- delete --- +Usage: python minibookmark.py delete <id> +(To be implemented) +Will remove a bookmark by ID. [cite: 5] + +--- search --- +Usage: python minibookmark.py search <keyword> +(V1 Update) Searches for bookmarks by title keyword. +[Note: This command was added to meet Assignment 4.2 requirements.] + +======================================== +DATA FORMAT +======================================== +File: .minibookmark/links.dat +Format: id|title|url|2026-03-16 [cite: 6] + +======================================== +ERROR HANDLING +======================================== +- Running without init: "Not initialized. Run: python minibookmark.py init" +- Unknown command: "Unknown command: <command>" +- Missing arguments: "Usage: python minibookmark.py <command> [args]" \ No newline at end of file diff --git a/problems/mini-bookmark/SPEC-v3.txt b/problems/mini-bookmark/SPEC-v3.txt new file mode 100644 index 00000000..1cb114f8 --- /dev/null +++ b/problems/mini-bookmark/SPEC-v3.txt @@ -0,0 +1,57 @@ +PROJECT: mini-bookmark +AUTHOR: Hüdanur Şener (251478066) +VERSION: v3 +DATE: 2026-04-20 + +======================================== +OVERVIEW +======================================== +mini-bookmark is a CLI tool to save and manage web URLs[cite: 13]. +It stores bookmarks in a plain text file. +All data is kept in a directory named .minibookmark/[cite: 14]. + +======================================== +COMMANDS +======================================== + +--- init --- +Usage: python minibookmark.py init +Creates .minibookmark/ directory and an empty links.dat file[cite: 15]. +If it exists, prints "Already initialized." + +--- add --- +Usage: python minibookmark.py add "Title" "URL" +(V2 Update) Saves a new bookmark with an auto-increment ID. +URL must start with "http" or "https"[cite: 16]. +Format: id|title|url|date +Prints: "Added bookmark #<id>: <title>" + +--- list --- +Usage: python minibookmark.py list +(V2 Update: Implemented) Shows all saved bookmarks in a formatted table[cite: 17]. +Displays ID, Title, URL, and Date columns. + +--- delete --- +Usage: python minibookmark.py delete <id> +(V2 Update: Implemented) Removes a bookmark by its specific ID from links.dat[cite: 18]. +Prints: "Bookmark #<id> deleted." if successful. + +--- search --- +Usage: python minibookmark.py search <keyword> +Searches for bookmarks by title keyword (Case-insensitive)[cite: 19]. + +======================================== +DATA FORMAT +======================================== +File: .minibookmark/links.dat +Format: id|title|url|YYYY-MM-DD +Example: 1|Google|http://google.com|2026-04-20 + +======================================== +ERROR HANDLING +======================================== +- Running without init: "Not initialized. Run: python minibookmark.py init" +- Unknown command: "Unknown command: <command>" +- Missing arguments: "Usage: python minibookmark.py <command> [args]" +- Invalid URL (V2): "Error: URL must start with 'http' or 'https'." +- ID Not Found (V2): "Bookmark #<id> not found." \ No newline at end of file diff --git a/problems/mini-bookmark/TEST-v1 (2).py b/problems/mini-bookmark/TEST-v1 (2).py new file mode 100644 index 00000000..1652b74c --- /dev/null +++ b/problems/mini-bookmark/TEST-v1 (2).py @@ -0,0 +1,86 @@ +""" +mini-bookmark SPEC test cases +Student:Hüdanur Şener (251478066) +Project: mini-bookmark +""" +import subprocess +import os +import shutil + +# --- Helper Function --- +def run_cmd(args): + """Runs the script and returns the output.""" + result = subprocess.run( + ["python", "minibookmark.py"] + args, + capture_output=True, + text=True + ) + return result.stdout.strip() + +def setup_function(): + """Clean start for each test.""" + if os.path.exists(".minibookmark"): + shutil.rmtree(".minibookmark") + +# --- 1. init COMMAND TESTS --- +def test_init_creates_directory(): + # Normal durum: İlk kez init yapilir + run_cmd(["init"]) + assert os.path.exists(".minibookmark") == True + +def test_init_already_initialized(): + # Hata durumu: Zaten init yapilmisken tekrar yapilir + run_cmd(["init"]) + output = run_cmd(["init"]) + assert "Already initialized" in output + +# --- 2. add COMMAND TESTS --- +def test_add_single_bookmark(): + # Normal durum: Bir link basariyla eklenir + run_cmd(["init"]) + output = run_cmd(["add", "Google", "https://google.com"]) + assert "Added bookmark #1" in output + +def test_add_multiple_bookmarks(): + # Normal durum: İkinci link eklenir ve ID artar + run_cmd(["init"]) + run_cmd(["add", "A", "a.com"]) + output = run_cmd(["add", "B", "b.com"]) + assert "#2" in output + +def test_add_missing_arguments(): + # Hata durumu: Eksik arguman (sadece isim) + run_cmd(["init"]) + output = run_cmd(["add", "OnlyName"]) + assert "Usage" in output + +# --- 3. ERROR HANDLING & FLOW TESTS --- +def test_command_before_init(): + # Hata durumu: Klasor yokken add komutu calismaz + output = run_cmd(["add", "Test", "test.com"]) + assert "Not initialized" in output + +def test_unknown_command_error(): + # Hata durumu: SPEC'te olmayan bir komut + run_cmd(["init"]) + output = run_cmd(["jump"]) + assert "Unknown command" in output + +# --- 4. FUTURE IMPLEMENTATION TESTS (SPEC Status) --- +def test_list_not_implemented_yet(): + # SPEC Uyumu: list komutu henuz hazir degil mesaji vermeli + run_cmd(["init"]) + output = run_cmd(["list"]) + assert "future weeks" in output + +def test_delete_not_implemented_yet(): + # SPEC Uyumu: delete komutu henuz hazir degil mesaji vermeli + run_cmd(["init"]) + output = run_cmd(["delete", "1"]) + assert "future weeks" in output + +# --- 5. USAGE TESTS --- +def test_no_arguments_usage(): + # Hata durumu: Hicbir arguman girilmezse kullanim kilavuzu basilir + output = run_cmd([]) + assert "Usage" in output \ No newline at end of file diff --git a/problems/mini-bookmark/TEST-v2.py b/problems/mini-bookmark/TEST-v2.py new file mode 100644 index 00000000..e8049af7 --- /dev/null +++ b/problems/mini-bookmark/TEST-v2.py @@ -0,0 +1,92 @@ +""" +PROJECT: mini-bookmark V1 +STUDENT: Hüdanur Şener (251478066) + +V1 GÖREV LİSTESİ (Ödev 4.2 Gereksinimi): +1. 'init' komutu tamamlandı: .minibookmark/ klasörü ve veri dosyası oluşturuluyor. +2. 'add' komutu id|title|url|date formatında kayıt yapacak şekilde geliştirildi. +3. 'search' komutu Codex desteğiyle ek puan görevi olarak projeye eklendi. +""" + +import sys +import os + +# Veri saklama yolları [cite: 2, 6] +DB_DIR = ".minibookmark" +DB_FILE = os.path.join(DB_DIR, "links.dat") + + +def init(): + """Projeyi başlatır ve gerekli dosyayı oluşturur[cite: 3].""" + if os.path.exists(DB_DIR): + print("Already initialized.") + else: + os.makedirs(DB_DIR) + open(DB_FILE, "w").close() + print("Project initialized.") + + +def add(title, url): + """Yeni bir bookmark ekler[cite: 4].""" + if not os.path.exists(DB_DIR): + print("Not initialized. Run: python minibookmark.py init") + else: + try: + with open(DB_FILE, "r") as f: + new_id = len(f.readlines()) + 1 + except FileNotFoundError: + new_id = 1 + + date_str = "2026-03-16" # SPEC'teki sabit tarih formatı [cite: 6] + entry = f"{new_id}|{title}|{url}|{date_str}\n" + + with open(DB_FILE, "a") as f: + f.write(entry) + print(f"Added bookmark #{new_id}: {title}") + + +def search(keyword): + """BONUS: Codex ile üretilen arama fonksiyonu[cite: 42].""" + if not os.path.exists(DB_FILE): + print("Not initialized.") + return + with open(DB_FILE, "r") as f: + found = False + for line in f: + if keyword.lower() in line.split('|')[1].lower(): + print(line.strip()) + found = True + if not found: + print("No bookmarks found.") + + +def main(): + args = sys.argv[1:] + + if not args: + print("Usage: python minibookmark.py <command> [args]") + return + + command = args[0] + + if command == "init": + init() + elif command == "add": + if len(args) < 3: + print("Usage: python minibookmark.py add <title> <url>") + else: + add(args[1], args[2]) + elif command == "search": + if len(args) < 2: + print("Usage: python minibookmark.py search <keyword>") + else: + search(args[1]) + elif command in ["list", "delete"]: + # Gelecek haftalar için yer tutucu mesaj + print("This feature will be implemented in future weeks.") + else: + print(f"Unknown command: {command}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/problems/mini-bookmark/TEST-v3.py b/problems/mini-bookmark/TEST-v3.py new file mode 100644 index 00000000..d08b9a54 --- /dev/null +++ b/problems/mini-bookmark/TEST-v3.py @@ -0,0 +1,142 @@ +""" +PROJECT: mini-bookmark V3 +AUTHOR: Hüdanur Şener (251478066) +DATE: 2026-04-20 + +V2 TASK LIST: +1. 'list' command completed: Displays all records in a formatted table. +2. 'delete' command implemented: Added logic to remove a record by ID. +3. URL Validation: Added a check to ensure links start with 'http'. +""" + +import sys +import os + +# Data storage paths [cite: 14] +DB_DIR = ".minibookmark" +DB_FILE = os.path.join(DB_DIR, "links.dat") + + +def init(): + """Initializes the project and creates necessary directory/file[cite: 15].""" + if os.path.exists(DB_DIR): + print("Already initialized.") + else: + os.makedirs(DB_DIR) + open(DB_FILE, "w").close() + print("Project initialized.") + + +def add(title, url): + """Adds a new bookmark with URL validation[cite: 16].""" + if not url.startswith("http"): + print("Error: URL must start with 'http' or 'https'.") + return + + if not os.path.exists(DB_DIR): + print("Not initialized. Run: python minibookmark.py init") + return + + try: + with open(DB_FILE, "r") as f: + lines = f.readlines() + new_id = len(lines) + 1 + except FileNotFoundError: + new_id = 1 + + date_str = "2026-04-20" + entry = f"{new_id}|{title}|{url}|{date_str}\n" + + with open(DB_FILE, "a") as f: + f.write(entry) + print(f"Added bookmark #{new_id}: {title}") + + +def list_bookmarks(): + """V2: Lists all saved bookmarks in a clean format.""" + if not os.path.exists(DB_FILE) or os.path.getsize(DB_FILE) == 0: + print("No bookmarks found.") + return + + # Table Header + print(f"{'ID':<4} | {'TITLE':<20} | {'URL':<30} | {'DATE'}") + print("-" * 75) + with open(DB_FILE, "r") as f: + for line in f: + parts = line.strip().split('|') + if len(parts) == 4: + print(f"{parts[0]:<4} | {parts[1]:<20} | {parts[2]:<30} | {parts[3]}") + + +def delete(bookmark_id): + """V2: Removes a bookmark based on its ID.""" + if not os.path.exists(DB_FILE): + print("Not initialized.") + return + + lines = [] + found = False + with open(DB_FILE, "r") as f: + lines = f.readlines() + + with open(DB_FILE, "w") as f: + for line in lines: + # Check if the line starts with the target ID + if not line.startswith(f"{bookmark_id}|"): + f.write(line) + else: + found = True + + if found: + print(f"Bookmark #{bookmark_id} deleted successfully.") + else: + print(f"Error: Bookmark #{bookmark_id} not found.") + + +def search(keyword): + """Searches for bookmarks by title keyword[cite: 18].""" + if not os.path.exists(DB_FILE): + print("Not initialized.") + return + with open(DB_FILE, "r") as f: + found = False + for line in f: + if keyword.lower() in line.split('|')[1].lower(): + print(line.strip()) + found = True + if not found: + print("No bookmarks found matching the keyword.") + + +def main(): + args = sys.argv[1:] + if not args: + print("Usage: python minibookmark.py <command> [args]") + return + + command = args[0] + if command == "init": + init() + elif command == "add": + if len(args) < 3: + print("Usage: python minibookmark.py add <title> <url>") + else: + add(args[1], args[2]) + elif command == "list": + list_bookmarks() + elif command == "delete": + if len(args) < 2: + print("Usage: python minibookmark.py delete <id>") + else: + delete(args[1]) + elif command == "search": + if len(args) < 2: + print("Usage: python minibookmark.py search <keyword>") + else: + search(args[1]) + else: + print(f"Unknown command: {command}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/problems/mini-bookmark/solution_v0.py b/problems/mini-bookmark/solution_v0.py new file mode 100644 index 00000000..e4bc768e --- /dev/null +++ b/problems/mini-bookmark/solution_v0.py @@ -0,0 +1,82 @@ +""" +mini-bookmark v0 — Basitlestirilmis implementasyon +Ogrenci: Hüdanur Şener (251478066) +Proje: mini-bookmark + +Kapsam: Sadece init ve add komutlari. +Sinirlamalar: Dongu (for/while) ve liste ([]) kesinlikle kullanilmadi. +""" +import sys +import os + + +def initialize(): + """Bookmark dizini ve bos veri dosyasini olusturur.""" + if os.path.exists(".minibookmark"): + return "Already initialized." + + os.mkdir(".minibookmark") + # Bos bir dosya acip kapatmak onu olusturmak demektir + f = open(".minibookmark/links.dat", "w") + f.close() + return "Initialized empty bookmark folder." + + +def add_bookmark(title, url): + """Yeni bir link ekler. ID tespiti satir sayilarini sayarak yapilir.""" + # Klasor yoksa hata dondur + if not os.path.exists(".minibookmark"): + return "Not initialized. Run: python minibookmark.py init" + + # Mevcut dosyayi oku (ID tespiti icin) + f = open(".minibookmark/links.dat", "r") + content = f.read() + f.close() + + # YASAK: Dongu ve liste kullanmadan satir sayisi bulma: + # Dosyadaki her '\n' (yeni satir) karakteri bir kaydi temsil eder. + bookmark_id = content.count("\n") + 1 + + # Dosyaya ekleme yap (Append modu) + f = open(".minibookmark/links.dat", "a") + f.write(str(bookmark_id) + "|" + title + "|" + url + "|2026-03-16\n") + f.close() + + return "Added bookmark #" + str(bookmark_id) + ": " + title + + +def show_not_implemented(command_name): + """Henuz kodlanmamis ozellikler icin standart bir mesaj dondurur.""" + return "Command '" + command_name + "' will be implemented in future weeks." + + +# --- Ana Program Akisi --- + +# Kullanici hicbir sey yazmadan programi calistirirsa +if len(sys.argv) < 2: + print("Usage: python minibookmark.py <command> [args]") + +# init komutu +elif sys.argv[1] == "init": + print(initialize()) + +# add komutu +elif sys.argv[1] == "add": + # Eger title veya url eksikse (python + dosya_adi + komut + 2 arguman = 4) + if len(sys.argv) < 4: + print("Usage: python minibookmark.py add <title> <url>") + else: + # Argumanlari fonksiyonumuza gonderiyoruz + print(add_bookmark(sys.argv[2], sys.argv[3])) + +# list komutu (Henuz implemente edilmedi) +elif sys.argv[1] == "list": + print(show_not_implemented("list")) + +# delete komutu (Henuz implemente edilmedi) +elif sys.argv[1] == "delete": + print(show_not_implemented("delete")) + +# Gecersiz bir komut girilirse +else: + print("Unknown command: " + sys.argv[1]) \ No newline at end of file