esgvoc 1.1.2__py3-none-any.whl → 1.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of esgvoc might be problematic. Click here for more details.

esgvoc/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  import esgvoc.core.logging_handler # noqa
2
2
 
3
- __version__ = "1.1.2"
3
+ __version__ = "1.1.3"
@@ -47,6 +47,25 @@ class DrsSpecification(BaseModel):
47
47
  """The parts of the DRS specification."""
48
48
 
49
49
 
50
+ class AttributeProperty(BaseModel):
51
+ """
52
+ A NetCDF global attribute property specification.
53
+ """
54
+
55
+ source_collection: str
56
+ "The project collection that originated the property."
57
+ is_required: bool
58
+ "Specifies if the attribute must be present in the NetCDF file."
59
+ value_type: str
60
+ "The type of the attribute value."
61
+ specific_key: str | None = None
62
+ "Specifies a specific key in the collection."
63
+ field_name: str | None = None
64
+ "The name of the attribute field."
65
+ default_value: str | None = None
66
+ "The default value for the attribute."
67
+
68
+
50
69
  class CatalogProperty(BaseModel):
51
70
  """
52
71
  A dataset property described in a catalog.
@@ -82,6 +101,9 @@ class CatalogProperties(BaseModel):
82
101
  """The extensions of the catalog."""
83
102
 
84
103
 
104
+ AttributeSpecification = list[AttributeProperty]
105
+
106
+
85
107
  class CatalogSpecification(BaseModel):
86
108
  """
87
109
  A catalog specifications.
@@ -113,4 +135,6 @@ class ProjectSpecs(BaseModel):
113
135
  # TODO: release = None when all projects have catalog_specs.yaml.
114
136
  catalog_specs: CatalogSpecification | None = None
115
137
  """The catalog specifications of the project."""
138
+ attr_specs: AttributeSpecification | None = None
139
+ """The NetCDF global attribute specifications of the project."""
116
140
  model_config = ConfigDict(extra="allow")
