Skip to content

Commit 10a648e

Browse files
RenzoMinelliclaude
andcommitted
feat: add SubjectGraphComponent and graph controllers
- SubjectGraphComponent builds nodes (with availability/completion status), edges (from prerequisite trees), and semester labels - Planner::GraphsController shows planned subjects grouped by user-defined semesters - Subjects::GraphsController shows curriculum subjects filtered by category - Add routes: planner/graph and subjects/graph Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4c9a35f commit 10a648e

5 files changed

Lines changed: 123 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div
2+
data-controller="subject-graph"
3+
data-subject-graph-nodes-value="<%= nodes.to_json %>"
4+
data-subject-graph-edges-value="<%= edges.to_json %>"
5+
data-subject-graph-semester-labels-value="<%= semester_labels.to_json %>"
6+
class="subject-graph-container border border-gray-200 rounded-lg w-[100vw] relative left-[50%] -translate-x-1/2 flex-1 min-h-0 overflow-hidden">
7+
</div>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
class SubjectGraphComponent < ViewComponent::Base
2+
def initialize(subjects:, current_student:, semester_map: nil)
3+
@subjects = subjects
4+
@current_student = current_student
5+
@semester_map = semester_map
6+
end
7+
8+
def nodes
9+
@subjects.map do |subject|
10+
{
11+
id: subject.id,
12+
code: subject.code,
13+
name: subject.short_name || subject.name,
14+
url: helpers.subject_path(subject),
15+
available: @current_student.available?(subject),
16+
completed: @current_student.approved?(subject),
17+
semester: semester_for(subject)
18+
}
19+
end
20+
end
21+
22+
def edges
23+
subject_ids = Set.new(@subjects.map(&:id))
24+
edges = []
25+
26+
@subjects.each do |subject|
27+
collect_prerequisite_edges(subject.course&.prerequisite_tree, subject.id, subject_ids, edges)
28+
end
29+
30+
edges.uniq
31+
end
32+
33+
def semester_labels
34+
semesters = @subjects.map { |s| semester_for(s) }.uniq.sort
35+
36+
semesters.index_with do |sem|
37+
semester_display_label(sem)
38+
end
39+
end
40+
41+
private
42+
43+
def semester_for(subject)
44+
if @semester_map
45+
@semester_map[subject.id] || 0
46+
else
47+
index = Subject::CATEGORIES.index(subject.category&.to_sym)
48+
index ? index + 1 : 0
49+
end
50+
end
51+
52+
def semester_display_label(semester)
53+
return "Otras" if semester == 0
54+
55+
if @semester_map
56+
"Semestre #{semester}"
57+
else
58+
category = Subject::CATEGORIES[semester - 1]
59+
category ? helpers.formatted_category(category.to_s) : "Semestre #{semester}"
60+
end
61+
end
62+
63+
def collect_prerequisite_edges(prerequisite, target_subject_id, subject_ids, edges)
64+
return unless prerequisite
65+
66+
case prerequisite
67+
when SubjectPrerequisite
68+
source_subject_id = prerequisite.approvable_needed.subject_id
69+
if subject_ids.include?(source_subject_id)
70+
edges << { source: source_subject_id, target: target_subject_id }
71+
end
72+
when LogicalPrerequisite
73+
# Skip "not" operators - they represent inverse relationships
74+
return if prerequisite.logical_operator == "not"
75+
76+
prerequisite.operands_prerequisites.each do |child|
77+
collect_prerequisite_edges(child, target_subject_id, subject_ids, edges)
78+
end
79+
end
80+
end
81+
end
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module Planner
2+
class GraphsController < ApplicationController
3+
before_action :authenticate_user!
4+
5+
def show
6+
subject_plans = current_user.subject_plans
7+
.includes(subject: [:course, :exam])
8+
9+
@semester_map = subject_plans.to_h do |subject_plan|
10+
[subject_plan.subject_id, subject_plan.semester]
11+
end
12+
13+
@subjects = subject_plans.map(&:subject)
14+
TreePreloader.preload(@subjects)
15+
end
16+
end
17+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module Subjects
2+
class GraphsController < ApplicationController
3+
def show
4+
categories = params[:categories] || []
5+
6+
@subjects = current_degree_plan.subjects
7+
.where(category: categories)
8+
.includes(:course, :exam)
9+
10+
TreePreloader.preload(@subjects)
11+
end
12+
end
13+
end

config/routes.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242

4343
namespace :planner do
4444
resources :not_planned_subjects, only: :index
45+
resource :graph, only: :show
46+
end
47+
48+
namespace :subjects do
49+
resource :graph, only: :show
4550
end
4651

4752
resource :user_onboardings, only: :update

0 commit comments

Comments
 (0)