Fix up menu code, add comment on front
This commit is contained in:
		
							
								
								
									
										154
									
								
								lex_scan.py
									
									
									
									
									
								
							
							
						
						
									
										154
									
								
								lex_scan.py
									
									
									
									
									
								
							@@ -1,3 +1,12 @@
 | 
			
		||||
#
 | 
			
		||||
# lex_scan.py - Audit .lex files
 | 
			
		||||
# -a - Make sure all lex stringsa in code and tmeplates are in en lex file
 | 
			
		||||
#    - delete any that aren't needed
 | 
			
		||||
# -e - Move all single word vqalues for lex strings to gthe general category
 | 
			
		||||
# -m - Examine all the controller files and make sure all headings and Descriptions are in the en general file 
 | 
			
		||||
#    - remove them from the controller en.lex file
 | 
			
		||||
# -l - Copy all panel and general files to each equivalent language file, fill in all translations over english where they exist in old .lex file
 | 
			
		||||
#
 | 
			
		||||
import argparse
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
@@ -264,25 +273,6 @@ def read_lex_file(filepath):
 | 
			
		||||
            lex_data[key] = value
 | 
			
		||||
    return lex_data
 | 
			
		||||
      
 | 
			
		||||
def write_lex_file(filepath, lex_data):
 | 
			
		||||
    """
 | 
			
		||||
    Writes a dictionary to a lex file, sorted alphabetically by key (case-insensitive).
 | 
			
		||||
    Adds a header with the current date and time.
 | 
			
		||||
    """
 | 
			
		||||
    # Sort the dictionary by key, case-insensitive
 | 
			
		||||
    sorted_items = sorted(lex_data.items(), key=lambda item: item[0].lower())
 | 
			
		||||
    now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
 | 
			
		||||
    # Extract panel name using regex
 | 
			
		||||
    match = re.search(r'.*/output/([^/]+)/', filepath)
 | 
			
		||||
    panel_name = match.group(1) if match else 'Unknown'
 | 
			
		||||
    logger.info(f"{filepath} {panel_name}")
 | 
			
		||||
    header = f"#\n# Lex file for {panel_name} generated on {now}\n#\n"
 | 
			
		||||
    with open(filepath, 'w', encoding='utf-8') as f:
 | 
			
		||||
        f.write(header)
 | 
			
		||||
        for key, value in sorted_items:
 | 
			
		||||
            value = value.replace("'", '"')
 | 
			
		||||
            f.write(f"'{key}' => '{value}',{os.linesep}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_languages_json(filepath):
 | 
			
		||||
    if not os.path.exists(filepath):
 | 
			
		||||
@@ -348,6 +338,50 @@ def update_file_with_new_lexical_string(filepath, old_string, new_string, filepa
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        logger.error(f"❌Error updating file {filepath} (writing to {filepath_new}): {e}")
 | 
			
		||||
 | 
			
		||||
def parse_smeserver_db(filepath):
 | 
			
		||||
    """
 | 
			
		||||
    Parses SME Server navigation DB entries from '|' delimited format lines like:
 | 
			
		||||
 | 
			
		||||
        key=panel|Description|Text|...|Heading|Text|...
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        list[dict]: List of dictionaries with 'Description' and/or 'Heading' fields.
 | 
			
		||||
    """
 | 
			
		||||
    filepath = os.path.expanduser(filepath)
 | 
			
		||||
    entries = []
 | 
			
		||||
 | 
			
		||||
    if not os.path.isfile(filepath):
 | 
			
		||||
        logger.error(f"SME server DB file not found: {filepath}")
 | 
			
		||||
        return entries
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        with open(filepath, 'r', encoding='utf-8') as f:
 | 
			
		||||
            for line in f:
 | 
			
		||||
                line = line.strip()
 | 
			
		||||
 | 
			
		||||
                if not line or line.startswith('#') or '=' not in line:
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                key, data = line.split('=', 1)
 | 
			
		||||
                parts = data.split('|')
 | 
			
		||||
 | 
			
		||||
                # Skip first part ("panel"), then parse key-value fields
 | 
			
		||||
                entry = {}
 | 
			
		||||
                for i in range(1, len(parts) - 1, 2):
 | 
			
		||||
                    field = parts[i].strip()
 | 
			
		||||
                    value = parts[i + 1].strip()
 | 
			
		||||
                    if field in ("Description", "Heading"):
 | 
			
		||||
                        entry[field] = value
 | 
			
		||||
 | 
			
		||||
                if entry:
 | 
			
		||||
                    entries.append(entry)
 | 
			
		||||
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        logger.error(f"Error parsing SME server DB: {e}")
 | 
			
		||||
 | 
			
		||||
    logger.info(f"Parsed {len(entries)} navigation entries with Description or Heading.")
 | 
			
		||||
    return entries
 | 
			
		||||
 | 
			
		||||
def export_sorted_missing_lex(input_file1, input_file2, output_file):
 | 
			
		||||
    """
 | 
			
		||||
    Reads two lex files, finds all entries in input_file1 missing from input_file2,
 | 
			
		||||
@@ -426,7 +460,52 @@ def delete_lex_output_files(panel_lex_output_dir):
 | 
			
		||||
 | 
			
		||||
    return deleted_files
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
def write_lex_file(filepath, lex_data):
 | 
			
		||||
    """
 | 
			
		||||
    Writes a dictionary to a lex file, sorted alphabetically by key (case-insensitive).
 | 
			
		||||
    Adds a header with the current date and time.
 | 
			
		||||
    """
 | 
			
		||||
    # Sort the dictionary by key, case-insensitive
 | 
			
		||||
    sorted_items = sorted(lex_data.items(), key=lambda item: item[0].lower())
 | 
			
		||||
    now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
 | 
			
		||||
 | 
			
		||||
    # Extract panel name using regex
 | 
			
		||||
    match = re.search(r'.*/output/([^/]+)/', filepath)
 | 
			
		||||
    panel_name = match.group(1) if match else 'Unknown'
 | 
			
		||||
    logger.info(f"{filepath} {panel_name}")
 | 
			
		||||
 | 
			
		||||
    header = f"#\n# Lex file for {panel_name} generated on {now}\n#\n"
 | 
			
		||||
    with open(filepath, 'w', encoding='utf-8') as f:
 | 
			
		||||
        f.write(header)
 | 
			
		||||
        for key, value in sorted_items:
 | 
			
		||||
            # Properly escape single quotes in values instead of replacing with double quotes
 | 
			
		||||
            escaped_value = value.replace("'", "\\'")
 | 
			
		||||
            f.write(f"'{key}' => '{escaped_value}',{os.linesep}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_new_lex_entries(general_lex_entries, menu_lex_strings):
 | 
			
		||||
    existing_keys = set(general_lex_entries.keys())
 | 
			
		||||
    new_entries = {}
 | 
			
		||||
 | 
			
		||||
    for item in menu_lex_strings:
 | 
			
		||||
        if isinstance(item, dict):
 | 
			
		||||
            for key in ("Description", "Heading"):
 | 
			
		||||
                val = item.get(key, "").strip()
 | 
			
		||||
                if val and val not in existing_keys:
 | 
			
		||||
                    existing_keys.add(val)
 | 
			
		||||
                    new_entries[val] = val
 | 
			
		||||
                    logger.info(f"New entry detected: {val}={val}")
 | 
			
		||||
        else:
 | 
			
		||||
            val = str(item).strip()
 | 
			
		||||
            if val and val not in existing_keys:
 | 
			
		||||
                existing_keys.add(val)
 | 
			
		||||
                new_entries[val] = val
 | 
			
		||||
                logger.info(f"New entry detected: {val}={val}")
 | 
			
		||||
 | 
			
		||||
    logger.info(f"Found {len(new_entries)} new entries for general lex file")
 | 
			
		||||
    return new_entries
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
def main():
 | 
			
		||||
	parser = argparse.ArgumentParser(description="Scan and audit Mojolicious application files for lexical strings.")
 | 
			
		||||
	parser.add_argument("-p", "--panel", required=True, help="Name of the Mojolicious panel (e.g., MyPanel).")
 | 
			
		||||
@@ -434,6 +513,7 @@ def main():
 | 
			
		||||
	parser.add_argument("-a", "--audit", action="store_true", help="Enable audit of all strings (default: False).")
 | 
			
		||||
	parser.add_argument("-e", "--edit", action="store_true", help="Enable audit of single words  (default: False).")
 | 
			
		||||
	parser.add_argument("-l", "--lang", action="store_true", help="Enable other language processing (default: False).")
 | 
			
		||||
	parser.add_argument("-m", "--menu", action="store_true", help="Check that all mewnu items are in the general lex(default: False).")
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	args = parser.parse_args()
 | 
			
		||||
@@ -443,6 +523,7 @@ def main():
 | 
			
		||||
	edit_files = args.edit
 | 
			
		||||
	do_lang = args.lang
 | 
			
		||||
	do_audit = args.audit
 | 
			
		||||
	do_menu = args.menu
 | 
			
		||||
	
 | 
			
		||||
	if panel in ['Support','Manual','Initial','Legacypanel','Roundcubepanel','Module','Request','Swttheme','','','','','','','','','']:
 | 
			
		||||
		logger.warning(f"Skipping {panel}")
 | 
			
		||||
@@ -585,8 +666,39 @@ def main():
 | 
			
		||||
 | 
			
		||||
			logger.info(f"New General English general lex file lines: {len(new_general_en_lex_data)} written to {general_en_lex_new_path}")
 | 
			
		||||
			logger.info(f"New English panel-specific lex file lines: {len(en_lex_data)} written to {en_lex_new_path}")
 | 
			
		||||
			logger.info("")
 | 
			
		||||
		
 | 
			
		||||
		if do_menu:
 | 
			
		||||
			logger.info(f"Running menu update procedure")
 | 
			
		||||
			
 | 
			
		||||
			# Look in the menu DB and check that all descriptions and headings are in the general lex 
 | 
			
		||||
			general_en_lex_path_orig = os.path.join(general_lex_output_dir, "general_en.lex.new1")
 | 
			
		||||
			general_en_lex_data_orig = read_lex_file(general_en_lex_path_orig)
 | 
			
		||||
			
 | 
			
		||||
			# Test write to verify original data integrity
 | 
			
		||||
			general_en_lex_path_test = os.path.join(general_lex_output_dir, "general_en.lex.new11")
 | 
			
		||||
			write_lex_file(general_en_lex_path_test, general_en_lex_data_orig)
 | 
			
		||||
			
 | 
			
		||||
			# Get menu descriptions
 | 
			
		||||
			menu_lex_strings = parse_smeserver_db('~/SME11/home/e-smith/db/navigation2/navigation.en')
 | 
			
		||||
			
 | 
			
		||||
			# Find new items that need to be added to the lex file
 | 
			
		||||
			new_items_dict = find_new_lex_entries(general_en_lex_data_orig, menu_lex_strings)
 | 
			
		||||
			
 | 
			
		||||
			# CORRECTED: Merge dictionaries instead of working with string lists
 | 
			
		||||
			# Start with original data to preserve existing key-value pairs
 | 
			
		||||
			final_lex_dict = general_en_lex_data_orig.copy()
 | 
			
		||||
			
 | 
			
		||||
			# Add new entries
 | 
			
		||||
			final_lex_dict.update(new_items_dict)
 | 
			
		||||
			
 | 
			
		||||
			# Write the final updated lex file
 | 
			
		||||
			general_en_lex_path_new = os.path.join(general_lex_output_dir, "general_en.lex.new2")
 | 
			
		||||
			write_lex_file(general_en_lex_path_new, final_lex_dict)
 | 
			
		||||
			
 | 
			
		||||
			logger.info(f"Updated lex file written to {general_en_lex_path_new}")
 | 
			
		||||
			logger.info(f"Total entries: {len(final_lex_dict)} (original: {len(general_en_lex_data_orig)}, new: {len(new_items_dict)})")
 | 
			
		||||
 | 
			
		||||
		logger.info("")
 | 
			
		||||
		if do_lang:
 | 
			
		||||
			# Panel specific lex files
 | 
			
		||||
			languages_json_path = os.path.join(".", "Templates", "languages.json") # Corrected path
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user