sistemato la schermata per la differenza dei file

This commit is contained in:
VALLONGOL 2025-11-27 13:51:51 +01:00
parent 7e0435ab7d
commit df6ebec3e2
5 changed files with 102 additions and 72 deletions

BIN
PyUcc.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 0 B

After

Width:  |  Height:  |  Size: 32 KiB

74
pyucc/_version.py Normal file
View File

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
# File generated by PyInstaller GUI Wrapper. DO NOT EDIT MANUALLY.
# Contains build-time information scraped from Git (if available)
# and a helper function to format version strings.
import re
# --- Version Data (Generated) ---
__version__ = "v.0.0.0.11-0-g7e0435a-dirty"
GIT_COMMIT_HASH = "7e0435ab7d0316bd2bcf5fdcd33cb84c39efa09c"
GIT_BRANCH = "master"
BUILD_TIMESTAMP = "2025-11-27T12:49:18.815605+00:00"
IS_GIT_REPO = True
# --- Default Values (for comparison or fallback) ---
DEFAULT_VERSION = "0.0.0+unknown"
DEFAULT_COMMIT = "Unknown"
DEFAULT_BRANCH = "Unknown"
# --- Helper Function ---
def get_version_string(format_string=None):
"""
Returns a formatted string based on the build version information.
Args:
format_string (str, optional): A format string using placeholders.
Defaults to "{version} ({branch}/{commit_short})" if None.
Placeholders:
{{version}}: Full version string (e.g., 'v1.0.0-5-gabcdef-dirty')
{{tag}}: Clean tag part if exists (e.g., 'v1.0.0'), else DEFAULT_VERSION.
{{commit}}: Full Git commit hash.
{{commit_short}}: Short Git commit hash (7 chars).
{{branch}}: Git branch name.
{{dirty}}: '-dirty' if the repo was dirty, empty otherwise.
{{timestamp}}: Full build timestamp (ISO 8601 UTC).
{{timestamp_short}}: Build date only (YYYY-MM-DD).
{{is_git}}: 'Git' if IS_GIT_REPO is True, 'Unknown' otherwise.
Returns:
str: The formatted version string, or an error message if formatting fails.
"""
if format_string is None:
format_string = "{version} ({branch}/{commit_short})" # Default format
replacements = {}
try:
replacements['version'] = __version__ if __version__ else DEFAULT_VERSION
replacements['commit'] = GIT_COMMIT_HASH if GIT_COMMIT_HASH else DEFAULT_COMMIT
replacements['commit_short'] = GIT_COMMIT_HASH[:7] if GIT_COMMIT_HASH and len(GIT_COMMIT_HASH) >= 7 else DEFAULT_COMMIT
replacements['branch'] = GIT_BRANCH if GIT_BRANCH else DEFAULT_BRANCH
replacements['timestamp'] = BUILD_TIMESTAMP if BUILD_TIMESTAMP else "Unknown"
replacements['timestamp_short'] = BUILD_TIMESTAMP.split('T')[0] if BUILD_TIMESTAMP and 'T' in BUILD_TIMESTAMP else "Unknown"
replacements['is_git'] = "Git" if IS_GIT_REPO else "Unknown"
replacements['dirty'] = "-dirty" if __version__ and __version__.endswith('-dirty') else ""
tag = DEFAULT_VERSION
if __version__ and IS_GIT_REPO:
match = re.match(r'^(v?([0-9]+(?:\.[0-9]+)*))', __version__)
if match:
tag = match.group(1)
replacements['tag'] = tag
output_string = format_string
for placeholder, value in replacements.items():
pattern = re.compile(r'{{\s*' + re.escape(placeholder) + r'\s*}}')
output_string = pattern.sub(str(value), output_string)
if re.search(r'{\s*\w+\s*}', output_string):
pass # Or log a warning: print(f"Warning: Unreplaced placeholders found: {output_string}")
return output_string
except Exception as e:
return f"[Formatting Error: {e}]"

View File

