diff --git a/esl/README.md b/esl/README.md new file mode 100644 index 0000000..6336155 --- /dev/null +++ b/esl/README.md @@ -0,0 +1,7 @@ +Sort Class by Level +------------------- + + +A [Pen](https://codepen.io/devkdo/pen/MWNyWKz) by [dvkdo](https://codepen.io/devkdo) on [CodePen](https://codepen.io). + +[License](https://codepen.io/license/pen/MWNyWKz). \ No newline at end of file diff --git a/esl/constants.js b/esl/constants.js new file mode 100644 index 0000000..3dbf855 --- /dev/null +++ b/esl/constants.js @@ -0,0 +1,21 @@ + +const constants = { + Q_NAME: "Nombre y Apellidos - Nomes e Sobrenomes - First and Last Names", + Q_EMAIL: "Email Address", + Q_PHONE: "Número de Teléfono - Número de Telefone - Phone Number", + Q_LEVEL: + "¿Cuál es su nivel de inglés? Qual é o seu nível de inglês? What is your level of English?", + Q_DAY: + "Which night do you prefer? ¿Qué noche prefiere? Qual noite você prefere?", + + A_LEVEL1: + "I don't speak or understand any English. No hablo ni entiendo nada de inglés. Eu não falo nem entendo nenhum inglês.", + A_LEVEL2: + "I speak and understand a little bit of English. Hablo y entiendo un poco de inglés. Falo e entendo um pouco de inglês.", + A_LEVEL3: + "I am comfortable having short conversations in English. Estoy cómodo para tener conversaciones cortas en inglés. Sinto-me confortável em conversas curtas em inglês.", + A_DAYMON: "Monday - lunes - segunda-feira", + A_DAYTUE: "Tuesday - martes - terça-feira" +}; + +export default constants; \ No newline at end of file diff --git a/esl/favicon.ico b/esl/favicon.ico new file mode 100644 index 0000000..f1e7116 Binary files /dev/null and b/esl/favicon.ico differ diff --git a/format_contacts.sh b/esl/format_contacts.sh similarity index 100% rename from format_contacts.sh rename to esl/format_contacts.sh diff --git a/esl/index.html b/esl/index.html new file mode 100644 index 0000000..e9921eb --- /dev/null +++ b/esl/index.html @@ -0,0 +1,45 @@ + + + + + + + Class Sort by Level + + + + + + + +
+
+

Upload XLS or XLSX file of Student Registration

+
+
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/esl/script.js b/esl/script.js new file mode 100644 index 0000000..8e133c0 --- /dev/null +++ b/esl/script.js @@ -0,0 +1,562 @@ +import constants from './constants.js'; + +document.getElementById("btn-sort").addEventListener("click", processXLSClasses); +document.getElementById("btn-emails").addEventListener("click", processXLSEmails); +document.getElementById("btn-students").addEventListener("click", processXLSEmailsAndPhone); +// document.getElementById("btn-dl").addEventListener("click", processAttendance); + +function callProcess(){ + resetOutput(); + const fileInput = document.getElementById("input-file"); + const { mondays, tuesdays } = processXLS(fileInput); + displayLists(mondays.beg, mondays.nonbeg, tuesdays.beg, tuesdays.nonbeg); + console.log("result", mondays, tuesdays); +} + +function processXLS(fileInput) { + const file = fileInput.files[0]; + + if (file) { + const reader = new FileReader(); + reader.onload = function (e) { + const fileData = new Uint8Array(e.target.result); + const workbook = XLSX.read(fileData, { type: "array" }); + const sheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[sheetName]; + const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); + const headers = jsonData[0]; + + const splitData = jsonData.slice(1).map((row) => { + let obj = {}; + headers.forEach((header, index) => { + if ( + header === constants.Q_NAME || + header === constants.Q_EMAIL || + header === constants.Q_DAY || + header === constants.Q_LEVEL + ) { + obj[header] = row[index]; + } + }); + return obj; + }); + + const mon1 = [], mon2 = [], monAny = []; + const tue1 = [], tue2 = [], tueAny = []; + + splitData.forEach((student) => { + const name = student[constants.Q_NAME]; + const email = student[constants.Q_EMAIL]; + const day = student[constants.Q_DAY]; + const level = student[constants.Q_LEVEL]; + const studentInfo = { name, email }; + + switch (day) { + case constants.A_DAYMON: + if (level === constants.A_LEVEL1) mon1.push(studentInfo); + else if (level === constants.A_LEVEL3) mon2.push(studentInfo); + else monAny.push(studentInfo); + break; + case constants.A_DAYTUE: + if (level === constants.A_LEVEL1) tue1.push(studentInfo); + else if (level === constants.A_LEVEL3) tue2.push(studentInfo); + else tueAny.push(studentInfo); + break; + default: + break; + } + }); + + const mondays = evenDistribute(monAny, mon1, mon2); + const tuesdays = evenDistribute(tueAny, tue1, tue2); + + // Return the lists for Mondays and Tuesdays + return { mondays, tuesdays }; + }; + reader.readAsArrayBuffer(file); + } else { + alert("Please upload an XLS/XLSX file."); + } +} + +function processXLSEmails() { + resetOutput(); + const fileInput = document.getElementById("input-file"); + const file = fileInput.files[0]; + + if (file) { + const reader = new FileReader(); + reader.onload = function (e) { + const fileData = new Uint8Array(e.target.result); + const workbook = XLSX.read(fileData, { type: "array" }); + const sheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[sheetName]; + const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); + + const headers = jsonData[0]; + const splitData = jsonData.slice(1).map((row) => { + let obj = {}; + headers.forEach((header, index) => { + if ( + header === constants.Q_NAME || + header === constants.Q_EMAIL || + header === constants.Q_DAY || + header === constants.Q_LEVEL + ) { + obj[header] = row[index]; + } + }); + return obj; + }); + + const mon1 = []; + const mon2 = []; + const monAny = []; + const tue1 = []; + const tue2 = []; + const tueAny = []; + + splitData.forEach((student) => { + const name = student[constants.Q_NAME]; + const email = validateAndFixEmail(student[constants.Q_EMAIL]); + const day = student[constants.Q_DAY]; + const level = student[constants.Q_LEVEL]; + const studentInfo = { name, email }; + + switch (day) { + case constants.A_DAYMON: + if (level === constants.A_LEVEL1) mon1.push(studentInfo); + else if (level === constants.A_LEVEL3) mon2.push(studentInfo); + else monAny.push(studentInfo); + break; + + case constants.A_DAYTUE: + if (level === constants.A_LEVEL1) tue1.push(studentInfo); + else if (level === constants.A_LEVEL3) tue2.push(studentInfo); + else tueAny.push(studentInfo); + break; + + default: + break; + } + }); + const mondays = evenDistribute(monAny, mon1, mon2); + const tuesdays = evenDistribute(tueAny, tue1, tue2); + displayEmails(mondays.beg, mondays.nonbeg, tuesdays.beg, tuesdays.nonbeg); + // createExcel(mondays.beg, mondays.nonbeg, tuesdays.beg, tuesdays.nonbeg); + + // result = (new Array([mondays, tuesdays])); + // console.log("result", result); + }; + reader.readAsArrayBuffer(file); + + } else { + alert("Please upload an XLS/XLSX file."); + } +} +function processXLSEmailsAndPhone() { + resetOutput(); + const fileInput = document.getElementById("input-file"); + const file = fileInput.files[0]; + + if (file) { + const reader = new FileReader(); + reader.onload = function (e) { + const fileData = new Uint8Array(e.target.result); + const workbook = XLSX.read(fileData, { type: "array" }); + const sheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[sheetName]; + const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); + + const headers = jsonData[0]; + const splitData = jsonData.slice(1).map((row) => { + let obj = {}; + headers.forEach((header, index) => { + if ( + header === constants.Q_NAME || + header === constants.Q_EMAIL || + header === constants.Q_PHONE || + header === constants.Q_DAY || + header === constants.Q_LEVEL + ) { + obj[header] = row[index]; + } + }); + return obj; + }); + + const mon1 = []; + const mon2 = []; + const monAny = []; + const tue1 = []; + const tue2 = []; + const tueAny = []; + + splitData.forEach((student) => { + const name = student[constants.Q_NAME]; + const email = validateAndFixEmail(student[constants.Q_EMAIL]); + const phone = validateAndFixPhone( student[constants.Q_PHONE]); + const day = student[constants.Q_DAY]; + const level = student[constants.Q_LEVEL]; + const studentInfo = { name, email, phone }; + + switch (day) { + case constants.A_DAYMON: + if (level === constants.A_LEVEL1) mon1.push(studentInfo); + else if (level === constants.A_LEVEL3) mon2.push(studentInfo); + else monAny.push(studentInfo); + break; + + case constants.A_DAYTUE: + if (level === constants.A_LEVEL1) tue1.push(studentInfo); + else if (level === constants.A_LEVEL3) tue2.push(studentInfo); + else tueAny.push(studentInfo); + break; + + default: + break; + } + }); + const mondays = evenDistribute(monAny, mon1, mon2); + const tuesdays = evenDistribute(tueAny, tue1, tue2); + displayStudentInfo(mondays.beg, mondays.nonbeg, tuesdays.beg, tuesdays.nonbeg); + // createExcel(mondays.beg, mondays.nonbeg, tuesdays.beg, tuesdays.nonbeg); + + // result = (new Array([mondays, tuesdays])); + // console.log("result", result); + }; + reader.readAsArrayBuffer(file); + + } else { + alert("Please upload an XLS/XLSX file."); + } +} +function processXLSClasses() { + resetOutput(); + const fileInput = document.getElementById("input-file"); + const file = fileInput.files[0]; + + if (file) { + const reader = new FileReader(); + reader.onload = function (e) { + const fileData = new Uint8Array(e.target.result); + const workbook = XLSX.read(fileData, { type: "array" }); + const sheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[sheetName]; + const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); + + const headers = jsonData[0]; + const splitData = jsonData.slice(1).map((row) => { + let obj = {}; + headers.forEach((header, index) => { + if ( + header === constants.Q_NAME || + header === constants.Q_EMAIL || + header === constants.Q_DAY || + header === constants.Q_LEVEL + ) { + obj[header] = row[index]; + } + }); + return obj; + }); + + const mon1 = []; + const mon2 = []; + const monAny = []; + const tue1 = []; + const tue2 = []; + const tueAny = []; + + splitData.forEach((student) => { + const name = student[constants.Q_NAME]; + const email = student[constants.Q_EMAIL]; + const day = student[constants.Q_DAY]; + const level = student[constants.Q_LEVEL]; + const studentInfo = { name, email }; + + switch (day) { + case constants.A_DAYMON: + if (level === constants.A_LEVEL1) mon1.push(studentInfo); + else if (level === constants.A_LEVEL3) mon2.push(studentInfo); + else monAny.push(studentInfo); + break; + + case constants.A_DAYTUE: + if (level === constants.A_LEVEL1) tue1.push(studentInfo); + else if (level === constants.A_LEVEL3) tue2.push(studentInfo); + else tueAny.push(studentInfo); + break; + + default: + break; + } + }); + const mondays = evenDistribute(monAny, mon1, mon2); + const tuesdays = evenDistribute(tueAny, tue1, tue2); + displayLists(mondays.beg, mondays.nonbeg, tuesdays.beg, tuesdays.nonbeg); + // createExcel(mondays.beg, mondays.nonbeg, tuesdays.beg, tuesdays.nonbeg); + }; + reader.readAsArrayBuffer(file); + + } else { + alert("Please upload an XLS/XLSX file."); + } +} +function evenDistribute(anyList, l1, l2) { + if (anyList.length == 0) return; + + const list1 = l1.slice(); + const list2 = l2.slice(); + const listAny = anyList.slice(); + const lengthListAny = listAny.length; + + console.log(`Registered: beginner (${list1.length}), intermediate (${listAny.length}), advanced (${list2.length})`); + + let loop = 0; + let done = false; + while (!done || listAny.length > 0) { + // default: case 0: same length + // split listany and add half to each + + // case 1: first list is bigger + // splice listany and add difference to second list + // split remaining listany between both lists + + // case 2: second list is bigger + // splice listany and add difference to first list + // split remaining listany between both lists + + let diff = list1.length - list2.length; + let maxIndex = + Math.abs(diff) < listAny.length ? Math.abs(diff) - 1 : listAny.length - 1; + if (maxIndex == 0) maxIndex = 1; + // console.log("diff", diff); + // console.log("maxIndex", maxIndex); + + if (diff > 0) { + list2.push(...listAny.splice(0, maxIndex)); + } else if (diff < 0) { + list1.push(...listAny.splice(0, maxIndex)); + } else { + list1.push(...listAny.splice(0, listAny.length / 2)); + list2.push(...listAny.splice(0, listAny.length)); + done = true; + } + loop++; + } + + console.log(`Distributed: beginner (${list1.length}), intermediate (${listAny.length}), nonbeginner (${list2.length})`); + + return { beg: list1, nonbeg: list2 }; +} + +function getEmailString(list){ + let emails = ""; + for(let student of list.values()){ + const entry = `"${student.name}"<${student.email}>,`; + emails += entry; + } + return emails; +} +function getStudentInfoString(list){ + let names = ""; + let emails = ""; + let numbers = ""; + let infoString = ""; + for(let student of list.values()){ + const entry = `${student.name}, ${student.email}, ${student.phone}
`; + names += "\n" + student.name; + emails += "\n" + student.email; + numbers += "\n" + student.phone; + infoString += entry; + } + console.log(names); + console.log(emails); + console.log(numbers); + console.log(infoString); + return infoString; +} + +function displayStudentInfo(l1, l2, l3, l4) { + const outputContainer = document.getElementById("output-container"); + outputContainer.classList.add("section-green"); + + const outputHeaders1 = document.getElementById("output-headers1"); + const outputHeaders2 = document.getElementById("output-headers2"); + const outputHeaders3 = document.getElementById("output-headers3"); + const outputHeaders4 = document.getElementById("output-headers4"); + const output1 = document.getElementById("output1"); + const output2 = document.getElementById("output2"); + const output3 = document.getElementById("output3"); + const output4 = document.getElementById("output4"); + outputHeaders1.innerHTML = `

Student Info

Monday Beginner (${l1.length})`; + outputHeaders2.innerHTML = `Monday NonBeginner (${l2.length})`; + outputHeaders3.innerHTML = `Tuesday Beginner (${l3.length})`; + outputHeaders4.innerHTML = `Tuesday NonBeginner (${l4.length})`; + console.log(outputHeaders1.textContent); + output1.innerHTML = getStudentInfoString(l1); + console.log(outputHeaders2.textContent); + output2.innerHTML = getStudentInfoString(l2); + console.log(outputHeaders3.textContent); + output3.innerHTML = getStudentInfoString(l3); + console.log(outputHeaders4.textContent); + output4.innerHTML = getStudentInfoString(l4); +} +function displayEmails(l1, l2, l3, l4) { + const outputContainer = document.getElementById("output-container"); + outputContainer.classList.add("section-green"); + + const outputHeaders1 = document.getElementById("output-headers1"); + const outputHeaders2 = document.getElementById("output-headers2"); + const outputHeaders3 = document.getElementById("output-headers3"); + const outputHeaders4 = document.getElementById("output-headers4"); + const output1 = document.getElementById("output1"); + const output2 = document.getElementById("output2"); + const output3 = document.getElementById("output3"); + const output4 = document.getElementById("output4"); + outputHeaders1.innerHTML = `

Mailing List

Monday Beginner (${l1.length})`; + outputHeaders2.innerHTML = `Monday NonBeginner (${l2.length})`; + outputHeaders3.innerHTML = `Tuesday Beginner (${l3.length})`; + outputHeaders4.innerHTML = `Tuesday NonBeginner (${l4.length})`; + output1.textContent += getEmailString(l1); + output2.textContent += getEmailString(l2); + output3.textContent += getEmailString(l3); + output4.textContent += getEmailString(l4); +} + +function displayLists(l1, l2, l3, l4) { + const outputContainer = document.getElementById("output-container"); + outputContainer.classList.add("section-green"); + + const outputHeaders1 = document.getElementById("output-headers1"); + const outputHeaders2 = document.getElementById("output-headers2"); + const outputHeaders3 = document.getElementById("output-headers3"); + const outputHeaders4 = document.getElementById("output-headers4"); + const output1 = document.getElementById("output1"); + const output2 = document.getElementById("output2"); + const output3 = document.getElementById("output3"); + const output4 = document.getElementById("output4"); + outputHeaders1.innerHTML = `

Monday Beginner (${l1.length})

`; + output1.innerHTML = `
${JSON.stringify(l1,null,2)}
`; + outputHeaders2.innerHTML = `

Monday Non-Beginner (${l2.length})

`; + output2.innerHTML = `
${JSON.stringify(    l2,    null,    2  )}
`; + outputHeaders3.innerHTML = `

Tuesday Beginner (${l3.length})

`; + output3.innerHTML = `
${JSON.stringify(    l3,    null,    2  )}
` + outputHeaders4.innerHTML = `

Tuesday Non-Beginner (${l4.length})

`; + output4.innerHTML = `
${JSON.stringify(l4,null,2)}
`; +} + +function resetOutput(){ + // const outputContainer = document.getElementById("output-container"); + // outputContainer.classList.remove("section-green"); + + const outputHeaders1 = document.getElementById("output-headers1"); + const outputHeaders2 = document.getElementById("output-headers2"); + const outputHeaders3 = document.getElementById("output-headers3"); + const outputHeaders4 = document.getElementById("output-headers4"); + const output1 = document.getElementById("output1"); + const output2 = document.getElementById("output2"); + const output3 = document.getElementById("output3"); + const output4 = document.getElementById("output4"); + outputHeaders1.innerHTML = ``; + outputHeaders2.innerHTML = ``; + outputHeaders3.innerHTML = ``; + outputHeaders4.innerHTML = ``; + output1.textContent = ``; + output2.textContent = ``; + output3.textContent = ``; + output4.textContent = ``; +} + + +function createExcel(data1, data2, data3, data4) { + // Create a new workbook + const workbook = XLSX.utils.book_new(); + + // Create worksheets for each data array + createWorksheet(data1, 'Class1'); + createWorksheet(data2, 'Class2'); + createWorksheet(data3, 'Class3'); + createWorksheet(data4, 'Class4'); + + const wbout = XLSX.write(workbook, { bookType: 'xlsx', type: 'binary' }); + + const blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'attendance.xlsx'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + + // Write the workbook to a file + // XLSX.writeFile(workbook, 'attendance.xlsx'); +} + + function createWorksheet(data, sheetName) { + const worksheetData = data.map(item => [item.name, item.email]); + const worksheet = XLSX.utils.aoa_to_sheet([['Name', 'Email'], ...worksheetData]); + XLSX.utils.book_append_sheet(workbook, worksheet, sheetName); + } + +function s2ab(s) { + const buf = new ArrayBuffer(s.length); + const view = new Uint8Array(buf); // view the buffer as array of 8-bit unsigned int + for (let i = 0; i < s.length; i++) { + view[i] = s.charCodeAt(i) & 0xFF; // Assign each character's code to the buffer + } + return buf; +} +function validateAndFixEmail(email) { + // Regular expression to validate email format + const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + + // Function to fix common domain typos + function fixDomainTypos(email) { + // List of common typos and their corrections + const typoCorrections = { + 'or': 'org', + 'cmo': 'com', + 'con': 'com', + 'co': 'com', + 'cm': 'com' + }; + + return email.replace(/(@[^\s@]+\.)[^@]+$/, function(match, domain) { + return domain + (typoCorrections[match.slice(domain.length)] || match.slice(domain.length)); + }); + } + + // Fix domain typos + let correctedEmail = fixDomainTypos(email); + + // Validate corrected email + if (emailPattern.test(correctedEmail)) { + console.log('Email', email, 'Corrected Email:', correctedEmail); + return correctedEmail; + } else { + console.error('Invalid email format.', email); + return "invalid:" + email; + } +} +function validateAndFixPhone(phone) { + // Remove all non-digit characters + let digits = phone.toString().replace(/\D/g, ''); + + let correctedPhone = ""; + // Format based on length and starting digit + if (digits.length === 10) { + correctedPhone = digits.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3'); + } else if (digits.length === 11 && digits.startsWith('1')) { + correctedPhone = digits.replace(/(\d{1})(\d{3})(\d{3})(\d{4})/, '$1-$2-$3-$4'); + } else if (digits.length > 10) { + correctedPhone = digits.replace(/(\d+)(\d{3})(\d{3})(\d{4})/, '$1-$2-$3-$4'); + } else { + console.error('Invalid phone format.', phone); + return "invalid:" + phone; + } + console.log('Phone', phone, 'Corrected Phone:', correctedPhone); + return correctedPhone; +} diff --git a/esl/styles.css b/esl/styles.css new file mode 100644 index 0000000..99998e9 --- /dev/null +++ b/esl/styles.css @@ -0,0 +1,14 @@ +.container{ + margin: 10px; + padding: 10px; +} +.section-green { + background-color: honeydew; + color: black; +/* border: 2px solid black; */ + margin: 20px; + padding: 20px; +} +.gap-bottom{ + margin-bottom: 20px; +}