ThesisProject / add_cebuano_data.py
JeyBii's picture
Upload folder using huggingface_hub
2b9b5b5 verified
Raw
History Blame Contribute Delete
10.2 kB
"""
Cebuano Fake News Data Entry Tool
===================================
Manually input Cebuano fake news articles into
data/raw/augmented_ceb_fakes.csv for model training.
Usage:
python add_cebuano_data.py
Features:
- Interactive CLI with menu options
- Paste single-line or multi-line articles
- Preview entries before saving
- View existing dataset stats
- Duplicate detection (warns if near-identical text exists)
"""
import csv
import os
import sys
import textwrap
from datetime import datetime
# ── Paths ──
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
CSV_PATH = os.path.join(SCRIPT_DIR, "data", "raw", "augmented_ceb_fakes.csv")
BACKUP_DIR = os.path.join(SCRIPT_DIR, "data", "raw", ".backups")
def _count_existing_rows() -> int:
"""Count how many articles are already in the CSV."""
if not os.path.exists(CSV_PATH):
return 0
with open(CSV_PATH, "r", encoding="utf-8") as f:
reader = csv.reader(f)
header = next(reader, None)
return sum(1 for _ in reader)
def _load_existing_articles() -> list[str]:
"""Load all existing article texts (for duplicate checking)."""
articles = []
if not os.path.exists(CSV_PATH):
return articles
with open(CSV_PATH, "r", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader, None) # skip header
for row in reader:
if row:
articles.append(row[0].strip().lower())
return articles
def _is_duplicate(new_text: str, existing: list[str], threshold: int = 50) -> bool:
"""Check if the first `threshold` characters match any existing article."""
snippet = new_text.strip().lower()[:threshold]
if len(snippet) < 20:
return False
for art in existing:
if art[:threshold] == snippet:
return True
return False
def _backup_csv():
"""Create a timestamped backup of the current CSV."""
if not os.path.exists(CSV_PATH):
return
os.makedirs(BACKUP_DIR, exist_ok=True)
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = os.path.join(BACKUP_DIR, f"augmented_ceb_fakes_{ts}.csv")
with open(CSV_PATH, "r", encoding="utf-8") as src:
with open(backup_path, "w", encoding="utf-8", newline="") as dst:
dst.write(src.read())
print(f" Backup saved: {os.path.relpath(backup_path, SCRIPT_DIR)}")
def _ensure_csv_exists():
"""Create the CSV with header if it doesn't exist yet."""
if not os.path.exists(CSV_PATH):
os.makedirs(os.path.dirname(CSV_PATH), exist_ok=True)
with open(CSV_PATH, "w", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow(["article"])
print(f" Created new CSV: {CSV_PATH}")
def _append_articles(articles: list[str]):
"""Append a list of article texts to the CSV."""
with open(CSV_PATH, "a", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
for text in articles:
writer.writerow([text])
def _preview_text(text: str, width: int = 80) -> str:
"""Return a truncated preview of article text."""
preview = text.replace("\n", " ").strip()
if len(preview) > width:
return preview[:width] + "..."
return preview
# ── CLI Display ──
def print_header():
print()
print("=" * 60)
print(" CEBUANO FAKE NEWS β€” DATA ENTRY TOOL")
print("=" * 60)
print(f" CSV: {os.path.relpath(CSV_PATH, SCRIPT_DIR)}")
print(f" Existing articles: {_count_existing_rows():,}")
print("=" * 60)
def print_menu():
print()
print(" [1] Add a single article")
print(" [2] Add multiple articles (batch mode)")
print(" [3] View dataset stats")
print(" [4] View last 5 entries")
print(" [5] Exit")
print()
def input_single_article() -> str | None:
"""Prompt the user to type/paste a single article.
Supports multi-line input: type on multiple lines, then enter
a blank line to finish.
"""
print()
print(" Paste or type the Cebuano fake news article below.")
print(" (Enter a blank line when done, or type 'cancel' to abort)")
print(" " + "-" * 50)
lines = []
while True:
try:
line = input(" > ")
except EOFError:
break
if line.strip().lower() == "cancel":
return None
if line.strip() == "" and lines:
break
lines.append(line)
text = " ".join(lines).strip()
if not text:
print(" (empty input β€” skipped)")
return None
return text
def add_single_article():
"""Menu option 1: add one article."""
_ensure_csv_exists()
existing = _load_existing_articles()
text = input_single_article()
if text is None:
print(" Cancelled.")
return
# Duplicate check
if _is_duplicate(text, existing):
print()
print(" ⚠ WARNING: This article appears to be a duplicate!")
confirm = input(" Add anyway? (y/n): ").strip().lower()
if confirm != "y":
print(" Skipped.")
return
# Preview and confirm
print()
print(" Preview:")
print(f" {_preview_text(text, 100)}")
print(f" Length: {len(text):,} chars, {len(text.split()):,} words")
print()
confirm = input(" Save this article? (y/n): ").strip().lower()
if confirm != "y":
print(" Discarded.")
return
_append_articles([text])
print(f" βœ“ Article saved! Total articles now: {_count_existing_rows():,}")
def add_batch_articles():
"""Menu option 2: add multiple articles in batch."""
_ensure_csv_exists()
existing = _load_existing_articles()
print()
print(" BATCH MODE β€” Enter one article per prompt.")
print(" Type 'done' when finished, 'cancel' to discard all.")
print()
pending: list[str] = []
article_num = 1
while True:
print(f" --- Article #{article_num} ---")
text = input_single_article()
if text is None:
# Check if user typed cancel
break
if _is_duplicate(text, existing):
print(" ⚠ Possible duplicate β€” skipping this one.")
continue
pending.append(text)
print(f" βœ“ Queued ({len(pending)} pending)")
article_num += 1
cont = input(" Add another? (y/n/done): ").strip().lower()
if cont in ("n", "done"):
break
if not pending:
print(" No articles to save.")
return
# Preview all
print()
print(f" === {len(pending)} articles ready to save ===")
for i, art in enumerate(pending, 1):
print(f" {i}. {_preview_text(art, 70)}")
print()
confirm = input(f" Save all {len(pending)} articles? (y/n): ").strip().lower()
if confirm != "y":
print(" Discarded all.")
return
_backup_csv()
_append_articles(pending)
print(f" βœ“ {len(pending)} articles saved! Total now: {_count_existing_rows():,}")
def show_stats():
"""Menu option 3: show dataset statistics."""
if not os.path.exists(CSV_PATH):
print(" CSV not found β€” no data yet.")
return
articles = _load_existing_articles()
total = len(articles)
if total == 0:
print(" Dataset is empty.")
return
word_counts = [len(a.split()) for a in articles]
char_counts = [len(a) for a in articles]
print()
print(" ╔══════════════════════════════════════╗")
print(" β•‘ DATASET STATISTICS β•‘")
print(" ╠══════════════════════════════════════╣")
print(f" β•‘ Total articles: {total:>10,} β•‘")
print(f" β•‘ Avg words/article: {sum(word_counts)//total:>10,} β•‘")
print(f" β•‘ Min words: {min(word_counts):>10,} β•‘")
print(f" β•‘ Max words: {max(word_counts):>10,} β•‘")
print(f" β•‘ Avg chars/article: {sum(char_counts)//total:>10,} β•‘")
print(f" β•‘ CSV size: {os.path.getsize(CSV_PATH):>10,} B β•‘")
print(" β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")
print()
def show_last_entries():
"""Menu option 4: show last 5 entries."""
if not os.path.exists(CSV_PATH):
print(" CSV not found β€” no data yet.")
return
articles = []
with open(CSV_PATH, "r", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader, None)
for row in reader:
if row:
articles.append(row[0])
if not articles:
print(" No articles in dataset.")
return
last5 = articles[-5:]
print()
print(f" Last {len(last5)} entries (of {len(articles)} total):")
print(" " + "-" * 50)
for i, art in enumerate(last5, len(articles) - len(last5) + 1):
print(f" #{i}: {_preview_text(art, 70)}")
print()
# ── Main Loop ──
def main():
print_header()
while True:
print_menu()
try:
choice = input(" Choose an option [1-5]: ").strip()
except (EOFError, KeyboardInterrupt):
print("\n Goodbye!")
break
if choice == "1":
add_single_article()
elif choice == "2":
add_batch_articles()
elif choice == "3":
show_stats()
elif choice == "4":
show_last_entries()
elif choice == "5":
print(" Goodbye!")
break
else:
print(" Invalid choice, try again.")
if __name__ == "__main__":
main()