Hoctar77 commited on
Commit
d4c5938
·
verified ·
1 Parent(s): a3c728a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +167 -137
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', self.HEADING_WORDS)
928
 
929
  # Standard acronyms that don't need to be defined
930
- predefined_acronyms = self.config_manager.config.get('predefined_acronyms', self.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
- # Split the paragraph into sentences
948
- sentences = re.split(r'(?<=[.!?])\s+', paragraph)
949
- definitions_in_paragraph = set()
950
-
951
- # First pass: Identify definitions
952
- for sentence in sentences:
953
- defined_matches = defined_pattern.findall(sentence)
954
- for full_term, acronym in defined_matches:
955
- if acronym not in predefined_acronyms:
956
- if acronym not in defined_acronyms:
957
- defined_acronyms[acronym] = {
958
- 'full_term': full_term.strip(),
959
- 'defined_at': sentence.strip(),
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
- usage_matches = acronym_pattern.finditer(sentence)
973
- for match in usage_matches:
974
- acronym = match.group()
 
975
 
976
- # Skip predefined acronyms
977
- if acronym in predefined_acronyms:
978
- continue
979
 
980
- # Skip if it's part of a heading or contains non-letter characters
981
- if (acronym in heading_words or
982
- any(not c.isalpha() for c in acronym) or
983
- len(acronym) > 10): # Usually acronyms aren't this long
984
- continue
985
 
986
- if acronym not in defined_acronyms:
987
- # Undefined acronym used
988
- issues.append({
989
- 'type': 'undefined_acronym',
990
- 'acronym': acronym,
991
- 'sentence': sentence.strip()
992
- })
993
- else:
994
- # Mark as used
995
- defined_acronyms[acronym]['used'] = True
996
- used_acronyms.add(acronym)
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.' # For ACs and Orders
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, unused acronyms, and duplicate definitions while recognizing common exceptions (like U.S.) that don\'t require definition.',
1783
- 'solution': 'Define each acronym at its first use and use it consistently throughout the document',
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 section symbol check issues consistently with other document checks."""
1888
  output = []
1889
 
1890
- # Skip if no issues
1891
- if not result.issues:
1892
- return output
1893
-
1894
- for issue in result.issues[:3]:
1895
- if 'sentence' in issue:
1896
- output.append(f" • {issue['sentence']}")
1897
- elif 'matches' in issue:
1898
- output.append(f" • Incorrect usage: {', '.join(issue['matches'])}")
1899
- else:
1900
- output.append(f" • {str(issue)}")
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 unused acronym issues
1921
- if issue.get('type') == 'unused_acronym':
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1922
  return textwrap.fill(
1923
- f" • Acronym '{issue['acronym']}' defined but not used again after definition.",
1924
  width=76,
1925
  subsequent_indent=' '
1926
  )
1927
- # Handle undefined acronym issues
1928
- if issue.get('type') == 'undefined_acronym':
 
1929
  return textwrap.fill(
1930
- f" • Undefined acronym '{issue['acronym']}' used in: {issue['sentence']}",
1931
  width=76,
1932
  subsequent_indent=' '
1933
  )
1934
- # Handle other issues with sentence
 
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
- # Handle issues with description and occurrences
1943
- elif 'description' in issue and 'occurrences' in issue:
1944
- occurrences = issue['occurrences']
1945
- examples = []
1946
- for occ in occurrences[:3]:
1947
- if 'sentence' in occ:
1948
- examples.append(occ['sentence'])
1949
- elif 'text' in occ:
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']} - Examples: {occurrences_text}",
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
- message_parts.append(f"{k}: {v}")
 
 
 
 
 
 
 
 
 
1965
  return f" • {'; '.join(message_parts)}"
1966
- else:
1967
- return f" • {str(issue)}"
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
- # Determine caption format based on document type
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 number format. This straightforward numbering system maintains document organization while facilitating clear references to specific tables.',
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 number format. This consistent numbering approach ensures clear figure identification and maintains parallel structure with table numbering.',
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
- # Get category details
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
- if category['example_fix']['before'] and category['example_fix']['after']:
2118
- output.append(f"\n {self._format_colored_text('Example Fix:', Fore.CYAN)}")
2119
- output.extend(self._format_example(category['example_fix']))
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."""