| import re |
| import math |
| import json |
| import string |
| import tempfile |
| import os |
| from collections import Counter |
| from html.parser import HTMLParser |
| from typing import Dict, List, Tuple, Any, Optional |
| import xml.etree.ElementTree as ET |
| import gradio as gr |
|
|
| |
| |
| |
|
|
| class SimpleHTMLInspector(HTMLParser): |
| """ |
| A lightweight, pure-Python HTML inspector utilizing standard HTMLParser. |
| Fulfills educational HTML analysis without external BeautifulSoup or library dependencies. |
| """ |
| def __init__(self): |
| super().__init__() |
| self.tags: List[str] = [] |
| self.content_map: List[Tuple[str, str]] = [] |
| self.tag_stack: List[str] = [] |
|
|
| def handle_starttag(self, tag: str, attrs: List[Tuple[str, Optional[str]]]): |
| self.tags.append(tag) |
| self.tag_stack.append(tag) |
|
|
| def handle_data(self, data: str): |
| cleaned = data.strip() |
| if cleaned: |
| current_tag = self.tag_stack[-1] if self.tag_stack else "text" |
| |
| if current_tag not in ["script", "style"]: |
| self.content_map.append((current_tag, cleaned)) |
|
|
| def handle_endtag(self, tag: str): |
| if self.tag_stack and self.tag_stack[-1] == tag: |
| self.tag_stack.pop() |
|
|
|
|
| def parse_csv_custom(text: str, delimiter: str = ",") -> Tuple[List[str], List[List[str]], int, int]: |
| """ |
| A custom state-machine CSV parser built from scratch. |
| Complies with rules: No import csv, manages quote boundaries, escaped quotes, and newlines correctly. |
| """ |
| lines = text.splitlines() |
| if not lines: |
| return [], [], 0, 0 |
|
|
| parsed_rows: List[List[str]] = [] |
| |
| for line in lines: |
| if not line.strip(): |
| continue |
| row_fields = [] |
| current_field = [] |
| in_quotes = False |
| i = 0 |
| n = len(line) |
| |
| while i < n: |
| char = line[i] |
| if in_quotes: |
| if char == '"': |
| |
| if i + 1 < n and line[i+1] == '"': |
| current_field.append('"') |
| i += 2 |
| continue |
| else: |
| in_quotes = False |
| else: |
| current_field.append(char) |
| else: |
| if char == '"': |
| in_quotes = True |
| elif char == delimiter: |
| row_fields.append("".join(current_field)) |
| current_field = [] |
| else: |
| current_field.append(char) |
| i += 1 |
| row_fields.append("".join(current_field)) |
| parsed_rows.append(row_fields) |
|
|
| if not parsed_rows: |
| return [], [], 0, 0 |
|
|
| headers = parsed_rows[0] |
| data_rows = parsed_rows[1:] if len(parsed_rows) > 1 else [] |
| |
| col_count = len(headers) |
| row_count = len(parsed_rows) |
| |
| return headers, data_rows, row_count, col_count |
|
|
|
|
| def detect_dominant_delimiter(text: str) -> str: |
| """ |
| Analyzes lines to guess if the delimiter is comma, semicolon, or tab. |
| Looks for the highest count with consistency across initial lines. |
| """ |
| candidates = [",", ";", "\t"] |
| sample_lines = [l for l in text.splitlines()[:5] if l.strip()] |
| if not sample_lines: |
| return "," |
|
|
| best_delim = "," |
| best_score = -1 |
|
|
| for delim in candidates: |
| counts = [line.count(delim) for line in sample_lines] |
| avg_count = sum(counts) / len(counts) |
| |
| variance = sum((c - avg_count)**2 for c in counts) / len(counts) |
| |
| if avg_count > 0.5: |
| score = avg_count / (1.0 + variance) |
| if score > best_score: |
| best_score = score |
| best_delim = delim |
| |
| return best_delim |
|
|
| |
| |
| |
|
|
| class TextAnalyzer: |
| """ |
| The monolithic analysis processor wrapping all character, |
| unicode, word, sentence, regex, and structured analyses. |
| """ |
| def __init__(self, text: str): |
| self.text = text |
| self.bytes_data = text.encode("utf-8") |
| |
| |
| self.lines = text.splitlines() |
| |
| |
| self.words_raw = re.findall(r'\b[a-zA-Z0-9_\'-]+\b', text) |
| self.words = [w.strip() for w in self.words_raw if w.strip()] |
| |
| |
| self.sentences = [s.strip() for s in re.split(r'[.!?]+(?=\s|$)', text) if s.strip()] |
|
|
| def get_overview(self) -> Dict[str, Any]: |
| """Module 1: General document measurements.""" |
| char_count = len(self.text) |
| word_count = len(self.words) |
| sentence_count = max(1 if word_count > 0 and not self.sentences else 0, len(self.sentences)) |
| line_count = len(self.lines) |
| |
| |
| paragraphs = [p for p in re.split(r'\n\s*\n', self.text) if p.strip()] |
| paragraph_count = len(paragraphs) |
| |
| byte_count = len(self.bytes_data) |
| |
| avg_word_length = sum(len(w) for w in self.words) / word_count if word_count > 0 else 0.0 |
| avg_sentence_length = word_count / sentence_count if sentence_count > 0 else 0.0 |
|
|
| return { |
| "char_count": char_count, |
| "word_count": word_count, |
| "sentence_count": sentence_count, |
| "line_count": line_count, |
| "paragraph_count": paragraph_count, |
| "byte_count": byte_count, |
| "avg_word_length": round(avg_word_length, 2), |
| "avg_sentence_length": round(avg_sentence_length, 2), |
| } |
|
|
| def generate_encoding_table(self, max_chars: int = 150) -> List[List[str]]: |
| """ |
| Module 2: Technical character encodings mappings. |
| Limits rows to avoid crashing browser windows with massive datasets. |
| """ |
| rows = [] |
| for char in self.text[:max_chars]: |
| dec = ord(char) |
| hex_val = f"0x{dec:X}" |
| bin_val = f"{dec:08b}" |
| |
| |
| ascii_repr = char if dec < 128 else "N/A" |
| if dec < 32 or dec == 127: |
| ascii_repr = "Control Char" if dec != 10 and dec != 9 else ("[LF]" if dec == 10 else "[TAB]") |
| |
| unicode_point = f"U+{dec:04X}" |
| |
| |
| utf8_bytes = char.encode("utf-8") |
| utf8_len = len(utf8_bytes) |
| utf8_bytes_str = " ".join(f"{b:02X}" for b in utf8_bytes) |
| |
| rows.append([ |
| char if dec >= 32 else " ", |
| ascii_repr, |
| unicode_point, |
| str(dec), |
| hex_val, |
| bin_val, |
| f"{utf8_len} byte(s)", |
| utf8_bytes_str |
| ]) |
| return rows |
|
|
| def generate_unicode_explorer(self, max_chars: int = 100) -> str: |
| """ |
| Module 3: Educational breakdown highlighting the prefix bit structure of UTF-8. |
| Provides highly descriptive, interactive visual representation of multibyte UTF-8. |
| """ |
| html = ['<div class="space-y-4">'] |
| |
| limit_text = self.text[:max_chars] |
| for idx, char in enumerate(limit_text): |
| dec_val = ord(char) |
| cp = f"U+{dec_val:04X}" |
| utf8_b = char.encode("utf-8") |
| |
| html.append('<div class="p-3 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 flex items-center justify-between shadow-sm hover:shadow-md transition">') |
| |
| |
| char_display = char if dec_val >= 32 else f"<span class='text-xs text-amber-500 font-mono'>CTRL({dec_val})</span>" |
| html.append(f'<div class="flex items-center space-x-4">') |
| html.append(f' <div class="w-12 h-12 bg-teal-50 dark:bg-teal-950 text-teal-700 dark:text-teal-300 rounded-lg flex items-center justify-center font-bold text-2xl border border-teal-200">{char_display}</div>') |
| html.append(f' <div>') |
| html.append(f' <div class="text-sm font-bold text-slate-800 dark:text-slate-100">Character #{idx + 1}</div>') |
| html.append(f' <div class="text-xs font-mono text-slate-500">{cp}</div>') |
| html.append(f' </div>') |
| html.append(f'</div>') |
| |
| |
| html.append('<div class="text-right font-mono text-xs">') |
| html.append('<div class="text-slate-400 mb-1">UTF-8 Bit Structure:</div>') |
| |
| for b in utf8_b: |
| bin_str = f"{b:08b}" |
| |
| if len(utf8_b) == 1: |
| |
| formatted_bin = f"<span class='text-green-600 dark:text-green-400 font-bold'>0</span>{bin_str[1:]}" |
| lbl = "ASCII Pattern" |
| elif len(utf8_b) == 2: |
| if b == utf8_b[0]: |
| formatted_bin = f"<span class='text-blue-600 dark:text-blue-400 font-bold'>110</span>{bin_str[3:]}" |
| lbl = "2-Byte Header" |
| else: |
| formatted_bin = f"<span class='text-purple-600 dark:text-purple-400 font-bold'>10</span>{bin_str[2:]}" |
| lbl = "Data Byte" |
| elif len(utf8_b) == 3: |
| if b == utf8_b[0]: |
| formatted_bin = f"<span class='text-orange-600 dark:text-orange-400 font-bold'>1110</span>{bin_str[4:]}" |
| lbl = "3-Byte Header" |
| else: |
| formatted_bin = f"<span class='text-purple-600 dark:text-purple-400 font-bold'>10</span>{bin_str[2:]}" |
| lbl = "Data Byte" |
| else: |
| if b == utf8_b[0]: |
| formatted_bin = f"<span class='text-red-600 dark:text-red-400 font-bold'>11110</span>{bin_str[5:]}" |
| lbl = "4-Byte Header (Emoji/Rare)" |
| else: |
| formatted_bin = f"<span class='text-purple-600 dark:text-purple-400 font-bold'>10</span>{bin_str[2:]}" |
| lbl = "Data Byte" |
| |
| html.append(f'<div class="flex items-center justify-end space-x-2">') |
| html.append(f' <span class="text-[10px] text-slate-400 italic">({lbl})</span>') |
| html.append(f' <span class="bg-slate-100 dark:bg-slate-900 px-1.5 py-0.5 rounded tracking-widest">{formatted_bin}</span>') |
| html.append(f'</div>') |
| |
| html.append('</div>') |
| html.append('</div>') |
| |
| html.append('</div>') |
| |
| if len(self.text) > max_chars: |
| html.append(f'<div class="p-3 text-center text-xs text-amber-600 bg-amber-50 rounded border border-amber-200 mt-2">Displaying first {max_chars} characters. Your input has {len(self.text)} total chars.</div>') |
| |
| return "\n".join(html) |
|
|
| def get_char_frequencies(self) -> List[Tuple[str, int, float]]: |
| """Module 4: Sort and measure character densities.""" |
| total = len(self.text) |
| if total == 0: |
| return [] |
| cnt = Counter(self.text) |
| sorted_chars = cnt.most_common() |
| return [(c, count, round((count / total) * 100, 2)) for c, count in sorted_chars] |
|
|
| def get_word_frequencies(self) -> List[Tuple[str, int]]: |
| """Module 5: Tokenize, clean and compute high vocabulary counts.""" |
| if not self.words: |
| return [] |
| normalized_words = [w.lower() for w in self.words] |
| cnt = Counter(normalized_words) |
| return cnt.most_common() |
|
|
| def get_string_statistics(self) -> Dict[str, Any]: |
| """Module 6: Text measurements and distributions.""" |
| unique_words = len(set(w.lower() for w in self.words)) |
| unique_chars = len(set(self.text)) |
| whitespace_count = sum(1 for c in self.text if c.isspace()) |
| tab_count = self.text.count('\t') |
| newline_count = self.text.count('\n') |
| digit_count = sum(1 for c in self.text if c.isdigit()) |
| uppercase_count = sum(1 for c in self.text if c.isupper()) |
| lowercase_count = sum(1 for c in self.text if c.islower()) |
| |
| |
| punct_set = set(string.punctuation) | {'โ', 'โ', 'โ', 'โ', 'โ', 'โ', 'โฆ', 'ยฟ', 'ยก'} |
| punctuation_count = sum(1 for c in self.text if c in punct_set) |
| |
| longest_word = max(self.words, key=len) if self.words else "" |
| shortest_word = min(self.words, key=len) if self.words else "" |
|
|
| return { |
| "longest_word": longest_word, |
| "shortest_word": shortest_word, |
| "unique_words": unique_words, |
| "unique_characters": unique_chars, |
| "whitespace_count": whitespace_count, |
| "tab_count": tab_count, |
| "newline_count": newline_count, |
| "digit_count": digit_count, |
| "uppercase_count": uppercase_count, |
| "lowercase_count": lowercase_count, |
| "punctuation_count": punctuation_count |
| } |
|
|
| def run_regex_explorer(self) -> Dict[str, List[str]]: |
| """ |
| Module 7: Matches semantic groups matching pre-defined structural regex rules. |
| """ |
| patterns = { |
| "Emails": r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', |
| "URLs": r'https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)', |
| "Phone numbers": r'\+?[0-9]{1,4}?[-.\s]?(?:\([0-9]{1,3}\)|[0-9]{1,3})[-.\s]?[0-9]{1,4}[-.\s]?[0-9]{1,4}[-.\s]?[0-9]{1,9}', |
| "Dates": r'\d{4}[-/.]\d{1,2}[-/.]\d{1,2}|\d{1,2}[-/.]\d{1,2}[-/.]\d{2,4}', |
| "Times": r'\b(?:[01]?\d|2[0-3]):[0-5]\d(?::[0-5]\d)?\s?(?:AM|PM|am|pm)?\b', |
| "Numbers": r'\b\d+(?:\.\d+)?\b', |
| "Currency values": r'(?:\$|โฌ|ยฃ|ยฅ|โน)\s?\d+(?:,\d{3})*(?:\.\d+)?\b|\b\d+(?:\.\d+)?\s?(?:USD|EUR|GBP|JPY|INR)\b', |
| "Hashtags": r'#[a-zA-Z0-9_]+', |
| "Mentions": r'@[a-zA-Z0-9_]+', |
| "IP addresses": r'\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b' |
| } |
| |
| matches = {} |
| for title, pattern in patterns.items(): |
| found = re.findall(pattern, self.text) |
| |
| matches[title] = [f.strip() for f in found if f.strip()] |
| return matches |
|
|
| def detect_format(self) -> Tuple[str, str]: |
| """ |
| Module 8: Text Format Detection. |
| Evaluates criteria and supplies natural explaining logic for findings. |
| """ |
| txt = self.text.strip() |
| if not txt: |
| return "Empty Input", "No text provided to detect structural formats." |
|
|
| |
| try: |
| json.loads(txt) |
| return "JSON (Valid)", "Parsed cleanly into key-value trees or arrays using standard python serialization (json.loads)." |
| except json.JSONDecodeError: |
| if (txt.startswith('{') and txt.endswith('}')) or (txt.startswith('[') and txt.endswith(']')): |
| return "JSON (Malformed)", "Begins/ends with brackets ({}, []), but contains syntactic errors like misplaced commas or string literals." |
|
|
| |
| try: |
| ET.fromstring(txt) |
| return "XML (Valid)", "Parsed successfully using standard ElementTree XML architecture." |
| except ET.ParseError: |
| if re.search(r'<\?xml', txt, re.I) or (txt.startswith('<') and txt.endswith('>')): |
| return "XML (Malformed)", "Starts/ends with tags or contains XML headers, but fails parser schemas." |
|
|
| |
| html_sig_tags = r'<!DOCTYPE html|<html|<body|<div\s+|<span\s+|<p\s+|<a\s+href' |
| tags_found = re.findall(r'<[a-zA-Z1-6]+(?:\s+[^>]*)*>', txt) |
| if re.search(html_sig_tags, txt, re.I) or len(tags_found) > 4: |
| return "HTML", f"Contains signature tags (matched {len(tags_found)} raw HTML elements) or document structures like '<!DOCTYPE>'." |
|
|
| |
| md_points = 0 |
| reasons = [] |
| if re.search(r'^(?:#|##|###|####|#####|######)\s+.+', txt, re.M): |
| md_points += 2 |
| reasons.append("Contains structural section indicators (# Header)") |
| if re.search(r'\[.+?\]\(https?://.+?\)', txt): |
| md_points += 2 |
| reasons.append("Identified Markdown link structures: [text](URL)") |
| if re.search(r'^[*-]\s+\w+', txt, re.M): |
| md_points += 1 |
| reasons.append("Identified bullet lists (- or *)") |
| if re.search(r'^```\w*\n', txt, re.M): |
| md_points += 3 |
| reasons.append("Identified structural code fencing tags (```)") |
|
|
| if md_points >= 3: |
| return "Markdown", f"Identified markdown layout markers: {', '.join(reasons)}." |
|
|
| |
| delim = detect_dominant_delimiter(self.text) |
| lines_with_delim = [l for l in self.text.splitlines() if delim in l] |
| if len(lines_with_delim) >= 2: |
| |
| counts = [l.count(delim) for l in lines_with_delim[:5]] |
| avg_count = sum(counts) / len(counts) |
| variance = sum((c - avg_count)**2 for c in counts) / len(counts) |
| if avg_count > 0 and variance < 1.5: |
| delim_name = "Comma" if delim == "," else ("Semicolon" if delim == ";" else "Tab") |
| return f"CSV / Delimited Text", f"Structured grid properties detected using consistently spaced delimiter: '{delim_name}' (average spacing density: {avg_count:.1f} per line)." |
|
|
| return "Plain Text (General)", "Default categorization. Contains no specialized programmatic structure, markup schemas, or clear delimiters." |
|
|
|
|
| |
| |
| |
|
|
| def analyze_all_inputs(text: str) -> List[Any]: |
| """ |
| Main callback updating all visual components in Gradio blocks concurrently. |
| """ |
| if not text or not text.strip(): |
| |
| empty_overview_html = "<div class='text-center text-slate-500 py-6'>Please supply valid text or load a sample.</div>" |
| empty_tbl = [] |
| return [ |
| empty_overview_html, empty_tbl, empty_overview_html, empty_tbl, empty_tbl, |
| empty_overview_html, empty_overview_html, empty_overview_html, empty_overview_html, "" |
| ] |
|
|
| analyzer = TextAnalyzer(text) |
| |
| |
| ov = analyzer.get_overview() |
| ov_html = f""" |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4"> |
| <div class="bg-indigo-50 dark:bg-indigo-950 p-4 rounded-xl border border-indigo-100 text-center"> |
| <span class="block text-indigo-500 text-xs font-semibold uppercase tracking-wider mb-1">Characters</span> |
| <span class="text-3xl font-extrabold text-indigo-900 dark:text-indigo-100">{ov['char_count']:,}</span> |
| </div> |
| <div class="bg-blue-50 dark:bg-blue-950 p-4 rounded-xl border border-blue-100 text-center"> |
| <span class="block text-blue-500 text-xs font-semibold uppercase tracking-wider mb-1">Words</span> |
| <span class="text-3xl font-extrabold text-blue-900 dark:text-blue-100">{ov['word_count']:,}</span> |
| </div> |
| <div class="bg-emerald-50 dark:bg-emerald-950 p-4 rounded-xl border border-emerald-100 text-center"> |
| <span class="block text-emerald-500 text-xs font-semibold uppercase tracking-wider mb-1">Sentences</span> |
| <span class="text-3xl font-extrabold text-emerald-900 dark:text-emerald-100">{ov['sentence_count']:,}</span> |
| </div> |
| <div class="bg-purple-50 dark:bg-purple-950 p-4 rounded-xl border border-purple-100 text-center"> |
| <span class="block text-purple-500 text-xs font-semibold uppercase tracking-wider mb-1">Bytes (UTF-8)</span> |
| <span class="text-3xl font-extrabold text-purple-900 dark:text-purple-100">{ov['byte_count']:,}</span> |
| </div> |
| </div> |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-4"> |
| <div class="bg-slate-50 dark:bg-slate-900 p-4 rounded-xl border border-slate-100 text-center"> |
| <span class="block text-slate-500 text-xs font-semibold uppercase tracking-wider mb-1">Lines</span> |
| <span class="text-xl font-bold text-slate-800 dark:text-slate-200">{ov['line_count']:,}</span> |
| </div> |
| <div class="bg-slate-50 dark:bg-slate-900 p-4 rounded-xl border border-slate-100 text-center"> |
| <span class="block text-slate-500 text-xs font-semibold uppercase tracking-wider mb-1">Paragraphs</span> |
| <span class="text-xl font-bold text-slate-800 dark:text-slate-200">{ov['paragraph_count']:,}</span> |
| </div> |
| <div class="bg-slate-50 dark:bg-slate-900 p-4 rounded-xl border border-slate-100 text-center"> |
| <span class="block text-slate-500 text-xs font-semibold uppercase tracking-wider mb-1">Avg Word Length</span> |
| <span class="text-xl font-bold text-slate-800 dark:text-slate-200">{ov['avg_word_length']} <small class="text-xs text-slate-400">chars</small></span> |
| </div> |
| <div class="bg-slate-50 dark:bg-slate-900 p-4 rounded-xl border border-slate-100 text-center"> |
| <span class="block text-slate-500 text-xs font-semibold uppercase tracking-wider mb-1">Avg Sentence Length</span> |
| <span class="text-xl font-bold text-slate-800 dark:text-slate-200">{ov['avg_sentence_length']} <small class="text-xs text-slate-400">words</small></span> |
| </div> |
| </div> |
| """ |
|
|
| |
| chars_stat = analyzer.get_string_statistics() |
| total_measurable = max(1, chars_stat["uppercase_count"] + chars_stat["lowercase_count"] + chars_stat["digit_count"] + chars_stat["punctuation_count"] + chars_stat["whitespace_count"]) |
| |
| def pct(val): |
| return (val / total_measurable) * 100 |
|
|
| overview_chart_html = f""" |
| <div class="mt-6 p-4 rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800"> |
| <h4 class="text-sm font-bold text-slate-800 dark:text-slate-200 mb-3">Character Class Distribution Ratio:</h4> |
| <div class="w-full flex h-6 rounded-lg overflow-hidden border border-slate-300 dark:border-slate-600 mb-3"> |
| <div style="width: {pct(chars_stat['lowercase_count'])}%" class="bg-emerald-500 hover:opacity-90" title="Lowercase ({chars_stat['lowercase_count']})"></div> |
| <div style="width: {pct(chars_stat['uppercase_count'])}%" class="bg-blue-500 hover:opacity-90" title="Uppercase ({chars_stat['uppercase_count']})"></div> |
| <div style="width: {pct(chars_stat['digit_count'])}%" class="bg-amber-500 hover:opacity-90" title="Digits ({chars_stat['digit_count']})"></div> |
| <div style="width: {pct(chars_stat['punctuation_count'])}%" class="bg-purple-500 hover:opacity-90" title="Punctuation ({chars_stat['punctuation_count']})"></div> |
| <div style="width: {pct(chars_stat['whitespace_count'])}%" class="bg-slate-400 hover:opacity-90" title="Whitespace ({chars_stat['whitespace_count']})"></div> |
| </div> |
| <div class="grid grid-cols-2 sm:grid-cols-5 gap-2 text-xs"> |
| <div class="flex items-center space-x-1.5"><span class="w-3 h-3 bg-emerald-500 rounded-sm"></span> <span>Lowercase ({chars_stat['lowercase_count']})</span></div> |
| <div class="flex items-center space-x-1.5"><span class="w-3 h-3 bg-blue-500 rounded-sm"></span> <span>Uppercase ({chars_stat['uppercase_count']})</span></div> |
| <div class="flex items-center space-x-1.5"><span class="w-3 h-3 bg-amber-500 rounded-sm"></span> <span>Digits ({chars_stat['digit_count']})</span></div> |
| <div class="flex items-center space-x-1.5"><span class="w-3 h-3 bg-purple-500 rounded-sm"></span> <span>Punctuation ({chars_stat['punctuation_count']})</span></div> |
| <div class="flex items-center space-x-1.5"><span class="w-3 h-3 bg-slate-400 rounded-sm"></span> <span>Whitespace ({chars_stat['whitespace_count']})</span></div> |
| </div> |
| </div> |
| """ |
|
|
| |
| encodings_data = analyzer.generate_encoding_table() |
|
|
| |
| unicode_html = analyzer.generate_unicode_explorer() |
|
|
| |
| char_freqs = [[repr(c)[1:-1] if c != '\n' else '[LF]', count, f"{pct:.1f}%"] for c, count, pct in analyzer.get_char_frequencies()] |
| word_freqs = [[word, count] for word, count in analyzer.get_word_frequencies()[:100]] |
|
|
| |
| stats_data = [ |
| ["Property Metric", "Value", "Educational Context"], |
| ["Longest word length", f"{len(chars_stat['longest_word'])} chars ('{chars_stat['longest_word']}')" if chars_stat['longest_word'] else "N/A", "Useful for finding outlier anomalies or unspaced strings."], |
| ["Shortest word length", f"{len(chars_stat['shortest_word'])} chars ('{chars_stat['shortest_word']}')" if chars_stat['shortest_word'] else "N/A", "Averages lower for basic grammar prepositions."], |
| ["Unique vocabulary (Words)", str(chars_stat['unique_words']), "Vocabulary density indicator before lemmatization / stemming."], |
| ["Unique character keys", str(chars_stat['unique_characters']), "Alphabet size of current document schema."], |
| ["Whitespace instances", str(chars_stat['whitespace_count']), "Sum of space, newline, tab occurrences."], |
| ["Tab character counts", str(chars_stat['tab_count']), "Important identifier for tab-separated grids or indent properties."], |
| ["Line feeds (Newlines)", str(chars_stat['newline_count']), "Tells us structural grouping spacing parameters."], |
| ["Number instances", str(chars_stat['digit_count']), "Shows the count of raw numerals."], |
| ["Uppercase keys", str(chars_stat['uppercase_count']), "Casing properties indicating structural sentences or nouns."], |
| ["Lowercase keys", str(chars_stat['lowercase_count']), "Standard core text body."], |
| ["Punctuation marks", str(chars_stat['punctuation_count']), "Sentence partitions and code punctuation keys."] |
| ] |
|
|
| |
| regex_matches = analyzer.run_regex_explorer() |
| regex_html = ['<div class="space-y-4">'] |
| for category, list_matches in regex_matches.items(): |
| count = len(list_matches) |
| color = "emerald" if count > 0 else "slate" |
| badge_class = f"bg-{color}-100 text-{color}-800 dark:bg-{color}-950 dark:text-{color}-300" |
| |
| regex_html.append(f'<div class="p-4 rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 shadow-sm">') |
| regex_html.append(f' <div class="flex items-center justify-between border-b border-slate-100 dark:border-slate-700 pb-2 mb-2">') |
| regex_html.append(f' <h4 class="text-sm font-bold text-slate-800 dark:text-slate-100">{category}</h4>') |
| regex_html.append(f' <span class="px-2 py-0.5 text-xs font-semibold rounded-full {badge_class}">{count} matches</span>') |
| regex_html.append(f' </div>') |
| |
| if count > 0: |
| |
| items_html = " ".join(f'<span class="inline-block px-2.5 py-1 bg-slate-100 dark:bg-slate-900 text-slate-800 dark:text-slate-200 rounded font-mono text-xs m-1 border border-slate-200 dark:border-slate-800">{m}</span>' for m in set(list_matches[:30])) |
| if len(list_matches) > 30: |
| items_html += f' <span class="text-xs text-slate-400 italic">...and {len(list_matches)-30} more</span>' |
| regex_html.append(f' <div class="flex flex-wrap">{items_html}</div>') |
| else: |
| regex_html.append(f' <p class="text-xs text-slate-400 italic">No matches detected in this pattern configuration.</p>') |
| |
| regex_html.append('</div>') |
| regex_html.append('</div>') |
| regex_html_str = "\n".join(regex_html) |
|
|
| |
| format_type, format_desc = analyzer.detect_format() |
| format_html_str = f""" |
| <div class="p-6 rounded-xl border border-indigo-100 dark:border-indigo-900/50 bg-indigo-50/50 dark:bg-indigo-950/30"> |
| <span class="text-[10px] uppercase font-extrabold tracking-widest text-indigo-500 block mb-1">Identified Schema Format</span> |
| <h3 class="text-2xl font-black text-indigo-900 dark:text-indigo-100 mb-2">{format_type}</h3> |
| <p class="text-sm text-indigo-700 dark:text-indigo-300 font-medium">{format_desc}</p> |
| </div> |
| """ |
|
|
| |
| csv_html_str = "<p class='text-xs text-slate-400 italic'>CSV structures not detected. This explorer will display formatting tables if text looks tabular.</p>" |
| if "CSV" in format_type or "," in text or ";" in text or "\t" in text: |
| delim = detect_dominant_delimiter(text) |
| try: |
| headers, data_rows, r_cnt, c_cnt = parse_csv_custom(text, delim) |
| if r_cnt > 0: |
| |
| preview_rows = data_rows[:10] |
| rows_html = "".join(f"<tr class='border-b border-slate-100 dark:border-slate-800 text-slate-600 dark:text-slate-300'>" + "".join(f"<td class='px-3 py-2 text-xs'>{col}</td>" for col in row) + "</tr>" for row in preview_rows) |
| headers_html = "".join(f"<th class='px-3 py-2 bg-slate-100 dark:bg-slate-900 text-left text-xs font-bold text-slate-700 dark:text-slate-300'>{h}</th>" for h in headers) |
| |
| csv_html_str = f""" |
| <div class="mt-4 border border-slate-200 dark:border-slate-700 rounded-lg overflow-hidden bg-white dark:bg-slate-800"> |
| <div class="p-3 bg-slate-50 dark:bg-slate-900 border-b border-slate-200 dark:border-slate-700 flex justify-between items-center text-xs"> |
| <span class="font-bold text-slate-700 dark:text-slate-300">Parser Output: {r_cnt} Rows ร {c_cnt} Columns</span> |
| <span class="px-2 py-0.5 bg-slate-200 dark:bg-slate-800 rounded font-mono text-slate-600 dark:text-slate-400">Delimiter: '{delim}'</span> |
| </div> |
| <div class="overflow-x-auto"> |
| <table class="w-full text-left border-collapse"> |
| <thead><tr>{headers_html}</tr></thead> |
| <tbody>{rows_html}</tbody> |
| </table> |
| </div> |
| {f'<div class="p-2 text-center text-[11px] text-slate-400 bg-slate-50 border-t border-slate-200">Showing top 10 preview rows</div>' if len(data_rows) > 10 else ''} |
| </div> |
| """ |
| except Exception as e: |
| csv_html_str = f"<p class='text-xs text-red-500 italic'>Failed standard CSV matrix processing: {str(e)}</p>" |
|
|
| |
| html_details_str = "<p class='text-xs text-slate-400 italic'>HTML markup pattern matches not found.</p>" |
| if "HTML" in format_type or "<" in text: |
| try: |
| parser = SimpleHTMLInspector() |
| parser.feed(text) |
| |
| tag_counts = Counter(parser.tags) |
| tags_stat_html = " ".join(f"<span class='inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold bg-blue-100 text-blue-800 dark:bg-blue-950 dark:text-blue-300 m-1'><{tag}> ({cnt})</span>" for tag, cnt in tag_counts.items()) |
| |
| |
| content_preview_html = "".join(f"<div class='p-2 bg-slate-50 dark:bg-slate-900 border-b border-slate-100 dark:border-slate-800 flex justify-between items-start text-xs'><span class='font-mono text-teal-600 font-bold'><{tag}></span><span class='text-slate-600 dark:text-slate-300 text-right w-2/3'>{data}</span></div>" for tag, data in parser.content_map[:15]) |
| |
| html_details_str = f""" |
| <div class="mt-4 space-y-4"> |
| <div class="p-3 border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 rounded-lg"> |
| <h5 class="text-xs font-bold text-slate-700 dark:text-slate-300 mb-2">Tag Counts (Structure Streams)</h5> |
| <div class="flex flex-wrap">{tags_stat_html if tags_stat_html else '<span class="text-xs text-slate-400 italic">No tag keys found.</span>'}</div> |
| </div> |
| |
| <div class="border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 rounded-lg overflow-hidden"> |
| <div class="p-2.5 bg-slate-50 dark:bg-slate-900 border-b border-slate-200 text-xs font-bold text-slate-700 dark:text-slate-300"> |
| Tag Text Extraction Preview |
| </div> |
| <div> |
| {content_preview_html if content_preview_html else '<div class="p-3 text-xs italic text-slate-400">No clean text stream mapped inside tags.</div>'} |
| </div> |
| {f'<div class="p-2 text-center text-[11px] text-slate-400 bg-slate-50 border-t border-slate-200">Displaying first 15 mapped nodes</div>' if len(parser.content_map) > 15 else ''} |
| </div> |
| </div> |
| """ |
| except Exception as e: |
| html_details_str = f"<p class='text-xs text-red-500 italic'>HTML parser trace error: {str(e)}</p>" |
|
|
| return [ |
| ov_html, |
| encodings_data, |
| unicode_html, |
| char_freqs, |
| word_freqs, |
| stats_data, |
| regex_html_str, |
| format_html_str, |
| csv_html_str, |
| html_details_str, |
| overview_chart_html |
| ] |
|
|
|
|
| def handle_file_upload(file_obj) -> str: |
| """ |
| Reads the file path securely and returns decoded text. |
| Handles decoding failures gracefully by falling back to errors or latin-1. |
| """ |
| if file_obj is None: |
| return "" |
| try: |
| |
| with open(file_obj.name, "rb") as f: |
| bytes_content = f.read() |
| try: |
| return bytes_content.decode("utf-8") |
| except UnicodeDecodeError: |
| |
| return bytes_content.decode("latin-1") |
| except Exception as e: |
| return f"File load error: {str(e)}" |
|
|
|
|
| def generate_report(text: str) -> str: |
| """ |
| Fulfills Module 11: Export downloadable analysis reports. |
| Formats analysis sections into a beautifully structured Markdown report file. |
| """ |
| if not text or not text.strip(): |
| |
| temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".md") |
| temp_file.write("# No analysis data available\nPlease submit text first.".encode('utf-8')) |
| temp_file.close() |
| return temp_file.name |
|
|
| analyzer = TextAnalyzer(text) |
| ov = analyzer.get_overview() |
| chars_stat = analyzer.get_string_statistics() |
| fmt, desc = analyzer.detect_format() |
| regex_matches = analyzer.run_regex_explorer() |
| char_freqs = analyzer.get_char_frequencies()[:20] |
| word_freqs = analyzer.get_word_frequencies()[:20] |
|
|
| report = [] |
| report.append("# Text Explorer Analysis Report") |
| report.append(f"Generated on: 2026-07-05 (System Analysis)\n") |
| report.append("---") |
| |
| report.append("## 1. Document Overview Statistics") |
| report.append(f"- **Characters (Count)**: {ov['char_count']}") |
| report.append(f"- **Words (Count)**: {ov['word_count']}") |
| report.append(f"- **Sentences**: {ov['sentence_count']}") |
| report.append(f"- **Line count**: {ov['line_count']}") |
| report.append(f"- **Paragraphs**: {ov['paragraph_count']}") |
| report.append(f"- **Bytes (Size)**: {ov['byte_count']} bytes") |
| report.append(f"- **Average Word Length**: {ov['avg_word_length']} characters") |
| report.append(f"- **Average Sentence Length**: {ov['avg_sentence_length']} words\n") |
|
|
| report.append("## 2. Text Format Classification") |
| report.append(f"- **Detected Structure Format**: {fmt}") |
| report.append(f"- **Deduction Reason**: {desc}\n") |
|
|
| report.append("## 3. String & Lexical Statistics") |
| report.append(f"- **Longest word**: '{chars_stat['longest_word']}'") |
| report.append(f"- **Shortest word**: '{chars_stat['shortest_word']}'") |
| report.append(f"- **Unique words**: {chars_stat['unique_words']}") |
| report.append(f"- **Unique characters**: {chars_stat['unique_characters']}") |
| report.append(f"- **Whitespaces total**: {chars_stat['whitespace_count']}") |
| report.append(f"- **Tabs**: {chars_stat['tab_count']}") |
| report.append(f"- **Newlines**: {chars_stat['newline_count']}") |
| report.append(f"- **Numerics (Digits)**: {chars_stat['digit_count']}") |
| report.append(f"- **Uppercase characters**: {chars_stat['uppercase_count']}") |
| report.append(f"- **Lowercase characters**: {chars_stat['lowercase_count']}") |
| report.append(f"- **Punctuation keys**: {chars_stat['punctuation_count']}\n") |
|
|
| report.append("## 4. Top 20 Character Frequencies") |
| report.append("| Character | Count | Percentage |") |
| report.append("| :--- | :--- | :--- |") |
| for char, cnt, pct in char_freqs: |
| c_repr = repr(char)[1:-1] if char != '\n' else '[LF]' |
| report.append(f"| `{c_repr}` | {cnt} | {pct:.1f}% |") |
| report.append("\n") |
|
|
| report.append("## 5. Top 20 Vocabulary Word Frequencies") |
| report.append("| Word | Count |") |
| report.append("| :--- | :--- |") |
| for word, cnt in word_freqs: |
| report.append(f"| {word} | {cnt} |") |
| report.append("\n") |
|
|
| report.append("## 6. Regex Inspector Discoveries") |
| for key, items in regex_matches.items(): |
| if items: |
| report.append(f"- **{key}** ({len(items)} found): {', '.join(set(items[:15]))}") |
| else: |
| report.append(f"- **{key}**: 0 instances detected.") |
| |
| report.append("\n---\n*Report generated by Text Explorer Space (Pure Python Educational Analyzer).*") |
|
|
| |
| temp_dir = tempfile.gettempdir() |
| file_path = os.path.join(temp_dir, "text_explorer_report.md") |
| with open(file_path, "w", encoding="utf-8") as f: |
| f.write("\n".join(report)) |
| |
| return file_path |
|
|
|
|
| |
| |
| |
|
|
| CSS = """ |
| body { |
| background-color: #f8fafc; |
| } |
| .gradio-container { |
| font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; |
| } |
| .header-hero { |
| background: linear-gradient(135deg, #4f46e5 0%, #2563eb 100%); |
| color: white !important; |
| border-radius: 1rem; |
| padding: 2.5rem 2rem; |
| margin-bottom: 2rem; |
| text-align: center; |
| box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); |
| } |
| .header-hero h1 { |
| color: white !important; |
| font-weight: 900 !important; |
| letter-spacing: -0.025em; |
| font-size: 2.25rem !important; |
| margin-bottom: 0.5rem; |
| } |
| .header-hero p { |
| color: #e0e7ff !important; |
| font-size: 1rem; |
| } |
| .educational-card { |
| background-color: #f0fdfa; |
| border: 1px solid #ccfbf1; |
| border-radius: 0.75rem; |
| padding: 1rem; |
| margin-bottom: 1rem; |
| } |
| .educational-card h4 { |
| color: #0f766e !important; |
| margin-top: 0 !important; |
| } |
| .educational-card p, .educational-card li { |
| color: #115e59 !important; |
| font-size: 0.875rem !important; |
| } |
| """ |
|
|
| |
| |
| |
|
|
| SAMPLES = { |
| "Simple English": "Hello World! Feel free to paste any text here to test the NLP Text Explorer system. It works immediately without external libraries.", |
| "Multilingual & Emoji": "Multilingual test: Bonjour, ใใใซใกใฏ, ์๋
ํ์ธ์! Here is an emoji breakdown to inspect UTF-8 bytes: ๐ ๐ ๐. What is their representation in binary memory space?", |
| "HTML document sample": '<!DOCTYPE html>\n<html>\n<head>\n <title>Sample Sandbox</title>\n</head>\n<body>\n <div class="content">\n <h1>Welcome to Text Explorer!</h1>\n <p>This is standard markup designed to verify our pure HTML-Parser extraction utilities.</p>\n <a href="https://example.com">Visit our project</a>\n </div>\n</body>\n</html>', |
| "Tabular CSV format": "username,email,role,joined_date\nsmith_john,john.smith@gmail.com,Administrator,2024-03-24\elizabeth_k,k.elizabeth@yahoo.com,Contributor,2025-05-15\ntech_support,support@domain.org,User,2026-01-10", |
| "Markdown document": ( |
| "# Markdown Document Guide\n\n" |
| "Welcome to this structural text parsing review. Here are some key attributes:\n\n" |
| "- Support lists\n- Supports link items: [Hugging Face Space](https://huggingface.co/spaces)\n\n" |
| "## Sub-headers\n" |
| "```python\n" |
| "def test():\n" |
| " return 'Hello, Markdown analyzer!'\n" |
| "```" |
| ) |
| } |
|
|
| |
| |
| |
|
|
| with gr.Blocks(css=CSS, title="Text Explorer - Educational Pre-NLP Inspector") as demo: |
| |
| |
| gr.HTML(""" |
| <div class="header-hero"> |
| <h1>๐ Text Explorer</h1> |
| <p>Analyze and demystify raw character representation, encodings, byte streams, and structural text layouts before high-level NLP tokenization.</p> |
| </div> |
| """) |
| |
| with gr.Row(): |
| |
| with gr.Column(scale=4): |
| gr.Markdown("### ๐ฅ Document Source Input") |
| input_text = gr.Textbox( |
| label="Source Text Area", |
| placeholder="Paste your paragraphs, CSV structure, HTML content, or multilingual strings here...", |
| lines=10, |
| max_lines=25, |
| elem_id="input-textbox" |
| ) |
| |
| with gr.Row(): |
| file_upload = gr.File( |
| label="Or Upload Text File (.txt, .csv, .json, .html, .md)", |
| file_types=[".txt", ".csv", ".json", ".html", ".md"], |
| type="filepath", |
| ) |
| |
| gr.Markdown("๐ก **Educational Presets**") |
| with gr.Row(): |
| |
| for name, content in SAMPLES.items(): |
| gr.Button(name, size="sm").click( |
| fn=lambda val=content: val, |
| outputs=input_text |
| ) |
|
|
| |
| with gr.Column(scale=6): |
| gr.Markdown("### โก Real-Time Analytical Breakdown") |
| |
| with gr.Tabs(): |
| |
| with gr.TabItem("๐ Overview"): |
| with gr.Accordion("๐ Educational Spotlight: The Lexical Units", open=True): |
| gr.HTML(""" |
| <div class="educational-card"> |
| <h4>How Computers Partition Text</h4> |
| <p>Before an NLP model can comprehend context, the document must be mathematically measured:</p> |
| <ul style="list-style-type: disc; margin-left: 1.25rem; margin-top: 0.5rem;"> |
| <li><b>Bytes</b>: The actual digital footprint in physical memory (UTF-8 allocates 1 to 4 bytes per character).</li> |
| <li><b>Words & Sentences</b>: Fundamental logic nodes. Without standard tools like NLTK/spaCy, computers extract tokens using punctuation borders and boundary regex.</li> |
| </ul> |
| </div> |
| """) |
| overview_html = gr.HTML("<div class='text-center text-slate-400 py-6'>Analyze text to generate document insights.</div>") |
| overview_chart = gr.HTML() |
|
|
| |
| with gr.TabItem("๐ข Encodings Table"): |
| with gr.Accordion("๐ซ Educational Spotlight: Encodings Simplified", open=False): |
| gr.HTML(""" |
| <div class="educational-card"> |
| <h4>Encoding Architecture Explained</h4> |
| <p>Every character is assigned a unique tracking coordinate:</p> |
| <ul style="list-style-type: disc; margin-left: 1.25rem; margin-top: 0.5rem;"> |
| <li><b>ASCII</b>: Traditional 7-bit system spanning decimal positions 0 through 127. Standard English keys only.</li> |
| <li><b>Unicode</b>: A universal catalog assigning identical codepoint indices (e.g., U+1F60A) for all global languages and emojis.</li> |
| <li><b>UTF-8</b>: An optimized encoding translating those codepoints into binary arrays, using smaller slices (1 byte) for English and wider slices for eastern languages or emojis.</li> |
| </ul> |
| </div> |
| """) |
| encoding_table = gr.DataFrame( |
| headers=["Char", "ASCII Representation", "Unicode Point", "Decimal", "Hexadecimal", "Binary Value", "UTF-8 Length", "UTF-8 Bytes"], |
| datatype=["str", "str", "str", "str", "str", "str", "str", "str"], |
| wrap=True |
| ) |
|
|
| |
| with gr.TabItem("๐ Unicode Explorer"): |
| with gr.Accordion("๐ Educational Spotlight: Bit Patterns of UTF-8", open=False): |
| gr.HTML(""" |
| <div class="educational-card"> |
| <h4>Deciphering the UTF-8 Multi-byte Rules</h4> |
| <p>UTF-8 dynamically sizes bytes based on prefix triggers. Inspect the binary tags below:</p> |
| <ul style="list-style-type: disc; margin-left: 1.25rem; margin-top: 0.5rem;"> |
| <li><b>0xxxxxxx</b> (1 Byte): Matches normal English ASCII indices seamlessly.</li> |
| <li><b>110xxxxx 10xxxxxx</b> (2 Bytes): Directs unicode sequences (e.g. Greek / Cyrillic).</li> |
| <li><b>1110xxxx 10xxxxxx 10xxxxxx</b> (3 Bytes): Covers general Asian characters (CJK blocks).</li> |
| <li><b>11110xxx 10xxxxxx 10xxxxxx 10xxxxxx</b> (4 Bytes): Represents deep emoticons and rare historical glyphs.</li> |
| </ul> |
| </div> |
| """) |
| unicode_explorer_html = gr.HTML("<div class='text-center text-slate-400 py-6'>Displaying individual unicode bit structures...</div>") |
|
|
| |
| with gr.TabItem("๐ Frequencies"): |
| with gr.Row(): |
| with gr.Column(): |
| gr.Markdown("#### Character Frequency Analysis") |
| char_freq_table = gr.DataFrame( |
| headers=["Character Key", "Occurrence", "Ratio"], |
| datatype=["str", "number", "str"] |
| ) |
| with gr.Column(): |
| gr.Markdown("#### Word Frequency Analysis (Vocabulary)") |
| word_freq_table = gr.DataFrame( |
| headers=["Lowercased Token", "Occurrence"], |
| datatype=["str", "number"] |
| ) |
|
|
| |
| with gr.TabItem("๐งฎ Detailed Statistics"): |
| stats_table = gr.DataFrame( |
| headers=["Property Metric", "Value", "Educational Context"], |
| datatype=["str", "str", "str"], |
| wrap=True |
| ) |
|
|
| |
| with gr.TabItem("๐ Regex Explorer"): |
| with gr.Accordion("๐ง Educational Spotlight: Structured Pattern Matching", open=False): |
| gr.HTML(""" |
| <div class="educational-card"> |
| <h4>Regex: The Structural Extraction Language</h4> |
| <p>Regular expressions look for matching text layouts. They are extremely effective before models compile lexical vectors:</p> |
| <ul style="list-style-type: disc; margin-left: 1.25rem; margin-top: 0.5rem;"> |
| <li><b>Emails</b>: Matches user accounts bounded by domains (<code>username@host.ext</code>).</li> |
| <li><b>IP Addresses</b>: Recognizes IPv4 address streams (digits separated by decimal boundaries).</li> |
| </ul> |
| </div> |
| """) |
| regex_explorer_html = gr.HTML("<div class='text-center text-slate-400 py-6'>Matches category analysis lists...</div>") |
|
|
| |
| with gr.TabItem("๐งฑ Layout Formats"): |
| with gr.Accordion("๐ฆ Educational Spotlight: Layout Analysis Systems", open=False): |
| gr.HTML(""" |
| <div class="educational-card"> |
| <h4>Markup and Matrix Structure Detection Rules</h4> |
| <p>Computers parse document systems using distinctive delimiters and structural tags:</p> |
| <ul style="list-style-type: disc; margin-left: 1.25rem; margin-top: 0.5rem;"> |
| <li><b>HTML/XML</b>: Uses bracket boundaries (<code><tag></code>) to represent nested elements.</li> |
| <li><b>CSV Grid Structure</b>: Uses consistent grid markers (such as commas) containing identical segments per line row.</li> |
| </ul> |
| </div> |
| """) |
| format_overview_html = gr.HTML() |
| |
| with gr.Accordion("CSV Preview Engine (Non-Library Machine)", open=True): |
| csv_preview_html = gr.HTML("<p class='text-xs text-slate-400 italic'>No CSV parsed grid display loaded yet.</p>") |
| |
| with gr.Accordion("HTML Tree Parser Detail", open=True): |
| html_preview_html = gr.HTML("<p class='text-xs text-slate-400 italic'>No HTML tag analysis details loaded yet.</p>") |
|
|
| |
| with gr.TabItem("๐พ Export Report"): |
| gr.Markdown("### Create Inspection Summary File") |
| gr.Markdown("Download a complete, beautifully structured Markdown text report mapping the calculations of the explorer for external study.") |
| report_btn = gr.Button("๐จ Compile Markdown Analysis Report", variant="primary") |
| report_file = gr.File(label="Downloadable Analysis Report", type="filepath") |
|
|
| |
| |
| |
|
|
| |
| file_upload.change( |
| fn=handle_file_upload, |
| inputs=file_upload, |
| outputs=input_text |
| ) |
|
|
| |
| ui_outputs = [ |
| overview_html, |
| encoding_table, |
| unicode_explorer_html, |
| char_freq_table, |
| word_freq_table, |
| stats_table, |
| regex_explorer_html, |
| format_overview_html, |
| csv_preview_html, |
| html_preview_html, |
| overview_chart |
| ] |
|
|
| |
| input_text.change( |
| fn=analyze_all_inputs, |
| inputs=input_text, |
| outputs=ui_outputs |
| ) |
|
|
| |
| report_btn.click( |
| fn=generate_report, |
| inputs=input_text, |
| outputs=report_file |
| ) |
|
|
| |
| if __name__ == "__main__": |
| demo.launch() |