Concept.java
package edu.ucsb.cs.scaffold.entity;
import jakarta.persistence.*;
import lombok.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "concepts")
public class Concept {
// Longest a top-level concept's label may be once its Markdown is rendered to plain text;
// enforced everywhere labels enter the system (single-concept endpoints and YAML upload).
// Top-level concepts are drawn as circular nodes on the concept graph, so their labels need
// to stay short.
public static final int MAX_RENDERED_LABEL_LENGTH = 32;
// Longest a subconcept's label may be once its Markdown is rendered to plain text. Subconcepts
// are listed as rows inside their parent's node (which wrap rather than needing to fit a
// circle), so they can afford to be longer than top-level concept labels.
public static final int MAX_RENDERED_SUBCONCEPT_LABEL_LENGTH = 60;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "course_id", nullable = false)
@ToString.Exclude
private Course course;
@Column(nullable = false)
private String label;
// length is set well above the default 255 so that Hibernate's schema
// validation matches the VARCHAR(1048576) column defined in the migration.
@Column(length = 1048576)
private String description;
@Column(length = 1048576)
private String example;
private String color;
private Integer x;
private Integer y;
// Longest-path rank from a root (no-prerequisite) concept, 1-based. Top-level only,
// recomputed by ConceptGraphService.reset(); see /api/course/scaffold/reset.
private Integer level;
// Author-chosen position of a subconcept within its parent's list, 0-based. Subconcepts
// only (null for top-level concepts). Not unique: a concurrent create can leave two
// subconcepts with the same value, which is harmless because display order always breaks
// ties by id, and the next reorder rewrites the whole list to 0..n-1.
private Integer sortOrder;
@ManyToOne
@JoinColumn(name = "parent_id")
@ToString.Exclude
private Concept parent;
public boolean isSubconcept() {
return parent != null;
}
}