From 58e99dea65d297b8e02860fb18f54ba3423e8ac7 Mon Sep 17 00:00:00 2001 From: rsnrk Date: Fri, 10 Apr 2026 07:15:57 +0000 Subject: [PATCH] better change file handling --- .gitignore | 8 + README.md | 71 +++-- config_selector.py | 558 +++++++++++++++++++++++++++++++------- deleted_files.csv | 408 ---------------------------- ignored_files-example.yml | 9 + ignored_files.yml | 6 + 6 files changed, 534 insertions(+), 526 deletions(-) delete mode 100644 deleted_files.csv create mode 100644 ignored_files-example.yml diff --git a/.gitignore b/.gitignore index 64544ad..f6635e3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,11 @@ original_config/** config_prefixes.json __pycache__ old_recipe_config +changed_files +ingest +new_recipe_config +changed_files_manifest.csv +new_recipe_not_in_original.csv +deleted_files.csv +ignored_files.yml +recipes/* diff --git a/README.md b/README.md index 951bfe1..64f627f 100644 --- a/README.md +++ b/README.md @@ -19,25 +19,42 @@ This script helps you manage and transfer specific Drupal configuration files by ## Installation and Setup The script operates in the current working directory where you run it. It will create the following subdirectories if they don't exist: -- `original_config/` - Place your source configuration files here +- `ingest/` - Drop your exported Drupal config archive here (named `config*.tar.gz`) +- `original_config/` - YAML files are extracted here automatically from the ingest archive - `new_recipe_config/` - Matched files will be copied here (with UUID and _core removed) -- `old_recipe_config/` - Optional: Place previous recipe version here for comparison - `changed_files/` - Files that changed between versions will be placed here - `ignored_files.yml` - Optional: List files to exclude from change tracking - `deleted_files.csv` - Tracks files that should be deleted ## Usage -1. Copy or move the script to your working directory (or use the full path to run it) -2. Place your source configuration files in the `original_config` directory -3. Run the script: +### Ingest: export from Drupal into `ingest/` + +1. In the Drupal admin UI, open the **full configuration export** page: **Configuration → Development → Configuration synchronization → Export**, then use the **Full archive** export (direct URL path: `admin/config/development/configuration/full/export`). +2. Download the generated archive (a `.tar.gz` of all site configuration). +3. Place that file in the `ingest/` directory under this tool’s working directory. The filename must match `config*.tar.gz` (for example `config.tar.gz` as downloaded, or a descriptive name like `config-localhost_3001-2026-04-09.tar.gz`). +4. Run `python3 config_selector.py` (or `python3 config_selector.py --force` if `original_config/` already has files and you want to replace them from the new archive). + +### Standard run (skip extraction if original_config/ already has files) ```bash python3 config_selector.py ``` -4. The script will create a default `config_prefixes.json` in the current directory if it doesn't exist -5. Use the TUI to manage your file selections and copy files +### Force re-extraction from the ingest archive + +Use `--force` when you have placed a new archive in `ingest/` and want to replace the current `original_config/` contents: + +```bash +python3 config_selector.py --force +``` + +### Workflow + +1. Follow **Ingest** above to place a full export `config*.tar.gz` in `ingest/`. +2. Run the script. It extracts the archive into `original_config/` automatically (skipped on subsequent runs unless `--force` is passed). +3. The script will create a default `config_prefixes.json` in the current directory if it doesn't exist. +4. Use the TUI to manage your file selections and copy files. ## Hierarchical Navigation @@ -63,7 +80,7 @@ The Text User Interface provides the following controls: - **O**: Choose a prefix from the list of unmatched prefixes - **D**: Delete the selected prefix (in matched view) or add the selected prefix (in unmatched view) - **C**: Copy all matched files to the new_recipe_config directory (with UUID and _core removal) -- **M**: Compare old and new recipe configs, track changes and deletions +- **M**: Compare committed recipe vs `new_recipe_config`, copy new/modified to `changed_files/`, list deletions - **S**: Save the current list of prefixes to the JSON file - **Q**: Quit the application @@ -98,16 +115,21 @@ You can modify this file directly or use the TUI to manage the prefixes. The script can compare configuration files between recipe versions: -- **M**: Compare old and new recipe configurations - - Compares files in `old_recipe_config/` with `new_recipe_config/` - - Copies changed files to `changed_files/` directory - - Tracks deleted files in `deleted_files.csv` +- **M**: Compare committed recipe to `new_recipe_config/` + - Baseline: `recipes/wisski_default_data_model/config/` (committed recipe) + - **New** files: present in `new_recipe_config/` but not in the committed recipe → copied to `changed_files/` (same layout, including `language//…`) + - **Modified** files: same relative path in both, different content → copied to `changed_files/` + - **Deleted** files: present in the committed recipe but missing from `new_recipe_config/` → listed in `deleted_files.csv` + - Each compare run **clears** `changed_files/` first, then repopulates it + - `changed_files_manifest.csv` lists every copied path with `kind` = `new` or `modified` -This feature helps you track what has changed between recipe versions and what files need to be removed. +Files matched in `ignored_files.yml` are not copied to `changed_files/` (whether new or modified). + +This feature helps you track additions, updates, and removals relative to the committed recipe. ## Ignoring Files from Change Tracking -You can create an `ignored_files.yml` file to prevent specific configuration files from being copied to the `changed_files/` folder during comparison. This is useful when certain files have intentional differences that should not be tracked as changes. +You can create an `ignored_files.yml` file to prevent specific configuration files from being copied to the `changed_files/` folder during comparison (for both **new** and **modified** paths). This is useful when certain files have intentional differences or should not be promoted from the export. Example `ignored_files.yml`: @@ -120,18 +142,21 @@ another_category: reason: custom modification required ``` -Files listed in this YAML will be excluded from the `changed_files/` directory even if they have changed between versions. +Files listed in this YAML will be excluded from the `changed_files/` directory even if they are new or changed relative to the committed recipe. ## Directory Structure ``` /your/working/directory/ -├── config_selector.py # Main script -├── config_prefixes.json # Configuration prefixes list -├── ignored_files.yml # Optional: List of files to ignore from change tracking -├── original_config/ # Source directory containing all configuration files -├── new_recipe_config/ # Target directory where matched files will be copied (cleaned) -├── old_recipe_config/ # Previous version of recipe config (for comparison) -├── changed_files/ # Files that have changed between old and new versions -└── deleted_files.csv # List of files that should be deleted from the recipe +├── config_selector.py # Main script +├── config_prefixes.json # Configuration prefixes list +├── ignored_files.yml # Optional: List of files to ignore from change tracking +├── ingest/ # Drop config*.tar.gz archives here +├── original_config/ # Extracted source YAML files (auto-populated from ingest/) +├── new_recipe_config/ # Target directory where matched files will be copied (cleaned) +├── recipes/wisski_default_data_model/config/ # Committed recipe config (used as baseline for comparison) +├── changed_files/ # New + modified YAML vs committed recipe (cleared each compare) +├── changed_files_manifest.csv # relative_path + kind (new|modified) for changed_files/ +├── new_recipe_not_in_original.csv # Paths in new_recipe_config missing from original export +└── deleted_files.csv # Paths in committed recipe missing from new_recipe_config ``` diff --git a/config_selector.py b/config_selector.py index f792e43..1cc5bf3 100644 --- a/config_selector.py +++ b/config_selector.py @@ -1,18 +1,97 @@ #!/usr/bin/env python3 import os +import sys import json import shutil import curses import re import csv import filecmp +import glob +import tarfile import yaml from typing import List, Dict, Set, Tuple, Optional -# Use current directory instead of hardcoded path +# Use current directory instead of hardcoded path. BASE_DIR = os.getcwd() +INGEST_DIR = os.path.join(BASE_DIR, "ingest") +ORIGINAL_CONFIG_DIR = os.path.join(BASE_DIR, "original_config") +RECIPE_CONFIG_DIR = os.path.join(BASE_DIR, "recipes", "wisski_default_data_model", "config") + + +def _clear_dir(path: str) -> int: + """Remove all files and subdirectories inside path. Returns count of removed items.""" + if not os.path.isdir(path): + return 0 + removedCount = 0 + for entry in os.listdir(path): + entryPath = os.path.join(path, entry) + if os.path.isdir(entryPath): + shutil.rmtree(entryPath) + else: + os.remove(entryPath) + removedCount += 1 + return removedCount + + +def ingest_config(force: bool = False) -> None: + """Extract a config*.tar.gz archive from ingest/ into original_config/. + + Skips extraction when YAML files are already present in original_config/, + unless force is True. With --force, original_config/, new_recipe_config/, + and changed_files/ are all cleared before re-extraction. + """ + os.makedirs(ORIGINAL_CONFIG_DIR, exist_ok=True) + + existingFiles = [f for f in os.listdir(ORIGINAL_CONFIG_DIR) if f.endswith(".yml")] + if existingFiles and not force: + print( + f"original_config/ already contains {len(existingFiles)} YAML files. " + "Use --force to re-extract." + ) + return + + archives = sorted(glob.glob(os.path.join(INGEST_DIR, "config*.tar.gz"))) + if not archives: + if not existingFiles: + print("No config*.tar.gz found in ingest/ and original_config/ is empty.") + return + + # Use the most recently named archive. + archive = archives[-1] + print(f"Extracting {os.path.basename(archive)} to original_config/...") + + if force: + _clear_dir(ORIGINAL_CONFIG_DIR) + newRecipeDir = os.path.join(BASE_DIR, "new_recipe_config") + changedFilesDir = os.path.join(BASE_DIR, "changed_files") + cleared = _clear_dir(newRecipeDir) + if cleared: + print(f"Cleared {cleared} items from new_recipe_config/.") + cleared = _clear_dir(changedFilesDir) + if cleared: + print(f"Cleared {cleared} items from changed_files/.") + + extractedCount = 0 + with tarfile.open(archive, "r:gz") as tar: + for member in tar.getmembers(): + if not (member.isfile() and member.name.endswith(".yml")): + continue + # Preserve subdirectory structure (e.g. language/de/foo.yml) but strip + # any leading archive root component that is not part of the config layout. + relPath = member.name.lstrip("./") + destPath = os.path.join(ORIGINAL_CONFIG_DIR, relPath) + os.makedirs(os.path.dirname(destPath), exist_ok=True) + srcFile = tar.extractfile(member) + if srcFile is not None: + with open(destPath, "wb") as dst: + dst.write(srcFile.read()) + extractedCount += 1 + + print(f"Extracted {extractedCount} YAML files to original_config/.") + class ConfigSelector: def __init__(self, json_path: str, original_config_path: str, config_path: str, old_recipe_path: str = None, changed_files_path: str = None, deleted_files_csv: str = None, @@ -23,6 +102,8 @@ class ConfigSelector: self.old_recipe_path = old_recipe_path or os.path.join(BASE_DIR, "old_recipe_config") self.changed_files_path = changed_files_path or os.path.join(BASE_DIR, "changed_files") self.deleted_files_csv = deleted_files_csv or os.path.join(BASE_DIR, "deleted_files.csv") + self.new_recipe_not_in_original_csv = os.path.join(BASE_DIR, "new_recipe_not_in_original.csv") + self.changed_files_manifest_csv = os.path.join(BASE_DIR, "changed_files_manifest.csv") self.ignored_files_yml = ignored_files_yml or os.path.join(BASE_DIR, "ignored_files.yml") self.prefixes: List[str] = [] self.matched_files: Dict[str, List[str]] = {} @@ -61,7 +142,13 @@ class ConfigSelector: # Iterate through all categories in the YAML. for category, info in data.items(): if isinstance(info, dict) and 'name' in info: - self.ignored_files.add(info['name']) + name = info['name'] + if not isinstance(name, str): + continue + self.ignored_files.add(name) + # Basenames on disk always include .yml; allow omitting it in YAML. + if not name.endswith('.yml'): + self.ignored_files.add(f'{name}.yml') except (yaml.YAMLError, FileNotFoundError) as e: print(f"Warning: Error loading ignored files YAML: {e}") @@ -107,101 +194,291 @@ class ConfigSelector: print(f"Error: Directory {self.original_config_path} not found") exit(1) + @staticmethod + def _strip_metadata(content: str) -> str: + """Remove uuid and _core keys from Drupal YAML config content.""" + content = re.sub(r'^uuid: [a-f0-9\-]+\n', '', content, flags=re.MULTILINE) + # Match exactly '_core:' at the start of a line (avoids touching wisski_core). + content = re.sub(r'^_core:\n([ \t]+[^\n]+\n)+', '', content, flags=re.MULTILINE) + return content + + def _collect_bundle_hashes(self) -> Set[str]: + """Return the set of bundle hashes found in matched wisski_core.wisski_bundle files.""" + bundlePrefix = "wisski_core.wisski_bundle." + bundleHashes: Set[str] = set() + for prefix in self.prefixes: + for filename in self.matched_files.get(prefix, []): + if filename.startswith(bundlePrefix) and filename.endswith(".yml"): + bundleHash = filename[len(bundlePrefix):-4] + bundleHashes.add(bundleHash) + return bundleHashes + + def _copy_file(self, src: str, dst: str) -> None: + """Read src, strip metadata, write to dst. Falls back to raw copy on error.""" + try: + with open(src, 'r') as f: + content = f.read() + content = self._strip_metadata(content) + with open(dst, 'w') as f: + f.write(content) + except Exception as e: + print(f"Error processing {os.path.basename(src)}: {e}") + shutil.copy2(src, dst) + + def _copy_with_language_overrides(self, filename: str) -> int: + """Copy original_config/language/{lang}/{filename} to new_recipe_config/language/{lang}/ + for every language that has an override for this file. Returns count of overrides copied.""" + langBase = os.path.join(self.original_config_path, "language") + if not os.path.isdir(langBase): + return 0 + overridesCopied = 0 + for langCode in os.listdir(langBase): + srcLangDir = os.path.join(langBase, langCode) + if not os.path.isdir(srcLangDir): + continue + srcOverride = os.path.join(srcLangDir, filename) + if not os.path.isfile(srcOverride): + continue + dstLangDir = os.path.join(self.config_path, "language", langCode) + os.makedirs(dstLangDir, exist_ok=True) + self._copy_file(srcOverride, os.path.join(dstLangDir, filename)) + overridesCopied += 1 + return overridesCopied + def copy_matched_files(self) -> None: """Copy all matched files from original_config to config, removing UUID and _core structures.""" if not os.path.exists(self.config_path): os.makedirs(self.config_path) - copied_count = 0 - processed_count = 0 + copiedCount = 0 + processedCount = 0 + overrideCopied = 0 for prefix in self.prefixes: for filename in self.matched_files[prefix]: src = os.path.join(self.original_config_path, filename) dst = os.path.join(self.config_path, filename) - - # Read the YAML file try: - with open(src, 'r') as file: - content = file.read() - - # Process the content to remove UUID and _core structures - # 1. Remove uuid key-value pair (ensuring we start at beginning of line) - content = re.sub(r'^uuid: [a-f0-9\-]+\n', '', content, flags=re.MULTILINE) - - # 2. Remove _core structure (careful not to remove wisski_core or similar) - # Match exactly '_core:' at the start of a line - content = re.sub(r'^_core:\n([ \t]+[^\n]+\n)+', '', content, flags=re.MULTILINE) - - # Write the modified content - with open(dst, 'w') as file: - file.write(content) - - processed_count += 1 + with open(src, 'r') as f: + content = f.read() + content = self._strip_metadata(content) + with open(dst, 'w') as f: + f.write(content) + processedCount += 1 except Exception as e: print(f"Error processing {filename}: {e}") - # Fall back to direct copy if processing fails shutil.copy2(src, dst) + copiedCount += 1 + overrideCopied += self._copy_with_language_overrides(filename) - copied_count += 1 + # For every matched bundle, also copy its language.content_settings counterpart. + langCopied = 0 + for bundleHash in self._collect_bundle_hashes(): + langFilename = f"language.content_settings.wisski_individual.{bundleHash}.yml" + langSrc = os.path.join(self.original_config_path, langFilename) + langDst = os.path.join(self.config_path, langFilename) + if os.path.exists(langSrc): + self._copy_file(langSrc, langDst) + langCopied += 1 + overrideCopied += self._copy_with_language_overrides(langFilename) - print(f"Copied {copied_count} files to {self.config_path} ({processed_count} files were processed to remove UUIDs and _core structures)") + print( + f"Copied {copiedCount} files to {self.config_path} " + f"({processedCount} processed to remove UUIDs and _core structures)" + ) + if langCopied: + print(f"Also copied {langCopied} corresponding language.content_settings files.") + if overrideCopied: + print(f"Also copied {overrideCopied} language override files from original_config/language/.") + pruned_export = self._prune_new_recipe_not_in_original() + if pruned_export: + print( + f"Removed {pruned_export} file(s) from new_recipe_config that are not in original_config " + f"(stale vs current export)." + ) + pruned = self._prune_stale_language_overrides() + if pruned: + print(f"Removed {pruned} stale language override file(s) (no longer in export or missing base config).") - def compare_and_track_changes(self) -> Tuple[int, int, int]: + @staticmethod + def _remove_empty_language_dirs(config_root: str) -> None: + """Remove empty language// and language/ under config_root if possible.""" + lang_root = os.path.join(config_root, "language") + if not os.path.isdir(lang_root): + return + for lang_code in list(os.listdir(lang_root)): + lang_dir = os.path.join(lang_root, lang_code) + if not os.path.isdir(lang_dir): + continue + try: + if not os.listdir(lang_dir): + os.rmdir(lang_dir) + except OSError: + pass + try: + if not os.listdir(lang_root): + os.rmdir(lang_root) + except OSError: + pass + + def _prune_new_recipe_not_in_original(self) -> int: + """Delete YAML under config_path whose relative path is absent from original_config. + + Skips when original_config has no YAML (avoid wiping new_recipe before an export). """ - Compare files between old_recipe_config and new_recipe_config. - Copy changed files to changed_files folder. - Track deleted files in deleted_files.csv. + orig_paths = self._collect_yaml_relpaths(self.original_config_path) + if not orig_paths: + return 0 + new_paths = self._collect_yaml_relpaths(self.config_path) + orphans = new_paths - orig_paths + removed = 0 + for rel in sorted(orphans): + path = self._abs_config_path(self.config_path, rel) + try: + if os.path.isfile(path): + os.remove(path) + removed += 1 + except OSError: + pass + self._remove_empty_language_dirs(self.config_path) + return removed + + def _prune_stale_language_overrides(self) -> int: + """Remove language/*/*.yml in config_path that should not be kept. + + Drops overrides that are missing from the current export (translation removed + on the site) or whose base config is not present under config_path (orphans + from earlier copies). Without this, compare would not list those as deleted. + """ + removed = 0 + lang_dst_root = os.path.join(self.config_path, "language") + if not os.path.isdir(lang_dst_root): + return 0 + lang_src_root = os.path.join(self.original_config_path, "language") + for lang_code in list(os.listdir(lang_dst_root)): + dst_lang_dir = os.path.join(lang_dst_root, lang_code) + if not os.path.isdir(dst_lang_dir): + continue + src_lang_dir = os.path.join(lang_src_root, lang_code) + for name in list(os.listdir(dst_lang_dir)): + if not name.endswith(".yml"): + continue + base_fp = os.path.join(self.config_path, name) + src_fp = os.path.join(src_lang_dir, name) + if os.path.isfile(base_fp) and os.path.isfile(src_fp): + continue + try: + os.remove(os.path.join(dst_lang_dir, name)) + removed += 1 + except OSError: + pass + self._remove_empty_language_dirs(self.config_path) + return removed + + @staticmethod + def _collect_yaml_relpaths(config_root: str) -> Set[str]: + """Relative paths (posix-style) for YAML under a Drupal config tree. + + Includes top-level *.yml and language//*.yml (same layout as exports). + """ + rel_paths: Set[str] = set() + if not os.path.isdir(config_root): + return rel_paths + try: + for name in os.listdir(config_root): + if name.endswith(".yml"): + rel_paths.add(name) + lang_root = os.path.join(config_root, "language") + if os.path.isdir(lang_root): + for lang_code in os.listdir(lang_root): + lang_dir = os.path.join(lang_root, lang_code) + if not os.path.isdir(lang_dir): + continue + for name in os.listdir(lang_dir): + if name.endswith(".yml"): + rel_paths.add(f"language/{lang_code}/{name}") + except OSError: + pass + return rel_paths + + def _abs_config_path(self, config_root: str, rel_path: str) -> str: + """Join config root with a posix rel_path from _collect_yaml_relpaths.""" + return os.path.normpath(os.path.join(config_root, *rel_path.split("/"))) + + def compare_and_track_changes(self) -> Tuple[int, int, int, int, int, int]: + """ + Compare committed recipe config (old_recipe_path) to new_recipe_config (config_path). + + - Copies **new** YAML (in new_recipe but not in committed recipe) to changed_files/. + - Copies **modified** YAML (same path, different content) to changed_files/. + - Respects ignored_files.yml (basename) for both new and modified copies. + - Writes deleted_files.csv: paths in committed recipe missing from new_recipe_config. + - Writes changed_files_manifest.csv: relative_path + kind (new|modified) for each copy. + - Writes new_recipe_not_in_original.csv for paths in new_recipe not in original export. Returns: - Tuple of (changed_count, deleted_count, unchanged_count) + Tuple of (added_count, modified_count, deleted_count, unchanged_count, + language_deleted_count, not_in_original_count) """ - # Ensure directories exist - if not os.path.exists(self.changed_files_path): - os.makedirs(self.changed_files_path) + _clear_dir(self.changed_files_path) + os.makedirs(self.changed_files_path, exist_ok=True) # Track statistics - changed_count = 0 + added_count = 0 + modified_count = 0 deleted_count = 0 unchanged_count = 0 ignored_count = 0 deleted_files_list = [] + manifest_rows: List[Tuple[str, str]] = [] - # Get all files in old_recipe_config - old_files = set() - if os.path.exists(self.old_recipe_path): - old_files = {f for f in os.listdir(self.old_recipe_path) if f.endswith('.yml')} + # Top-level and language//*.yml (matches Drupal export layout) + old_files = self._collect_yaml_relpaths(self.old_recipe_path) + new_files = self._collect_yaml_relpaths(self.config_path) - # Get all files in new_recipe_config - new_files = set() - if os.path.exists(self.config_path): - new_files = {f for f in os.listdir(self.config_path) if f.endswith('.yml')} - - # Find deleted files (in old but not in new) + # Deleted: in committed recipe but not in new_recipe_config deleted_files = old_files - new_files - for filename in sorted(deleted_files): - deleted_files_list.append(filename) + for rel_path in sorted(deleted_files): + deleted_files_list.append(rel_path) deleted_count += 1 - # Find common files and check for changes + # Modified: same path in both, content differs common_files = old_files & new_files - for filename in sorted(common_files): - old_file_path = os.path.join(self.old_recipe_path, filename) - new_file_path = os.path.join(self.config_path, filename) + for rel_path in sorted(common_files): + old_file_path = self._abs_config_path(self.old_recipe_path, rel_path) + new_file_path = self._abs_config_path(self.config_path, rel_path) + base_name = os.path.basename(rel_path) - # Compare file contents if filecmp.cmp(old_file_path, new_file_path, shallow=False): - # Files are identical unchanged_count += 1 else: - # Files have changed - check if ignored - if filename in self.ignored_files: - # Skip copying ignored files + if base_name in self.ignored_files: ignored_count += 1 else: - # Copy to changed_files - changed_dest = os.path.join(self.changed_files_path, filename) + changed_dest = os.path.join(self.changed_files_path, *rel_path.split("/")) + os.makedirs(os.path.dirname(changed_dest), exist_ok=True) shutil.copy2(new_file_path, changed_dest) - changed_count += 1 + modified_count += 1 + manifest_rows.append((rel_path, "modified")) + + # New: in new_recipe (from original_config via prefixes) but not in committed recipe + added_files = new_files - old_files + for rel_path in sorted(added_files): + new_file_path = self._abs_config_path(self.config_path, rel_path) + base_name = os.path.basename(rel_path) + if base_name in self.ignored_files: + ignored_count += 1 + else: + changed_dest = os.path.join(self.changed_files_path, *rel_path.split("/")) + os.makedirs(os.path.dirname(changed_dest), exist_ok=True) + shutil.copy2(new_file_path, changed_dest) + added_count += 1 + manifest_rows.append((rel_path, "new")) + + with open(self.changed_files_manifest_csv, "w", newline="") as csvfile: + writer = csv.writer(csvfile) + writer.writerow(["relative_path", "kind"]) + for rel_path, kind in sorted(manifest_rows, key=lambda x: (x[1], x[0])): + writer.writerow([rel_path, kind]) # Write deleted files to CSV with open(self.deleted_files_csv, 'w', newline='') as csvfile: @@ -211,9 +488,49 @@ class ConfigSelector: writer.writerow([filename]) if ignored_count > 0: - print(f"Ignored {ignored_count} changed file(s) as per ignored_files.yml") + print( + f"Ignored {ignored_count} new/modified file(s) as per ignored_files.yml " + "(not copied to changed_files/)." + ) + total_copied = added_count + modified_count + if total_copied: + print( + f"Copied {total_copied} file(s) to changed_files/ " + f"({added_count} new vs committed recipe, {modified_count} modified)." + ) - return changed_count, deleted_count, unchanged_count + language_deleted_count = sum(1 for p in deleted_files_list if p.startswith("language/")) + if language_deleted_count: + print( + f"Deleted list includes {language_deleted_count} language override path(s) " + f"(see deleted_files.csv under language//)." + ) + + orig_paths = self._collect_yaml_relpaths(self.original_config_path) + new_paths = self._collect_yaml_relpaths(self.config_path) + not_in_original = sorted(new_paths - orig_paths) + not_in_original_count = len(not_in_original) + with open(self.new_recipe_not_in_original_csv, "w", newline="") as csvfile: + writer = csv.writer(csvfile) + writer.writerow(["filename"]) + for rel_path in not_in_original: + writer.writerow([rel_path]) + if not_in_original_count: + print( + f"{not_in_original_count} path(s) in new_recipe_config are not in original_config " + f"(see {os.path.basename(self.new_recipe_not_in_original_csv)})." + ) + elif not orig_paths: + print("Warning: original_config has no YAML; orphan CSV is empty.") + + return ( + added_count, + modified_count, + deleted_count, + unchanged_count, + language_deleted_count, + not_in_original_count, + ) def add_prefix(self, prefix: str) -> None: """Add a new prefix to the list if it doesn't exist.""" @@ -978,10 +1295,29 @@ def confirm_yaml_cleaning(stdscr, selector): return confirm_dialog(stdscr, "Copy all matched files to config directory and remove UUID and _core?") -def show_comparison_results(stdscr, changed_count, deleted_count, unchanged_count): +def show_comparison_results( + stdscr, + added_count: int, + modified_count: int, + deleted_count: int, + unchanged_count: int, + language_deleted_count: int = 0, + not_in_original_count: int = 0, +): """Show the results of file comparison.""" h, w = stdscr.getmaxyx() - dialog_h, dialog_w = 10, 60 + dialog_w = 60 + total_out = added_count + modified_count + # Rows from 3: 5 stats + optional notes + blank + 2 CSV hints; footer at dialog_h - 2 + lines = ( + 5 + + (1 if not_in_original_count else 0) + + (1 if language_deleted_count else 0) + + 1 + + 2 + ) + last_content_row = 2 + lines + dialog_h = min(h - 2, max(14, last_content_row + 3)) dialog_y = (h - dialog_h) // 2 dialog_x = (w - dialog_w) // 2 @@ -989,11 +1325,36 @@ def show_comparison_results(stdscr, changed_count, deleted_count, unchanged_coun dialog_win = curses.newwin(dialog_h, dialog_w, dialog_y, dialog_x) dialog_win.box() dialog_win.addstr(1, 2, "File Comparison Results", curses.A_BOLD) - dialog_win.addstr(3, 4, f"Changed files: {changed_count}") - dialog_win.addstr(4, 4, f"Deleted files: {deleted_count}") - dialog_win.addstr(5, 4, f"Unchanged files: {unchanged_count}") - dialog_win.addstr(7, 4, "Changed files copied to: changed_files/") - dialog_win.addstr(8, 4, "Deleted files tracked in: deleted_files.csv") + row = 3 + dialog_win.addstr(row, 4, f"New (not in committed recipe): {added_count}"[: dialog_w - 6]) + row += 1 + dialog_win.addstr(row, 4, f"Modified vs committed: {modified_count}"[: dialog_w - 6]) + row += 1 + dialog_win.addstr(row, 4, f"Total → changed_files/: {total_out}"[: dialog_w - 6]) + row += 1 + deleted_line = f"Deleted (recipe, missing in new): {deleted_count}" + if language_deleted_count: + deleted_line += f" ({language_deleted_count} language/)" + dialog_win.addstr(row, 4, deleted_line[: dialog_w - 6]) + row += 1 + dialog_win.addstr(row, 4, f"Unchanged: {unchanged_count}"[: dialog_w - 6]) + row += 1 + if not_in_original_count: + dialog_win.addstr( + row, + 4, + f"Not in original export: {not_in_original_count} (new_recipe_not_in_original.csv)"[ + : dialog_w - 6 + ], + ) + row += 1 + if language_deleted_count: + dialog_win.addstr(row, 4, "Language deletions: paths under language/… in deleted CSV") + row += 1 + row += 1 + dialog_win.addstr(row, 4, "Manifest → changed_files_manifest.csv") + row += 1 + dialog_win.addstr(row, 4, "Deletions → deleted_files.csv") dialog_win.addstr(dialog_h - 2, 2, "Press any key to continue...") dialog_win.refresh() @@ -1001,36 +1362,26 @@ def show_comparison_results(stdscr, changed_count, deleted_count, unchanged_coun stdscr.getch() def main(stdscr): - # Set paths relative to current directory - json_path = os.path.join(BASE_DIR, "config_prefixes.json") - original_config_path = os.path.join(BASE_DIR, "original_config") - config_path = os.path.join(BASE_DIR, "new_recipe_config") + # Set paths relative to current directory. + jsonPath = os.path.join(BASE_DIR, "config_prefixes.json") + configPath = os.path.join(BASE_DIR, "new_recipe_config") - # Create directories if they don't exist - if not os.path.exists(original_config_path): - os.makedirs(original_config_path) - print(f"Created directory: {original_config_path}") - print("Please place your source configuration files in this directory.") + # Create directories if they don't exist. + os.makedirs(ORIGINAL_CONFIG_DIR, exist_ok=True) + os.makedirs(configPath, exist_ok=True) - if not os.path.exists(config_path): - os.makedirs(config_path) - print(f"Created directory: {config_path}") + # Check if we need to create default JSON file. + if not os.path.exists(jsonPath): + with open(jsonPath, "w") as f: + json.dump({"prefixes": ["put.your.prefixes.here"]}, f, indent=2) + print(f"Created default {jsonPath}") - # Check if we need to create default JSON file - if not os.path.exists(json_path): - with open(json_path, 'w') as f: - json.dump({ - "prefixes": [ - "put.your.prefixes.here", - ] - }, f, indent=2) - print(f"Created default {json_path}") - - # Initialize selector + # Initialize selector. selector = ConfigSelector( - json_path=json_path, - original_config_path=original_config_path, - config_path=config_path + json_path=jsonPath, + original_config_path=ORIGINAL_CONFIG_DIR, + config_path=configPath, + old_recipe_path=RECIPE_CONFIG_DIR, ) # Menu state @@ -1172,8 +1523,23 @@ def main(stdscr): selector.copy_matched_files() elif key == ord('m') or key == ord('M'): if confirm_dialog(stdscr, "Compare old and new recipe configs and track changes?"): - changed_count, deleted_count, unchanged_count = selector.compare_and_track_changes() - show_comparison_results(stdscr, changed_count, deleted_count, unchanged_count) + ( + added_count, + modified_count, + deleted_count, + unchanged_count, + language_deleted_count, + not_in_original_count, + ) = selector.compare_and_track_changes() + show_comparison_results( + stdscr, + added_count, + modified_count, + deleted_count, + unchanged_count, + language_deleted_count, + not_in_original_count, + ) elif key == ord('s') or key == ord('S'): selector.save_prefixes() elif key in (10, 13): # ENTER key to select individual files @@ -1231,6 +1597,8 @@ def main(stdscr): selector.matched_files[target_prefix].append(file) if __name__ == "__main__": + forceIngest = "--force" in sys.argv + ingest_config(force=forceIngest) try: curses.wrapper(main) except KeyboardInterrupt: diff --git a/deleted_files.csv b/deleted_files.csv deleted file mode 100644 index 42bb67e..0000000 --- a/deleted_files.csv +++ /dev/null @@ -1,408 +0,0 @@ -filename -core.entity_form_display.wisski_individual.b0bb97d703b09623b04167df1b5f3f49.default.yml -core.entity_form_display.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.default.yml -core.entity_form_display.wisski_individual.b29098a2dbcbda0f61cba5ba9e686f20.default.yml -core.entity_form_display.wisski_individual.b29e446d50baead6233e4f0486368ed6.default.yml -core.entity_form_display.wisski_individual.b29e446d50baead6233e4f0486368ed6.reference_detailed.yml -core.entity_form_display.wisski_individual.b29e446d50baead6233e4f0486368ed6.reference_text.yml -core.entity_form_display.wisski_individual.b4e3bc7ca312a6c46228f35fbf7e1cee.default.yml -core.entity_form_display.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.default.yml -core.entity_form_display.wisski_individual.b66dd83c2f07cd0d188b31b879775f72.default.yml -core.entity_form_display.wisski_individual.b66dd83c2f07cd0d188b31b879775f72.reference_detailed.yml -core.entity_form_display.wisski_individual.b66dd83c2f07cd0d188b31b879775f72.reference_text.yml -core.entity_form_display.wisski_individual.b6fdcccc1c6a08cb48abffecc5a31cfc.default.yml -core.entity_form_display.wisski_individual.b7f7eceaa540597c1440f1c470bebe07.default.yml -core.entity_form_display.wisski_individual.b8a35d61f1bd2672835d376015222d21.default.yml -core.entity_form_display.wisski_individual.ba115ec3f3676cbec7ba8297a94bea48.default.yml -core.entity_form_display.wisski_individual.bad1642b861feb14ecad423091d4495b.default.yml -core.entity_form_display.wisski_individual.bb48a22d36f1c15cd16b143d23466812.default.yml -core.entity_form_display.wisski_individual.bb6bf561b183a2b1733ca97f262ab930.default.yml -core.entity_form_display.wisski_individual.bb79e2aca604c4feee0889e8979f00a6.default.yml -core.entity_form_display.wisski_individual.bc076202c79a386a55b105b7c82e252d.default.yml -core.entity_form_display.wisski_individual.bd0e80d09d3a2618e10a9bda2f85076b.default.yml -core.entity_form_display.wisski_individual.bd649063d3131a245922f7718d962ec6.default.yml -core.entity_form_display.wisski_individual.be6c0e85956559c87483917da3888e23.default.yml -core.entity_form_display.wisski_individual.be9be1c504c5df35dca1836f16d0ee73.default.yml -core.entity_form_display.wisski_individual.bf3b15207bd74301804eb30c87e0fe00.default.yml -core.entity_form_mode.wisski_individual.reference_detailed.yml -core.entity_form_mode.wisski_individual.reference_text.yml -core.entity_view_display.wisski_individual.b0bb97d703b09623b04167df1b5f3f49.default.yml -core.entity_view_display.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.default.yml -core.entity_view_display.wisski_individual.b29098a2dbcbda0f61cba5ba9e686f20.default.yml -core.entity_view_display.wisski_individual.b29e446d50baead6233e4f0486368ed6.default.yml -core.entity_view_display.wisski_individual.b4e3bc7ca312a6c46228f35fbf7e1cee.default.yml -core.entity_view_display.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.default.yml -core.entity_view_display.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.in_data_record.yml -core.entity_view_display.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.reference.yml -core.entity_view_display.wisski_individual.b66dd83c2f07cd0d188b31b879775f72.default.yml -core.entity_view_display.wisski_individual.b6fdcccc1c6a08cb48abffecc5a31cfc.default.yml -core.entity_view_display.wisski_individual.b7f7eceaa540597c1440f1c470bebe07.default.yml -core.entity_view_display.wisski_individual.b7f7eceaa540597c1440f1c470bebe07.multiple.yml -core.entity_view_display.wisski_individual.b7f7eceaa540597c1440f1c470bebe07.reference.yml -core.entity_view_display.wisski_individual.b8a35d61f1bd2672835d376015222d21.default.yml -core.entity_view_display.wisski_individual.ba115ec3f3676cbec7ba8297a94bea48.default.yml -core.entity_view_display.wisski_individual.bad1642b861feb14ecad423091d4495b.default.yml -core.entity_view_display.wisski_individual.bb48a22d36f1c15cd16b143d23466812.default.yml -core.entity_view_display.wisski_individual.bb48a22d36f1c15cd16b143d23466812.reference.yml -core.entity_view_display.wisski_individual.bb6bf561b183a2b1733ca97f262ab930.default.yml -core.entity_view_display.wisski_individual.bb79e2aca604c4feee0889e8979f00a6.default.yml -core.entity_view_display.wisski_individual.bc076202c79a386a55b105b7c82e252d.default.yml -core.entity_view_display.wisski_individual.bd0e80d09d3a2618e10a9bda2f85076b.default.yml -core.entity_view_display.wisski_individual.bd649063d3131a245922f7718d962ec6.default.yml -core.entity_view_display.wisski_individual.be6c0e85956559c87483917da3888e23.default.yml -core.entity_view_display.wisski_individual.be6c0e85956559c87483917da3888e23.preferred_name.yml -core.entity_view_display.wisski_individual.be9be1c504c5df35dca1836f16d0ee73.default.yml -core.entity_view_display.wisski_individual.bf3b15207bd74301804eb30c87e0fe00.default.yml -core.entity_view_mode.wisski_individual.in_data_record.yml -core.entity_view_mode.wisski_individual.multiple.yml -core.entity_view_mode.wisski_individual.preferred_name.yml -core.entity_view_mode.wisski_individual.reference.yml -field.field.wisski_individual.b0bb97d703b09623b04167df1b5f3f49.f1ff3b967723f445933b8a9d7035aad2.yml -field.field.wisski_individual.b0bb97d703b09623b04167df1b5f3f49.fd26906e13c587b8451a6b2af4c08eb4.yml -field.field.wisski_individual.b0bb97d703b09623b04167df1b5f3f49.febe969b98b649621d4c11d0c0e32480.yml -field.field.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.f02460c4dea0c764d17fe66be8f02508.yml -field.field.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.f04ed8c79450669ddd5e93467f2a1a51.yml -field.field.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.f239c64d0bd4b9138bb8fb9e79942110.yml -field.field.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.f295382ce36e43723aefe2fdcbb0d825.yml -field.field.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.f539313cc1cf9a0db4bbe8f9aa5a8e45.yml -field.field.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.f573e1715065fb91e0d9b8a9cd3676ec.yml -field.field.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.f81d5c928f704125c6859e38f6b20499.yml -field.field.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.f9bf6956a0719fd938b76bdf59701563.yml -field.field.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.fae0f9922ce4953c25f6b6d7643459ac.yml -field.field.wisski_individual.b0fa9cc21598a987c556bc05d5e3889e.ffa950f2daa5ea8b4a36265e8afc3fd9.yml -field.field.wisski_individual.b29098a2dbcbda0f61cba5ba9e686f20.f0eee74eab34dac2e3f3042eb332d703.yml -field.field.wisski_individual.b29098a2dbcbda0f61cba5ba9e686f20.f883e31b3ad23a22cd4aa2fbd11159e2.yml -field.field.wisski_individual.b29098a2dbcbda0f61cba5ba9e686f20.fa7cfbe46f0dfc896c7c66fbc0a011ba.yml -field.field.wisski_individual.b29098a2dbcbda0f61cba5ba9e686f20.fb3e71f75bccac3e7aee7efdb2cb14d8.yml -field.field.wisski_individual.b29098a2dbcbda0f61cba5ba9e686f20.fcce64514b22835881e127a12fa0262d.yml -field.field.wisski_individual.b29098a2dbcbda0f61cba5ba9e686f20.fed7ddd55a3909def3c1a9c6f60b44ab.yml -field.field.wisski_individual.b29098a2dbcbda0f61cba5ba9e686f20.ff967c7b141984e1d4e0982c46d02490.yml -field.field.wisski_individual.b29e446d50baead6233e4f0486368ed6.f0d486a436c752d203c3d61be724ec0e.yml -field.field.wisski_individual.b29e446d50baead6233e4f0486368ed6.f3d7e2759ca9321d8f3cb142c21b2450.yml -field.field.wisski_individual.b29e446d50baead6233e4f0486368ed6.f92436d0dc6d2bc4d51d242302707128.yml -field.field.wisski_individual.b29e446d50baead6233e4f0486368ed6.fbd761428160e6e71e34a89c8947436b.yml -field.field.wisski_individual.b29e446d50baead6233e4f0486368ed6.ff261d2c17cf334dd083acf2f917c0d2.yml -field.field.wisski_individual.b4e3bc7ca312a6c46228f35fbf7e1cee.f15ccea9926a79f0d5a64969f2d281ff.yml -field.field.wisski_individual.b4e3bc7ca312a6c46228f35fbf7e1cee.f4cfb767e90ca07edc4f68299663c4de.yml -field.field.wisski_individual.b4e3bc7ca312a6c46228f35fbf7e1cee.f64fbe1124b1d557590cb01c7fdef63c.yml -field.field.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.f0665bd9a349ddb1f3c710369fab8716.yml -field.field.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.f3fd2d803386046f79aeda1387255c21.yml -field.field.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.f8cada4d5f594d0b03f9a39c77492828.yml -field.field.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.f92639d54a47119d4883a941cca24977.yml -field.field.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.fcb7753907b00a53a4d85b2b76990ce8.yml -field.field.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.fcda28daa1b9a3542d4b50c42b3768d1.yml -field.field.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.fd06abbd8a2b0d0945cab573866f8b69.yml -field.field.wisski_individual.b619446f296dfb7c22cf022aac8ce20c.feed4362addfb137b333877006505ed1.yml -field.field.wisski_individual.b66dd83c2f07cd0d188b31b879775f72.f07178031ead71efc4add43c67d498bf.yml -field.field.wisski_individual.b66dd83c2f07cd0d188b31b879775f72.f6fdf07c98933aa4f6f7e6c28cb3fa33.yml -field.field.wisski_individual.b66dd83c2f07cd0d188b31b879775f72.faa0cb687be0c5af723fbab86edfffd1.yml -field.field.wisski_individual.b66dd83c2f07cd0d188b31b879775f72.fc26b447ad175ecae3d6eeb5f463a0e9.yml -field.field.wisski_individual.b66dd83c2f07cd0d188b31b879775f72.fe6c9de47066f3468c6aa80e2fc81acd.yml -field.field.wisski_individual.b6fdcccc1c6a08cb48abffecc5a31cfc.f2c5fa9933fe76dd4dbf5bb90b56b136.yml -field.field.wisski_individual.b6fdcccc1c6a08cb48abffecc5a31cfc.f6821bd09ffdef06b0c5fefaf02df2f0.yml -field.field.wisski_individual.b7f7eceaa540597c1440f1c470bebe07.f35ec1009a594afbd49536c5fc558e04.yml -field.field.wisski_individual.b7f7eceaa540597c1440f1c470bebe07.f62a3579fc0b41ba01707d58d5cef641.yml -field.field.wisski_individual.b7f7eceaa540597c1440f1c470bebe07.f9ed89e6c011d4a0ed5ac9cd5561ce38.yml -field.field.wisski_individual.b7f7eceaa540597c1440f1c470bebe07.feb35e71b6bcf82847c5f3f6a57f6274.yml -field.field.wisski_individual.b8a35d61f1bd2672835d376015222d21.f1aee0c3ecfa2791fe7b1271561c0e12.yml -field.field.wisski_individual.b8a35d61f1bd2672835d376015222d21.f5f4cf8c6abf0ea929305a98956a4380.yml -field.field.wisski_individual.b8a35d61f1bd2672835d376015222d21.f71837c561d00ed05d853339747a44bc.yml -field.field.wisski_individual.ba115ec3f3676cbec7ba8297a94bea48.f481c15a6aeb4fad22258aa4a00064c6.yml -field.field.wisski_individual.ba115ec3f3676cbec7ba8297a94bea48.f5fc867134b7662a70778dfb75523c7c.yml -field.field.wisski_individual.ba115ec3f3676cbec7ba8297a94bea48.f7e9004d4e3ca106c0a39c63c1f1386c.yml -field.field.wisski_individual.ba115ec3f3676cbec7ba8297a94bea48.f86ee13d36aa1ccad56e6b250af24209.yml -field.field.wisski_individual.bad1642b861feb14ecad423091d4495b.f7724300936afe7f841689ce8dda3519.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.f0a4220f174873b3b1dff5c4f0ab038d.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.f2b4b472cafb7f4c46a7c822794ed9dd.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.f2de9f0b2efc868a635f5adfad12a810.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.f2f58a56928267034a028100735346f1.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.f3a37ff34347b001f327ac2bac9dec86.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.f65a6586daed6cba734f174af630bdcf.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.f8478ccaf30ed82bc2287f238406cda8.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.f8e173631161acd62dba6f8cbd3592de.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.f9188639e85ca8d8b8703232f59f1e97.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.f9744c24e4e75df81149232ff393ea47.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.fbbd0fc7866781fbe7913c284ffd6427.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.fcbad61edd92acfbf7014e118c757c7c.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.ff49657fae8a4f553b233b221ba33f8e.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.ff95e2be93a5fa409f76e3d6fc338bf1.yml -field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.field__vf__title.yml -field.field.wisski_individual.bb6bf561b183a2b1733ca97f262ab930.f44970b58e7b700acf846fbac6d527c3.yml -field.field.wisski_individual.bb6bf561b183a2b1733ca97f262ab930.fbc6fd9d737b5047b949baa68c1c543a.yml -field.field.wisski_individual.bb6bf561b183a2b1733ca97f262ab930.fc8e3f1ccd8987fbb8567b52ced21d25.yml -field.field.wisski_individual.bb6bf561b183a2b1733ca97f262ab930.fdbb81d6d2bb33a8e75567aaff54bbd8.yml -field.field.wisski_individual.bb79e2aca604c4feee0889e8979f00a6.ffed4df47440f25499f56fa7e4d74710.yml -field.field.wisski_individual.bc076202c79a386a55b105b7c82e252d.fb1e7967174594ce391b147209a1a9a4.yml -field.field.wisski_individual.bc076202c79a386a55b105b7c82e252d.fbdb442d391782be5eede091f56a81de.yml -field.field.wisski_individual.bd0e80d09d3a2618e10a9bda2f85076b.f0f8bd10e7b63f0a1868f707c9a21c39.yml -field.field.wisski_individual.bd0e80d09d3a2618e10a9bda2f85076b.f6086615f0c43675ad484d5c999f3f75.yml -field.field.wisski_individual.bd0e80d09d3a2618e10a9bda2f85076b.f87beac06a07ace7adf427cd85de0e6e.yml -field.field.wisski_individual.bd649063d3131a245922f7718d962ec6.f19bb59b1cf639dc539f94aefe256dd4.yml -field.field.wisski_individual.be6c0e85956559c87483917da3888e23.f2b62e51766b434f435e6accb85ced1b.yml -field.field.wisski_individual.be6c0e85956559c87483917da3888e23.f565edc65db3ac9ae753871b344a9918.yml -field.field.wisski_individual.be6c0e85956559c87483917da3888e23.f7397b24e80a4f0044962496b226c077.yml -field.field.wisski_individual.be6c0e85956559c87483917da3888e23.fb7c4c986649cff59847eaa6ba5f3c52.yml -field.field.wisski_individual.be6c0e85956559c87483917da3888e23.fe7df91c90d7aa9931e5926ac4393d00.yml -field.field.wisski_individual.be6c0e85956559c87483917da3888e23.ff4f59194bdfa4ae75cdad83a38ba197.yml -field.field.wisski_individual.be6c0e85956559c87483917da3888e23.ffd2f8520a495b47b848996f6d373fd1.yml -field.field.wisski_individual.be9be1c504c5df35dca1836f16d0ee73.f01c7d46b9913b99909f50c098a1920a.yml -field.field.wisski_individual.be9be1c504c5df35dca1836f16d0ee73.f8dba8630367c32aeb2557097a2d0259.yml -field.field.wisski_individual.be9be1c504c5df35dca1836f16d0ee73.f971858ff1b65215d8e24508998be0d7.yml -field.field.wisski_individual.be9be1c504c5df35dca1836f16d0ee73.fd9046c3faf34e0e09444744b19aad49.yml -field.field.wisski_individual.bf3b15207bd74301804eb30c87e0fe00.f75e5d31023925b916d99cb819c720d5.yml -field.field.wisski_individual.bf3b15207bd74301804eb30c87e0fe00.f90b172c9e7d5bf32cbd3ad8fc2eff90.yml -field.field.wisski_individual.bf3b15207bd74301804eb30c87e0fe00.fa4020f71a81e0ebf7cc3ebd60c39017.yml -field.storage.wisski_individual.f01c7d46b9913b99909f50c098a1920a.yml -field.storage.wisski_individual.f02460c4dea0c764d17fe66be8f02508.yml -field.storage.wisski_individual.f04ed8c79450669ddd5e93467f2a1a51.yml -field.storage.wisski_individual.f0665bd9a349ddb1f3c710369fab8716.yml -field.storage.wisski_individual.f07178031ead71efc4add43c67d498bf.yml -field.storage.wisski_individual.f0a4220f174873b3b1dff5c4f0ab038d.yml -field.storage.wisski_individual.f0d486a436c752d203c3d61be724ec0e.yml -field.storage.wisski_individual.f0eee74eab34dac2e3f3042eb332d703.yml -field.storage.wisski_individual.f0f8bd10e7b63f0a1868f707c9a21c39.yml -field.storage.wisski_individual.f15ccea9926a79f0d5a64969f2d281ff.yml -field.storage.wisski_individual.f19bb59b1cf639dc539f94aefe256dd4.yml -field.storage.wisski_individual.f1aee0c3ecfa2791fe7b1271561c0e12.yml -field.storage.wisski_individual.f1ff3b967723f445933b8a9d7035aad2.yml -field.storage.wisski_individual.f239c64d0bd4b9138bb8fb9e79942110.yml -field.storage.wisski_individual.f295382ce36e43723aefe2fdcbb0d825.yml -field.storage.wisski_individual.f2b4b472cafb7f4c46a7c822794ed9dd.yml -field.storage.wisski_individual.f2b62e51766b434f435e6accb85ced1b.yml -field.storage.wisski_individual.f2c5fa9933fe76dd4dbf5bb90b56b136.yml -field.storage.wisski_individual.f2de9f0b2efc868a635f5adfad12a810.yml -field.storage.wisski_individual.f2f58a56928267034a028100735346f1.yml -field.storage.wisski_individual.f35ec1009a594afbd49536c5fc558e04.yml -field.storage.wisski_individual.f3a37ff34347b001f327ac2bac9dec86.yml -field.storage.wisski_individual.f3d7e2759ca9321d8f3cb142c21b2450.yml -field.storage.wisski_individual.f3fd2d803386046f79aeda1387255c21.yml -field.storage.wisski_individual.f44970b58e7b700acf846fbac6d527c3.yml -field.storage.wisski_individual.f481c15a6aeb4fad22258aa4a00064c6.yml -field.storage.wisski_individual.f4cfb767e90ca07edc4f68299663c4de.yml -field.storage.wisski_individual.f539313cc1cf9a0db4bbe8f9aa5a8e45.yml -field.storage.wisski_individual.f565edc65db3ac9ae753871b344a9918.yml -field.storage.wisski_individual.f573e1715065fb91e0d9b8a9cd3676ec.yml -field.storage.wisski_individual.f5f4cf8c6abf0ea929305a98956a4380.yml -field.storage.wisski_individual.f5fc867134b7662a70778dfb75523c7c.yml -field.storage.wisski_individual.f6086615f0c43675ad484d5c999f3f75.yml -field.storage.wisski_individual.f62a3579fc0b41ba01707d58d5cef641.yml -field.storage.wisski_individual.f64fbe1124b1d557590cb01c7fdef63c.yml -field.storage.wisski_individual.f65a6586daed6cba734f174af630bdcf.yml -field.storage.wisski_individual.f6821bd09ffdef06b0c5fefaf02df2f0.yml -field.storage.wisski_individual.f6fdf07c98933aa4f6f7e6c28cb3fa33.yml -field.storage.wisski_individual.f71837c561d00ed05d853339747a44bc.yml -field.storage.wisski_individual.f7397b24e80a4f0044962496b226c077.yml -field.storage.wisski_individual.f75e5d31023925b916d99cb819c720d5.yml -field.storage.wisski_individual.f7724300936afe7f841689ce8dda3519.yml -field.storage.wisski_individual.f7e9004d4e3ca106c0a39c63c1f1386c.yml -field.storage.wisski_individual.f81d5c928f704125c6859e38f6b20499.yml -field.storage.wisski_individual.f8478ccaf30ed82bc2287f238406cda8.yml -field.storage.wisski_individual.f86ee13d36aa1ccad56e6b250af24209.yml -field.storage.wisski_individual.f87beac06a07ace7adf427cd85de0e6e.yml -field.storage.wisski_individual.f883e31b3ad23a22cd4aa2fbd11159e2.yml -field.storage.wisski_individual.f8cada4d5f594d0b03f9a39c77492828.yml -field.storage.wisski_individual.f8dba8630367c32aeb2557097a2d0259.yml -field.storage.wisski_individual.f8e173631161acd62dba6f8cbd3592de.yml -field.storage.wisski_individual.f90b172c9e7d5bf32cbd3ad8fc2eff90.yml -field.storage.wisski_individual.f9188639e85ca8d8b8703232f59f1e97.yml -field.storage.wisski_individual.f92436d0dc6d2bc4d51d242302707128.yml -field.storage.wisski_individual.f92639d54a47119d4883a941cca24977.yml -field.storage.wisski_individual.f971858ff1b65215d8e24508998be0d7.yml -field.storage.wisski_individual.f9744c24e4e75df81149232ff393ea47.yml -field.storage.wisski_individual.f9bf6956a0719fd938b76bdf59701563.yml -field.storage.wisski_individual.f9ed89e6c011d4a0ed5ac9cd5561ce38.yml -field.storage.wisski_individual.fa4020f71a81e0ebf7cc3ebd60c39017.yml -field.storage.wisski_individual.fa7cfbe46f0dfc896c7c66fbc0a011ba.yml -field.storage.wisski_individual.faa0cb687be0c5af723fbab86edfffd1.yml -field.storage.wisski_individual.fae0f9922ce4953c25f6b6d7643459ac.yml -field.storage.wisski_individual.fb1e7967174594ce391b147209a1a9a4.yml -field.storage.wisski_individual.fb3e71f75bccac3e7aee7efdb2cb14d8.yml -field.storage.wisski_individual.fb7c4c986649cff59847eaa6ba5f3c52.yml -field.storage.wisski_individual.fbbd0fc7866781fbe7913c284ffd6427.yml -field.storage.wisski_individual.fbc6fd9d737b5047b949baa68c1c543a.yml -field.storage.wisski_individual.fbd761428160e6e71e34a89c8947436b.yml -field.storage.wisski_individual.fbdb442d391782be5eede091f56a81de.yml -field.storage.wisski_individual.fc26b447ad175ecae3d6eeb5f463a0e9.yml -field.storage.wisski_individual.fc8e3f1ccd8987fbb8567b52ced21d25.yml -field.storage.wisski_individual.fcb7753907b00a53a4d85b2b76990ce8.yml -field.storage.wisski_individual.fcbad61edd92acfbf7014e118c757c7c.yml -field.storage.wisski_individual.fcce64514b22835881e127a12fa0262d.yml -field.storage.wisski_individual.fcda28daa1b9a3542d4b50c42b3768d1.yml -field.storage.wisski_individual.fd06abbd8a2b0d0945cab573866f8b69.yml -field.storage.wisski_individual.fd26906e13c587b8451a6b2af4c08eb4.yml -field.storage.wisski_individual.fd9046c3faf34e0e09444744b19aad49.yml -field.storage.wisski_individual.fdbb81d6d2bb33a8e75567aaff54bbd8.yml -field.storage.wisski_individual.fe6c9de47066f3468c6aa80e2fc81acd.yml -field.storage.wisski_individual.fe7df91c90d7aa9931e5926ac4393d00.yml -field.storage.wisski_individual.feb35e71b6bcf82847c5f3f6a57f6274.yml -field.storage.wisski_individual.febe969b98b649621d4c11d0c0e32480.yml -field.storage.wisski_individual.fed7ddd55a3909def3c1a9c6f60b44ab.yml -field.storage.wisski_individual.feed4362addfb137b333877006505ed1.yml -field.storage.wisski_individual.ff261d2c17cf334dd083acf2f917c0d2.yml -field.storage.wisski_individual.ff49657fae8a4f553b233b221ba33f8e.yml -field.storage.wisski_individual.ff4f59194bdfa4ae75cdad83a38ba197.yml -field.storage.wisski_individual.ff95e2be93a5fa409f76e3d6fc338bf1.yml -field.storage.wisski_individual.ff967c7b141984e1d4e0982c46d02490.yml -field.storage.wisski_individual.ffa950f2daa5ea8b4a36265e8afc3fd9.yml -field.storage.wisski_individual.ffd2f8520a495b47b848996f6d373fd1.yml -field.storage.wisski_individual.ffed4df47440f25499f56fa7e4d74710.yml -field.storage.wisski_individual.field__vf__title.yml -views.view.b0bb97d703b09623b04167df1b5f3f49.yml -views.view.b0fa9cc21598a987c556bc05d5e3889e.yml -views.view.b29098a2dbcbda0f61cba5ba9e686f20.yml -views.view.b29e446d50baead6233e4f0486368ed6.yml -views.view.b4e3bc7ca312a6c46228f35fbf7e1cee.yml -views.view.b619446f296dfb7c22cf022aac8ce20c.yml -views.view.b66dd83c2f07cd0d188b31b879775f72.yml -views.view.b6fdcccc1c6a08cb48abffecc5a31cfc.yml -views.view.b7f7eceaa540597c1440f1c470bebe07.yml -views.view.b8a35d61f1bd2672835d376015222d21.yml -views.view.ba115ec3f3676cbec7ba8297a94bea48.yml -views.view.bad1642b861feb14ecad423091d4495b.yml -views.view.bb48a22d36f1c15cd16b143d23466812.yml -views.view.bb6bf561b183a2b1733ca97f262ab930.yml -views.view.bb79e2aca604c4feee0889e8979f00a6.yml -views.view.bc076202c79a386a55b105b7c82e252d.yml -views.view.bd0e80d09d3a2618e10a9bda2f85076b.yml -views.view.bd649063d3131a245922f7718d962ec6.yml -views.view.be6c0e85956559c87483917da3888e23.yml -views.view.be9be1c504c5df35dca1836f16d0ee73.yml -views.view.bf3b15207bd74301804eb30c87e0fe00.yml -wisski_core.wisski_bundle.b0bb97d703b09623b04167df1b5f3f49.yml -wisski_core.wisski_bundle.b0fa9cc21598a987c556bc05d5e3889e.yml -wisski_core.wisski_bundle.b29098a2dbcbda0f61cba5ba9e686f20.yml -wisski_core.wisski_bundle.b29e446d50baead6233e4f0486368ed6.yml -wisski_core.wisski_bundle.b4e3bc7ca312a6c46228f35fbf7e1cee.yml -wisski_core.wisski_bundle.b619446f296dfb7c22cf022aac8ce20c.yml -wisski_core.wisski_bundle.b66dd83c2f07cd0d188b31b879775f72.yml -wisski_core.wisski_bundle.b6fdcccc1c6a08cb48abffecc5a31cfc.yml -wisski_core.wisski_bundle.b7f7eceaa540597c1440f1c470bebe07.yml -wisski_core.wisski_bundle.b8a35d61f1bd2672835d376015222d21.yml -wisski_core.wisski_bundle.ba115ec3f3676cbec7ba8297a94bea48.yml -wisski_core.wisski_bundle.bad1642b861feb14ecad423091d4495b.yml -wisski_core.wisski_bundle.bb48a22d36f1c15cd16b143d23466812.yml -wisski_core.wisski_bundle.bb6bf561b183a2b1733ca97f262ab930.yml -wisski_core.wisski_bundle.bb79e2aca604c4feee0889e8979f00a6.yml -wisski_core.wisski_bundle.bc076202c79a386a55b105b7c82e252d.yml -wisski_core.wisski_bundle.bd0e80d09d3a2618e10a9bda2f85076b.yml -wisski_core.wisski_bundle.bd649063d3131a245922f7718d962ec6.yml -wisski_core.wisski_bundle.be6c0e85956559c87483917da3888e23.yml -wisski_core.wisski_bundle.be9be1c504c5df35dca1836f16d0ee73.yml -wisski_core.wisski_bundle.bf3b15207bd74301804eb30c87e0fe00.yml -wisski_pathbuilder.settings.yml -wisski_pathbuilder.wisski_path.f__actor__alter_names.yml -wisski_pathbuilder.wisski_path.f__actor__authority_data.yml -wisski_pathbuilder.wisski_path.f__actor__begin_of_exist.yml -wisski_pathbuilder.wisski_path.f__actor__display_authority_name.yml -wisski_pathbuilder.wisski_path.f__actor__display_custom_name.yml -wisski_pathbuilder.wisski_path.f__actor__end_of_exist.yml -wisski_pathbuilder.wisski_path.f__actor__name.yml -wisski_pathbuilder.wisski_path.f__actor__occupation.yml -wisski_pathbuilder.wisski_path.f__actor__organisation_type.yml -wisski_pathbuilder.wisski_path.f__actor__type.yml -wisski_pathbuilder.wisski_path.f__auth_data__authority_document.yml -wisski_pathbuilder.wisski_path.f__auth_data__entity_type.yml -wisski_pathbuilder.wisski_path.f__auth_data__preferred_term.yml -wisski_pathbuilder.wisski_path.f__auth_data__url.yml -wisski_pathbuilder.wisski_path.f__auth_doc__acronym.yml -wisski_pathbuilder.wisski_path.f__auth_doc__name.yml -wisski_pathbuilder.wisski_path.f__auth_doc__website.yml -wisski_pathbuilder.wisski_path.f__boe__participants.yml -wisski_pathbuilder.wisski_path.f__boe__period.yml -wisski_pathbuilder.wisski_path.f__boe__place.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__data_record.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__dimension.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__discription.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__identifier.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__item_history_activity.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__keyword.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__material.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__media_file.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__place.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__ressoucen.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__technique.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__title.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__title_ref.yml -wisski_pathbuilder.wisski_path.f__cult_her_item__type.yml -wisski_pathbuilder.wisski_path.f__custom_term__alternative_name.yml -wisski_pathbuilder.wisski_path.f__custom_term__preferred_name.yml -wisski_pathbuilder.wisski_path.f__data_record__actor.yml -wisski_pathbuilder.wisski_path.f__data_record__dig_proc.yml -wisski_pathbuilder.wisski_path.f__data_record__id.yml -wisski_pathbuilder.wisski_path.f__data_record__language.yml -wisski_pathbuilder.wisski_path.f__data_record__media_file.yml -wisski_pathbuilder.wisski_path.f__data_record__rights.yml -wisski_pathbuilder.wisski_path.f__data_record__type.yml -wisski_pathbuilder.wisski_path.f__date__appellation.yml -wisski_pathbuilder.wisski_path.f__date__day.yml -wisski_pathbuilder.wisski_path.f__date__month.yml -wisski_pathbuilder.wisski_path.f__date__operator.yml -wisski_pathbuilder.wisski_path.f__date__year.yml -wisski_pathbuilder.wisski_path.f__dig_proc__creator.yml -wisski_pathbuilder.wisski_path.f__dim__type.yml -wisski_pathbuilder.wisski_path.f__dim__unit.yml -wisski_pathbuilder.wisski_path.f__dim__value.yml -wisski_pathbuilder.wisski_path.f__eoe__participants.yml -wisski_pathbuilder.wisski_path.f__eoe__place.yml -wisski_pathbuilder.wisski_path.f__eoe__zeitraum.yml -wisski_pathbuilder.wisski_path.f__item_history_activity__actor.yml -wisski_pathbuilder.wisski_path.f__item_history_activity__date.yml -wisski_pathbuilder.wisski_path.f__item_history_activity__place.yml -wisski_pathbuilder.wisski_path.f__item_history_activity__type.yml -wisski_pathbuilder.wisski_path.f__keyword__appellation.yml -wisski_pathbuilder.wisski_path.f__keyword__used_by_object.yml -wisski_pathbuilder.wisski_path.f__material__type.yml -wisski_pathbuilder.wisski_path.f__media_file__audio.yml -wisski_pathbuilder.wisski_path.f__media_file__description.yml -wisski_pathbuilder.wisski_path.f__media_file__document.yml -wisski_pathbuilder.wisski_path.f__media_file__image.yml -wisski_pathbuilder.wisski_path.f__media_file__rights.yml -wisski_pathbuilder.wisski_path.f__media_file__type.yml -wisski_pathbuilder.wisski_path.f__media_file__url.yml -wisski_pathbuilder.wisski_path.f__media_file__video.yml -wisski_pathbuilder.wisski_path.f__period__begin_det.yml -wisski_pathbuilder.wisski_path.f__period__begin_text.yml -wisski_pathbuilder.wisski_path.f__period__end_det.yml -wisski_pathbuilder.wisski_path.f__period__end_text.yml -wisski_pathbuilder.wisski_path.f__period__precision.yml -wisski_pathbuilder.wisski_path.f__place__alternative_name.yml -wisski_pathbuilder.wisski_path.f__place__authority_data.yml -wisski_pathbuilder.wisski_path.f__place__display_authority_name.yml -wisski_pathbuilder.wisski_path.f__place__display_custom_name.yml -wisski_pathbuilder.wisski_path.f__place__place.yml -wisski_pathbuilder.wisski_path.f__place__preferred_name.yml -wisski_pathbuilder.wisski_path.f__place__spatial_coordinates.yml -wisski_pathbuilder.wisski_path.f__resource__appellation.yml -wisski_pathbuilder.wisski_path.f__resource__comment.yml -wisski_pathbuilder.wisski_path.f__resource__link.yml -wisski_pathbuilder.wisski_path.f__resource__type.yml -wisski_pathbuilder.wisski_path.f__right__actor.yml -wisski_pathbuilder.wisski_path.f__right__usage.yml -wisski_pathbuilder.wisski_path.f__right_description.yml -wisski_pathbuilder.wisski_path.f__technique__type.yml -wisski_pathbuilder.wisski_path.f__title_ass__objekt_id.yml -wisski_pathbuilder.wisski_path.f__title_ass__status.yml -wisski_pathbuilder.wisski_path.f__title_ass__titel.yml -wisski_pathbuilder.wisski_path.f__title_ass__visibility.yml -wisski_pathbuilder.wisski_path.g__actor.yml -wisski_pathbuilder.wisski_path.g__authority_document.yml -wisski_pathbuilder.wisski_path.g__begin_of_existence.yml -wisski_pathbuilder.wisski_path.g__culture_heritage_item.yml -wisski_pathbuilder.wisski_path.g__custom_term.yml -wisski_pathbuilder.wisski_path.g__data_record.yml -wisski_pathbuilder.wisski_path.g__date.yml -wisski_pathbuilder.wisski_path.g__digitization_process.yml -wisski_pathbuilder.wisski_path.g__dimensions.yml -wisski_pathbuilder.wisski_path.g__end_of_existence.yml -wisski_pathbuilder.wisski_path.g__item_history_activity.yml -wisski_pathbuilder.wisski_path.g__keyword.yml -wisski_pathbuilder.wisski_path.g__material.yml -wisski_pathbuilder.wisski_path.g__media_file.yml -wisski_pathbuilder.wisski_path.g__period.yml -wisski_pathbuilder.wisski_path.g__place.yml -wisski_pathbuilder.wisski_path.g__ressources.yml -wisski_pathbuilder.wisski_path.g__right.yml -wisski_pathbuilder.wisski_path.g__technique.yml -wisski_pathbuilder.wisski_path.g__titel_assignment.yml -wisski_pathbuilder.wisski_path.g__type.yml -wisski_pathbuilder.wisski_pathbuilder.default.yml diff --git a/ignored_files-example.yml b/ignored_files-example.yml new file mode 100644 index 0000000..cebc19c --- /dev/null +++ b/ignored_files-example.yml @@ -0,0 +1,9 @@ +field: + name: field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.field__vf__title + reason: because we need target id instead of uuid +field: + name: field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.field__object__vf__ownership + reason: because we need target id instead of uuid +views: + name: views.view.block_content + reason: false positive diff --git a/ignored_files.yml b/ignored_files.yml index 43b909c..cebc19c 100644 --- a/ignored_files.yml +++ b/ignored_files.yml @@ -1,3 +1,9 @@ field: name: field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.field__vf__title reason: because we need target id instead of uuid +field: + name: field.field.wisski_individual.bb48a22d36f1c15cd16b143d23466812.field__object__vf__ownership + reason: because we need target id instead of uuid +views: + name: views.view.block_content + reason: false positive