Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -924,10 +924,10 @@ class FAADocumentChecker(DocumentChecker):
|
|
924 |
return DocumentCheckResult(success=False, issues=[{'error': 'Invalid document input'}])
|
925 |
|
926 |
# Common words that might appear in uppercase but aren't acronyms
|
927 |
-
heading_words = self.config_manager.config.get('heading_words',
|
928 |
|
929 |
# Standard acronyms that don't need to be defined
|
930 |
-
predefined_acronyms = self.config_manager.config.get('predefined_acronyms',
|
931 |
|
932 |
# Tracking structures
|
933 |
defined_acronyms = {} # Stores definition info
|
@@ -936,7 +936,7 @@ class FAADocumentChecker(DocumentChecker):
|
|
936 |
|
937 |
# Patterns
|
938 |
defined_pattern = re.compile(r'\b([\w\s&]+?)\s*\((\b[A-Z]{2,}\b)\)')
|
939 |
-
acronym_pattern = re.compile(r'\b[A-Z]{2,}\b(?!\s*[:.]\s*)')
|
940 |
|
941 |
for paragraph in doc:
|
942 |
# Skip lines that appear to be headings (all uppercase with common heading words)
|
@@ -944,56 +944,46 @@ class FAADocumentChecker(DocumentChecker):
|
|
944 |
if all(word.isupper() for word in words) and any(word in heading_words for word in words):
|
945 |
continue
|
946 |
|
947 |
-
#
|
948 |
-
|
949 |
-
|
950 |
-
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
'used': False # Initially not used
|
961 |
-
}
|
962 |
-
definitions_in_paragraph.add(sentence)
|
963 |
-
else:
|
964 |
-
# Handle duplicate definitions if necessary
|
965 |
-
pass # You may add logic for duplicate definitions
|
966 |
-
|
967 |
-
# Second pass: Check for acronym usage in sentences that are not definitions
|
968 |
-
for sentence in sentences:
|
969 |
-
if sentence in definitions_in_paragraph:
|
970 |
-
continue # Skip definition sentences
|
971 |
|
972 |
-
|
973 |
-
|
974 |
-
|
|
|
975 |
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
|
998 |
# Check for defined but unused acronyms
|
999 |
unused_acronyms = [
|
@@ -1015,6 +1005,7 @@ class FAADocumentChecker(DocumentChecker):
|
|
1015 |
|
1016 |
return DocumentCheckResult(success=success, issues=issues)
|
1017 |
|
|
|
1018 |
@profile_performance
|
1019 |
def check_terminology(self, doc: List[str]) -> DocumentCheckResult:
|
1020 |
"""
|
@@ -1765,7 +1756,7 @@ class DocumentCheckResultsFormatter:
|
|
1765 |
'solution': 'Format heading periods according to document type requirements',
|
1766 |
'example_fix': {
|
1767 |
'before': 'Purpose',
|
1768 |
-
'after': 'Purpose.'
|
1769 |
}
|
1770 |
},
|
1771 |
'table_figure_reference_check': {
|
@@ -1779,8 +1770,8 @@ class DocumentCheckResultsFormatter:
|
|
1779 |
},
|
1780 |
'acronym_check': {
|
1781 |
'title': 'Acronym Definition Issues',
|
1782 |
-
'description': 'Ensures every acronym is properly introduced with its full term at first use. The check identifies undefined acronyms
|
1783 |
-
'solution': 'Define each acronym at its first use
|
1784 |
'example_fix': {
|
1785 |
'before': 'This order establishes general FAA organizational policies.',
|
1786 |
'after': 'This order establishes general Federal Aviation Administration (FAA) organizational policies.'
|
@@ -1841,13 +1832,29 @@ class DocumentCheckResultsFormatter:
|
|
1841 |
}
|
1842 |
}
|
1843 |
}
|
1844 |
-
|
|
|
1845 |
def _format_colored_text(self, text: str, color: str) -> str:
|
1846 |
-
"""Helper method to format colored text with reset.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1847 |
return f"{color}{text}{Style.RESET_ALL}"
|
1848 |
|
1849 |
def _format_example(self, example_fix: Dict[str, str]) -> List[str]:
|
1850 |
-
"""Format example fixes consistently.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1851 |
return [
|
1852 |
f" ❌ Incorrect: {example_fix['before']}",
|
1853 |
f" ✓ Correct: {example_fix['after']}"
|
@@ -1870,7 +1877,7 @@ class DocumentCheckResultsFormatter:
|
|
1870 |
output.append(f" • {heading}")
|
1871 |
|
1872 |
return output
|
1873 |
-
|
1874 |
def _format_period_issues(self, result: DocumentCheckResult) -> List[str]:
|
1875 |
"""Format period check issues consistently."""
|
1876 |
output = []
|
@@ -1882,28 +1889,26 @@ class DocumentCheckResultsFormatter:
|
|
1882 |
output.append(f" • {issue['message']}")
|
1883 |
|
1884 |
return output
|
1885 |
-
|
1886 |
def _format_reference_issues(self, result: DocumentCheckResult) -> List[str]:
|
1887 |
-
"""Format
|
1888 |
output = []
|
1889 |
|
1890 |
-
|
1891 |
-
|
1892 |
-
|
1893 |
-
|
1894 |
-
|
1895 |
-
|
1896 |
-
|
1897 |
-
|
1898 |
-
|
1899 |
-
|
1900 |
-
|
1901 |
-
|
1902 |
-
if len(result.issues) > 3:
|
1903 |
-
output.append(f"\n ... and {len(result.issues) - 3} more similar issues.")
|
1904 |
|
1905 |
return output
|
1906 |
-
|
1907 |
def _format_caption_issues(self, result: DocumentCheckResult) -> List[str]:
|
1908 |
"""Format caption issues consistently."""
|
1909 |
output = []
|
@@ -1913,25 +1918,55 @@ class DocumentCheckResultsFormatter:
|
|
1913 |
output.append(f" • {issue.get('incorrect_caption', '')} (correct format: {issue.get('correct_format', '')})")
|
1914 |
|
1915 |
return output
|
1916 |
-
|
1917 |
def _format_standard_issue(self, issue: Dict[str, Any]) -> str:
|
1918 |
"""Format a standard issue consistently."""
|
1919 |
if isinstance(issue, dict):
|
1920 |
-
# Handle
|
1921 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1922 |
return textwrap.fill(
|
1923 |
-
f" •
|
1924 |
width=76,
|
1925 |
subsequent_indent=' '
|
1926 |
)
|
1927 |
-
|
1928 |
-
|
|
|
1929 |
return textwrap.fill(
|
1930 |
-
f" •
|
1931 |
width=76,
|
1932 |
subsequent_indent=' '
|
1933 |
)
|
1934 |
-
|
|
|
1935 |
elif 'sentence' in issue:
|
1936 |
return textwrap.fill(
|
1937 |
issue['sentence'],
|
@@ -1939,32 +1974,55 @@ class DocumentCheckResultsFormatter:
|
|
1939 |
initial_indent=' • ',
|
1940 |
subsequent_indent=' '
|
1941 |
)
|
1942 |
-
|
1943 |
-
|
1944 |
-
|
1945 |
-
|
1946 |
-
|
1947 |
-
|
1948 |
-
|
1949 |
-
|
1950 |
-
examples.append(occ['text'])
|
1951 |
-
else:
|
1952 |
-
examples.append(str(occ))
|
1953 |
-
occurrences_text = '; '.join(examples)
|
1954 |
return textwrap.fill(
|
1955 |
-
f" • {issue['description']} -
|
1956 |
width=76,
|
1957 |
subsequent_indent=' '
|
1958 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1959 |
else:
|
1960 |
-
# Generic issue formatting
|
1961 |
message_parts = []
|
1962 |
for k, v in issue.items():
|
1963 |
if k not in ['type', 'error']:
|
1964 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1965 |
return f" • {'; '.join(message_parts)}"
|
1966 |
-
|
1967 |
-
|
1968 |
|
1969 |
def format_results(self, results: Dict[str, Any], doc_type: str) -> str:
|
1970 |
"""
|
@@ -1977,7 +2035,7 @@ class DocumentCheckResultsFormatter:
|
|
1977 |
Returns:
|
1978 |
str: Formatted report with consistent styling
|
1979 |
"""
|
1980 |
-
|
1981 |
if doc_type in ["Advisory Circular", "Order"]:
|
1982 |
table_format = {
|
1983 |
'title': 'Table Caption Format Issues',
|
@@ -2000,7 +2058,7 @@ class DocumentCheckResultsFormatter:
|
|
2000 |
else:
|
2001 |
table_format = {
|
2002 |
'title': 'Table Caption Format Issues',
|
2003 |
-
'description': f'Analyzes table captions to ensure they follow the FAA\'s sequential numbering system for {doc_type}s. Tables must be numbered consecutively throughout the document using a single
|
2004 |
'solution': 'Use the format "Table X" where X is a sequential number',
|
2005 |
'example_fix': {
|
2006 |
'before': 'Table 5-1. | Table A-1',
|
@@ -2009,7 +2067,7 @@ class DocumentCheckResultsFormatter:
|
|
2009 |
}
|
2010 |
figure_format = {
|
2011 |
'title': 'Figure Caption Format Issues',
|
2012 |
-
'description': f'Analyzes figure captions to ensure they follow the FAA\'s sequential numbering system for {doc_type}s. Figures must be numbered consecutively throughout the document using a single
|
2013 |
'solution': 'Use the format "Figure X" where X is a sequential number',
|
2014 |
'example_fix': {
|
2015 |
'before': 'Figure 5-1. | Figure A-1.',
|
@@ -2027,7 +2085,7 @@ class DocumentCheckResultsFormatter:
|
|
2027 |
"types": ["Advisory Circular"],
|
2028 |
"italics": True,
|
2029 |
"quotes": False,
|
2030 |
-
"description": "For Advisory Circulars, referenced document titles should be italicized but not quoted
|
2031 |
"example": "See AC 25.1309-1B, *System Design and Analysis*, for information on X."
|
2032 |
},
|
2033 |
"quotes_only": {
|
@@ -2038,14 +2096,14 @@ class DocumentCheckResultsFormatter:
|
|
2038 |
],
|
2039 |
"italics": False,
|
2040 |
"quotes": True,
|
2041 |
-
"description": "For this document type, referenced document titles should be in quotes without italics
|
2042 |
"example": 'See AC 25.1309-1B, "System Design and Analysis," for information on X.'
|
2043 |
},
|
2044 |
"no_formatting": {
|
2045 |
"types": ["Policy Statement", "Other"],
|
2046 |
"italics": False,
|
2047 |
"quotes": False,
|
2048 |
-
"description": "For this document type, referenced document titles should not use italics or quotes
|
2049 |
"example": "See AC 25.1309-1B, System Design and Analysis, for information on X."
|
2050 |
}
|
2051 |
}
|
@@ -2093,17 +2151,8 @@ class DocumentCheckResultsFormatter:
|
|
2093 |
|
2094 |
# Process all check results consistently
|
2095 |
for check_name, result in results.items():
|
2096 |
-
if not result.success:
|
2097 |
-
|
2098 |
-
category = self.issue_categories.get(check_name, {
|
2099 |
-
'title': check_name.replace('_', ' ').title(),
|
2100 |
-
'description': 'No description available for this check.',
|
2101 |
-
'solution': 'Review the issues and correct them accordingly.',
|
2102 |
-
'example_fix': {
|
2103 |
-
'before': '',
|
2104 |
-
'after': ''
|
2105 |
-
}
|
2106 |
-
})
|
2107 |
|
2108 |
# Add extra line break before each category
|
2109 |
output.append("\n")
|
@@ -2114,10 +2163,9 @@ class DocumentCheckResultsFormatter:
|
|
2114 |
output.append(f" {self._format_colored_text('How to fix: ' + category['solution'], Fore.GREEN)}")
|
2115 |
|
2116 |
# Example Fix
|
2117 |
-
|
2118 |
-
|
2119 |
-
|
2120 |
-
output.append("") # Add blank line after example
|
2121 |
|
2122 |
# Actual Issues Found
|
2123 |
output.append(f" {self._format_colored_text('Issues found in your document:', Fore.CYAN)}")
|
@@ -2155,24 +2203,6 @@ class DocumentCheckResultsFormatter:
|
|
2155 |
output.append(f"\n{Fore.CYAN}{'='*80}{Style.RESET_ALL}\n")
|
2156 |
|
2157 |
return '\n'.join(output)
|
2158 |
-
|
2159 |
-
def save_report(self, results: Dict[str, Any], filepath: str, doc_type: str) -> None:
|
2160 |
-
"""Save the formatted results to a file with proper formatting."""
|
2161 |
-
try:
|
2162 |
-
with open(filepath, 'w', encoding='utf-8') as f:
|
2163 |
-
# Create a report without color codes
|
2164 |
-
report = self.format_results(results, doc_type)
|
2165 |
-
|
2166 |
-
# Strip color codes
|
2167 |
-
for color in [Fore.CYAN, Fore.GREEN, Fore.YELLOW, Fore.RED, Style.RESET_ALL]:
|
2168 |
-
report = report.replace(str(color), '')
|
2169 |
-
|
2170 |
-
# Convert markdown-style italics to alternative formatting for plain text
|
2171 |
-
report = report.replace('*', '_')
|
2172 |
-
|
2173 |
-
f.write(report)
|
2174 |
-
except Exception as e:
|
2175 |
-
print(f"Error saving report: {e}")
|
2176 |
|
2177 |
def save_report(self, results: Dict[str, Any], filepath: str, doc_type: str) -> None:
|
2178 |
"""Save the formatted results to a file with proper formatting."""
|
|
|
924 |
return DocumentCheckResult(success=False, issues=[{'error': 'Invalid document input'}])
|
925 |
|
926 |
# Common words that might appear in uppercase but aren't acronyms
|
927 |
+
heading_words = self.config_manager.config.get('heading_words', HEADING_WORDS)
|
928 |
|
929 |
# Standard acronyms that don't need to be defined
|
930 |
+
predefined_acronyms = self.config_manager.config.get('predefined_acronyms', PREDEFINED_ACRONYMS)
|
931 |
|
932 |
# Tracking structures
|
933 |
defined_acronyms = {} # Stores definition info
|
|
|
936 |
|
937 |
# Patterns
|
938 |
defined_pattern = re.compile(r'\b([\w\s&]+?)\s*\((\b[A-Z]{2,}\b)\)')
|
939 |
+
acronym_pattern = re.compile(r'(?<!\()\b[A-Z]{2,}\b(?!\s*[:.]\s*)')
|
940 |
|
941 |
for paragraph in doc:
|
942 |
# Skip lines that appear to be headings (all uppercase with common heading words)
|
|
|
944 |
if all(word.isupper() for word in words) and any(word in heading_words for word in words):
|
945 |
continue
|
946 |
|
947 |
+
# Check for acronym definitions first
|
948 |
+
defined_matches = defined_pattern.findall(paragraph)
|
949 |
+
for full_term, acronym in defined_matches:
|
950 |
+
if acronym not in predefined_acronyms:
|
951 |
+
if acronym not in defined_acronyms:
|
952 |
+
defined_acronyms[acronym] = {
|
953 |
+
'full_term': full_term.strip(),
|
954 |
+
'defined_at': paragraph.strip(),
|
955 |
+
'used': False # Initially not used
|
956 |
+
}
|
957 |
+
else:
|
958 |
+
# Handle duplicate definitions if necessary
|
959 |
+
pass # You may add logic for duplicate definitions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
960 |
|
961 |
+
# Check for acronym usage
|
962 |
+
usage_matches = acronym_pattern.finditer(paragraph)
|
963 |
+
for match in usage_matches:
|
964 |
+
acronym = match.group()
|
965 |
|
966 |
+
# Skip predefined acronyms
|
967 |
+
if acronym in predefined_acronyms:
|
968 |
+
continue
|
969 |
|
970 |
+
# Skip if it's part of a heading or contains non-letter characters
|
971 |
+
if (acronym in heading_words or
|
972 |
+
any(not c.isalpha() for c in acronym) or
|
973 |
+
len(acronym) > 10): # Usually acronyms aren't this long
|
974 |
+
continue
|
975 |
|
976 |
+
if acronym not in defined_acronyms:
|
977 |
+
# Undefined acronym used
|
978 |
+
issues.append({
|
979 |
+
'type': 'undefined_acronym',
|
980 |
+
'acronym': acronym,
|
981 |
+
'sentence': paragraph.strip()
|
982 |
+
})
|
983 |
+
else:
|
984 |
+
# Mark as used
|
985 |
+
defined_acronyms[acronym]['used'] = True
|
986 |
+
used_acronyms.add(acronym)
|
987 |
|
988 |
# Check for defined but unused acronyms
|
989 |
unused_acronyms = [
|
|
|
1005 |
|
1006 |
return DocumentCheckResult(success=success, issues=issues)
|
1007 |
|
1008 |
+
|
1009 |
@profile_performance
|
1010 |
def check_terminology(self, doc: List[str]) -> DocumentCheckResult:
|
1011 |
"""
|
|
|
1756 |
'solution': 'Format heading periods according to document type requirements',
|
1757 |
'example_fix': {
|
1758 |
'before': 'Purpose',
|
1759 |
+
'after': 'Purpose.' # For ACs and Orders
|
1760 |
}
|
1761 |
},
|
1762 |
'table_figure_reference_check': {
|
|
|
1770 |
},
|
1771 |
'acronym_check': {
|
1772 |
'title': 'Acronym Definition Issues',
|
1773 |
+
'description': 'Ensures every acronym is properly introduced with its full term at first use. The check identifies undefined acronyms while recognizing common exceptions (like U.S.) that don\'t require definition.',
|
1774 |
+
'solution': 'Define each acronym at its first use, e.g., "Federal Aviation Administration (FAA)"',
|
1775 |
'example_fix': {
|
1776 |
'before': 'This order establishes general FAA organizational policies.',
|
1777 |
'after': 'This order establishes general Federal Aviation Administration (FAA) organizational policies.'
|
|
|
1832 |
}
|
1833 |
}
|
1834 |
}
|
1835 |
+
|
1836 |
+
# Add these two helper methods here, after __init__ and before other methods
|
1837 |
def _format_colored_text(self, text: str, color: str) -> str:
|
1838 |
+
"""Helper method to format colored text with reset.
|
1839 |
+
|
1840 |
+
Args:
|
1841 |
+
text: The text to be colored
|
1842 |
+
color: The color to apply (from colorama.Fore)
|
1843 |
+
|
1844 |
+
Returns:
|
1845 |
+
str: The colored text with reset styling
|
1846 |
+
"""
|
1847 |
return f"{color}{text}{Style.RESET_ALL}"
|
1848 |
|
1849 |
def _format_example(self, example_fix: Dict[str, str]) -> List[str]:
|
1850 |
+
"""Format example fixes consistently.
|
1851 |
+
|
1852 |
+
Args:
|
1853 |
+
example_fix: Dictionary containing 'before' and 'after' examples
|
1854 |
+
|
1855 |
+
Returns:
|
1856 |
+
List[str]: Formatted example lines
|
1857 |
+
"""
|
1858 |
return [
|
1859 |
f" ❌ Incorrect: {example_fix['before']}",
|
1860 |
f" ✓ Correct: {example_fix['after']}"
|
|
|
1877 |
output.append(f" • {heading}")
|
1878 |
|
1879 |
return output
|
1880 |
+
|
1881 |
def _format_period_issues(self, result: DocumentCheckResult) -> List[str]:
|
1882 |
"""Format period check issues consistently."""
|
1883 |
output = []
|
|
|
1889 |
output.append(f" • {issue['message']}")
|
1890 |
|
1891 |
return output
|
1892 |
+
|
1893 |
def _format_reference_issues(self, result: DocumentCheckResult) -> List[str]:
|
1894 |
+
"""Format reference issues consistently."""
|
1895 |
output = []
|
1896 |
|
1897 |
+
for issue in result.issues:
|
1898 |
+
if isinstance(issue, dict):
|
1899 |
+
reference_text = f" • {issue['reference']} should be {issue['correct_form']}"
|
1900 |
+
output.append(reference_text)
|
1901 |
+
if 'sentence' in issue:
|
1902 |
+
context = textwrap.fill(
|
1903 |
+
issue['sentence'],
|
1904 |
+
width=76,
|
1905 |
+
initial_indent=' ',
|
1906 |
+
subsequent_indent=' '
|
1907 |
+
)
|
1908 |
+
output.append(f"{Fore.YELLOW}Context: {context}{Style.RESET_ALL}")
|
|
|
|
|
1909 |
|
1910 |
return output
|
1911 |
+
|
1912 |
def _format_caption_issues(self, result: DocumentCheckResult) -> List[str]:
|
1913 |
"""Format caption issues consistently."""
|
1914 |
output = []
|
|
|
1918 |
output.append(f" • {issue.get('incorrect_caption', '')} (correct format: {issue.get('correct_format', '')})")
|
1919 |
|
1920 |
return output
|
1921 |
+
|
1922 |
def _format_standard_issue(self, issue: Dict[str, Any]) -> str:
|
1923 |
"""Format a standard issue consistently."""
|
1924 |
if isinstance(issue, dict):
|
1925 |
+
# Handle grouped issues per sentence
|
1926 |
+
if 'incorrect_terms' in issue and 'sentence' in issue:
|
1927 |
+
# Build the replacements text
|
1928 |
+
replacements = '; '.join(
|
1929 |
+
f"'{inc}' with '{corr}'" if corr else f"Remove '{inc}'"
|
1930 |
+
for inc, corr in sorted(issue['incorrect_terms'])
|
1931 |
+
)
|
1932 |
+
# Start building the output lines
|
1933 |
+
lines = []
|
1934 |
+
lines.append(f" • In: {issue['sentence']}")
|
1935 |
+
lines.append(f" Replace {replacements}")
|
1936 |
+
# Format each line individually
|
1937 |
+
formatted_lines = [
|
1938 |
+
textwrap.fill(line, width=76, subsequent_indent=' ')
|
1939 |
+
for line in lines
|
1940 |
+
]
|
1941 |
+
return '\n'.join(formatted_lines)
|
1942 |
+
|
1943 |
+
# Handle issues with occurrences list
|
1944 |
+
if 'occurrences' in issue:
|
1945 |
+
# Format the first 3 occurrences
|
1946 |
+
examples = issue['occurrences'][:3]
|
1947 |
+
formatted_examples = []
|
1948 |
+
for example in examples:
|
1949 |
+
if 'sentence' in example:
|
1950 |
+
formatted_examples.append(example['sentence'])
|
1951 |
+
elif isinstance(example, str):
|
1952 |
+
formatted_examples.append(example)
|
1953 |
+
|
1954 |
+
description = issue.get('description', '')
|
1955 |
return textwrap.fill(
|
1956 |
+
f" • {description} - Examples: {'; '.join(formatted_examples)}",
|
1957 |
width=76,
|
1958 |
subsequent_indent=' '
|
1959 |
)
|
1960 |
+
|
1961 |
+
# Handle unused acronym issues
|
1962 |
+
if issue.get('type') == 'unused_acronym':
|
1963 |
return textwrap.fill(
|
1964 |
+
f" • Acronym '{issue['acronym']}' defined but not used again after definition.",
|
1965 |
width=76,
|
1966 |
subsequent_indent=' '
|
1967 |
)
|
1968 |
+
|
1969 |
+
# Handle issues with direct sentence reference
|
1970 |
elif 'sentence' in issue:
|
1971 |
return textwrap.fill(
|
1972 |
issue['sentence'],
|
|
|
1974 |
initial_indent=' • ',
|
1975 |
subsequent_indent=' '
|
1976 |
)
|
1977 |
+
|
1978 |
+
# Handle issues with specific error messages
|
1979 |
+
elif 'error' in issue:
|
1980 |
+
return f" • Error: {issue['error']}"
|
1981 |
+
|
1982 |
+
# Handle issues with description and matches
|
1983 |
+
elif all(k in issue for k in ['issue_type', 'description', 'matches']):
|
1984 |
+
matches_str = '; '.join(str(m) for m in issue['matches'][:3])
|
|
|
|
|
|
|
|
|
1985 |
return textwrap.fill(
|
1986 |
+
f" • {issue['description']} - Found: {matches_str}",
|
1987 |
width=76,
|
1988 |
subsequent_indent=' '
|
1989 |
)
|
1990 |
+
|
1991 |
+
# Handle terminology issues
|
1992 |
+
if all(k in issue for k in ['incorrect_term', 'correct_term', 'sentence']):
|
1993 |
+
return textwrap.fill(
|
1994 |
+
f" • Replace '{issue['incorrect_term']}' with '{issue['correct_term']}' in: "
|
1995 |
+
f"{issue['sentence']}",
|
1996 |
+
width=76,
|
1997 |
+
subsequent_indent=' '
|
1998 |
+
)
|
1999 |
+
|
2000 |
+
# Handle placeholder issues
|
2001 |
+
elif 'placeholder' in issue:
|
2002 |
+
return textwrap.fill(
|
2003 |
+
f" • Found placeholder '{issue['placeholder']}' in: {issue.get('sentence', '')}",
|
2004 |
+
width=76,
|
2005 |
+
subsequent_indent=' '
|
2006 |
+
)
|
2007 |
+
|
2008 |
+
# Handle other dictionary formats
|
2009 |
else:
|
|
|
2010 |
message_parts = []
|
2011 |
for k, v in issue.items():
|
2012 |
if k not in ['type', 'error']:
|
2013 |
+
if isinstance(v, list):
|
2014 |
+
if all(isinstance(item, dict) for item in v):
|
2015 |
+
# Handle list of dictionaries
|
2016 |
+
v_str = '; '.join(str(item.get('sentence', str(item))) for item in v[:3])
|
2017 |
+
else:
|
2018 |
+
# Handle list of strings
|
2019 |
+
v_str = ', '.join(str(item) for item in v[:3])
|
2020 |
+
message_parts.append(f"{k}: {v_str}")
|
2021 |
+
else:
|
2022 |
+
message_parts.append(f"{k}: {v}")
|
2023 |
return f" • {'; '.join(message_parts)}"
|
2024 |
+
|
2025 |
+
return f" • {str(issue)}"
|
2026 |
|
2027 |
def format_results(self, results: Dict[str, Any], doc_type: str) -> str:
|
2028 |
"""
|
|
|
2035 |
Returns:
|
2036 |
str: Formatted report with consistent styling
|
2037 |
"""
|
2038 |
+
# Determine caption format based on document type
|
2039 |
if doc_type in ["Advisory Circular", "Order"]:
|
2040 |
table_format = {
|
2041 |
'title': 'Table Caption Format Issues',
|
|
|
2058 |
else:
|
2059 |
table_format = {
|
2060 |
'title': 'Table Caption Format Issues',
|
2061 |
+
'description': f'Analyzes table captions to ensure they follow the FAA\'s sequential numbering system for {doc_type}s. Tables must be numbered consecutively throughout the document using a single-number format. This straightforward numbering system maintains document organization while facilitating clear references to specific tables.',
|
2062 |
'solution': 'Use the format "Table X" where X is a sequential number',
|
2063 |
'example_fix': {
|
2064 |
'before': 'Table 5-1. | Table A-1',
|
|
|
2067 |
}
|
2068 |
figure_format = {
|
2069 |
'title': 'Figure Caption Format Issues',
|
2070 |
+
'description': f'Analyzes figure captions to ensure they follow the FAA\'s sequential numbering system for {doc_type}s. Figures must be numbered consecutively throughout the document using a single-number format. This consistent numbering approach ensures clear figure identification and maintains parallel structure with table numbering.',
|
2071 |
'solution': 'Use the format "Figure X" where X is a sequential number',
|
2072 |
'example_fix': {
|
2073 |
'before': 'Figure 5-1. | Figure A-1.',
|
|
|
2085 |
"types": ["Advisory Circular"],
|
2086 |
"italics": True,
|
2087 |
"quotes": False,
|
2088 |
+
"description": "For Advisory Circulars, referenced document titles should be italicized but not quoted",
|
2089 |
"example": "See AC 25.1309-1B, *System Design and Analysis*, for information on X."
|
2090 |
},
|
2091 |
"quotes_only": {
|
|
|
2096 |
],
|
2097 |
"italics": False,
|
2098 |
"quotes": True,
|
2099 |
+
"description": "For this document type, referenced document titles should be in quotes without italics",
|
2100 |
"example": 'See AC 25.1309-1B, "System Design and Analysis," for information on X.'
|
2101 |
},
|
2102 |
"no_formatting": {
|
2103 |
"types": ["Policy Statement", "Other"],
|
2104 |
"italics": False,
|
2105 |
"quotes": False,
|
2106 |
+
"description": "For this document type, referenced document titles should not use italics or quotes",
|
2107 |
"example": "See AC 25.1309-1B, System Design and Analysis, for information on X."
|
2108 |
}
|
2109 |
}
|
|
|
2151 |
|
2152 |
# Process all check results consistently
|
2153 |
for check_name, result in results.items():
|
2154 |
+
if not result.success and check_name in self.issue_categories:
|
2155 |
+
category = self.issue_categories[check_name]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2156 |
|
2157 |
# Add extra line break before each category
|
2158 |
output.append("\n")
|
|
|
2163 |
output.append(f" {self._format_colored_text('How to fix: ' + category['solution'], Fore.GREEN)}")
|
2164 |
|
2165 |
# Example Fix
|
2166 |
+
output.append(f"\n {self._format_colored_text('Example Fix:', Fore.CYAN)}")
|
2167 |
+
output.extend(self._format_example(category['example_fix']))
|
2168 |
+
output.append("") # Add blank line after example
|
|
|
2169 |
|
2170 |
# Actual Issues Found
|
2171 |
output.append(f" {self._format_colored_text('Issues found in your document:', Fore.CYAN)}")
|
|
|
2203 |
output.append(f"\n{Fore.CYAN}{'='*80}{Style.RESET_ALL}\n")
|
2204 |
|
2205 |
return '\n'.join(output)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2206 |
|
2207 |
def save_report(self, results: Dict[str, Any], filepath: str, doc_type: str) -> None:
|
2208 |
"""Save the formatted results to a file with proper formatting."""
|