Clean up: remove unused scripts, debug files, and mongodb dependency
This commit is contained in:
parent
47993f76cb
commit
b43b8e6eaf
|
|
@ -1,57 +0,0 @@
|
||||||
const { MongoClient, GridFSBucket } = require('mongodb');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
async function exportGridFS() {
|
|
||||||
const client = new MongoClient('mongodb://localhost:27017/');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await client.connect();
|
|
||||||
const db = client.db('portfolio');
|
|
||||||
|
|
||||||
const buckets = ['images', 'album_art', 'scores', 'pubs'];
|
|
||||||
|
|
||||||
for (const bucketName of buckets) {
|
|
||||||
const bucket = new GridFSBucket(db, { bucketName });
|
|
||||||
const outputDir = path.join(__dirname, 'public', bucketName);
|
|
||||||
|
|
||||||
if (!fs.existsSync(outputDir)) {
|
|
||||||
fs.mkdirSync(outputDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const cursor = bucket.find({});
|
|
||||||
const files = await cursor.toArray();
|
|
||||||
|
|
||||||
console.log(`Exporting ${files.length} files from ${bucketName}...`);
|
|
||||||
|
|
||||||
const promises = files.map(file => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const outputPath = path.join(outputDir, file.filename);
|
|
||||||
const stream = bucket.openDownloadStreamByName(file.filename);
|
|
||||||
const writeStream = fs.createWriteStream(outputPath);
|
|
||||||
|
|
||||||
stream.pipe(writeStream);
|
|
||||||
|
|
||||||
stream.on('end', () => {
|
|
||||||
console.log(` Saved: ${file.filename}`);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
stream.on('error', (err) => {
|
|
||||||
console.error(` Error saving ${file.filename}:`, err.message);
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
console.log(`Finished ${bucketName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Done!');
|
|
||||||
} finally {
|
|
||||||
await client.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exportGridFS();
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
//import { defineNuxtConfig } from 'nuxt3'
|
|
||||||
|
|
||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
modules: ['@nuxtjs/tailwindcss', '@nuxt/image', '@nuxt/icon', '@pinia/nuxt', 'nuxt-headlessui', 'nuxt-swiper', 'nuxt-umami'],
|
modules: ['@nuxtjs/tailwindcss', '@nuxt/image', '@nuxt/icon', '@pinia/nuxt', 'nuxt-headlessui', 'nuxt-swiper', 'nuxt-umami'],
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
"@formkit/themes": "^1.7.2",
|
"@formkit/themes": "^1.7.2",
|
||||||
"@formkit/vue": "^1.7.2",
|
"@formkit/vue": "^1.7.2",
|
||||||
"@pinia/nuxt": "^0.11.3",
|
"@pinia/nuxt": "^0.11.3",
|
||||||
"mongodb": "^7.1.0",
|
|
||||||
"nuxt": "^4.3.1",
|
"nuxt": "^4.3.1",
|
||||||
"nuxt-swiper": "^1.2.2",
|
"nuxt-swiper": "^1.2.2",
|
||||||
"nuxt-umami": "^3.2.1",
|
"nuxt-umami": "^3.2.1",
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
\relax
|
|
||||||
|
|
@ -1,242 +0,0 @@
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const { execSync } = require('child_process')
|
|
||||||
|
|
||||||
const DATA_DIR = path.join(__dirname, '../server/data')
|
|
||||||
const PUBLIC_DIR = path.join(__dirname, '../public')
|
|
||||||
|
|
||||||
function readJson(filename) {
|
|
||||||
const data = fs.readFileSync(path.join(DATA_DIR, filename), 'utf-8')
|
|
||||||
const parsed = JSON.parse(data)
|
|
||||||
|
|
||||||
function cleanDates(obj) {
|
|
||||||
if (Array.isArray(obj)) return obj.map(cleanDates)
|
|
||||||
if (obj && typeof obj === 'object') {
|
|
||||||
if (obj.$date?.$numberLong) return new Date(parseInt(obj.$date.$numberLong)).toISOString()
|
|
||||||
const cleaned = {}
|
|
||||||
for (const [k, v] of Object.entries(obj)) cleaned[k] = cleanDates(v)
|
|
||||||
return cleaned
|
|
||||||
}
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
return cleanDates(parsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatMonth(dateStr) {
|
|
||||||
if (!dateStr) return 'Present'
|
|
||||||
const date = new Date(dateStr)
|
|
||||||
if (isNaN(date)) return dateStr
|
|
||||||
return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' })
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatYear(dateStr) {
|
|
||||||
if (!dateStr) return ''
|
|
||||||
const date = new Date(dateStr)
|
|
||||||
if (isNaN(date)) return dateStr
|
|
||||||
return date.getFullYear()
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatShortDate(dateStr) {
|
|
||||||
if (!dateStr) return ''
|
|
||||||
const date = new Date(dateStr)
|
|
||||||
if (isNaN(date)) return dateStr
|
|
||||||
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeLatex(str) {
|
|
||||||
if (!str) return ''
|
|
||||||
str = str.replace(/[\u0300-\u036f]/g, '')
|
|
||||||
return str.replace(/[&%$#_{}]/g, '\\$&').replace(/~/g, '\\textasciitilde{}').replace(/\^/g, '\\textasciicircum{}').replace(/ä/g, '{\\"a}').replace(/ö/g, '{\\"o}').replace(/ü/g, '{\\"u}').replace(/Ä/g, '{\\"A}').replace(/Ö/g, '{\\"O}').replace(/Ü/g, '{\\"U}').replace(/á/g, "\\'a").replace(/é/g, "\\'e").replace(/í/g, "\\'i").replace(/ó/g, "\\'o").replace(/ú/g, "\\'u").replace(/à/g, "\\`a").replace(/è/g, "\\`e").replace(/ì/g, "\\`i").replace(/ò/g, "\\`o").replace(/ù/g, "\\`u").replace(/ã/g, '{\\~a}').replace(/ñ/g, '{\\~n}').replace(/–/g, '--').replace(/—/g, '---').replace(/ç/g, '\\c c')
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateCvLatex(resume, talks) {
|
|
||||||
const basics = resume.basics || {}
|
|
||||||
|
|
||||||
const talksByYear = {}
|
|
||||||
for (const talk of talks || []) {
|
|
||||||
if (!talk.date) continue
|
|
||||||
const year = new Date(talk.date).getFullYear()
|
|
||||||
if (!talksByYear[year]) talksByYear[year] = []
|
|
||||||
talksByYear[year].push(talk)
|
|
||||||
}
|
|
||||||
const sortedYears = Object.keys(talksByYear).sort((a, b) => b - a)
|
|
||||||
|
|
||||||
let latex = '\\documentclass[11pt]{article}\n'
|
|
||||||
latex += '\\usepackage[utf8]{inputenc}\n'
|
|
||||||
latex += '\\usepackage[T1]{fontenc}\n'
|
|
||||||
latex += '\\usepackage{geometry}\n'
|
|
||||||
latex += '\\geometry{letterpaper, top=1in, bottom=1in, left=1in, right=1in}\n'
|
|
||||||
latex += '\\usepackage{enumitem}\n'
|
|
||||||
latex += '\\usepackage{titlesec}\n'
|
|
||||||
latex += '\\pagestyle{empty}\n'
|
|
||||||
latex += '\\titleformat{\\section}{\\bfseries\\Large}{\\thesection}{1em}{}\n'
|
|
||||||
latex += '\\titleformat{\\subsection}{\\bfseries\\large}{\\thesubsection}{1em}{}\n'
|
|
||||||
latex += '\\titlespacing{\\section}{0pt}{12pt}{6pt}\n'
|
|
||||||
latex += '\\titlespacing{\\subsection}{0pt}{8pt}{4pt}\n'
|
|
||||||
latex += '\\begin{document}\n'
|
|
||||||
|
|
||||||
// Header
|
|
||||||
latex += '\\begin{center}\n'
|
|
||||||
latex += '{\\LARGE\\bfseries ' + escapeLatex(basics.name || '') + '}\\\\[4pt]\n'
|
|
||||||
latex += '{\\large Curriculum Vitae}\\\\[8pt]\n'
|
|
||||||
latex += escapeLatex(basics.email || '') + ' \\quad ' + escapeLatex(basics.phone || '') + ' \\quad ' + escapeLatex(basics.website || '') + '\n'
|
|
||||||
latex += '\\end{center}\n'
|
|
||||||
latex += '\\hrulefill\\\\[12pt]\n'
|
|
||||||
|
|
||||||
// Education
|
|
||||||
latex += '\\section{Education}\n'
|
|
||||||
for (const edu of resume.education || []) {
|
|
||||||
latex += '{\\bfseries ' + escapeLatex(edu.studyType) + ' in ' + escapeLatex(edu.area) + '}, ' + escapeLatex(edu.institution) + ', ' + formatYear(edu.endDate) + '\\\\[4pt]\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Teaching
|
|
||||||
latex += '\\section{Teaching}\n'
|
|
||||||
for (const teach of resume.teaching || []) {
|
|
||||||
latex += '{\\bfseries ' + escapeLatex(teach.company) + '}, \\textit{' + escapeLatex(teach.position) + '}\\\\[2pt]\n'
|
|
||||||
latex += escapeLatex(formatMonth(teach.startDate)) + ' -- ' + escapeLatex(formatMonth(teach.endDate)) + '\\\\[8pt]\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lectures
|
|
||||||
latex += '\\section{Lectures}\n'
|
|
||||||
for (const year of sortedYears) {
|
|
||||||
latex += '\\subsection{' + year + '}\n'
|
|
||||||
for (const talk of talksByYear[year]) {
|
|
||||||
latex += '{\\bfseries ' + escapeLatex(talk.location) + '}\\\\\n'
|
|
||||||
const titles = Array.isArray(talk.title) ? talk.title : [talk.title]
|
|
||||||
for (const t of titles) latex += '\\hspace{8pt}\\textit{' + escapeLatex(t) + '}\\\\\n'
|
|
||||||
}
|
|
||||||
latex += '\\\\[4pt]\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relevant Work
|
|
||||||
latex += '\\section{Relevant Work}\n'
|
|
||||||
for (const w of resume.work || []) {
|
|
||||||
latex += '{\\bfseries ' + escapeLatex(w.company) + '}, \\textit{' + escapeLatex(w.position) + '}\\\\[2pt]\n'
|
|
||||||
latex += escapeLatex(formatMonth(w.startDate)) + ' -- ' + escapeLatex(formatMonth(w.endDate)) + '\\\\[8pt]\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skills
|
|
||||||
latex += '\\section{Skills}\n'
|
|
||||||
latex += (resume.skills?.[0]?.keywords?.map(s => escapeLatex(s)).join(', ') || '') + '\\\\[8pt]\n'
|
|
||||||
|
|
||||||
// Languages
|
|
||||||
latex += '\\section{Languages}\n'
|
|
||||||
latex += (resume.languages || []).map(l => escapeLatex(l.language) + ' (' + escapeLatex(l.fluency) + ')').join(', ') + '\\\\[8pt]\n'
|
|
||||||
|
|
||||||
// Recordings
|
|
||||||
latex += '\\section{Recordings}\n'
|
|
||||||
latex += '\\subsection{Solo Albums}\n'
|
|
||||||
for (const rel of resume.solo_releases || []) {
|
|
||||||
latex += '{\\bfseries ' + escapeLatex(rel.title) + '}. ' + escapeLatex(rel.publisher) + '. ' + escapeLatex(rel.media_type) + '. ' + escapeLatex(rel.date) + '.\\\\[4pt]\n'
|
|
||||||
}
|
|
||||||
latex += '\\subsection{Compilation Albums}\n'
|
|
||||||
for (const rel of resume.compilation_releases || []) {
|
|
||||||
latex += '{\\bfseries ' + escapeLatex(rel.title) + '}. ' + escapeLatex(rel.publisher) + '. ' + escapeLatex(rel.media_type) + '. ' + escapeLatex(rel.date) + '. \\textit{featuring ' + escapeLatex(rel.work) + '}.\\\\[4pt]\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Residencies
|
|
||||||
latex += '\\section{Residencies and Awards}\n'
|
|
||||||
for (const res of resume.residencies || []) {
|
|
||||||
latex += '{\\bfseries ' + escapeLatex(res.org) + '}, ' + escapeLatex(res.date) + '\\\\[4pt]\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
// References
|
|
||||||
latex += '\\section{References}\n'
|
|
||||||
for (const ref of resume.references || []) {
|
|
||||||
latex += '{\\bfseries ' + escapeLatex(ref.name) + '}\\\\[2pt]\n'
|
|
||||||
latex += escapeLatex(ref.position) + '\\\\[2pt]\n'
|
|
||||||
latex += escapeLatex(ref.email) + '\\\\[8pt]\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
latex += '\\end{document}\n'
|
|
||||||
return latex
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateWorksListLatex(resume, works, events) {
|
|
||||||
const basics = resume.basics || {}
|
|
||||||
|
|
||||||
const worksByYear = {}
|
|
||||||
for (const work of works || []) {
|
|
||||||
const year = work.date ? new Date(work.date).getFullYear() : 'Unknown'
|
|
||||||
if (!worksByYear[year]) worksByYear[year] = []
|
|
||||||
const workEvents = (events || []).filter(e => e.program && e.program.some(p => p.work?.toLowerCase().includes(work.title.toLowerCase())))
|
|
||||||
worksByYear[year].push({ ...work, events: workEvents })
|
|
||||||
}
|
|
||||||
const sortedYears = Object.keys(worksByYear).sort((a, b) => b - a)
|
|
||||||
|
|
||||||
let latex = '\\documentclass[11pt]{article}\n'
|
|
||||||
latex += '\\usepackage[utf8]{inputenc}\n'
|
|
||||||
latex += '\\usepackage[T1]{fontenc}\n'
|
|
||||||
latex += '\\usepackage{geometry}\n'
|
|
||||||
latex += '\\geometry{letterpaper, top=1in, bottom=1in, left=1in, right=1in}\n'
|
|
||||||
latex += '\\usepackage{enumitem}\n'
|
|
||||||
latex += '\\usepackage{titlesec}\n'
|
|
||||||
latex += '\\pagestyle{empty}\n'
|
|
||||||
latex += '\\titleformat{\\section}{\\bfseries\\Large}{\\thesection}{1em}{}\n'
|
|
||||||
latex += '\\titlespacing{\\section}{0pt}{12pt}{6pt}\n'
|
|
||||||
latex += '\\begin{document}\n'
|
|
||||||
|
|
||||||
// Header
|
|
||||||
latex += '\\begin{center}\n'
|
|
||||||
latex += '{\\LARGE\\bfseries ' + escapeLatex(basics.name || '') + '}\\\\[4pt]\n'
|
|
||||||
latex += '{\\large Works List with Presentation History}\\\\[8pt]\n'
|
|
||||||
latex += escapeLatex(basics.email || '') + ' \\quad ' + escapeLatex(basics.phone || '') + ' \\quad ' + escapeLatex(basics.website || '') + '\n'
|
|
||||||
latex += '\\end{center}\n'
|
|
||||||
latex += '\\hrulefill\\\\[12pt]\n'
|
|
||||||
|
|
||||||
latex += 'A chronological performance / exhibition history, scores, and recordings are available at\\\\\n'
|
|
||||||
latex += 'www.unboundedpress.org.\\\\\n'
|
|
||||||
latex += 'All scores are also published or forthcoming through Frog Peak at\\\\\n'
|
|
||||||
latex += 'www.frogpeak.org/fpartists/fpwinter.html.\\\\[12pt]\n'
|
|
||||||
latex += '\\hrulefill\\\\[12pt]\n'
|
|
||||||
|
|
||||||
for (const year of sortedYears) {
|
|
||||||
latex += '\\section{' + year + '}\n'
|
|
||||||
for (const work of worksByYear[year]) {
|
|
||||||
latex += '{\\bfseries\\textit{' + escapeLatex(work.title) + '}}\\\\[-4pt]\n'
|
|
||||||
if (work.instrument_tags) latex += '\\hspace{8pt}' + escapeLatex(work.instrument_tags.join(', ')) + '\\\\[-4pt]\n'
|
|
||||||
for (const event of work.events || []) {
|
|
||||||
const venue = event.venue || {}
|
|
||||||
latex += '\\hspace{8pt}' + escapeLatex(venue.name) + '; ' + escapeLatex(venue.city) + ', ' + escapeLatex(venue.state) + ' -- ' + escapeLatex(formatShortDate(event.start_date)) + '\\\\\n'
|
|
||||||
}
|
|
||||||
latex += '\\\\[4pt]\n'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
latex += '\\end{document}\n'
|
|
||||||
return latex
|
|
||||||
}
|
|
||||||
|
|
||||||
function compileLatex(latexContent, outputPath) {
|
|
||||||
const tempDir = path.join(__dirname, 'temp')
|
|
||||||
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true })
|
|
||||||
|
|
||||||
const texPath = path.join(tempDir, 'temp.tex')
|
|
||||||
fs.writeFileSync(texPath, latexContent)
|
|
||||||
|
|
||||||
try {
|
|
||||||
execSync('cd ' + tempDir + ' && pdflatex temp.tex', { stdio: 'pipe' })
|
|
||||||
const pdfPath = path.join(tempDir, 'temp.pdf')
|
|
||||||
if (fs.existsSync(pdfPath)) {
|
|
||||||
fs.copyFileSync(pdfPath, outputPath)
|
|
||||||
console.log('Generated:', outputPath)
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
fs.writeFileSync(path.join(PUBLIC_DIR, 'debug.tex'), latexContent)
|
|
||||||
console.error('Error generating PDF')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resume = readJson('resume.json')[0]
|
|
||||||
const talks = readJson('talks.json')
|
|
||||||
const works = readJson('works.json')
|
|
||||||
const events = readJson('events.json')
|
|
||||||
|
|
||||||
console.log('Generating CV...')
|
|
||||||
compileLatex(generateCvLatex(resume, talks), path.join(PUBLIC_DIR, 'cv.pdf'))
|
|
||||||
|
|
||||||
console.log('Generating Works List...')
|
|
||||||
compileLatex(generateWorksListLatex(resume, works, events), path.join(PUBLIC_DIR, 'works_list.pdf'))
|
|
||||||
|
|
||||||
console.log('Done!')
|
|
||||||
Loading…
Reference in a new issue