11import { Command } from 'commander' ;
22import * as fs from 'fs' ;
33import * as yaml from 'js-yaml' ;
4- import { CourseYamlSchema } from '@graspful/shared' ;
5- import type { CourseYaml } from '@graspful/shared' ;
4+ import { CourseYamlSchema , describeCourse } from '@graspful/shared' ;
65import { output , outputError } from '../lib/output' ;
76import { cliCapture } from '../lib/analytics' ;
87
9- function computeGraphDepth ( concepts : CourseYaml [ 'concepts' ] ) : number {
10- const graph = new Map < string , string [ ] > ( ) ;
11- for ( const c of concepts ) {
12- graph . set ( c . id , c . prerequisites ) ;
13- }
14-
15- const memo = new Map < string , number > ( ) ;
16-
17- function depth ( id : string , visited : Set < string > ) : number {
18- if ( memo . has ( id ) ) return memo . get ( id ) ! ;
19- if ( visited . has ( id ) ) return 0 ; // cycle, avoid infinite loop
20- visited . add ( id ) ;
21-
22- const prereqs = graph . get ( id ) ?? [ ] ;
23- let maxPrereqDepth = 0 ;
24- for ( const prereq of prereqs ) {
25- if ( graph . has ( prereq ) ) {
26- maxPrereqDepth = Math . max ( maxPrereqDepth , depth ( prereq , visited ) ) ;
27- }
28- }
29-
30- const d = maxPrereqDepth + 1 ;
31- memo . set ( id , d ) ;
32- return d ;
33- }
34-
35- let maxDepth = 0 ;
36- for ( const c of concepts ) {
37- maxDepth = Math . max ( maxDepth , depth ( c . id , new Set ( ) ) ) ;
38- }
39- return maxDepth ;
40- }
41-
428export function registerDescribeCommand ( program : Command ) {
439 program
4410 . command ( 'describe <file>' )
@@ -64,89 +30,25 @@ export function registerDescribeCommand(program: Command) {
6430 process . exit ( 1 ) ;
6531 }
6632
67- const data = result . data ;
68- const concepts = data . concepts ;
69- const sections = data . sections ;
70-
71- const authoredConcepts = concepts . filter ( ( c ) => c . knowledgePoints . length > 0 ) ;
72- const stubConcepts = concepts . filter ( ( c ) => c . knowledgePoints . length === 0 ) ;
73-
74- const kpCount = concepts . reduce ( ( sum , c ) => sum + c . knowledgePoints . length , 0 ) ;
75- const problemCount = concepts . reduce (
76- ( sum , c ) => sum + c . knowledgePoints . reduce ( ( s , kp ) => s + kp . problems . length , 0 ) ,
77- 0 ,
78- ) ;
79-
80- const graphDepth = computeGraphDepth ( concepts ) ;
81-
82- const conceptsWithoutKps = stubConcepts . map ( ( c ) => c . id ) ;
83- const kpsWithoutProblems : string [ ] = [ ] ;
84- for ( const c of concepts ) {
85- for ( const kp of c . knowledgePoints ) {
86- if ( kp . problems . length === 0 ) {
87- kpsWithoutProblems . push ( `${ c . id } /${ kp . id } ` ) ;
88- }
89- }
90- }
91-
92- // Section breakdown
93- const sectionBreakdown : Array < { section : string ; concepts : number ; kps : number ; problems : number } > = [ ] ;
94- if ( sections . length > 0 ) {
95- for ( const section of sections ) {
96- const sectionConcepts = concepts . filter ( ( c ) => c . section === section . id ) ;
97- const sKps = sectionConcepts . reduce ( ( sum , c ) => sum + c . knowledgePoints . length , 0 ) ;
98- const sProblems = sectionConcepts . reduce (
99- ( sum , c ) => sum + c . knowledgePoints . reduce ( ( s , kp ) => s + kp . problems . length , 0 ) ,
100- 0 ,
101- ) ;
102- sectionBreakdown . push ( { section : section . id , concepts : sectionConcepts . length , kps : sKps , problems : sProblems } ) ;
103- }
104-
105- // Concepts without a section
106- const unsectioned = concepts . filter ( ( c ) => ! c . section ) ;
107- if ( unsectioned . length > 0 ) {
108- const uKps = unsectioned . reduce ( ( sum , c ) => sum + c . knowledgePoints . length , 0 ) ;
109- const uProblems = unsectioned . reduce (
110- ( sum , c ) => sum + c . knowledgePoints . reduce ( ( s , kp ) => s + kp . problems . length , 0 ) ,
111- 0 ,
112- ) ;
113- sectionBreakdown . push ( { section : '(unsectioned)' , concepts : unsectioned . length , kps : uKps , problems : uProblems } ) ;
114- }
115- }
116-
117- const stats = {
118- courseName : data . course . name ,
119- courseId : data . course . id ,
120- version : data . course . version ,
121- estimatedHours : data . course . estimatedHours ,
122- concepts : concepts . length ,
123- authoredConcepts : authoredConcepts . length ,
124- stubConcepts : stubConcepts . length ,
125- knowledgePoints : kpCount ,
126- problems : problemCount ,
127- graphDepth,
128- conceptsWithoutKps : conceptsWithoutKps . length ,
129- kpsWithoutProblems : kpsWithoutProblems . length ,
130- sections : sectionBreakdown ,
131- } ;
33+ const stats = describeCourse ( result . data ) ;
13234
13335 const humanLines = [
134- `Course: "${ data . course . name } " (v${ data . course . version } )` ,
135- `Concepts: ${ concepts . length } (${ authoredConcepts . length } authored, ${ stubConcepts . length } stubs)` ,
136- `KPs: ${ kpCount } , Problems: ${ problemCount } ` ,
137- `Graph depth: ${ graphDepth } ` ,
138- `Missing: ${ conceptsWithoutKps . length } concepts need KPs, ${ kpsWithoutProblems . length } KPs need problems` ,
36+ `Course: "${ stats . courseName } " (v${ stats . version } )` ,
37+ `Concepts: ${ stats . concepts } (${ stats . authoredConcepts } authored, ${ stats . stubConcepts } stubs)` ,
38+ `KPs: ${ stats . knowledgePoints } , Problems: ${ stats . problems } ` ,
39+ `Graph depth: ${ stats . graphDepth } ` ,
40+ `Missing: ${ stats . conceptsWithoutKps } concepts need KPs, ${ stats . kpsWithoutProblems } KPs need problems` ,
13941 ] ;
14042
141- if ( sectionBreakdown . length > 0 ) {
43+ if ( stats . sections . length > 0 ) {
14244 humanLines . push ( '' ) ;
14345 humanLines . push ( 'Sections:' ) ;
144- for ( const s of sectionBreakdown ) {
46+ for ( const s of stats . sections ) {
14547 humanLines . push ( ` ${ s . section } : ${ s . concepts } concepts, ${ s . kps } KPs, ${ s . problems } problems` ) ;
14648 }
14749 }
14850
149- cliCapture ( 'course described' , { concept_count : concepts . length , kp_count : kpCount , problem_count : problemCount } ) ;
51+ cliCapture ( 'course described' , { concept_count : stats . concepts , kp_count : stats . knowledgePoints , problem_count : stats . problems } ) ;
15052 output ( stats , humanLines . join ( '\n' ) ) ;
15153 } ) ;
15254}
0 commit comments