EmDashCounter / app.py
silveroxides's picture
Upload folder using huggingface_hub
0c179cc verified
Raw
History Blame Contribute Delete
12.9 kB
import gradio as gr
import requests
import zipfile
import io
import os
import tempfile
from pypdf import PdfReader
# Video URL of The Count
VIDEO_URL = "https://images-ext-1.discordapp.net/external/PD3SdBHrOGxZtFU8zypW2PjGXabBG-OwY3rXB8XWG7Y/https/media.tenor.com/1nFPylOyXZIAAAPo/sesame-street-the-count.mp4"
def get_count_response(count):
if count == 0:
return "Zero! Zero em dashes! Ah ah ah!"
elif count == 1:
return "One! One em dash! Ah ah ah!"
elif count <= 20:
counting = " ".join([f"{i}!" for i in range(1, count + 1)])
return f"{counting} {count} em dashes! Ah ah ah!"
else:
return f"One! Two! Three! ... {count}! {count} em dashes! Ah ah ah!"
def find_em_dashes_in_bytes(content_bytes, filename):
locations = []
ext = os.path.splitext(filename.lower())[1]
if ext == '.pdf':
try:
reader = PdfReader(io.BytesIO(content_bytes))
for page_number, page in enumerate(reader.pages, start=1):
text = page.extract_text()
if text:
for line_number, line in enumerate(text.splitlines(), start=1):
count = line.count('—')
if count:
locations.append({
"file": filename,
"page": page_number,
"line": line_number,
"count": count,
"text": line.strip(),
})
except Exception:
pass
else:
# Assume text-based for other common extensions
try:
content = content_bytes.decode('utf-8', errors='ignore')
for line_number, line in enumerate(content.splitlines(), start=1):
count = line.count('—')
if count:
locations.append({
"file": filename,
"page": None,
"line": line_number,
"count": count,
"text": line.strip(),
})
except Exception:
pass
return locations
def markdown_code(text):
return text.replace("`", "ˋ")
def create_audit_markdown(locations):
total_count = sum(item["count"] for item in locations)
lines = [
"# Em Dash Audit",
"",
"> **Instruction for the LLM agent:** Audit every em dash at the locations listed below, including its surrounding text and repository context. Report recommended edits that could clean up the repository. **Do not edit any files during this audit.** Only recommend changes and provide a plan for applying them.",
"",
f"**Summary:** {total_count} em dash{'es' if total_count != 1 else ''} across {len(locations)} line{'s' if len(locations) != 1 else ''}.",
"",
"## Locations",
"",
]
if not locations:
lines.append("No em dashes were found.")
else:
current_file = None
for item in locations:
if item["file"] != current_file:
current_file = item["file"]
lines.extend([f"### `{markdown_code(current_file)}`", ""])
position = f"Page {item['page']}, line {item['line']}" if item["page"] else f"Line {item['line']}"
occurrence_label = f" — {item['count']} occurrences" if item["count"] > 1 else ""
lines.append(f"- **{position}**{occurrence_label}: `{markdown_code(item['text'])}`")
lines.append("")
with tempfile.NamedTemporaryFile(
mode="w",
encoding="utf-8",
suffix=".md",
prefix="em-dash-audit-",
delete=False,
) as audit_file:
audit_file.write("\n".join(lines))
return audit_file.name
def process_input(input_url, uploaded_file):
locations = []
processed = False
# Handle Uploaded File
if uploaded_file is not None:
with open(uploaded_file.name, "rb") as f:
file_bytes = f.read()
locations.extend(find_em_dashes_in_bytes(file_bytes, os.path.basename(uploaded_file.name)))
processed = True
# Handle URL
if input_url and input_url.strip():
url = input_url.strip()
is_github = "github.com" in url
is_hf = "huggingface.co" in url
# Check if it's likely a Repo (GitHub or Hugging Face)
if (is_github or is_hf) and "/archive/" not in url and not any(url.lower().endswith(ext) for ext in ['.pdf', '.txt', '.md', '.py', '.js']):
if is_github:
# Normalize GitHub URL
base_url = url.rstrip('/')
if base_url.endswith('.git'):
base_url = base_url[:-4]
branches = ['main', 'master']
r = None
for branch in branches:
test_url = f"{base_url}/archive/refs/heads/{branch}.zip"
try:
response = requests.get(test_url, timeout=20)
if response.status_code == 200:
r = response
break
except Exception:
continue
if r:
try:
with zipfile.ZipFile(io.BytesIO(r.content)) as z:
for filename in z.namelist():
if filename.endswith('/'): continue
text_extensions = {'.py', '.md', '.txt', '.js', '.ts', '.html', '.css', '.c', '.cpp', '.h', '.java', '.rs', '.go', '.json', '.yml', '.yaml'}
if any(filename.lower().endswith(ext) for ext in text_extensions):
with z.open(filename) as f:
locations.extend(find_em_dashes_in_bytes(f.read(), filename))
processed = True
except Exception:
pass
elif is_hf:
try:
# Parse Hugging Face URL to get repo paths
parts = url.split("huggingface.co/")[-1].strip("/").split("/")
if len(parts) >= 2:
if parts[0] in ["datasets", "spaces"]:
repo_path = f"{parts[0]}/{parts[1]}/{parts[2]}"
repo_type = parts[0]
repo_id = f"{parts[1]}/{parts[2]}"
else:
repo_path = f"{parts[0]}/{parts[1]}"
repo_type = "models"
repo_id = repo_path
# Fetch root directory tree via API
api_url = f"https://huggingface.co/api/{repo_type}/{repo_id}/tree/main"
tree_response = requests.get(api_url, timeout=20)
if tree_response.status_code == 200:
text_extensions = {'.py', '.md', '.txt', '.js', '.ts', '.html', '.css', '.c', '.cpp', '.h', '.java', '.rs', '.go', '.json', '.yml', '.yaml'}
for item in tree_response.json():
if item.get("type") == "file":
filename = item.get("path")
if any(filename.lower().endswith(ext) for ext in text_extensions):
# Fetch raw file content
raw_url = f"https://huggingface.co/{repo_path}/resolve/main/{filename}"
file_resp = requests.get(raw_url, timeout=20)
if file_resp.status_code == 200:
locations.extend(find_em_dashes_in_bytes(file_resp.content, filename))
processed = True
except Exception:
pass
else:
# Handle as single file URL
try:
response = requests.get(url, timeout=20)
if response.status_code == 200:
filename = url.split('/')[-1] or "file.txt"
locations.extend(find_em_dashes_in_bytes(response.content, filename))
processed = True
except Exception:
pass
if not processed:
return (
"I could not find anything to count! Provide a valid URL or upload a file! Ah ah ah!",
gr.update(visible=False),
gr.update(value=None, visible=False),
)
total_count = sum(item["count"] for item in locations)
audit_path = create_audit_markdown(locations)
return get_count_response(total_count), gr.update(visible=True), gr.update(value=audit_path, visible=True)
# Define custom CSS for a Sesame Street / The Count theme
custom_css = """
body, .gradio-container { background-color: #000000 !important; color: #e0e0e0 !important; font-family: 'Georgia', serif !important; }
.gr-box { background-color: #1a0633 !important; border: 2px solid #4b0082 !important; }
#large-input textarea, #large-input input {
background-color: #2b0b4d !important;
color: #ffffff !important;
font-size: 1.5rem !important;
border: 2px solid #9932cc !important;
}
#large-output textarea, #large-output input {
background-color: #000000 !important;
color: #32cd32 !important;
font-size: 1.8rem !important;
font-weight: bold !important;
border: 3px solid #32cd32 !important;
text-shadow: 2px 2px #1a0633;
}
#large-button {
background-color: #4b0082 !important;
color: #32cd32 !important;
font-size: 1.6rem !important;
font-weight: bold !important;
border: 4px solid #32cd32 !important;
height: 80px !important;
box-shadow: 0 0 10px #4b0082;
transition: all 0.3s ease;
cursor: pointer;
}
#large-button:hover {
background-color: #9932cc !important;
color: #ffffff !important;
box-shadow: 0 0 20px #32cd32;
transform: scale(1.02);
}
.gr-form label span {
font-size: 1.4rem !important;
color: #9932cc !important;
font-weight: bold !important;
text-transform: uppercase;
letter-spacing: 2px;
}
h1 { color: #9932cc !important; text-shadow: 2px 2px #000000 !important; font-size: 3rem !important; text-align: center !important; }
h3 { color: #e0e0e0 !important; text-align: center !important; margin-bottom: 2rem !important; }
.file-upload { background-color: #2b0b4d !important; border: 2px dashed #9932cc !important; }
"""
with gr.Blocks(title="The Count's Em Dash Counter") as demo:
gr.Markdown("# 🧛‍♂️ The Count's Em Dash Counter")
gr.Markdown("### Provide a GitHub repo, a file URL, or upload documents to count em dashes (—)! Ah ah ah!")
with gr.Row():
with gr.Column(scale=4):
repo_url = gr.Textbox(
label="GitHub or File URL",
placeholder="https://github.com/... OR https://example.com/file.pdf",
lines=1,
elem_id="large-input"
)
file_upload = gr.File(
label="Upload Documents (PDF, TXT, MD...)",
file_types=[".pdf", ".txt", ".md", ".py", ".js", ".ts", ".html", ".css", ".json"],
elem_classes="file-upload"
)
count_btn = gr.Button("Count them! Ah ah ah!", variant="primary", elem_id="large-button")
result_text = gr.Textbox(
label="The Count Says:",
interactive=False,
elem_id="large-output",
lines=3
)
with gr.Row():
gr.Markdown("Download a Markdown location report with audit-only instructions for an LLM agent.")
audit_download = gr.DownloadButton(
"DOWNLOAD",
visible=False,
variant="secondary",
)
with gr.Column(scale=5):
video = gr.Video(
value=VIDEO_URL,
label="The Count",
autoplay=True,
loop=True,
show_label=False,
interactive=False,
visible=False
)
count_btn.click(
fn=process_input,
inputs=[repo_url, file_upload],
outputs=[result_text, video, audit_download]
)
if __name__ == "__main__":
demo.launch(css=custom_css)