feat: Add SchemaElementPopup component for displaying LinkML schema element previews
- Implemented a draggable, resizable, and minimizable popup component for displaying previews of LinkML schema elements (classes, slots, enums). - Integrated loading states and error handling for fetching element information. - Added navigation functionality to go to full element view. - Enhanced user experience with type badges and detailed descriptions for each element type. chore: Migrate AudioEventSegment, BayNumber, BoxNumber, and BudgetStatus classes to new YAML schema format - Created new YAML definitions for AudioEventSegment, BayNumber, BoxNumber, and BudgetStatus classes with detailed descriptions and attributes. - Migrated from deprecated slots to new class structures as part of Rule 53. - Updated imports and prefixes for consistency across schemas. chore: Archive deprecated slots for audio_event_segments, bay_number, and box_number - Archived previous slot definitions for audio_event_segments, bay_number, and box_number to maintain historical records. - Updated slot descriptions and ensured proper URI mappings for future reference.
This commit is contained in:
parent
b927bc4b43
commit
7c7d8c0270
17 changed files with 1652 additions and 49 deletions
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"generated": "2026-01-14T12:29:52.423Z",
|
||||
"generated": "2026-01-14T14:05:38.322Z",
|
||||
"schemaRoot": "/schemas/20251121/linkml",
|
||||
"totalFiles": 2884,
|
||||
"categoryCounts": {
|
||||
|
|
|
|||
|
|
@ -1316,3 +1316,26 @@ fixes:
|
|||
type: slot
|
||||
- label: TimeSpan
|
||||
type: class
|
||||
- original_slot_id: https://nde.nl/ontology/hc/slot/xpath_matched_text
|
||||
revision:
|
||||
- label: has_or_had_text
|
||||
type: slot
|
||||
- label: TextSegment
|
||||
type: class
|
||||
- original_slot_id: https://nde.nl/ontology/hc/slot/xpath_match_score
|
||||
revision:
|
||||
- label: has_or_had_score
|
||||
type: slot
|
||||
- label: XPathScore
|
||||
type: class
|
||||
- original_slot_id: https://nde.nl/ontology/hc/slot/xpath
|
||||
revision:
|
||||
- label: has_or_had_provenance
|
||||
type: slot
|
||||
- label: Provenance
|
||||
type: class
|
||||
- label: has_or_had_provenance_path
|
||||
type: slot
|
||||
- label: XPath
|
||||
type: class
|
||||
-
|
||||
|
|
|
|||
479
frontend/src/components/linkml/SchemaElementPopup.css
Normal file
479
frontend/src/components/linkml/SchemaElementPopup.css
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
/**
|
||||
* SchemaElementPopup.css
|
||||
*
|
||||
* Styles for the schema element popup shown when clicking on class/slot/enum
|
||||
* names in the Imports/Exports accordion sections in the LinkML viewer.
|
||||
*/
|
||||
|
||||
.schema-popup {
|
||||
position: fixed;
|
||||
width: 400px;
|
||||
height: 320px;
|
||||
max-height: calc(100vh - 100px);
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
z-index: 10200; /* Above SemanticDetailsPanel (10100) */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e5e7eb;
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
|
||||
/* Dragging state */
|
||||
.schema-popup--dragging {
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25), 0 8px 20px rgba(0, 0, 0, 0.15);
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
/* Minimized state */
|
||||
.schema-popup--minimized {
|
||||
height: auto !important;
|
||||
max-height: 52px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.schema-popup--minimized .schema-popup__header {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Header - default blue for classes */
|
||||
.schema-popup__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
|
||||
color: #ffffff;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
cursor: grab;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.schema-popup--dragging .schema-popup__header {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.schema-popup__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.schema-popup__name {
|
||||
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.schema-popup__controls {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.schema-popup__minimize,
|
||||
.schema-popup__close {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: none;
|
||||
color: white;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.schema-popup__minimize:hover,
|
||||
.schema-popup__close:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Type badge */
|
||||
.schema-popup__type-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Content */
|
||||
.schema-popup__content {
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.schema-popup__loading,
|
||||
.schema-popup__error {
|
||||
padding: 24px 16px;
|
||||
text-align: center;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.schema-popup__error {
|
||||
color: #dc2626;
|
||||
background: #fef2f2;
|
||||
border-radius: 8px;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
/* Sections */
|
||||
.schema-popup__section {
|
||||
margin-bottom: 14px;
|
||||
padding-bottom: 14px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.schema-popup__section:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.schema-popup__section-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.schema-popup__description {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
/* URI display */
|
||||
.schema-popup__uri {
|
||||
display: block;
|
||||
background: #f1f5f9;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #475569;
|
||||
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* Values */
|
||||
.schema-popup__value {
|
||||
display: inline-block;
|
||||
background: #f1f5f9;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
color: #1e40af;
|
||||
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
|
||||
}
|
||||
|
||||
.schema-popup__count {
|
||||
font-size: 13px;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
/* Properties (for slots) */
|
||||
.schema-popup__properties {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.schema-popup__prop {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.schema-popup__prop--true {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
/* Mappings summary */
|
||||
.schema-popup__mappings {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.schema-popup__mapping-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.schema-popup__mapping-badge--exact {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.schema-popup__mapping-badge--close {
|
||||
background: #dbeafe;
|
||||
color: #1e40af;
|
||||
}
|
||||
|
||||
.schema-popup__mapping-badge--related {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
.schema-popup__actions {
|
||||
margin-top: 16px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.schema-popup__navigate {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 16px;
|
||||
background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
|
||||
color: white;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s, transform 0.1s;
|
||||
}
|
||||
|
||||
.schema-popup__navigate:hover {
|
||||
background: linear-gradient(135deg, #1e3a8a 0%, #2563eb 100%);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Resize handles */
|
||||
.schema-popup__resize {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.schema-popup__resize--n {
|
||||
top: 0;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
height: 6px;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.schema-popup__resize--s {
|
||||
bottom: 0;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
height: 6px;
|
||||
cursor: s-resize;
|
||||
}
|
||||
|
||||
.schema-popup__resize--e {
|
||||
right: 0;
|
||||
top: 8px;
|
||||
bottom: 8px;
|
||||
width: 6px;
|
||||
cursor: e-resize;
|
||||
}
|
||||
|
||||
.schema-popup__resize--w {
|
||||
left: 0;
|
||||
top: 8px;
|
||||
bottom: 8px;
|
||||
width: 6px;
|
||||
cursor: w-resize;
|
||||
}
|
||||
|
||||
.schema-popup__resize--ne {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
cursor: ne-resize;
|
||||
}
|
||||
|
||||
.schema-popup__resize--nw {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
cursor: nw-resize;
|
||||
}
|
||||
|
||||
.schema-popup__resize--se {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
cursor: se-resize;
|
||||
}
|
||||
|
||||
.schema-popup__resize--sw {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
cursor: sw-resize;
|
||||
}
|
||||
|
||||
/* SE corner grip indicator */
|
||||
.schema-popup__resize--se::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
bottom: 3px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-right: 2px solid #94a3b8;
|
||||
border-bottom: 2px solid #94a3b8;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.schema-popup__resize--se:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Dark mode */
|
||||
[data-theme="dark"] .schema-popup {
|
||||
background: #1e293b;
|
||||
border-color: #334155;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__header {
|
||||
background: linear-gradient(135deg, #1e3a8a 0%, #1d4ed8 100%);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__content {
|
||||
background: #1e293b;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__section {
|
||||
border-bottom-color: #334155;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__section-label {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__description {
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__uri {
|
||||
background: #334155;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__value {
|
||||
background: #334155;
|
||||
color: #93c5fd;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__count {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__prop {
|
||||
background: #334155;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__prop--true {
|
||||
background: rgba(34, 197, 94, 0.15);
|
||||
color: #86efac;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__mapping-badge--exact {
|
||||
background: rgba(34, 197, 94, 0.15);
|
||||
color: #86efac;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__mapping-badge--close {
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
color: #93c5fd;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__mapping-badge--related {
|
||||
background: rgba(245, 158, 11, 0.15);
|
||||
color: #fcd34d;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__actions {
|
||||
border-top-color: #334155;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__error {
|
||||
background: rgba(220, 38, 38, 0.15);
|
||||
color: #fca5a5;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__loading {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .schema-popup__resize--se::after {
|
||||
border-color: #64748b;
|
||||
}
|
||||
|
||||
/* Responsive - Full screen on mobile */
|
||||
@media (max-width: 600px) {
|
||||
.schema-popup {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
max-height: 100vh !important;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.schema-popup__header {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.schema-popup__resize {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.schema-popup__minimize {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.schema-popup__close {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
549
frontend/src/components/linkml/SchemaElementPopup.tsx
Normal file
549
frontend/src/components/linkml/SchemaElementPopup.tsx
Normal file
|
|
@ -0,0 +1,549 @@
|
|||
/**
|
||||
* SchemaElementPopup.tsx
|
||||
*
|
||||
* A popup component that displays LinkML schema element previews when clicking on
|
||||
* class/slot/enum names in the Imports/Exports accordion sections.
|
||||
*
|
||||
* Features:
|
||||
* - Draggable (drag from header)
|
||||
* - Minimizable (collapse to title bar only)
|
||||
* - Resizable (drag edges and corners)
|
||||
* - Shows preview info for classes, slots, and enums
|
||||
* - "Go to" button to navigate to full element view
|
||||
*/
|
||||
|
||||
import React, { useEffect, useLayoutEffect, useState, useRef, useCallback } from 'react';
|
||||
import { linkmlSchemaService } from '../../lib/linkml/linkml-schema-service';
|
||||
import { isTargetInsideAny } from '../../utils/dom';
|
||||
import './SchemaElementPopup.css';
|
||||
|
||||
export type SchemaElementType = 'class' | 'slot' | 'enum';
|
||||
|
||||
interface SchemaElementPopupProps {
|
||||
/** The element name to look up */
|
||||
elementName: string;
|
||||
/** Type of element */
|
||||
elementType: SchemaElementType;
|
||||
/** Called when user wants to close the popup */
|
||||
onClose: () => void;
|
||||
/** Called when user clicks "Go to" button */
|
||||
onNavigate: (elementName: string, elementType: SchemaElementType) => void;
|
||||
/** Initial position (optional, defaults to center) */
|
||||
initialPosition?: { x: number; y: number };
|
||||
}
|
||||
|
||||
interface Position {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface Size {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
type ResizeDirection = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw' | null;
|
||||
|
||||
// Minimum panel dimensions
|
||||
const MIN_WIDTH = 300;
|
||||
const MIN_HEIGHT = 150;
|
||||
const DEFAULT_WIDTH = 400;
|
||||
const DEFAULT_HEIGHT = 320;
|
||||
|
||||
/**
|
||||
* Unified element info structure
|
||||
*/
|
||||
interface ElementInfo {
|
||||
name: string;
|
||||
type: SchemaElementType;
|
||||
description?: string;
|
||||
uri?: string;
|
||||
parentClass?: string;
|
||||
range?: string;
|
||||
required?: boolean;
|
||||
multivalued?: boolean;
|
||||
slotCount?: number;
|
||||
valueCount?: number;
|
||||
mappings?: {
|
||||
exact?: string[];
|
||||
close?: string[];
|
||||
related?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export const SchemaElementPopup: React.FC<SchemaElementPopupProps> = ({
|
||||
elementName,
|
||||
elementType,
|
||||
onClose,
|
||||
onNavigate,
|
||||
initialPosition,
|
||||
}) => {
|
||||
const [elementInfo, setElementInfo] = useState<ElementInfo | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Draggable state
|
||||
const [position, setPosition] = useState<Position | null>(initialPosition || null);
|
||||
const [isPositioned, setIsPositioned] = useState(!!initialPosition);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const dragStartRef = useRef<{ x: number; y: number; posX: number; posY: number } | null>(null);
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Minimized state
|
||||
const [isMinimized, setIsMinimized] = useState(false);
|
||||
|
||||
// Resize state
|
||||
const [size, setSize] = useState<Size>({ width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT });
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const [resizeDirection, setResizeDirection] = useState<ResizeDirection>(null);
|
||||
const resizeStartRef = useRef<{
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
posX: number;
|
||||
posY: number;
|
||||
} | null>(null);
|
||||
|
||||
// Load element information
|
||||
useEffect(() => {
|
||||
const loadInfo = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
let info: ElementInfo | null = null;
|
||||
|
||||
if (elementType === 'class') {
|
||||
const semanticInfo = await linkmlSchemaService.getClassSemanticInfo(elementName);
|
||||
if (semanticInfo) {
|
||||
info = {
|
||||
name: semanticInfo.className,
|
||||
type: 'class',
|
||||
description: semanticInfo.description,
|
||||
uri: semanticInfo.classUri,
|
||||
parentClass: semanticInfo.parentClass,
|
||||
slotCount: semanticInfo.slots?.length || 0,
|
||||
mappings: semanticInfo.mappings,
|
||||
};
|
||||
}
|
||||
} else if (elementType === 'slot') {
|
||||
const slotInfo = await linkmlSchemaService.getSlotInfo(elementName);
|
||||
if (slotInfo) {
|
||||
info = {
|
||||
name: slotInfo.name,
|
||||
type: 'slot',
|
||||
description: slotInfo.description,
|
||||
uri: slotInfo.slotUri,
|
||||
range: slotInfo.range,
|
||||
required: slotInfo.required,
|
||||
multivalued: slotInfo.multivalued,
|
||||
};
|
||||
}
|
||||
} else if (elementType === 'enum') {
|
||||
const enumInfo = await linkmlSchemaService.getEnumSemanticInfo(elementName);
|
||||
if (enumInfo) {
|
||||
info = {
|
||||
name: enumInfo.enumName,
|
||||
type: 'enum',
|
||||
description: enumInfo.description,
|
||||
// EnumSemanticInfo doesn't have URI - leave undefined
|
||||
valueCount: enumInfo.values?.length || 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (info) {
|
||||
setElementInfo(info);
|
||||
} else {
|
||||
setError(`Could not find ${elementType} "${elementName}"`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[SchemaElementPopup] Error loading element info:', err);
|
||||
setError(`Failed to load: ${err}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadInfo();
|
||||
}, [elementName, elementType]);
|
||||
|
||||
// Center the popup on initial load if no position provided
|
||||
useLayoutEffect(() => {
|
||||
if (!initialPosition && panelRef.current && !position) {
|
||||
const rect = panelRef.current.getBoundingClientRect();
|
||||
setPosition({
|
||||
x: (window.innerWidth - rect.width) / 2,
|
||||
y: (window.innerHeight - rect.height) / 3,
|
||||
});
|
||||
setIsPositioned(true);
|
||||
}
|
||||
}, [initialPosition, position]);
|
||||
|
||||
// Drag handlers
|
||||
const handleMouseDown = useCallback((e: React.MouseEvent) => {
|
||||
if (isTargetInsideAny(e.target, ['.schema-popup__close', '.schema-popup__minimize', '.schema-popup__navigate'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
setIsDragging(true);
|
||||
|
||||
const panel = panelRef.current;
|
||||
if (panel) {
|
||||
const rect = panel.getBoundingClientRect();
|
||||
const currentX = position?.x ?? rect.left;
|
||||
const currentY = position?.y ?? rect.top;
|
||||
|
||||
dragStartRef.current = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
posX: currentX,
|
||||
posY: currentY,
|
||||
};
|
||||
}
|
||||
}, [position]);
|
||||
|
||||
const handleMouseMove = useCallback((e: MouseEvent) => {
|
||||
if (!isDragging || !dragStartRef.current) return;
|
||||
|
||||
const deltaX = e.clientX - dragStartRef.current.x;
|
||||
const deltaY = e.clientY - dragStartRef.current.y;
|
||||
|
||||
const newX = dragStartRef.current.posX + deltaX;
|
||||
const newY = dragStartRef.current.posY + deltaY;
|
||||
|
||||
const maxX = window.innerWidth - 100;
|
||||
const maxY = window.innerHeight - 50;
|
||||
|
||||
setPosition({
|
||||
x: Math.max(0, Math.min(newX, maxX)),
|
||||
y: Math.max(0, Math.min(newY, maxY)),
|
||||
});
|
||||
}, [isDragging]);
|
||||
|
||||
const handleMouseUp = useCallback(() => {
|
||||
setIsDragging(false);
|
||||
dragStartRef.current = null;
|
||||
}, []);
|
||||
|
||||
// Add/remove global mouse event listeners for dragging
|
||||
useEffect(() => {
|
||||
if (isDragging) {
|
||||
window.addEventListener('mousemove', handleMouseMove);
|
||||
window.addEventListener('mouseup', handleMouseUp);
|
||||
document.body.style.cursor = 'grabbing';
|
||||
document.body.style.userSelect = 'none';
|
||||
} else {
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
window.removeEventListener('mouseup', handleMouseUp);
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
};
|
||||
}, [isDragging, handleMouseMove, handleMouseUp]);
|
||||
|
||||
// Toggle minimize
|
||||
const toggleMinimize = useCallback(() => {
|
||||
setIsMinimized(prev => !prev);
|
||||
}, []);
|
||||
|
||||
// Resize handlers
|
||||
const handleResizeMouseDown = useCallback((direction: ResizeDirection) => (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
setIsResizing(true);
|
||||
setResizeDirection(direction);
|
||||
|
||||
const panel = panelRef.current;
|
||||
if (panel) {
|
||||
const rect = panel.getBoundingClientRect();
|
||||
resizeStartRef.current = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
posX: position?.x ?? rect.left,
|
||||
posY: position?.y ?? rect.top,
|
||||
};
|
||||
}
|
||||
}, [size, position]);
|
||||
|
||||
const handleResizeMouseMove = useCallback((e: MouseEvent) => {
|
||||
if (!isResizing || !resizeStartRef.current || !resizeDirection) return;
|
||||
|
||||
const deltaX = e.clientX - resizeStartRef.current.x;
|
||||
const deltaY = e.clientY - resizeStartRef.current.y;
|
||||
|
||||
let newWidth = resizeStartRef.current.width;
|
||||
let newHeight = resizeStartRef.current.height;
|
||||
let newX = resizeStartRef.current.posX;
|
||||
let newY = resizeStartRef.current.posY;
|
||||
|
||||
if (resizeDirection.includes('e')) {
|
||||
newWidth = Math.max(MIN_WIDTH, resizeStartRef.current.width + deltaX);
|
||||
}
|
||||
if (resizeDirection.includes('w')) {
|
||||
const potentialWidth = resizeStartRef.current.width - deltaX;
|
||||
if (potentialWidth >= MIN_WIDTH) {
|
||||
newWidth = potentialWidth;
|
||||
newX = resizeStartRef.current.posX + deltaX;
|
||||
}
|
||||
}
|
||||
|
||||
if (resizeDirection.includes('s')) {
|
||||
newHeight = Math.max(MIN_HEIGHT, resizeStartRef.current.height + deltaY);
|
||||
}
|
||||
if (resizeDirection.includes('n')) {
|
||||
const potentialHeight = resizeStartRef.current.height - deltaY;
|
||||
if (potentialHeight >= MIN_HEIGHT) {
|
||||
newHeight = potentialHeight;
|
||||
newY = resizeStartRef.current.posY + deltaY;
|
||||
}
|
||||
}
|
||||
|
||||
setSize({ width: newWidth, height: newHeight });
|
||||
|
||||
if (resizeDirection.includes('w') || resizeDirection.includes('n')) {
|
||||
setPosition({ x: newX, y: newY });
|
||||
}
|
||||
}, [isResizing, resizeDirection]);
|
||||
|
||||
const handleResizeMouseUp = useCallback(() => {
|
||||
setIsResizing(false);
|
||||
setResizeDirection(null);
|
||||
resizeStartRef.current = null;
|
||||
}, []);
|
||||
|
||||
// Add/remove global mouse event listeners for resizing
|
||||
useEffect(() => {
|
||||
if (isResizing) {
|
||||
window.addEventListener('mousemove', handleResizeMouseMove);
|
||||
window.addEventListener('mouseup', handleResizeMouseUp);
|
||||
document.body.style.userSelect = 'none';
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', handleResizeMouseMove);
|
||||
window.removeEventListener('mouseup', handleResizeMouseUp);
|
||||
if (!isDragging) {
|
||||
document.body.style.userSelect = '';
|
||||
}
|
||||
};
|
||||
}, [isResizing, handleResizeMouseMove, handleResizeMouseUp, isDragging]);
|
||||
|
||||
// Handle navigate button click
|
||||
const handleNavigate = useCallback(() => {
|
||||
onNavigate(elementName, elementType);
|
||||
onClose();
|
||||
}, [elementName, elementType, onNavigate, onClose]);
|
||||
|
||||
// Render type badge
|
||||
const renderTypeBadge = (type: SchemaElementType) => {
|
||||
const colors: Record<SchemaElementType, string> = {
|
||||
'class': '#3B82F6', // Blue
|
||||
'slot': '#10B981', // Green
|
||||
'enum': '#F59E0B', // Amber
|
||||
};
|
||||
return (
|
||||
<span
|
||||
className="schema-popup__type-badge"
|
||||
style={{ backgroundColor: colors[type] }}
|
||||
>
|
||||
{type}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
// Calculate panel style
|
||||
const panelStyle: React.CSSProperties = {
|
||||
width: size.width,
|
||||
height: isMinimized ? 'auto' : size.height,
|
||||
visibility: isPositioned ? 'visible' : 'hidden',
|
||||
...(position && {
|
||||
position: 'fixed',
|
||||
left: position.x,
|
||||
top: position.y,
|
||||
}),
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={panelRef}
|
||||
className={`schema-popup ${isMinimized ? 'schema-popup--minimized' : ''} ${isDragging ? 'schema-popup--dragging' : ''}`}
|
||||
style={panelStyle}
|
||||
>
|
||||
{/* Header */}
|
||||
<div
|
||||
className="schema-popup__header"
|
||||
onMouseDown={handleMouseDown}
|
||||
>
|
||||
<div className="schema-popup__title">
|
||||
<code className="schema-popup__name">{elementName}</code>
|
||||
{renderTypeBadge(elementType)}
|
||||
</div>
|
||||
<div className="schema-popup__controls">
|
||||
<button
|
||||
className="schema-popup__minimize"
|
||||
onClick={toggleMinimize}
|
||||
title={isMinimized ? 'Expand' : 'Minimize'}
|
||||
>
|
||||
{isMinimized ? '□' : '−'}
|
||||
</button>
|
||||
<button
|
||||
className="schema-popup__close"
|
||||
onClick={onClose}
|
||||
title="Close"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
{!isMinimized && (
|
||||
<div className="schema-popup__content">
|
||||
{loading && (
|
||||
<div className="schema-popup__loading">
|
||||
Loading {elementType} information...
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="schema-popup__error">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && !error && elementInfo && (
|
||||
<>
|
||||
{/* Description */}
|
||||
{elementInfo.description && (
|
||||
<div className="schema-popup__section">
|
||||
<span className="schema-popup__section-label">Description</span>
|
||||
<p className="schema-popup__description">{elementInfo.description}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* URI */}
|
||||
{elementInfo.uri && (
|
||||
<div className="schema-popup__section">
|
||||
<span className="schema-popup__section-label">URI</span>
|
||||
<code className="schema-popup__uri">{elementInfo.uri}</code>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Parent class (for classes) */}
|
||||
{elementInfo.parentClass && (
|
||||
<div className="schema-popup__section">
|
||||
<span className="schema-popup__section-label">Parent Class</span>
|
||||
<code className="schema-popup__value">{elementInfo.parentClass}</code>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Range (for slots) */}
|
||||
{elementInfo.range && (
|
||||
<div className="schema-popup__section">
|
||||
<span className="schema-popup__section-label">Range</span>
|
||||
<code className="schema-popup__value">{elementInfo.range}</code>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Properties (for slots) */}
|
||||
{elementInfo.type === 'slot' && (
|
||||
<div className="schema-popup__section schema-popup__properties">
|
||||
{elementInfo.required !== undefined && (
|
||||
<span className={`schema-popup__prop ${elementInfo.required ? 'schema-popup__prop--true' : ''}`}>
|
||||
{elementInfo.required ? 'required' : 'optional'}
|
||||
</span>
|
||||
)}
|
||||
{elementInfo.multivalued !== undefined && (
|
||||
<span className={`schema-popup__prop ${elementInfo.multivalued ? 'schema-popup__prop--true' : ''}`}>
|
||||
{elementInfo.multivalued ? 'multivalued' : 'single-valued'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Slot count (for classes) */}
|
||||
{elementInfo.slotCount !== undefined && elementInfo.slotCount > 0 && (
|
||||
<div className="schema-popup__section">
|
||||
<span className="schema-popup__section-label">Slots</span>
|
||||
<span className="schema-popup__count">{elementInfo.slotCount} slots</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Value count (for enums) */}
|
||||
{elementInfo.valueCount !== undefined && elementInfo.valueCount > 0 && (
|
||||
<div className="schema-popup__section">
|
||||
<span className="schema-popup__section-label">Values</span>
|
||||
<span className="schema-popup__count">{elementInfo.valueCount} permissible values</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Mappings (summary) */}
|
||||
{elementInfo.mappings && (
|
||||
(elementInfo.mappings.exact?.length ||
|
||||
elementInfo.mappings.close?.length ||
|
||||
elementInfo.mappings.related?.length) ? (
|
||||
<div className="schema-popup__section">
|
||||
<span className="schema-popup__section-label">Mappings</span>
|
||||
<div className="schema-popup__mappings">
|
||||
{elementInfo.mappings.exact?.length ? (
|
||||
<span className="schema-popup__mapping-badge schema-popup__mapping-badge--exact">
|
||||
{elementInfo.mappings.exact.length} exact
|
||||
</span>
|
||||
) : null}
|
||||
{elementInfo.mappings.close?.length ? (
|
||||
<span className="schema-popup__mapping-badge schema-popup__mapping-badge--close">
|
||||
{elementInfo.mappings.close.length} close
|
||||
</span>
|
||||
) : null}
|
||||
{elementInfo.mappings.related?.length ? (
|
||||
<span className="schema-popup__mapping-badge schema-popup__mapping-badge--related">
|
||||
{elementInfo.mappings.related.length} related
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
)}
|
||||
|
||||
{/* Navigate button */}
|
||||
<div className="schema-popup__actions">
|
||||
<button
|
||||
className="schema-popup__navigate"
|
||||
onClick={handleNavigate}
|
||||
>
|
||||
Go to {elementType} →
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Resize handles */}
|
||||
{!isMinimized && (
|
||||
<>
|
||||
<div className="schema-popup__resize schema-popup__resize--n" onMouseDown={handleResizeMouseDown('n')} />
|
||||
<div className="schema-popup__resize schema-popup__resize--s" onMouseDown={handleResizeMouseDown('s')} />
|
||||
<div className="schema-popup__resize schema-popup__resize--e" onMouseDown={handleResizeMouseDown('e')} />
|
||||
<div className="schema-popup__resize schema-popup__resize--w" onMouseDown={handleResizeMouseDown('w')} />
|
||||
<div className="schema-popup__resize schema-popup__resize--ne" onMouseDown={handleResizeMouseDown('ne')} />
|
||||
<div className="schema-popup__resize schema-popup__resize--nw" onMouseDown={handleResizeMouseDown('nw')} />
|
||||
<div className="schema-popup__resize schema-popup__resize--se" onMouseDown={handleResizeMouseDown('se')} />
|
||||
<div className="schema-popup__resize schema-popup__resize--sw" onMouseDown={handleResizeMouseDown('sw')} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SchemaElementPopup;
|
||||
|
|
@ -46,6 +46,7 @@ import {
|
|||
isUniversalElement,
|
||||
} from '../lib/schema-custodian-mapping';
|
||||
import { OntologyTermPopup } from '../components/ontology/OntologyTermPopup';
|
||||
import { SchemaElementPopup, type SchemaElementType } from '../components/linkml/SchemaElementPopup';
|
||||
import './LinkMLViewerPage.css';
|
||||
|
||||
/**
|
||||
|
|
@ -1299,6 +1300,9 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
// State for ontology term popup (shows when clicking mapping tags like rico:Rule)
|
||||
const [ontologyPopupCurie, setOntologyPopupCurie] = useState<string | null>(null);
|
||||
|
||||
// State for schema element popup (shows when clicking class/slot/enum links in Imports/Exports sections)
|
||||
const [schemaElementPopup, setSchemaElementPopup] = useState<{ name: string; type: SchemaElementType } | null>(null);
|
||||
|
||||
// Sync custodian filter to URL params
|
||||
useEffect(() => {
|
||||
const currentParam = searchParams.get('custodian');
|
||||
|
|
@ -1835,6 +1839,18 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
}
|
||||
}, [categories, setSearchParams]);
|
||||
|
||||
// Handler for when user clicks "Go to" in the schema element popup
|
||||
const handleSchemaElementNavigate = useCallback((elementName: string, elementType: SchemaElementType) => {
|
||||
if (elementType === 'class') {
|
||||
navigateToClass(elementName);
|
||||
} else if (elementType === 'enum') {
|
||||
navigateToEnum(elementName);
|
||||
} else if (elementType === 'slot') {
|
||||
navigateToSlot(elementName);
|
||||
}
|
||||
setSchemaElementPopup(null);
|
||||
}, [navigateToClass, navigateToEnum, navigateToSlot]);
|
||||
|
||||
// Track if initialization has already happened (prevents re-init on URL param changes)
|
||||
const isInitializedRef = useRef(false);
|
||||
|
||||
|
|
@ -2470,7 +2486,7 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
<div className="linkml-viewer__imports-list">
|
||||
<button
|
||||
className="linkml-viewer__imports-link"
|
||||
onClick={() => navigateToClass(classImports[cls.name].parentClass!)}
|
||||
onClick={() => setSchemaElementPopup({ name: classImports[cls.name].parentClass!, type: 'class' })}
|
||||
>
|
||||
{classImports[cls.name].parentClass}
|
||||
</button>
|
||||
|
|
@ -2486,7 +2502,7 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
<button
|
||||
key={mixin}
|
||||
className="linkml-viewer__imports-link"
|
||||
onClick={() => navigateToClass(mixin)}
|
||||
onClick={() => setSchemaElementPopup({ name: mixin, type: 'class' })}
|
||||
>
|
||||
{mixin}
|
||||
</button>
|
||||
|
|
@ -3191,11 +3207,10 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
className="linkml-viewer__imports-link"
|
||||
onClick={() => {
|
||||
const range = slotImports[slot.name].rangeType!;
|
||||
if (range.isClass) {
|
||||
navigateToClass(range.name);
|
||||
} else if (range.isEnum) {
|
||||
navigateToEnum(range.name);
|
||||
}
|
||||
setSchemaElementPopup({
|
||||
name: range.name,
|
||||
type: range.isClass ? 'class' : 'enum'
|
||||
});
|
||||
}}
|
||||
>
|
||||
{slotImports[slot.name].rangeType!.name}
|
||||
|
|
@ -3214,11 +3229,10 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
key={type.name}
|
||||
className="linkml-viewer__imports-link"
|
||||
onClick={() => {
|
||||
if (type.isClass) {
|
||||
navigateToClass(type.name);
|
||||
} else if (type.isEnum) {
|
||||
navigateToEnum(type.name);
|
||||
}
|
||||
setSchemaElementPopup({
|
||||
name: type.name,
|
||||
type: type.isClass ? 'class' : 'enum'
|
||||
});
|
||||
}}
|
||||
>
|
||||
{type.name}
|
||||
|
|
@ -3267,7 +3281,7 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
<button
|
||||
key={cls}
|
||||
className="linkml-viewer__exports-link"
|
||||
onClick={() => navigateToClass(cls)}
|
||||
onClick={() => setSchemaElementPopup({ name: cls, type: 'class' })}
|
||||
>
|
||||
{cls}
|
||||
</button>
|
||||
|
|
@ -3284,7 +3298,7 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
<button
|
||||
key={cls}
|
||||
className="linkml-viewer__exports-link"
|
||||
onClick={() => navigateToClass(cls)}
|
||||
onClick={() => setSchemaElementPopup({ name: cls, type: 'class' })}
|
||||
title={`Overrides: ${overrides.join(', ')}`}
|
||||
>
|
||||
{cls} <span className="linkml-viewer__exports-via">{overrides.length} {overrides.length === 1 ? 'override' : 'overrides'}</span>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"generated": "2026-01-14T14:05:38.322Z",
|
||||
"generated": "2026-01-14T14:13:07.428Z",
|
||||
"schemaRoot": "/schemas/20251121/linkml",
|
||||
"totalFiles": 2884,
|
||||
"categoryCounts": {
|
||||
|
|
|
|||
172
schemas/20251121/linkml/modules/classes/AudioEventSegment.yaml
Normal file
172
schemas/20251121/linkml/modules/classes/AudioEventSegment.yaml
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
id: https://nde.nl/ontology/hc/class/AudioEventSegment
|
||||
name: audio_event_segment_class
|
||||
title: Audio Event Segment Class
|
||||
description: |
|
||||
A temporal segment of audio containing a detected audio event (speech, music, silence, etc.).
|
||||
|
||||
MIGRATED from audio_event_segments slot (Rule 53).
|
||||
Uses generic has_or_had_segment slot with range narrowed to AudioEventSegment.
|
||||
imports:
|
||||
- linkml:types
|
||||
- ../slots/start_seconds
|
||||
- ../slots/end_seconds
|
||||
- ../slots/start_time
|
||||
- ../slots/end_time
|
||||
- ../slots/segment_index
|
||||
- ../slots/segment_text
|
||||
- ../slots/confidence
|
||||
- ../slots/specificity_annotation
|
||||
- ../slots/template_specificity
|
||||
- ./SpecificityAnnotation
|
||||
- ./TemplateSpecificityScores
|
||||
- ../enums/AudioEventTypeEnum
|
||||
prefixes:
|
||||
linkml: https://w3id.org/linkml/
|
||||
hc: https://nde.nl/ontology/hc/
|
||||
schema: http://schema.org/
|
||||
dcterms: http://purl.org/dc/terms/
|
||||
crm: http://www.cidoc-crm.org/cidoc-crm/
|
||||
oa: http://www.w3.org/ns/oa#
|
||||
ma: http://www.w3.org/ns/ma-ont#
|
||||
default_prefix: hc
|
||||
|
||||
classes:
|
||||
AudioEventSegment:
|
||||
class_uri: hc:AudioEventSegment
|
||||
description: |
|
||||
A temporal segment of audio containing a detected audio event.
|
||||
|
||||
**DEFINITION**:
|
||||
|
||||
AudioEventSegment represents a bounded temporal portion of audio content
|
||||
where a specific type of audio event has been detected. This includes:
|
||||
- Speech segments (with optional speaker/language info)
|
||||
- Music segments (with optional genre/type info)
|
||||
- Silence segments (gaps between audio)
|
||||
- Sound event segments (applause, laughter, ambient sounds)
|
||||
- Noise segments (for quality assessment)
|
||||
|
||||
**RELATIONSHIP TO VideoTimeSegment**:
|
||||
|
||||
AudioEventSegment is a specialized sibling of VideoTimeSegment:
|
||||
- Both extend CIDOC-CRM E52_Time-Span concept
|
||||
- VideoTimeSegment: general video temporal segments
|
||||
- AudioEventSegment: audio-specific event segments
|
||||
|
||||
**AUDIO EVENT TYPES**:
|
||||
|
||||
| Event Type | Description | Example |
|
||||
|------------|-------------|---------|
|
||||
| SPEECH | Human speech detected | Interview segment |
|
||||
| MUSIC | Music detected | Background soundtrack |
|
||||
| SILENCE | Very low or no audio | Gap between segments |
|
||||
| SOUND_EVENT | Non-speech/music sounds | Applause, footsteps |
|
||||
| NOISE | Noise/interference | Quality issue marker |
|
||||
| MIXED | Multiple event types | Overlapping audio |
|
||||
|
||||
**HERITAGE USE CASES**:
|
||||
|
||||
| Content Type | Application |
|
||||
|--------------|-------------|
|
||||
| Oral histories | Speech segment identification |
|
||||
| Virtual tours | Background music detection |
|
||||
| Lecture recordings | Audience reaction segments |
|
||||
| Conservation videos | Narration vs ambient sound |
|
||||
| Archival footage | Audio quality assessment |
|
||||
|
||||
**PROVENANCE**:
|
||||
|
||||
Created as part of slot migration (Rule 53) from deprecated
|
||||
`audio_event_segments` slot to generic `has_or_had_segment` pattern.
|
||||
exact_mappings:
|
||||
- hc:AudioEventSegment
|
||||
close_mappings:
|
||||
- crm:E52_Time-Span
|
||||
- ma:MediaFragment
|
||||
related_mappings:
|
||||
- oa:FragmentSelector
|
||||
slots:
|
||||
- start_seconds
|
||||
- end_seconds
|
||||
- start_time
|
||||
- end_time
|
||||
- segment_index
|
||||
- segment_text
|
||||
- confidence
|
||||
- specificity_annotation
|
||||
- template_specificity
|
||||
attributes:
|
||||
audio_event_type:
|
||||
range: AudioEventTypeEnum
|
||||
required: true
|
||||
description: The type of audio event detected in this segment.
|
||||
examples:
|
||||
- value: SPEECH
|
||||
description: Speech detected in this segment
|
||||
- value: MUSIC
|
||||
description: Music detected in this segment
|
||||
slot_usage:
|
||||
start_seconds:
|
||||
range: float
|
||||
required: true
|
||||
minimum_value: 0.0
|
||||
description: Start time in seconds for this audio event segment.
|
||||
examples:
|
||||
- value: 0.0
|
||||
description: Audio event starts at beginning
|
||||
- value: 45.5
|
||||
description: Audio event starts at 45.5 seconds
|
||||
end_seconds:
|
||||
range: float
|
||||
required: true
|
||||
minimum_value: 0.0
|
||||
description: End time in seconds for this audio event segment.
|
||||
examples:
|
||||
- value: 15.0
|
||||
description: Audio event ends at 15 seconds
|
||||
- value: 60.0
|
||||
description: Audio event ends at 1 minute
|
||||
start_time:
|
||||
range: string
|
||||
required: false
|
||||
pattern: "^PT(\\d+H)?(\\d+M)?(\\d+(\\.\\d+)?S)?$"
|
||||
description: Start time in ISO 8601 duration format.
|
||||
examples:
|
||||
- value: PT0M30S
|
||||
description: 30 seconds from start
|
||||
end_time:
|
||||
range: string
|
||||
required: false
|
||||
pattern: "^PT(\\d+H)?(\\d+M)?(\\d+(\\.\\d+)?S)?$"
|
||||
description: End time in ISO 8601 duration format.
|
||||
examples:
|
||||
- value: PT0M45S
|
||||
description: 45 seconds from start
|
||||
segment_text:
|
||||
range: string
|
||||
required: false
|
||||
description: Text content for this segment (e.g., speech transcript, music description).
|
||||
examples:
|
||||
- value: "Welcome to the Rijksmuseum"
|
||||
description: Speech transcript text
|
||||
- value: "Classical background music"
|
||||
description: Music segment description
|
||||
confidence:
|
||||
range: float
|
||||
required: false
|
||||
minimum_value: 0.0
|
||||
maximum_value: 1.0
|
||||
description: Confidence score (0.0-1.0) for the audio event detection.
|
||||
examples:
|
||||
- value: 0.95
|
||||
description: High confidence detection
|
||||
- value: 0.72
|
||||
description: Medium confidence detection
|
||||
comments:
|
||||
- Audio event segment for speech, music, silence, sound event detection
|
||||
- Temporal boundaries with start/end seconds (primary) and ISO 8601 (secondary)
|
||||
- Confidence scoring for AI-generated detections
|
||||
- Part of Rule 53 slot migration from audio_event_segments
|
||||
see_also:
|
||||
- https://www.w3.org/TR/media-frags/
|
||||
- https://www.w3.org/ns/ma-ont
|
||||
92
schemas/20251121/linkml/modules/classes/BayNumber.yaml
Normal file
92
schemas/20251121/linkml/modules/classes/BayNumber.yaml
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
id: https://nde.nl/ontology/hc/class/BayNumber
|
||||
name: bay_number_class
|
||||
title: Bay Number Class
|
||||
description: |
|
||||
A storage bay or section identifier within a storage row.
|
||||
|
||||
MIGRATED from bay_number slot (Rule 53).
|
||||
Uses generic has_or_had_identifier slot with range narrowed to BayNumber.
|
||||
imports:
|
||||
- linkml:types
|
||||
- ../slots/identifier_value
|
||||
- ../slots/specificity_annotation
|
||||
- ../slots/template_specificity
|
||||
- ./SpecificityAnnotation
|
||||
- ./TemplateSpecificityScores
|
||||
prefixes:
|
||||
linkml: https://w3id.org/linkml/
|
||||
hc: https://nde.nl/ontology/hc/
|
||||
schema: http://schema.org/
|
||||
dcterms: http://purl.org/dc/terms/
|
||||
crm: http://www.cidoc-crm.org/cidoc-crm/
|
||||
default_prefix: hc
|
||||
|
||||
classes:
|
||||
BayNumber:
|
||||
class_uri: hc:BayNumber
|
||||
description: |
|
||||
An identifier for a storage bay or section within a row/aisle of a storage facility.
|
||||
|
||||
**DEFINITION**:
|
||||
|
||||
BayNumber represents a discrete location identifier within a storage system.
|
||||
In heritage storage facilities, storage is typically organized hierarchically:
|
||||
|
||||
```
|
||||
Storage Facility
|
||||
└── Zone (environmental control)
|
||||
└── Row/Aisle (physical corridor)
|
||||
└── Bay/Section (THIS CLASS - vertical unit in row)
|
||||
└── Shelf (horizontal level within bay)
|
||||
└── Storage Unit (box, drawer, etc.)
|
||||
```
|
||||
|
||||
**TYPICAL VALUES**:
|
||||
|
||||
| Format | Example | Description |
|
||||
|--------|---------|-------------|
|
||||
| Numeric | "3", "12" | Sequential bay numbers |
|
||||
| Alphabetic | "A", "C", "AA" | Lettered bays |
|
||||
| Mixed | "3A", "B2" | Combined formats |
|
||||
| Descriptive | "North-3" | Location-based |
|
||||
|
||||
**HERITAGE USE CASES**:
|
||||
|
||||
| Institution Type | Bay Naming Convention |
|
||||
|------------------|----------------------|
|
||||
| Archives | Sequential numeric (1, 2, 3...) |
|
||||
| Museums | Alphanumeric by collection area |
|
||||
| Libraries | By call number range |
|
||||
| Natural history | By specimen type |
|
||||
|
||||
**PROVENANCE**:
|
||||
|
||||
Created as part of slot migration (Rule 53) from deprecated
|
||||
`bay_number` slot to generic `has_or_had_identifier` pattern.
|
||||
exact_mappings:
|
||||
- hc:BayNumber
|
||||
close_mappings:
|
||||
- crm:E42_Identifier
|
||||
related_mappings:
|
||||
- schema:identifier
|
||||
slots:
|
||||
- specificity_annotation
|
||||
- template_specificity
|
||||
attributes:
|
||||
value:
|
||||
range: string
|
||||
required: true
|
||||
description: The bay number/identifier value.
|
||||
examples:
|
||||
- value: "3"
|
||||
description: Numeric bay number
|
||||
- value: "C"
|
||||
description: Alphabetic bay identifier
|
||||
- value: "North-3"
|
||||
description: Location-descriptive bay
|
||||
comments:
|
||||
- Storage bay identifier within a row/aisle
|
||||
- Part of hierarchical storage location addressing
|
||||
- Part of Rule 53 slot migration from bay_number
|
||||
see_also:
|
||||
- https://nde.nl/ontology/hc/StorageUnit
|
||||
97
schemas/20251121/linkml/modules/classes/BoxNumber.yaml
Normal file
97
schemas/20251121/linkml/modules/classes/BoxNumber.yaml
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
id: https://nde.nl/ontology/hc/class/BoxNumber
|
||||
name: box_number_class
|
||||
title: Box Number Class
|
||||
description: |
|
||||
A storage box number or position identifier on a shelf.
|
||||
|
||||
MIGRATED from box_number slot (Rule 53).
|
||||
Uses generic has_or_had_identifier slot with range narrowed to BoxNumber.
|
||||
imports:
|
||||
- linkml:types
|
||||
- ../slots/identifier_value
|
||||
- ../slots/specificity_annotation
|
||||
- ../slots/template_specificity
|
||||
- ./SpecificityAnnotation
|
||||
- ./TemplateSpecificityScores
|
||||
prefixes:
|
||||
linkml: https://w3id.org/linkml/
|
||||
hc: https://nde.nl/ontology/hc/
|
||||
schema: http://schema.org/
|
||||
dcterms: http://purl.org/dc/terms/
|
||||
crm: http://www.cidoc-crm.org/cidoc-crm/
|
||||
default_prefix: hc
|
||||
|
||||
classes:
|
||||
BoxNumber:
|
||||
class_uri: hc:BoxNumber
|
||||
description: |
|
||||
An identifier for a storage box or its position on a shelf.
|
||||
|
||||
**DEFINITION**:
|
||||
|
||||
BoxNumber represents the position or identifier of a storage box within
|
||||
a storage unit hierarchy. Archive boxes are the most common physical
|
||||
containers for heritage materials, particularly in archives.
|
||||
|
||||
```
|
||||
Shelf
|
||||
└── Box 1 (THIS CLASS - position on shelf)
|
||||
└── Box 2
|
||||
└── Box 3
|
||||
...
|
||||
```
|
||||
|
||||
**TYPICAL VALUES**:
|
||||
|
||||
| Type | Example | Description |
|
||||
|------|---------|-------------|
|
||||
| Sequential | 1, 2, 3, 12 | Position on shelf left-to-right |
|
||||
| Inventory | 145, 2024-0042 | Unique box inventory number |
|
||||
| Combined | 12.3 | Bay 12, Box 3 |
|
||||
|
||||
**ARCHIVE BOX STANDARDS**:
|
||||
|
||||
Heritage institutions typically use acid-free archive boxes conforming to:
|
||||
- ISO 16245 (Boxes for documents)
|
||||
- ANSI/NISO Z39.77 (Guidelines for materials in archives)
|
||||
|
||||
**HERITAGE USE CASES**:
|
||||
|
||||
| Material Type | Box Format |
|
||||
|---------------|------------|
|
||||
| Documents | Standard archive box (legal/letter) |
|
||||
| Photographs | Photo storage boxes |
|
||||
| Oversized | Flat boxes, tubes |
|
||||
| Fragile | Custom padded boxes |
|
||||
|
||||
**PROVENANCE**:
|
||||
|
||||
Created as part of slot migration (Rule 53) from deprecated
|
||||
`box_number` slot to generic `has_or_had_identifier` pattern.
|
||||
exact_mappings:
|
||||
- hc:BoxNumber
|
||||
close_mappings:
|
||||
- crm:E42_Identifier
|
||||
related_mappings:
|
||||
- schema:identifier
|
||||
slots:
|
||||
- specificity_annotation
|
||||
- template_specificity
|
||||
attributes:
|
||||
value:
|
||||
range: integer
|
||||
required: true
|
||||
minimum_value: 1
|
||||
description: The box number (position on shelf or inventory number).
|
||||
examples:
|
||||
- value: 12
|
||||
description: Box at position 12 on shelf
|
||||
- value: 145
|
||||
description: Archive box inventory number 145
|
||||
comments:
|
||||
- Storage box position identifier
|
||||
- Typically integer representing shelf position or inventory number
|
||||
- Part of Rule 53 slot migration from box_number
|
||||
see_also:
|
||||
- https://nde.nl/ontology/hc/StorageUnit
|
||||
- https://www.wikidata.org/wiki/Q854619
|
||||
|
|
@ -16,7 +16,10 @@ imports:
|
|||
- ../slots/budget_currency
|
||||
- ../slots/has_or_had_description
|
||||
- ../slots/has_or_had_label
|
||||
- ../slots/budget_status
|
||||
# REMOVED - migrated to has_or_had_status with range BudgetStatus (Rule 53)
|
||||
# - ../slots/budget_status
|
||||
- ../slots/has_or_had_status
|
||||
- ./BudgetStatus
|
||||
- ../slots/has_or_had_type
|
||||
- ../slots/capital_budget
|
||||
- ./BudgetType
|
||||
|
|
@ -97,7 +100,9 @@ classes:
|
|||
- budget_currency
|
||||
- has_or_had_description
|
||||
- has_or_had_label
|
||||
- budget_status
|
||||
# MIGRATED from budget_status to has_or_had_status (Rule 53)
|
||||
# - budget_status
|
||||
- has_or_had_status
|
||||
- has_or_had_type
|
||||
- capital_budget
|
||||
- digitization_budget
|
||||
|
|
@ -249,12 +254,25 @@ classes:
|
|||
description: Agent (person/organization) that approved this budget
|
||||
range: string
|
||||
required: false
|
||||
budget_status:
|
||||
range: string
|
||||
# MIGRATED from budget_status to has_or_had_status (Rule 53)
|
||||
# budget_status:
|
||||
# range: string
|
||||
# required: true
|
||||
# examples:
|
||||
# - value: ACTIVE
|
||||
# description: Current fiscal year budget in effect
|
||||
has_or_had_status:
|
||||
description: |
|
||||
MIGRATED from budget_status (Rule 53).
|
||||
Current status of this budget in its lifecycle.
|
||||
Uses BudgetStatus class for structured status tracking.
|
||||
range: BudgetStatus
|
||||
required: true
|
||||
examples:
|
||||
- value: ACTIVE
|
||||
description: Current fiscal year budget in effect
|
||||
- value: '{value: "ACTIVE", effective_date: "2024-01-01"}'
|
||||
description: Budget currently in effect
|
||||
- value: '{value: "DRAFT", effective_date: "2023-10-01"}'
|
||||
description: Budget under development
|
||||
revision_number:
|
||||
range: integer
|
||||
required: false
|
||||
|
|
@ -319,7 +337,9 @@ classes:
|
|||
endowment_draw: 5000000.0
|
||||
approval_date: '2023-11-15'
|
||||
is_or_was_approved_by: Board of Directors
|
||||
budget_status: ACTIVE
|
||||
has_or_had_status:
|
||||
value: ACTIVE
|
||||
effective_date: '2024-01-01'
|
||||
refers_to_custodian: https://nde.nl/ontology/hc/nl-nh-ams-m-rm-q190804
|
||||
description: Major museum annual operating budget
|
||||
- value:
|
||||
|
|
@ -341,6 +361,8 @@ classes:
|
|||
internal_funding: 2500000.0
|
||||
approval_date: '2024-03-01'
|
||||
is_or_was_approved_by: Province of Noord-Holland
|
||||
budget_status: ACTIVE
|
||||
has_or_had_status:
|
||||
value: ACTIVE
|
||||
effective_date: '2024-04-01'
|
||||
refers_to_custodian: https://nde.nl/ontology/hc/nl-nh-haa-a-nha
|
||||
description: Regional archive government-funded budget
|
||||
|
|
|
|||
105
schemas/20251121/linkml/modules/classes/BudgetStatus.yaml
Normal file
105
schemas/20251121/linkml/modules/classes/BudgetStatus.yaml
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
id: https://nde.nl/ontology/hc/class/BudgetStatus
|
||||
name: budget_status_class
|
||||
title: Budget Status Class
|
||||
description: |
|
||||
Status of a heritage custodian budget throughout its lifecycle.
|
||||
|
||||
MIGRATED from budget_status slot (Rule 53).
|
||||
Uses generic has_or_had_status slot with range narrowed to BudgetStatus.
|
||||
imports:
|
||||
- linkml:types
|
||||
- ../slots/specificity_annotation
|
||||
- ../slots/template_specificity
|
||||
- ./SpecificityAnnotation
|
||||
- ./TemplateSpecificityScores
|
||||
prefixes:
|
||||
linkml: https://w3id.org/linkml/
|
||||
hc: https://nde.nl/ontology/hc/
|
||||
schema: http://schema.org/
|
||||
dcterms: http://purl.org/dc/terms/
|
||||
frapo: http://purl.org/cerif/frapo/
|
||||
default_prefix: hc
|
||||
|
||||
classes:
|
||||
BudgetStatus:
|
||||
class_uri: hc:BudgetStatus
|
||||
description: |
|
||||
Status of a budget document throughout its lifecycle.
|
||||
|
||||
**DEFINITION**:
|
||||
|
||||
BudgetStatus represents the current state of a budget document
|
||||
as it moves through the approval and execution lifecycle.
|
||||
|
||||
**BUDGET LIFECYCLE STAGES**:
|
||||
|
||||
```
|
||||
DRAFT → PROPOSED → APPROVED → ACTIVE → REVISED → CLOSED
|
||||
↓ ↓
|
||||
REJECTED SUPERSEDED
|
||||
```
|
||||
|
||||
**STATUS VALUES**:
|
||||
|
||||
| Status | Description | Typical Duration |
|
||||
|--------|-------------|------------------|
|
||||
| DRAFT | Under development | Weeks/months |
|
||||
| PROPOSED | Submitted for approval | Days/weeks |
|
||||
| APPROVED | Officially approved | Until fiscal start |
|
||||
| ACTIVE | Currently in effect | Fiscal year |
|
||||
| REVISED | Modified after approval | Variable |
|
||||
| CLOSED | Fiscal period ended | Permanent |
|
||||
| REJECTED | Not approved | Terminal |
|
||||
| SUPERSEDED | Replaced by revision | Terminal |
|
||||
|
||||
**HERITAGE INSTITUTION CONTEXT**:
|
||||
|
||||
Heritage institution budgets typically follow these approval paths:
|
||||
|
||||
| Institution Type | Approval Authority |
|
||||
|------------------|-------------------|
|
||||
| Museum (stichting) | Board of Directors |
|
||||
| Regional Archive | Provincial Government |
|
||||
| National Library | Ministry of Culture |
|
||||
| University Collection | University Board |
|
||||
|
||||
**PROVENANCE**:
|
||||
|
||||
Created as part of slot migration (Rule 53) from deprecated
|
||||
`budget_status` slot to generic `has_or_had_status` pattern.
|
||||
exact_mappings:
|
||||
- hc:BudgetStatus
|
||||
close_mappings:
|
||||
- schema:status
|
||||
related_mappings:
|
||||
- dcterms:status
|
||||
slots:
|
||||
- specificity_annotation
|
||||
- template_specificity
|
||||
attributes:
|
||||
value:
|
||||
range: string
|
||||
required: true
|
||||
description: |
|
||||
The budget status value.
|
||||
Valid values: DRAFT, PROPOSED, APPROVED, ACTIVE, REVISED, CLOSED, REJECTED, SUPERSEDED
|
||||
examples:
|
||||
- value: ACTIVE
|
||||
description: Budget currently in effect
|
||||
- value: DRAFT
|
||||
description: Budget under development
|
||||
- value: CLOSED
|
||||
description: Fiscal period ended
|
||||
effective_date:
|
||||
range: date
|
||||
required: false
|
||||
description: Date when this status became effective.
|
||||
examples:
|
||||
- value: "2024-01-01"
|
||||
description: Status effective from start of year
|
||||
comments:
|
||||
- Budget lifecycle status tracking
|
||||
- Supports audit trail of budget state changes
|
||||
- Part of Rule 53 slot migration from budget_status
|
||||
see_also:
|
||||
- https://nde.nl/ontology/hc/Budget
|
||||
|
|
@ -17,8 +17,13 @@ imports:
|
|||
- ../slots/unit_name
|
||||
- ../slots/unit_type
|
||||
- ../slots/capacity_item
|
||||
- ../slots/bay_number
|
||||
- ../slots/box_number
|
||||
# REMOVED - migrated to has_or_had_identifier with range BayNumber (Rule 53)
|
||||
# - ../slots/bay_number
|
||||
# REMOVED - migrated to has_or_had_identifier with range BoxNumber (Rule 53)
|
||||
# - ../slots/box_number
|
||||
- ../slots/has_or_had_identifier
|
||||
- ./BayNumber
|
||||
- ./BoxNumber
|
||||
- ../slots/current_item_count
|
||||
- ../slots/drawer_number
|
||||
- ../slots/part_of_storage
|
||||
|
|
@ -75,8 +80,10 @@ classes:
|
|||
- hc:EnvironmentalZone
|
||||
- schema:Place
|
||||
slots:
|
||||
- bay_number
|
||||
- box_number
|
||||
# MIGRATED from bay_number and box_number to has_or_had_identifier (Rule 53)
|
||||
# - bay_number
|
||||
# - box_number
|
||||
- has_or_had_identifier
|
||||
- capacity_item
|
||||
- current_item_count
|
||||
- drawer_number
|
||||
|
|
@ -124,11 +131,22 @@ classes:
|
|||
- value: A
|
||||
- value: '12'
|
||||
- value: North-3
|
||||
bay_number:
|
||||
has_or_had_identifier:
|
||||
description: |
|
||||
MIGRATED from bay_number and box_number (Rule 53).
|
||||
Storage location identifiers including bay and box numbers.
|
||||
Use BayNumber for bay/section identifiers, BoxNumber for box positions.
|
||||
range: string
|
||||
multivalued: true
|
||||
examples:
|
||||
- value: '3'
|
||||
- value: C
|
||||
- value: '[{type: BayNumber, value: "3"}, {type: BoxNumber, value: 12}]'
|
||||
description: Bay 3, Box 12
|
||||
# DEPRECATED - use has_or_had_identifier with range BayNumber
|
||||
# bay_number:
|
||||
# range: string
|
||||
# examples:
|
||||
# - value: '3'
|
||||
# - value: C
|
||||
shelf_number:
|
||||
range: integer
|
||||
examples:
|
||||
|
|
@ -138,10 +156,11 @@ classes:
|
|||
range: integer
|
||||
examples:
|
||||
- value: 3
|
||||
box_number:
|
||||
range: integer
|
||||
examples:
|
||||
- value: 12
|
||||
# DEPRECATED - use has_or_had_identifier with range BoxNumber
|
||||
# box_number:
|
||||
# range: integer
|
||||
# examples:
|
||||
# - value: 12
|
||||
capacity_item:
|
||||
range: integer
|
||||
current_item_count:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ imports:
|
|||
- linkml:types
|
||||
- ./VideoAnnotation
|
||||
- ./VideoTimeSegment
|
||||
- ../slots/audio_event_segments
|
||||
- ./AudioEventSegment
|
||||
# REMOVED - migrated to has_or_had_segment with range AudioEventSegment (Rule 53)
|
||||
# - ../slots/audio_event_segments
|
||||
- ../slots/has_or_had_segment
|
||||
- ../slots/has_audio_quality_score
|
||||
- ../slots/diarization_confidence
|
||||
- ../slots/diarization_enabled
|
||||
|
|
@ -112,7 +115,9 @@ classes:
|
|||
- wikidata:Q11028
|
||||
- wikidata:Q638
|
||||
slots:
|
||||
- audio_event_segments
|
||||
# MIGRATED from audio_event_segments to has_or_had_segment (Rule 53)
|
||||
# - audio_event_segments
|
||||
- has_or_had_segment
|
||||
- audio_quality_score
|
||||
- diarization_enabled
|
||||
- has_or_had_diarization_segment
|
||||
|
|
@ -138,6 +143,20 @@ classes:
|
|||
- has_or_had_speech_segment
|
||||
- template_specificity
|
||||
slot_usage:
|
||||
has_or_had_segment:
|
||||
description: |
|
||||
MIGRATED from audio_event_segments (Rule 53).
|
||||
Audio event segments detected in the video content.
|
||||
range: AudioEventSegment
|
||||
multivalued: true
|
||||
required: false
|
||||
inlined_as_list: true
|
||||
examples:
|
||||
- value: '[{audio_event_type: SPEECH, start_seconds: 0.0, end_seconds: 15.0, segment_text: "Speech detected - Speaker 1", confidence: 0.95}]'
|
||||
description: Speech detection segment
|
||||
- value: '[{audio_event_type: MUSIC, start_seconds: 30.0, end_seconds: 60.0, segment_text: "Background classical music", confidence: 0.88}]'
|
||||
description: Music detection segment
|
||||
# NOTE: has_audio_event_segment is deprecated - use has_or_had_segment above
|
||||
has_audio_event_segment:
|
||||
range: VideoTimeSegment
|
||||
multivalued: true
|
||||
|
|
|
|||
|
|
@ -546,10 +546,14 @@ fixes:
|
|||
|
||||
- original_slot_id: https://nde.nl/ontology/hc/slot/audio_event_segments
|
||||
processed:
|
||||
status: false
|
||||
timestamp: null
|
||||
session: null
|
||||
notes: "Maps to existing AudioEventSegment class"
|
||||
status: true
|
||||
timestamp: "2026-01-14T10:30:00Z"
|
||||
session: "slot-migration-session-8"
|
||||
notes: |
|
||||
MIGRATED: audio_event_segments → has_or_had_segment + AudioEventSegment
|
||||
- Created AudioEventSegment.yaml class (hc:AudioEventSegment)
|
||||
- Updated VideoAudioAnnotation.yaml: imports, slots, slot_usage
|
||||
- Archived to modules/slots/archive/audio_event_segments_archived_20260114.yaml
|
||||
revision:
|
||||
- label: has_or_had_segment
|
||||
type: slot
|
||||
|
|
@ -686,10 +690,14 @@ fixes:
|
|||
|
||||
- original_slot_id: https://nde.nl/ontology/hc/slot/bay_number
|
||||
processed:
|
||||
status: false
|
||||
timestamp: null
|
||||
session: null
|
||||
notes: "Requires BayNumber class creation"
|
||||
status: true
|
||||
timestamp: "2026-01-14T10:45:00Z"
|
||||
session: "slot-migration-session-8"
|
||||
notes: |
|
||||
MIGRATED: bay_number → has_or_had_identifier + BayNumber
|
||||
- Created BayNumber.yaml class (hc:BayNumber)
|
||||
- Updated StorageUnit.yaml: imports, slots, slot_usage
|
||||
- Archived to modules/slots/archive/bay_number_archived_20260114.yaml
|
||||
revision:
|
||||
- label: has_or_had_identifier
|
||||
type: slot
|
||||
|
|
@ -939,10 +947,14 @@ fixes:
|
|||
|
||||
- original_slot_id: https://nde.nl/ontology/hc/slot/box_number
|
||||
processed:
|
||||
status: false
|
||||
timestamp: null
|
||||
session: null
|
||||
notes: "Requires BoxNumber class creation"
|
||||
status: true
|
||||
timestamp: "2026-01-14T10:45:00Z"
|
||||
session: "slot-migration-session-8"
|
||||
notes: |
|
||||
MIGRATED: box_number → has_or_had_identifier + BoxNumber
|
||||
- Created BoxNumber.yaml class (hc:BoxNumber)
|
||||
- Updated StorageUnit.yaml: imports, slots, slot_usage
|
||||
- Archived to modules/slots/archive/box_number_archived_20260114.yaml
|
||||
revision:
|
||||
- label: has_or_had_identifier
|
||||
type: slot
|
||||
|
|
|
|||
Loading…
Reference in a new issue