From 745a5d3812c6c6ccc1c75801be7a6cc76c76b1c3 Mon Sep 17 00:00:00 2001 From: Gaspard Jankowiak Date: Fri, 5 Jun 2026 09:19:47 +0200 Subject: [PATCH] add applicants.csv --- content.js | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/content.js b/content.js index a39f368..e22537f 100644 --- a/content.js +++ b/content.js @@ -100,6 +100,97 @@ function sanitizeZipPathSegment(value, fallback = "unnamed") { return sanitized === "" ? fallback : sanitized } +const CSV_FIELDS = [ + "full_name_sorting_variant", + "id", + "date_entered", + "email", + "gender", + "academic_title_before_name", + "academic_title_after_name", + "professional_title", + "first_name", + "last_name", + "telephone_number", + "street", + "zip_code", + "city", + "country", + "date_of_birth", + "highest_educational_degree", + "highest_academic_education", + "final_year_studies", + "first_language", + "relevant_links", + "orcid_id", + "google_scholar_profile", + "nationality_arr", + "how_did_you_become_aware_arr", + "gdpr_consent", + "contact_id", + "job_procedure", + "status", + "suitability", + "suitability_hightlight_color", + "related_procedures_count", + "interview_date_time", + "avg_rating", + "comment", + "hearing_videos_link", + "dir_name", + "details_url" +] + +function getApplicantDirectoryName(applicantDetails) { + return `${sanitizeZipPathSegment(applicantDetails.last_name)}_${sanitizeZipPathSegment(applicantDetails.first_name)}` +} + +function getApplicantDetailsUrl(applicantId) { + return `https://personal.uni-graz.at/#/applicants/record/${applicantId}` +} + +function getApplicantCsvValue(applicantDetails, fieldName) { + if (fieldName === "dir_name") { + return getApplicantDirectoryName(applicantDetails) + } + + if (fieldName === "details_url") { + return getApplicantDetailsUrl(applicantDetails.id) + } + + if (fieldName === "job_procedure") { + return applicantDetails.job_procedure?.date_entered ?? "" + } + + if (fieldName === "nationality_arr" || fieldName === "how_did_you_become_aware_arr") { + return Array.isArray(applicantDetails[fieldName]) ? applicantDetails[fieldName].join(" ") : "" + } + + return applicantDetails[fieldName] ?? "" +} + +function escapeCsvValue(value) { + const stringValue = String(value ?? "") + if (!/[",\n\r]/.test(stringValue)) { + return stringValue + } + + return `"${stringValue.replace(/"/g, "\"\"")}"` +} + +function createApplicantsCsv(applicantDetailsList) { + const rows = [ + CSV_FIELDS.join(",") + ] + + applicantDetailsList.forEach((applicantDetails) => { + const values = CSV_FIELDS.map((fieldName) => escapeCsvValue(getApplicantCsvValue(applicantDetails, fieldName))) + rows.push(values.join(",")) + }) + + return rows.join("\n") +} + function downloadBlob(blob, fileName) { const url = URL.createObjectURL(blob) const link = document.createElement("a") @@ -284,6 +375,7 @@ function rip(event) { progressDialog.setStatus("Creating zip archive...") const zip = new JSZip() + const successfulApplicants = [] applicantDetailsResults.forEach((result) => { if (result.status !== "fulfilled") { @@ -291,10 +383,12 @@ function rip(event) { } const applicantDetails = result.value - const dirName = `${sanitizeZipPathSegment(applicantDetails.last_name)}_${sanitizeZipPathSegment(applicantDetails.first_name)}` + const dirName = getApplicantDirectoryName(applicantDetails) const applicantDir = zip.folder(dirName) const files = applicantDetails.application_files ?? [] + successfulApplicants.push(applicantDetails) + files.forEach((afile) => { const ext = afile.file_name.split(".").slice(-1) const filename = `${afile.field_name}.${ext}` @@ -302,6 +396,7 @@ function rip(event) { }) }) + zip.file("applicants.csv", createApplicantsCsv(successfulApplicants)) const zipBlob = await zip.generateAsync({ type: "blob" }) progressDialog.setStatus("Download ready.") downloadBlob(zipBlob, `applications_${aid}.zip`)