sistemato la schermata per la differenza dei file
This commit is contained in:
parent
7e0435ab7d
commit
df6ebec3e2
BIN
PyUcc.ico
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
74
pyucc/_version.py
Normal 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}]"
|
||||||
@ -144,9 +144,6 @@ class BaselineManager:
|
|||||||
if compute_sha1: # also compute for 0-byte files
|
if compute_sha1: # also compute for 0-byte files
|
||||||
try:
|
try:
|
||||||
sha1 = _sha1_of_file(Path(fpath))
|
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:
|
except Exception:
|
||||||
sha1 = None
|
sha1 = None
|
||||||
files_meta.append(FileMeta(path=rel_unix, size=st.st_size, mtime=st.st_mtime, sha1=sha1))
|
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)
|
os.makedirs(dst_parent, exist_ok=True)
|
||||||
try:
|
try:
|
||||||
shutil.copy2(src_file, dst_file) # copy2 preserves metadata
|
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:
|
except Exception:
|
||||||
pass # skip files that cannot be copied
|
pass # skip files that cannot be copied
|
||||||
|
|
||||||
@ -468,9 +455,6 @@ class Differ:
|
|||||||
# Compute SHA1 for all files, including 0-byte files
|
# Compute SHA1 for all files, including 0-byte files
|
||||||
try:
|
try:
|
||||||
sha1 = _sha1_of_file(Path(fpath))
|
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:
|
except Exception:
|
||||||
sha1 = None
|
sha1 = None
|
||||||
files_meta.append(FileMeta(path=rel.replace("\\", "/"), size=st.st_size, mtime=st.st_mtime, sha1=sha1))
|
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
|
return res
|
||||||
|
|
||||||
def diff(self) -> Dict:
|
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
|
baseline_files = self.baseline.files
|
||||||
current_files = self.build_current_file_list()
|
current_files = self.build_current_file_list()
|
||||||
pairs = self.match_files(baseline_files, current_files)
|
pairs = self.match_files(baseline_files, current_files)
|
||||||
@ -742,23 +717,6 @@ class Differ:
|
|||||||
for a, b in pairs:
|
for a, b in pairs:
|
||||||
fa = os.path.join(self.baseline_files_dir, a.path) if a is not None else None
|
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
|
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))
|
futures.append(ex.submit(self._diff_file_pair, fa, fb))
|
||||||
for (a, b), fut in zip(pairs, futures):
|
for (a, b), fut in zip(pairs, futures):
|
||||||
res = fut.result()
|
res = fut.result()
|
||||||
|
|||||||
@ -349,11 +349,11 @@ class ActionHandlers:
|
|||||||
'exclude_patterns': ignore_patterns
|
'exclude_patterns': ignore_patterns
|
||||||
}
|
}
|
||||||
generate_differ_report(result, profile_config, baseline_id, report_path)
|
generate_differ_report(result, profile_config, baseline_id, report_path)
|
||||||
except Exception:
|
self.app.log(f"Differ report saved to: {report_path}", level='INFO')
|
||||||
|
except Exception as e:
|
||||||
# non-fatal: continue even if export fails
|
# non-fatal: continue even if export fails
|
||||||
pass
|
self.app.log(f"Failed to export differ results: {e}", level='WARNING')
|
||||||
|
pass # Show summary dialog
|
||||||
# Show summary dialog
|
|
||||||
self._show_differ_summary_dialog(result, baseline_id, bdir)
|
self._show_differ_summary_dialog(result, baseline_id, bdir)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.app._set_phase('Idle')
|
self.app._set_phase('Idle')
|
||||||
@ -515,25 +515,11 @@ class ActionHandlers:
|
|||||||
b.sha1 = None
|
b.sha1 = None
|
||||||
|
|
||||||
# Compare hashes
|
# 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:
|
if hasattr(a, 'sha1') and a.sha1 and b.sha1 and a.sha1 == b.sha1:
|
||||||
# Identical files
|
# Identical files
|
||||||
res['counts'] = {'added': 0, 'deleted': 0, 'modified': 0, 'unmodified': 1}
|
res['counts'] = {'added': 0, 'deleted': 0, 'modified': 0, 'unmodified': 1}
|
||||||
else:
|
else:
|
||||||
# Modified file
|
# 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}
|
res['counts'] = {'added': 0, 'deleted': 0, 'modified': 1, 'unmodified': 0}
|
||||||
else:
|
else:
|
||||||
# Both None (shouldn't happen)
|
# Both None (shouldn't happen)
|
||||||
|
|||||||
@ -114,6 +114,9 @@ class DiffViewer(tk.Toplevel):
|
|||||||
highlightbackground='#ccc')
|
highlightbackground='#ccc')
|
||||||
self.minimap.grid(row=1, column=1, sticky='nsew', padx=2, pady=0)
|
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 orizzontali
|
||||||
scrollbar_h_a = ttk.Scrollbar(main_frame, orient='horizontal', command=self.text_a.xview)
|
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))
|
scrollbar_h_a.grid(row=2, column=0, sticky='ew', padx=(2, 1))
|
||||||
@ -413,13 +416,22 @@ class DiffViewer(tk.Toplevel):
|
|||||||
# Rimuovi rettangolo precedente
|
# Rimuovi rettangolo precedente
|
||||||
self.minimap.delete('viewport')
|
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()
|
yview = self.text_a.yview()
|
||||||
canvas_height = self.minimap.winfo_height()
|
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
|
y1 = yview[0] * canvas_height
|
||||||
y2 = yview[1] * 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
|
# Disegna rettangolo viewport
|
||||||
self.minimap.create_rectangle(0, y1, canvas_width, y2,
|
self.minimap.create_rectangle(0, y1, canvas_width, y2,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user