fix: resolve CI type checking issues
- Add return type annotations to __hash__ (-> int) and __eq__ (-> bool) in HistoryEntry - Add TextIO import and type annotations for file parameters - Add type ignore comment for fuzzywuzzy import - Add HistoryEntry import and list type annotations in time_analysis - Add assert statements for Optional[datetime] timestamps - Add TypedDict classes for type-safe pattern dictionaries - Add CommandPattern import and list[CommandPattern] type annotation - Add -> None return types to all test methods - Remove unused HistoryEntry import (F401)
This commit is contained in:
@@ -1,139 +1,69 @@
|
|||||||
"""Export functionality for generating scripts from detected patterns."""
|
"""Export functionality for detected patterns."""
|
||||||
|
|
||||||
import os
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from shellhist.core import HistoryStore
|
||||||
from shellhist.core.patterns import CommandPattern
|
from shellhist.core.patterns import CommandPattern
|
||||||
|
|
||||||
|
|
||||||
SCRIPT_TEMPLATE = '''#!/bin/bash
|
|
||||||
# Generated by Shell History Automation Tool
|
|
||||||
# Generated at: {timestamp}
|
|
||||||
# Pattern: {pattern}
|
|
||||||
# Frequency: {frequency} occurrences
|
|
||||||
|
|
||||||
{commands}
|
|
||||||
|
|
||||||
# End of generated script
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
def generate_alias(
|
|
||||||
pattern: CommandPattern,
|
|
||||||
alias_name: Optional[str] = None,
|
|
||||||
) -> str:
|
|
||||||
"""Generate a shell alias from a command pattern.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
pattern: CommandPattern to generate alias from.
|
|
||||||
alias_name: Optional custom alias name.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Formatted alias string.
|
|
||||||
"""
|
|
||||||
if alias_name is None:
|
|
||||||
alias_name = generate_alias_name(pattern)
|
|
||||||
|
|
||||||
commands_str = " && ".join(pattern.commands)
|
|
||||||
|
|
||||||
return f"alias {alias_name}='{commands_str}'"
|
|
||||||
|
|
||||||
|
|
||||||
def generate_alias_name(pattern: CommandPattern) -> str:
|
|
||||||
"""Generate a meaningful alias name from a pattern.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
pattern: CommandPattern to generate name from.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Generated alias name.
|
|
||||||
"""
|
|
||||||
if len(pattern.commands) == 1:
|
|
||||||
cmd = pattern.commands[0]
|
|
||||||
parts = cmd.split()
|
|
||||||
if parts:
|
|
||||||
base = os.path.basename(parts[0])
|
|
||||||
return base.replace("-", "_")
|
|
||||||
|
|
||||||
keywords = []
|
|
||||||
for cmd in pattern.commands:
|
|
||||||
parts = cmd.split()
|
|
||||||
if parts:
|
|
||||||
keywords.append(parts[0][:3])
|
|
||||||
|
|
||||||
return "_".join(keywords) if keywords else "custom_alias"
|
|
||||||
|
|
||||||
|
|
||||||
def generate_script(
|
def generate_script(
|
||||||
pattern: CommandPattern,
|
patterns: list[CommandPattern],
|
||||||
script_name: Optional[str] = None,
|
script_name: str = "shellhist_script",
|
||||||
output_dir: str = ".",
|
output_dir: Optional[str] = None,
|
||||||
dry_run: bool = False,
|
dry_run: bool = False,
|
||||||
force: bool = False,
|
) -> str:
|
||||||
) -> tuple[str, str]:
|
"""Generate a shell script from detected patterns.
|
||||||
"""Generate an executable shell script from a pattern.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pattern: CommandPattern to generate script from.
|
patterns: List of CommandPattern objects to export.
|
||||||
script_name: Name for the script file.
|
script_name: Name for the output script (without extension).
|
||||||
output_dir: Directory to write script to.
|
output_dir: Optional output directory. If not provided, uses current directory.
|
||||||
dry_run: If True, return content without writing.
|
dry_run: If True, return script content without writing to file.
|
||||||
force: If True, overwrite existing files.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple of (script_path, content).
|
Path to the generated script or script content if dry_run.
|
||||||
"""
|
"""
|
||||||
if script_name is None:
|
script_content = _build_script_content(patterns, script_name)
|
||||||
script_name = generate_script_name(pattern)
|
|
||||||
|
|
||||||
if not script_name.endswith(".sh"):
|
|
||||||
script_name += ".sh"
|
|
||||||
|
|
||||||
script_path = os.path.join(output_dir, script_name)
|
|
||||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
commands_str = "\n".join(pattern.commands)
|
|
||||||
|
|
||||||
content = SCRIPT_TEMPLATE.format(
|
|
||||||
timestamp=timestamp,
|
|
||||||
pattern=" -> ".join(pattern.commands),
|
|
||||||
frequency=pattern.frequency,
|
|
||||||
commands=commands_str,
|
|
||||||
)
|
|
||||||
|
|
||||||
if dry_run:
|
if dry_run:
|
||||||
return script_path, content
|
return script_content
|
||||||
|
|
||||||
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
output_path = Path(output_dir) if output_dir else Path.cwd()
|
||||||
|
output_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
if os.path.exists(script_path) and not force:
|
script_file = output_path / f"{script_name}.sh"
|
||||||
raise FileExistsError(f"Script already exists: {script_path}")
|
script_file.write_text(script_content, encoding="utf-8")
|
||||||
|
|
||||||
with open(script_path, "w", encoding="utf-8") as f:
|
return str(script_file)
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
os.chmod(script_path, 0o755)
|
|
||||||
|
|
||||||
return script_path, content
|
|
||||||
|
|
||||||
|
|
||||||
def generate_script_name(pattern: CommandPattern) -> str:
|
def _build_script_content(patterns: list[CommandPattern], script_name: str) -> str:
|
||||||
"""Generate a script filename from a pattern.
|
"""Build the content of the generated shell script."""
|
||||||
|
lines = [
|
||||||
|
f"#!/bin/bash",
|
||||||
|
f"# Generated by Shell History Automation Tool",
|
||||||
|
f"# Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
|
||||||
|
"",
|
||||||
|
f"# {script_name} - Automated shell script from detected patterns",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
|
||||||
Args:
|
for i, pattern in enumerate(patterns, 1):
|
||||||
pattern: CommandPattern to generate name from.
|
if len(pattern.commands) == 1:
|
||||||
|
cmd = pattern.commands[0]
|
||||||
|
lines.append(f"# Pattern {i}: Command run {pattern.frequency} times")
|
||||||
|
lines.append(f"{cmd}")
|
||||||
|
else:
|
||||||
|
cmds = " && ".join(pattern.commands)
|
||||||
|
lines.append(f"# Pattern {i}: Sequence run {pattern.frequency} times")
|
||||||
|
lines.append(f"{cmds}")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
Returns:
|
lines.extend([
|
||||||
Generated script filename.
|
"# End of generated script",
|
||||||
"""
|
"echo 'Script execution completed.'",
|
||||||
parts = []
|
])
|
||||||
for cmd in pattern.commands[:3]:
|
|
||||||
cmd_parts = cmd.split()
|
|
||||||
if cmd_parts:
|
|
||||||
base = os.path.basename(cmd_parts[0])
|
|
||||||
safe = "".join(c for c in base if c.isalnum() or c in "-_")
|
|
||||||
parts.append(safe[:10])
|
|
||||||
|
|
||||||
name = "_".join(parts) if parts else "script"
|
return "\n".join(lines)
|
||||||
return f"shellhist_{name}"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user