jee-default 1.0.1 → 1.0.2
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.
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
slides_dir="${1:-notes}"
|
|
5
|
+
output_file="${2:-illustration.md}"
|
|
6
|
+
pattern="${3:-*question.png}"
|
|
7
|
+
|
|
8
|
+
if [ "$slides_dir" = "-h" ] || [ "$slides_dir" = "--help" ]; then
|
|
9
|
+
cat <<'EOF'
|
|
10
|
+
Usage: ./generate-question-illustration.sh [slides-dir] [output-file] [pattern]
|
|
11
|
+
|
|
12
|
+
slides-dir Directory to scan recursively for question images.
|
|
13
|
+
output-file Markdown file to generate (default: illustration.md).
|
|
14
|
+
pattern Filename pattern to match question images (default: '*question.png').
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
./generate-question-illustration.sh notes/typeOfChargesAndTheirProperties illustration.md
|
|
18
|
+
./generate-question-illustration.sh notes illustration.md '*question.png'
|
|
19
|
+
EOF
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if [ ! -d "$slides_dir" ]; then
|
|
24
|
+
echo "Error: slides directory not found: $slides_dir" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
29
|
+
echo "Error: Node.js is required. Install Node.js to continue." >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
34
|
+
node "$SCRIPT_DIR/question-illustration.js" "$slides_dir" "$output_file" "$pattern"
|
|
35
|
+
exit $?
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
source_dir="${1:-questoinPractices}"
|
|
5
|
+
output_file="${2:-questionSheet.md}"
|
|
6
|
+
pattern="${3:-*.png}"
|
|
7
|
+
|
|
8
|
+
if [ "$source_dir" = "-h" ] || [ "$source_dir" = "--help" ]; then
|
|
9
|
+
cat <<'EOF'
|
|
10
|
+
Usage: ./generate-question-practice-sheet.sh [source-dir] [output-file] [pattern]
|
|
11
|
+
|
|
12
|
+
source-dir Directory containing practice question images.
|
|
13
|
+
output-file Markdown file to write (default: questionSheet.md).
|
|
14
|
+
pattern Filename glob to match images (default: '*.png').
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
./generate-question-practice-sheet.sh questoinPractices questionSheet.md
|
|
18
|
+
./generate-question-practice-sheet.sh questoinPractices questionSheet.md 'slide*question.png'
|
|
19
|
+
EOF
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if [ ! -d "$source_dir" ]; then
|
|
24
|
+
echo "Error: source directory not found: $source_dir" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
29
|
+
echo "Error: Node.js is required. Install Node.js to continue." >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
34
|
+
node "$SCRIPT_DIR/question-practice-sheet.js" "$source_dir" "$output_file" "$pattern"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jee-default",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Generate DSA question practice sheets instantly",
|
|
5
5
|
"main": "./bin/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
11
|
+
"generate-question-illustration.sh",
|
|
12
|
+
"generate-question-practice-sheet.sh",
|
|
13
|
+
"pdf-to-slides.sh",
|
|
14
|
+
"question-illustration.js",
|
|
15
|
+
"question-practice-sheet.js",
|
|
11
16
|
"questionPractices/"
|
|
12
17
|
],
|
|
13
18
|
"keywords": [
|
package/pdf-to-slides.sh
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
pdf_source="${1:-source.pdf}"
|
|
5
|
+
out_dir="${2:-notes}"
|
|
6
|
+
pages="${3:-all}"
|
|
7
|
+
|
|
8
|
+
tmp_dir=$(mktemp -d)
|
|
9
|
+
trap 'rm -rf "$tmp_dir"' EXIT
|
|
10
|
+
|
|
11
|
+
if [[ "$pdf_source" =~ ^https?:// ]]; then
|
|
12
|
+
pdf_file="$tmp_dir/source.pdf"
|
|
13
|
+
if command -v curl >/dev/null 2>&1; then
|
|
14
|
+
curl -L -o "$pdf_file" "$pdf_source"
|
|
15
|
+
elif command -v wget >/dev/null 2>&1; then
|
|
16
|
+
wget -O "$pdf_file" "$pdf_source"
|
|
17
|
+
else
|
|
18
|
+
echo "Error: curl or wget is required to download PDF from URL." >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
else
|
|
22
|
+
pdf_file="$pdf_source"
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
mkdir -p "$out_dir"
|
|
26
|
+
|
|
27
|
+
if command -v pdf-parse >/dev/null 2>&1; then
|
|
28
|
+
pdfparse_cmd=(pdf-parse)
|
|
29
|
+
elif command -v npx >/dev/null 2>&1; then
|
|
30
|
+
pdfparse_cmd=(npx --yes pdf-parse)
|
|
31
|
+
elif [ -x "./node_modules/.bin/pdf-parse" ]; then
|
|
32
|
+
pdfparse_cmd=(./node_modules/.bin/pdf-parse)
|
|
33
|
+
else
|
|
34
|
+
echo "Error: pdf-parse CLI not found. Install it globally with 'npm install -g pdf-parse' or use npx." >&2
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
if [ "$pages" = "all" ]; then
|
|
39
|
+
pages_arg=()
|
|
40
|
+
else
|
|
41
|
+
pages_arg=(--pages "$pages")
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
"${pdfparse_cmd[@]}" screenshot "$pdf_file" "${pages_arg[@]}" --output "$tmp_dir"
|
|
45
|
+
|
|
46
|
+
# Rename generated screenshot files in page order
|
|
47
|
+
count=1
|
|
48
|
+
while IFS= read -r -d '' img; do
|
|
49
|
+
cp "$img" "$out_dir/slide${count}.png"
|
|
50
|
+
count=$((count + 1))
|
|
51
|
+
done < <(find "$tmp_dir" -maxdepth 1 -type f -name '*_screenshot.png' -print0 | sort -z -V)
|
|
52
|
+
|
|
53
|
+
echo "Created $((count - 1)) slide images in '$out_dir' from '$pdf_file'."
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
function usage() {
|
|
6
|
+
console.log('Usage: node question-illustration.js <slides-dir> <output-file> [pattern]');
|
|
7
|
+
console.log('Example: node question-illustration.js notes illustration.md "*question.png"');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function patternToRegex(pattern) {
|
|
11
|
+
const escaped = pattern.replace(/[-[\]{}()+?.,\\^$|#\s*]/g, '\\$&');
|
|
12
|
+
const regex = '^' + escaped.replace(/\\\*/g, '.*').replace(/\\\?/g, '.') + '$';
|
|
13
|
+
return new RegExp(regex, 'i');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function walkDir(dir, matcher) {
|
|
17
|
+
const files = [];
|
|
18
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
19
|
+
const fullPath = path.join(dir, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
files.push(...walkDir(fullPath, matcher));
|
|
22
|
+
} else if (entry.isFile() && matcher(entry.name)) {
|
|
23
|
+
files.push(fullPath);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return files.sort();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function cleanTitle(name) {
|
|
30
|
+
let title = name.replace(/question$/i, '');
|
|
31
|
+
title = title.replace(/[-_]+$/, '');
|
|
32
|
+
title = title.replace(/[-_]+/g, ' ');
|
|
33
|
+
title = title.trim();
|
|
34
|
+
if (!title) title = name;
|
|
35
|
+
return title.charAt(0).toUpperCase() + title.slice(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function relativePath(from, to) {
|
|
39
|
+
let rel = path.relative(from, to).replace(/\\/g, '/');
|
|
40
|
+
if (!rel.startsWith('.')) rel = './' + rel;
|
|
41
|
+
return rel;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function run() {
|
|
45
|
+
const [,, slidesDir, outputFile, patternArg] = process.argv;
|
|
46
|
+
if (!slidesDir || !outputFile) {
|
|
47
|
+
usage();
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const pattern = patternArg || '*question.png';
|
|
52
|
+
const matcher = patternToRegex(pattern);
|
|
53
|
+
|
|
54
|
+
if (!fs.existsSync(slidesDir) || !fs.statSync(slidesDir).isDirectory()) {
|
|
55
|
+
console.error(`Error: slides directory not found: ${slidesDir}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const images = walkDir(slidesDir, name => matcher.test(name));
|
|
60
|
+
if (images.length === 0) {
|
|
61
|
+
console.error(`No files matching pattern ${pattern} found under ${slidesDir}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const outputDir = path.dirname(outputFile) || '.';
|
|
66
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
67
|
+
|
|
68
|
+
const lines = [];
|
|
69
|
+
lines.push('# Question Illustration');
|
|
70
|
+
lines.push('');
|
|
71
|
+
lines.push('Generated from images in `' + slidesDir + '` on ' + new Date().toISOString());
|
|
72
|
+
lines.push('');
|
|
73
|
+
|
|
74
|
+
for (const imagePath of images) {
|
|
75
|
+
const relativeImage = relativePath(outputDir, imagePath);
|
|
76
|
+
const baseName = path.basename(imagePath, path.extname(imagePath));
|
|
77
|
+
const title = cleanTitle(baseName);
|
|
78
|
+
|
|
79
|
+
lines.push(`## ${title}`);
|
|
80
|
+
lines.push('');
|
|
81
|
+
lines.push(``);
|
|
82
|
+
lines.push('');
|
|
83
|
+
lines.push('---');
|
|
84
|
+
lines.push('');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fs.writeFileSync(outputFile, lines.join('\n'), 'utf8');
|
|
88
|
+
console.log(`\nGenerated ${outputFile} with ${images.length} entries.`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
run().catch(err => {
|
|
92
|
+
console.error(err);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
function usage() {
|
|
6
|
+
console.log('Usage: node question-practice-sheet.js <source-dir> <output-file> [pattern]');
|
|
7
|
+
console.log('Example: node question-practice-sheet.js questoinPractices questionSheet.md "*.png"');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function wildcardToRegex(pattern) {
|
|
11
|
+
const regex = pattern
|
|
12
|
+
.split('')
|
|
13
|
+
.map(char => {
|
|
14
|
+
if (char === '*') return '.*';
|
|
15
|
+
if (char === '?') return '.';
|
|
16
|
+
return char.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
17
|
+
})
|
|
18
|
+
.join('');
|
|
19
|
+
return new RegExp(`^${regex}$`, 'i');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function findImages(dir, matcher) {
|
|
23
|
+
const matches = [];
|
|
24
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
25
|
+
const fullPath = path.join(dir, entry.name);
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
matches.push(...findImages(fullPath, matcher));
|
|
28
|
+
} else if (entry.isFile() && matcher.test(entry.name)) {
|
|
29
|
+
matches.push(fullPath);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return matches.sort();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function makeTitle(filename) {
|
|
36
|
+
let name = path.basename(filename, path.extname(filename));
|
|
37
|
+
name = name.replace(/question$/i, '');
|
|
38
|
+
name = name.replace(/[-_]+/g, ' ');
|
|
39
|
+
name = name.replace(/\s+/g, ' ').trim();
|
|
40
|
+
if (!name) {
|
|
41
|
+
name = path.basename(filename);
|
|
42
|
+
}
|
|
43
|
+
return name
|
|
44
|
+
.split(' ')
|
|
45
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
46
|
+
.join(' ');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function relativeMarkdownPath(from, to) {
|
|
50
|
+
let rel = path.relative(from, to).replace(/\\/g, '/');
|
|
51
|
+
if (!rel.startsWith('.') && !rel.startsWith('/')) {
|
|
52
|
+
rel = './' + rel;
|
|
53
|
+
}
|
|
54
|
+
return rel;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function groupByFolder(files, rootDir) {
|
|
58
|
+
const groups = new Map();
|
|
59
|
+
for (const file of files) {
|
|
60
|
+
const folder = path.relative(rootDir, path.dirname(file)) || '.';
|
|
61
|
+
if (!groups.has(folder)) {
|
|
62
|
+
groups.set(folder, []);
|
|
63
|
+
}
|
|
64
|
+
groups.get(folder).push(file);
|
|
65
|
+
}
|
|
66
|
+
return Array.from(groups.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function buildMarkdown(groups, rootDir, outputDir) {
|
|
70
|
+
const lines = [];
|
|
71
|
+
lines.push('# Question Practice Sheet');
|
|
72
|
+
lines.push('');
|
|
73
|
+
lines.push(`Generated from images in \`${rootDir}\` on ${new Date().toISOString()}`);
|
|
74
|
+
lines.push('');
|
|
75
|
+
|
|
76
|
+
for (const [folder, files] of groups) {
|
|
77
|
+
const section = folder === '.' ? 'General' : folder.replace(/[-_]+/g, ' ');
|
|
78
|
+
lines.push(`## ${section}`);
|
|
79
|
+
lines.push('');
|
|
80
|
+
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
const title = makeTitle(file);
|
|
83
|
+
const imagePath = relativeMarkdownPath(outputDir, file);
|
|
84
|
+
lines.push(`### ${title}`);
|
|
85
|
+
lines.push('');
|
|
86
|
+
lines.push(``);
|
|
87
|
+
lines.push('');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
lines.push('---');
|
|
91
|
+
lines.push('');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return lines.join('\n');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function main() {
|
|
98
|
+
const [,, sourceDir, outputFile, patternArg] = process.argv;
|
|
99
|
+
if (!sourceDir || !outputFile) {
|
|
100
|
+
usage();
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const pattern = patternArg || '*.png';
|
|
105
|
+
const matcher = wildcardToRegex(pattern);
|
|
106
|
+
|
|
107
|
+
if (!fs.existsSync(sourceDir) || !fs.statSync(sourceDir).isDirectory()) {
|
|
108
|
+
console.error(`Error: source directory not found: ${sourceDir}`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const imageFiles = findImages(sourceDir, matcher);
|
|
113
|
+
if (imageFiles.length === 0) {
|
|
114
|
+
console.error(`No images matching ${pattern} found under ${sourceDir}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const outputDir = path.dirname(outputFile) || '.';
|
|
119
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
120
|
+
|
|
121
|
+
const groups = groupByFolder(imageFiles, sourceDir);
|
|
122
|
+
const markdown = buildMarkdown(groups, sourceDir, outputDir);
|
|
123
|
+
|
|
124
|
+
fs.writeFileSync(outputFile, markdown, 'utf8');
|
|
125
|
+
console.log(`Generated ${outputFile} with ${imageFiles.length} questions.`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
main();
|