@ -144,9 +144,6 @@ class BaselineManager:
if compute_sha1: # also compute for 0-byte files
try:
sha1 = _sha1_of_file(Path(fpath))
# Log empty __init__.py files
if st.st_size == 0 and "__init__.py" in fn:
print(f"[BASELINE-SCAN] {rel_unix} (size={st.st_size}) SHA1={sha1}")
except Exception:
sha1 = None
files_meta.append(FileMeta(path=rel_unix, size=st.st_size, mtime=st.st_mtime, sha1=sha1))
@ -209,16 +206,6 @@ class BaselineManager:
os.makedirs(dst_parent, exist_ok=True)
try:
shutil.copy2(src_file, dst_file) # copy2 preserves metadata
# Verify copy for empty __init__.py files
if fm.size == 0 and "__init__.py" in fm.path:
sha1_src = _sha1_of_file(Path(src_file))
sha1_dst = _sha1_of_file(Path(dst_file))
print(f"[BASELINE-COPY] {fm.path}")
print(f" Source SHA1: {sha1_src}")
print(f" Dest SHA1: {sha1_dst}")
print(f" Saved SHA1: {fm.sha1}")
if sha1_src != fm.sha1:
print(f" WARNING: Source file changed after initial scan!")
except Exception:
pass # skip files that cannot be copied
@ -468,9 +455,6 @@ class Differ:
# Compute SHA1 for all files, including 0-byte files
try:
sha1 = _sha1_of_file(Path(fpath))
# Log empty __init__.py files
if st.st_size == 0 and "__init__.py" in fn:
print(f"[DIFFER-CURRENT] {rel.replace(chr(92), '/')} (size={st.st_size}) SHA1={sha1}")
except Exception:
sha1 = None
files_meta.append(FileMeta(path=rel.replace("\\", "/"), size=st.st_size, mtime=st.st_mtime, sha1=sha1))
@ -715,15 +699,6 @@ class Differ:
return res
def diff(self) -> Dict:
import sys
sys.stdout.flush()
sys.stderr.flush()
print(f"\n{'='*80}", flush=True)
print(f"[DIFFER-START] Using baseline: {self.baseline.baseline_id}", flush=True)
print(f"[DIFFER-START] Baseline files dir: {self.baseline_files_dir}", flush=True)
print(f"[DIFFER-START] Current dir: {self.current_dir}", flush=True)
print(f"{'='*80}\n", flush=True)
baseline_files = self.baseline.files
current_files = self.build_current_file_list()
pairs = self.match_files(baseline_files, current_files)
@ -742,23 +717,6 @@ class Differ:
for a, b in pairs:
fa = os.path.join(self.baseline_files_dir, a.path) if a is not None else None
fb = os.path.join(self.current_dir, b.path) if b is not None else None
# DEBUG: Log empty __init__.py comparisons
if a and b and a.size == 0 and "__init__.py" in a.path:
import sys
print(f"\n[DIFFER-COMPARE] {a.path}", flush=True)
print(f" Baseline SHA1 (metadata): {a.sha1}", flush=True)
print(f" Current SHA1 (metadata): {b.sha1}", flush=True)
print(f" Baseline file: {fa}", flush=True)
print(f" Current file: {fb}", flush=True)
if fa and os.path.exists(fa):
sha1_baseline_actual = _sha1_of_file(Path(fa))
print(f" Baseline SHA1 (actual): {sha1_baseline_actual}", flush=True)
if fb and os.path.exists(fb):
sha1_current_actual = _sha1_of_file(Path(fb))
print(f" Current SHA1 (actual): {sha1_current_actual}", flush=True)
sys.stdout.flush()
futures.append(ex.submit(self._diff_file_pair, fa, fb))
for (a, b), fut in zip(pairs, futures):
res = fut.result()

View File