@@ -124,15 +124,17 @@ def _process_composite_term(term: UTerm | PTerm, universe_session: Session,
124
124
  return property_key, property_values, has_pattern
125
125
 
126
126
 
127
- def _process_col_pattern_terms(collection: PCollection) -> tuple[str, str]:
128
- # The generation of the value of the field pattern for the collections with more than one term
129
- # is not specified yet.
127
+ def _process_col_pattern_terms(collection: PCollection) -> tuple[str, str | list[dict]]:
130
128
  if len(collection.terms) == 1:
131
129
  term = collection.terms[0]
132
- return _process_pattern_term(term)
130
+ property_key, property_value = _process_pattern_term(term)
133
131
  else:
134
- msg = f"unsupported collection of term pattern with more than one term for '{collection.id}'"
135
- raise EsgvocNotImplementedError(msg)
132
+ property_key = 'anyOf'
133
+ property_value = list()
134
+ for term in collection.terms:
135
+ pkey, pvalue = _process_pattern_term(term)
136
+ property_value.append({pkey: pvalue})
137
+ return property_key, property_value
136
138
 
137
139
 
138
140
  def _process_pattern_term(term: PTerm) -> tuple[str, str]:
@@ -290,7 +292,7 @@ def generate_json_schema(project_id: str) -> dict:
290
292
  result = json.loads(json_raw_str)
291
293
  return result
292
294
  except Exception as e:
293
- raise EsgvocException(f'unable to produce schema compliant to JSON: {e}') from e
295
+ raise EsgvocException(f'JSON error: {e}') from e
294
296
  else:
295
297
  raise EsgvocNotFoundError(f"catalog properties for the project '{project_id}' " +
296
298
  "are missing")
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "id": {
39
39
  "type": "string",
40
- "pattern": "{{ drs_dataset_id_regex }}"
40
+ "pattern": {{ drs_dataset_id_regex|tojson }}
41
41
  },
42
42
  "collection": {
43
43
  "const": "{{ project_id|upper }}"
@@ -82,7 +82,7 @@ class CVTester:
82
82
 
83
83
  def get_available_projects(self) -> List[str]:
84
84
  """Get list of all available project CVs"""
85
- return list(ServiceSettings.DEFAULT_PROJECT_CONFIGS.keys())
85
+ return list(ServiceSettings._get_default_project_configs().keys())
86
86
 
87
87
  def configure_for_testing(
88
88
  self,
@@ -121,7 +121,7 @@ class CVTester:
121
121
  # Use custom repo/branch if provided, otherwise use defaults
122
122
  if repo_url or branch:
123
123
  # Custom configuration
124
- default_config = ServiceSettings.DEFAULT_PROJECT_CONFIGS[project_name]
124
+ default_config = ServiceSettings._get_default_project_configs()[project_name]
125
125
  project_config = {
126
126
  "project_name": project_name,
127
127
  "github_repo": repo_url or default_config["github_repo"],
@@ -134,7 +134,7 @@ class CVTester:
134
134
  console.print(f" Branch: {project_config['branch']}")
135
135
  else:
136
136
  # Default configuration
137
- project_config = ServiceSettings.DEFAULT_PROJECT_CONFIGS[project_name].copy()
137
+ project_config = ServiceSettings._get_default_project_configs()[project_name].copy()
138
138
  console.print(f"[blue]Using default configuration for {project_name}[/blue]")
139
139
 
140
140
  # Create temporary test configuration with universe and single project
@@ -391,9 +391,12 @@ class CVTester:
391
391
  """Test YAML specification files (project_specs.yaml, drs_specs.yaml, catalog_spec.yaml, attr_specs.yaml)"""
392
392
  errors = []
393
393
 
394
+ # Add clear section header
395
+ console.print(f"\n[bold blue]📋 Testing YAML Specification Files[/bold blue]")
396
+ console.print(f"[dim]Repository path: {repo_dir}[/dim]")
397
+
394
398
  # Import constants and YAML handling
395
399
  try:
396
- import yaml
397
400
  from esgvoc.core.constants import (
398
401
  PROJECT_SPECS_FILENAME,
399
402
  DRS_SPECS_FILENAME,
@@ -401,12 +404,24 @@ class CVTester:
401
404
  ATTRIBUTES_SPECS_FILENAME
402
405
  )
403
406
  except ImportError as e:
404
- errors.append(f"❌ Missing required dependencies: {e}")
407
+ error_msg = f"❌ Missing required esgvoc constants: {e}"
408
+ errors.append(error_msg)
409
+ console.print(f"[red]{error_msg}[/red]")
410
+ return errors
411
+
412
+ try:
413
+ import yaml
414
+ except ImportError:
415
+ error_msg = f"❌ PyYAML not installed. Install with: pip install PyYAML"
416
+ errors.append(error_msg)
417
+ console.print(f"[red]{error_msg}[/red]")
405
418
  return errors
406
419
 
407
420
  # Get existing collections for validation
408
421
  existing_collections = {d.name for d in collection_directories}
409
422
  source_collections = set()
423
+ # Track which files contain each collection reference for better error reporting
424
+ collection_file_mapping = {} # collection_name -> set of files that reference it
410
425
  files_tested = 0
411
426
 
412
427
  # Test project_specs.yaml
@@ -419,11 +434,17 @@ class CVTester:
419
434
  console.print(f" [green]✅ {PROJECT_SPECS_FILENAME} parsed successfully[/green]")
420
435
  files_tested += 1
421
436
  except yaml.YAMLError as e:
422
- errors.append(f"❌ {PROJECT_SPECS_FILENAME}: Invalid YAML syntax - {e}")
437
+ error_msg = f"❌ {PROJECT_SPECS_FILENAME}: Invalid YAML syntax - {e}"
438
+ errors.append(error_msg)
439
+ console.print(f" [red]{error_msg}[/red]")
423
440
  except Exception as e:
424
- errors.append(f"❌ Error reading {PROJECT_SPECS_FILENAME}: {e}")
441
+ error_msg = f"❌ Error reading {PROJECT_SPECS_FILENAME}: {e}"
442
+ errors.append(error_msg)
443
+ console.print(f" [red]{error_msg}[/red]")
425
444
  else:
426
- errors.append(f"❌ Required file {PROJECT_SPECS_FILENAME} not found")
445
+ error_msg = f"❌ Required file {PROJECT_SPECS_FILENAME} not found"
446
+ errors.append(error_msg)
447
+ console.print(f"📄 [red]{error_msg}[/red]")
427
448
 
428
449
  # Test drs_specs.yaml
429
450
  drs_specs_file = repo_dir / DRS_SPECS_FILENAME
@@ -442,15 +463,24 @@ class CVTester:
442
463
  collection_ref = part.get("collection_id") or part.get("source_collection")
443
464
  if collection_ref:
444
465
  source_collections.add(collection_ref)
466
+ if collection_ref not in collection_file_mapping:
467
+ collection_file_mapping[collection_ref] = set()
468
+ collection_file_mapping[collection_ref].add(DRS_SPECS_FILENAME)
445
469
 
446
470
  console.print(f" [green]✅ {DRS_SPECS_FILENAME} parsed successfully[/green]")
447
471
  files_tested += 1
448
472
  except yaml.YAMLError as e:
449
- errors.append(f"❌ {DRS_SPECS_FILENAME}: Invalid YAML syntax - {e}")
473
+ error_msg = f"❌ {DRS_SPECS_FILENAME}: Invalid YAML syntax - {e}"
474
+ errors.append(error_msg)
475
+ console.print(f" [red]{error_msg}[/red]")
450
476
  except Exception as e:
451
- errors.append(f"❌ Error reading {DRS_SPECS_FILENAME}: {e}")
477
+ error_msg = f"❌ Error reading {DRS_SPECS_FILENAME}: {e}"
478
+ errors.append(error_msg)
479
+ console.print(f" [red]{error_msg}[/red]")
452
480
  else:
453
- errors.append(f"❌ Required file {DRS_SPECS_FILENAME} not found")
481
+ error_msg = f"❌ Required file {DRS_SPECS_FILENAME} not found"
482
+ errors.append(error_msg)
483
+ console.print(f"📄 [red]{error_msg}[/red]")
454
484
 
455
485
  # Test catalog_spec.yaml (optional)
456
486
  catalog_specs_file = repo_dir / CATALOG_SPECS_FILENAME
@@ -467,61 +497,215 @@ class CVTester:
467
497
  if prop_type in catalog_specs and isinstance(catalog_specs[prop_type], list):
468
498
  for prop in catalog_specs[prop_type]:
469
499
  if isinstance(prop, dict) and "source_collection" in prop:
470
- source_collections.add(prop["source_collection"])
500
+ collection_ref = prop["source_collection"]
501
+ source_collections.add(collection_ref)
502
+ if collection_ref not in collection_file_mapping:
503
+ collection_file_mapping[collection_ref] = set()
504
+ collection_file_mapping[collection_ref].add(CATALOG_SPECS_FILENAME)
471
505
 
472
506
  console.print(f" [green]✅ {CATALOG_SPECS_FILENAME} parsed successfully[/green]")
473
507
  files_tested += 1
474
508
  except yaml.YAMLError as e:
475
- errors.append(f"❌ {CATALOG_SPECS_FILENAME}: Invalid YAML syntax - {e}")
509
+ error_msg = f"❌ {CATALOG_SPECS_FILENAME}: Invalid YAML syntax - {e}"
510
+ errors.append(error_msg)
511
+ console.print(f" [red]{error_msg}[/red]")
476
512
  except Exception as e:
477
- errors.append(f"❌ Error reading {CATALOG_SPECS_FILENAME}: {e}")
513
+ error_msg = f"❌ Error reading {CATALOG_SPECS_FILENAME}: {e}"
514
+ errors.append(error_msg)
515
+ console.print(f" [red]{error_msg}[/red]")
478
516
  else:
479
517
  console.print(f" [yellow]⚠️ Optional file {CATALOG_SPECS_FILENAME} not found[/yellow]")
480
518
 
481
- # Test attr_specs.yaml (currently not ingested by esgvoc, but test for syntax)
519
+ # Test attr_specs.yaml (now ingested by esgvoc as confirmed by project_ingestion.py updates)
482
520
  attr_specs_file = repo_dir / ATTRIBUTES_SPECS_FILENAME
483
521
  if attr_specs_file.exists():
484
- console.print(f"📄 Testing {ATTRIBUTES_SPECS_FILENAME} (syntax only - not ingested by esgvoc)...")
522
+ console.print(f"📄 Testing {ATTRIBUTES_SPECS_FILENAME}...")
485
523
  try:
486
524
  with open(attr_specs_file, "r", encoding="utf-8") as f:
487
525
  attr_specs = yaml.safe_load(f)
488
526
 
489
- # Extract collection references from attribute specs if they exist
490
- if isinstance(attr_specs, dict):
491
- # Check for global_attributes_specs or similar structures
527
+ # Extract collection references from attribute specs
528
+ if isinstance(attr_specs, list):
529
+ # New format: list of AttributeProperty objects
530
+ for attr_spec in attr_specs:
531
+ if isinstance(attr_spec, dict) and "source_collection" in attr_spec:
532
+ collection_ref = attr_spec["source_collection"]
533
+ source_collections.add(collection_ref)
534
+ if collection_ref not in collection_file_mapping:
535
+ collection_file_mapping[collection_ref] = set()
536
+ collection_file_mapping[collection_ref].add(ATTRIBUTES_SPECS_FILENAME)
537
+ elif isinstance(attr_specs, dict):
538
+ # Legacy format: nested structure with "specs" key
492
539
  if "specs" in attr_specs:
493
540
  specs = attr_specs["specs"]
494
541
  if isinstance(specs, dict):
495
542
  for attr_name, attr_spec in specs.items():
496
543
  if isinstance(attr_spec, dict) and "source_collection" in attr_spec:
497
- source_collections.add(attr_spec["source_collection"])
544
+ collection_ref = attr_spec["source_collection"]
545
+ source_collections.add(collection_ref)
546
+ if collection_ref not in collection_file_mapping:
547
+ collection_file_mapping[collection_ref] = set()
548
+ collection_file_mapping[collection_ref].add(ATTRIBUTES_SPECS_FILENAME)
549
+ elif isinstance(specs, list):
550
+ for attr_spec in specs:
551
+ if isinstance(attr_spec, dict) and "source_collection" in attr_spec:
552
+ collection_ref = attr_spec["source_collection"]
553
+ source_collections.add(collection_ref)
554
+ if collection_ref not in collection_file_mapping:
555
+ collection_file_mapping[collection_ref] = set()
556
+ collection_file_mapping[collection_ref].add(ATTRIBUTES_SPECS_FILENAME)
498
557
 
499
558
  console.print(f" [green]✅ {ATTRIBUTES_SPECS_FILENAME} parsed successfully[/green]")
500
- console.print(f" [yellow]⚠️ Note: {ATTRIBUTES_SPECS_FILENAME} is not currently ingested by esgvoc[/yellow]")
501
559
  files_tested += 1
502
560
  except yaml.YAMLError as e:
503
- errors.append(f"❌ {ATTRIBUTES_SPECS_FILENAME}: Invalid YAML syntax - {e}")
561
+ error_msg = f"❌ {ATTRIBUTES_SPECS_FILENAME}: Invalid YAML syntax - {e}"
562
+ errors.append(error_msg)
563
+ console.print(f" [red]{error_msg}[/red]")
504
564
  except Exception as e:
505
- errors.append(f"❌ Error reading {ATTRIBUTES_SPECS_FILENAME}: {e}")
565
+ error_msg = f"❌ Error reading {ATTRIBUTES_SPECS_FILENAME}: {e}"
566
+ errors.append(error_msg)
567
+ console.print(f" [red]{error_msg}[/red]")
506
568
  else:
507
569
  console.print(f" [yellow]⚠️ Optional file {ATTRIBUTES_SPECS_FILENAME} not found[/yellow]")
508
570
 
509
571
  # Validate collection references
572
+ console.print(f"\n📂 Validating collection references...")
510
573
  if source_collections:
511
574
  console.print(f" Found {len(source_collections)} source_collection references")
512
575
 
513
576
  for collection in source_collections:
514
577
  if collection not in existing_collections:
515
- errors.append(f"❌ YAML specs reference non-existent collection: '{collection}'")
578
+ # Enhanced error message showing which files contain the reference
579
+ referencing_files = collection_file_mapping.get(collection, set())
580
+ files_list = ", ".join(sorted(referencing_files))
581
+ error_msg = f"❌ YAML specs reference non-existent collection: '{collection}' (referenced in: {files_list})"
582
+ errors.append(error_msg)
583
+ console.print(f" [red]{error_msg}[/red]")
516
584
  else:
517
585
  console.print(f" [green]✅ Reference '{collection}' exists[/green]")
518
586
  else:
519
587
  console.print(" [yellow]⚠️ No collection references found in YAML specs[/yellow]")
520
588
 
589
+ # Final YAML validation summary
590
+ console.print(f"\n📊 YAML Validation Summary:")
521
591
  if files_tested == 0:
522
- errors.append("❌ No YAML specification files found")
592
+ error_msg = "❌ No YAML specification files found"
593
+ errors.append(error_msg)
594
+ console.print(f" [red]{error_msg}[/red]")
523
595
  else:
524
- console.print(f" [blue]📊 Successfully tested {files_tested} YAML specification files[/blue]")
596
+ if errors:
597
+ console.print(f" [red]❌ {len(errors)} errors found in YAML files[/red]")
598
+ else:
599
+ console.print(f" [green]✅ All {files_tested} YAML specification files are valid[/green]")
600
+
601
+ console.print(f" [blue]Files tested: {files_tested}[/blue]")
602
+
603
+ return errors
604
+
605
+ def _test_esgvoc_specs_ingestion(self, project_name: str, repo_dir: Path) -> List[str]:
606
+ """Test that YAML specs are properly ingested into esgvoc and accessible via API"""
607
+ errors = []
608
+
609
+ try:
610
+ # Import esgvoc API and constants
611
+ import esgvoc.api as ev
612
+ from esgvoc.core.constants import ATTRIBUTES_SPECS_FILENAME
613
+ except ImportError as e:
614
+ errors.append(f"❌ Cannot import esgvoc modules for ingestion testing: {e}")
615
+ return errors
616
+
617
+ try:
618
+ import yaml
619
+ except ImportError:
620
+ errors.append(f"❌ PyYAML not installed. Install with: pip install PyYAML")
621
+ return errors
622
+
623
+ console.print(f"🔍 Testing esgvoc ingestion compatibility for {project_name}...")
624
+
625
+ # Get the project specs from esgvoc
626
+ try:
627
+ project = ev.get_project(project_name)
628
+ console.print(f" [green]✅ Project '{project_name}' found in esgvoc[/green]")
629
+
630
+ if hasattr(project, 'attr_specs') and hasattr(project, 'drs_specs'):
631
+ # Project is properly loaded with specs - convert to dict format for compatibility
632
+ specs = {}
633
+ if hasattr(project, 'attr_specs') and project.attr_specs:
634
+ specs["attr_specs"] = project.attr_specs
635
+ if hasattr(project, 'drs_specs') and project.drs_specs:
636
+ specs["drs_specs"] = project.drs_specs
637
+ if hasattr(project, 'catalog_specs') and project.catalog_specs:
638
+ specs["catalog_specs"] = project.catalog_specs
639
+
640
+ console.print(f" [blue]📊 Project specs loaded with keys: {list(specs.keys())}[/blue]")
641
+
642
+ # Test attr_specs ingestion specifically
643
+ attr_specs_file = repo_dir / ATTRIBUTES_SPECS_FILENAME
644
+ if attr_specs_file.exists() and "attr_specs" in specs:
645
+ console.print(f" [green]✅ attr_specs found in ingested project data[/green]")
646
+
647
+ # Load the original YAML for comparison
648
+ with open(attr_specs_file, "r", encoding="utf-8") as f:
649
+ original_attr_specs = yaml.safe_load(f)
650
+
651
+ ingested_attr_specs = specs["attr_specs"]
652
+
653
+ # Validate structure compatibility
654
+ if isinstance(original_attr_specs, list) and isinstance(ingested_attr_specs, list):
655
+ console.print(f" [green]✅ attr_specs structure matches: {len(original_attr_specs)} items in YAML, {len(ingested_attr_specs)} items ingested[/green]")
656
+
657
+ # Check for source_collection fields
658
+ yaml_collections = set()
659
+ ingested_collections = set()
660
+
661
+ for item in original_attr_specs:
662
+ if isinstance(item, dict) and "source_collection" in item:
663
+ yaml_collections.add(item["source_collection"])
664
+
665
+ for item in ingested_attr_specs:
666
+ if isinstance(item, dict) and "source_collection" in item:
667
+ ingested_collections.add(item["source_collection"])
668
+ elif hasattr(item, "source_collection"):
669
+ # Handle Pydantic model objects
670
+ ingested_collections.add(item.source_collection)
671
+
672
+ if yaml_collections == ingested_collections:
673
+ console.print(f" [green]✅ Collection references preserved: {sorted(yaml_collections)}[/green]")
674
+ else:
675
+ errors.append(f"❌ Collection reference mismatch - YAML: {sorted(yaml_collections)}, Ingested: {sorted(ingested_collections)}")
676
+ else:
677
+ console.print(f" [yellow]⚠️ Structure difference: YAML type={type(original_attr_specs)}, Ingested type={type(ingested_attr_specs)}[/yellow]")
678
+
679
+ elif attr_specs_file.exists():
680
+ errors.append(f"❌ attr_specs.yaml exists but not found in ingested project specs")
681
+
682
+ # Test drs_specs ingestion
683
+ if "drs_specs" in specs:
684
+ console.print(f" [green]✅ drs_specs found in ingested project data[/green]")
685
+ else:
686
+ errors.append(f"❌ drs_specs not found in ingested project data")
687
+
688
+ # Test catalog_specs ingestion
689
+ if "catalog_specs" in specs:
690
+ console.print(f" [green]✅ catalog_specs found in ingested project data[/green]")
691
+ else:
692
+ console.print(f" [yellow]⚠️ catalog_specs not found in ingested project data (may be optional)[/yellow]")
693
+
694
+ else:
695
+ # More detailed error message about missing specs
696
+ expected_specs = ["project_specs", "attr_specs", "drs_specs", "catalog_specs (optional)"]
697
+ if hasattr(project, 'attr_specs') or hasattr(project, 'drs_specs'):
698
+ missing_specs = []
699
+ if not hasattr(project, 'attr_specs') or not project.attr_specs:
700
+ missing_specs.append("attr_specs")
701
+ if not hasattr(project, 'drs_specs') or not project.drs_specs:
702
+ missing_specs.append("drs_specs")
703
+ errors.append(f"❌ Project '{project_name}' missing required specs: {missing_specs}. Expected specs: {', '.join(expected_specs)}")
704
+ else:
705
+ errors.append(f"❌ Project '{project_name}' has no specs attributes. Expected specs: {', '.join(expected_specs)}")
706
+
707
+ except Exception as e:
708
+ errors.append(f"❌ Failed to retrieve project '{project_name}' from esgvoc: {e}")
525
709
 
526
710
  return errors
527
711
 
@@ -1198,8 +1382,9 @@ class CVTester:
1198
1382
  try:
1199
1383
  from esgvoc.core.service.configuration.setting import ServiceSettings
1200
1384
 
1201
- if project_name in ServiceSettings.DEFAULT_PROJECT_CONFIGS:
1202
- default_local_path = ServiceSettings.DEFAULT_PROJECT_CONFIGS[project_name]["local_path"]
1385
+ default_configs = ServiceSettings._get_default_project_configs()
1386
+ if project_name in default_configs:
1387
+ default_local_path = default_configs[project_name]["local_path"]
1203
1388
  config_manager = service.get_config_manager()
1204
1389
 
1205
1390
  # Try different path constructions to find where the repository actually is
@@ -1231,14 +1416,30 @@ class CVTester:
1231
1416
  console.print("[yellow]⚠️ Could not determine CV repository path, using current directory[/yellow]")
1232
1417
 
1233
1418
  # Step 3: Test repository structure
1234
- if not self.test_repository_structure(repo_path):
1419
+ console.print(f"[dim]Debug: About to test repository structure with path: {repo_path}[/dim]")
1420
+ try:
1421
+ if not self.test_repository_structure(repo_path):
1422
+ success = False
1423
+ except Exception as e:
1424
+ console.print(f"[red]❌ Repository structure test failed with exception: {e}[/red]")
1235
1425
  success = False
1236
1426
 
1237
1427
  # Debug: Check what configuration is active before API test
1238
1428
  current_active = service.get_config_manager().get_active_config_name()
1239
1429
  console.print(f"[dim]Debug: Active config before API test: {current_active}[/dim]")
1240
1430
 
1241
- # Step 4: Test esgvoc API access
1431
+ # Step 4: Test YAML specs ingestion compatibility
1432
+ console.print(f"[blue]Testing YAML specs ingestion compatibility...[/blue]")
1433
+ ingestion_errors = self._test_esgvoc_specs_ingestion(project_name, Path(repo_path))
1434
+ if ingestion_errors:
1435
+ console.print(f"[red]❌ YAML specs ingestion test failed with {len(ingestion_errors)} errors:[/red]")
1436
+ for error in ingestion_errors:
1437
+ console.print(f" {error}")
1438
+ success = False
1439
+ else:
1440
+ console.print(f"[green]✅ YAML specs ingestion test passed![/green]")
1441
+
1442
+ # Step 5: Test esgvoc API access
1242
1443
  if not self.test_esgvoc_api_access(project_name, repo_path):
1243
1444
  success = False
1244
1445
 
@@ -1306,7 +1507,7 @@ def main():
1306
1507
  projects = tester.get_available_projects()
1307
1508
  console.print(f"[blue]Available projects ({len(projects)}):[/blue]")
1308
1509
  for project in projects:
1309
- config = ServiceSettings.DEFAULT_PROJECT_CONFIGS[project]
1510
+ config = ServiceSettings._get_default_project_configs()[project]
1310
1511
  console.print(f" [cyan]{project}[/cyan] - {config['github_repo']} (branch: {config['branch']})")
1311
1512
 
1312
1513
  elif command == "configure":