@ -340,20 +340,20 @@ class ActionHandlers:
rows = (_row_from_pair(p) for p in result.get('pairs', []))
export_rows_to_csv(csv_path, headers, rows)
# Generate text report
profile_config = {
'name': profile_name,
'root': project,
'paths': paths if paths else [project],
'languages': allowed_exts if allowed_exts else [],
'exclude_patterns': ignore_patterns
}
generate_differ_report(result, profile_config, baseline_id, report_path)
except Exception:
# non-fatal: continue even if export fails
pass
# Show summary dialog
# Generate text report
profile_config = {
'name': profile_name,
'root': project,
'paths': paths if paths else [project],
'languages': allowed_exts if allowed_exts else [],
'exclude_patterns': ignore_patterns
}
generate_differ_report(result, profile_config, baseline_id, report_path)
self.app.log(f"Differ report saved to: {report_path}", level='INFO')
except Exception as e:
# non-fatal: continue even if export fails
self.app.log(f"Failed to export differ results: {e}", level='WARNING')
pass # Show summary dialog
self._show_differ_summary_dialog(result, baseline_id, bdir)
except Exception:
self.app._set_phase('Idle')
@ -515,25 +515,11 @@ class ActionHandlers:
b.sha1 = None
# Compare hashes
# DEBUG: Log SHA1 comparison for empty __init__.py files
if a and b and a.size == 0 and "__init__.py" in str(a.path):
import sys
print(f"\n[ACTION_HANDLERS] SHA1 comparison for {a.path}", flush=True)
print(f" a.sha1={getattr(a, 'sha1', 'NO ATTR')}", flush=True)
print(f" b.sha1={getattr(b, 'sha1', 'NO ATTR')}", flush=True)
print(f" hasattr(a, 'sha1')={hasattr(a, 'sha1')}", flush=True)
print(f" a.sha1 is truthy={bool(a.sha1) if hasattr(a, 'sha1') else False}", flush=True)
print(f" b.sha1 is truthy={bool(b.sha1)}", flush=True)
print(f" Match: {a.sha1 == b.sha1 if (hasattr(a, 'sha1') and a.sha1 and b.sha1) else 'N/A'}", flush=True)
sys.stdout.flush()
if hasattr(a, 'sha1') and a.sha1 and b.sha1 and a.sha1 == b.sha1:
# Identical files
res['counts'] = {'added': 0, 'deleted': 0, 'modified': 0, 'unmodified': 1}
else:
# Modified file
if a and b and a.size == 0 and "__init__.py" in str(a.path):
print(f" → Marked as MODIFIED!", flush=True)
res['counts'] = {'added': 0, 'deleted': 0, 'modified': 1, 'unmodified': 0}
else:
# Both None (shouldn't happen)

View File

@ -114,6 +114,9 @@ class DiffViewer(tk.Toplevel):
highlightbackground='#ccc')
self.minimap.grid(row=1, column=1, sticky='nsew', padx=2, pady=0)
# Ridisegna minimap quando il canvas viene ridimensionato
self.minimap.bind('<Configure>', lambda e: self._draw_minimap())
# Scrollbar orizzontali
scrollbar_h_a = ttk.Scrollbar(main_frame, orient='horizontal', command=self.text_a.xview)
scrollbar_h_a.grid(row=2, column=0, sticky='ew', padx=(2, 1))
@ -413,13 +416,22 @@ class DiffViewer(tk.Toplevel):
# Rimuovi rettangolo precedente
self.minimap.delete('viewport')
# Calcola posizione viewport
if not hasattr(self, 'minimap_blocks') or not self.minimap_blocks:
return
# Calcola posizione viewport basandosi sul totale delle linee
yview = self.text_a.yview()
canvas_height = self.minimap.winfo_height()
canvas_width = self.minimap.winfo_width()
# yview restituisce (frazione_inizio, frazione_fine) del documento
# Usa queste frazioni direttamente sull'altezza del canvas
y1 = yview[0] * canvas_height
y2 = yview[1] * canvas_height
canvas_width = self.minimap.winfo_width()
# Assicurati che il viewport sia visibile (minimo 5 pixel)
if y2 - y1 < 5:
y2 = y1 + 5
# Disegna rettangolo viewport
self.minimap.create_rectangle(0, y1, canvas_width, y2,