unclean SPARC

This commit is contained in:
STEINNI
2025-08-27 07:03:09 +00:00
commit f308460931
430 changed files with 54426 additions and 0 deletions
@@ -0,0 +1,260 @@
class MailingImportDialog extends EICDialogContent {
actions = [
{
label: 'Cancel',
onclick: this.cancel.bind(this),
severity: 'secondary',
role: 'cancel'
},
{
label: 'Import recipients',
onclick: this.add.bind(this),
severity: 'primary',
role: 'add',
disabled: true
}
]
constructor(...args) {
super(...args)
Object.assign(this, app.helpers.validators)
}
DOMContentLoaded(options) {
this.model = options.model
this.mid = options.mid
let components = ui.eicfy(this.el)
this.fileUpload = new FileUpload(this.find('.import-dialog .browseBtn'), {
uploadUrl: '#',
uploadMethod: '',
submitLabel:'Analyse file',
allowedExtensions: ['xls','xlsx'],
allowedMimes:['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-excel'],
allowDrop: true,
maxFiles: 1,
minFiles: 1,
dndLabel: 'Drop your recipents Excel list here !',
noUpload: true,
});
this.fileUpload.onFileAdded = this.onFileAdded.bind(this)
this.fileUpload.onFileRemoved = this.onFileRemoved.bind(this)
this.fileUpload.onWrongFileType = this.onWrongFileType.bind(this)
this.report = this.find('.import-dialog .report')
this.reportTitle = this.find('.import-dialog .report-title')
this.reportDetails = this.find('.import-dialog .report-details')
this.removeDups = components.find(item => (item.el.classList.contains('remove-dups')))
this.recipientsToImport = {}
}
onFileAdded(fileIdx, file){
let reader = new FileReader()
reader.onload = this.verifyData.bind(this, reader, file)
this.fileUpload.browseBtn.loading = true
reader.readAsArrayBuffer(file)
}
onWrongFileType(file){
ui.show(this.report)
this.fileUpload.browseBtn.loading = false
// because stopping the loading reactivates the button by default (god's will)
if(this.fileUpload.filesList.length==0) this.fileUpload.browseBtn.disabled = false
else this.fileUpload.browseBtn.disabled = true
this.reportTitle.classList.add('error')
this.reportTitle.innerHTML = `Could not import file !`
this.reportDetails.innerHTML = `Wrong file type (${file.type})`
}
verifyData(reader, file){
this.fileUpload.browseBtn.loading = false
// because stopping the loading reactivates the button by default (god's will)
if(this.fileUpload.filesList.length==0) this.fileUpload.browseBtn.disabled = false
else this.fileUpload.browseBtn.disabled = true
let error
let workbook = null
let recipientsArray = null
let recipientsList = null
try{
workbook = XLSX.read(reader.result)
} catch(e) {
console.warn('Trying to parse Excel file:',e)
error = 'Error parsing this as Excel file !'
}
if((!error) && workbook){
let first_sheet = workbook.Sheets[workbook.SheetNames[0]]
try{
recipientsArray = XLSX.utils.sheet_to_json(first_sheet, {
header: 1
})
} catch(e) {
recipientsArray = null
error = 'Error parsing this as Excel sheet !'
}
if(recipientsArray && (!first_sheet['!margins'])) {
recipientsArray = null
error = 'Could not parse this as Excel file !'
}
}
let result = {}
if(recipientsArray){
result = this.getEmailRows(recipientsArray, file)
}
ui.show(this.report)
if(result.recipientsList){
this.reportTitle.classList.remove('error')
this.reportTitle.innerHTML = `Ready to import ${result.recipientsList.length} recipients !`
let headersList = ''
if(result.availableColumns.some(item => item.hint)){
headersList = result.availableColumns.map(item=>item.hint).join(', ')
}
this.reportDetails.innerHTML = `
<ul>
<li>Column used for the emails: <span success>${result.emailColName}</span></li>
<li><span success>${(result.rejectedRows.length>0) ? result.rejectedRows.length : 'No'}</span> ${ (result.rejectedRows.length==1) ? 'row was' : 'rows where' } rejected because column ${result.emailColName} did not contain an email.</li>
<li> ${(this.removeDups.value=='yes') ?
`Removed <span success>${result.nbDups}</span> emails that ${result.nbDups.length>1 ? 'where duplicates' : 'was a duplicate'}`:
`<span danger >I detected ${result.nbDups} duplicate${result.nbDups.length>1 ? 's' : ''} !</span> (which I did not remove)`}
</li>
<li><span ${headersList ? 'success': 'warning'}>${headersList ? 'Potential headers row: ': 'None headers found.'}</span>${headersList}</li>
</ul>
`
this.recipientsToImport = result
this.recipientsToImport.sourceName = file.name
this.recipientsToImport.sourceType = 'Excel'
} else {
this.reportTitle.classList.add('error')
this.reportTitle.innerHTML = error
this.reportDetails.innerHTML = ``
this.recipientsToImport = {}
}
if(this.fileUpload.filesList.length==0) this.actions.find(o => o.role == 'add').button.disabled = true
else this.actions.find(o => o.role == 'add').button.disabled = false
}
getEmailRows(recipientsArray, file){
if((!Array.isArray(recipientsArray)) || (recipientsArray.length<1)){
return({
err: 'The parsed Excel is not an array or has no rows !',
recipientsList:[],
importedData:[],
rejectedRows: [],
nbDups: 0,
emailColIdx:null,
emailColName:null,
availableColumns: null,
})
}
//const mailRFC5322Regex = /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*$/
//Spot the column where there are the most email-addresses
let colsCount = []
for(let row of recipientsArray){
let colIdx = 0
for(let cell of row){
if(typeof(cell) === null) cell = ''
if(typeof(cell)=='string'){
if(typeof(colsCount[colIdx])=='undefined') colsCount[colIdx] = 0
if(this.validators.isValidEmail(cell)) colsCount[colIdx]++
}
colIdx++
}
}
// cleanup empty slots => don't use "in", or "of" !
for(let idx=0; idx<colsCount.length; idx++){ if(typeof(colsCount[idx])!='number') colsCount[idx]=0 }
// find winning column
let emailCol = colsCount.reduce((acc,val, idx)=>(val>colsCount[acc]?idx:acc),0)
// Now that we know where to look, remove rows without email in that col
let cleanRecipients = []
let rejectedRows = []
let firstValidRow = null
let rowIdx = 1
for(let row of recipientsArray){
let cell = row[emailCol]
if(typeof(cell)!='string'){
rejectedRows.push(rowIdx)
} else {
if(this.validators.isValidEmail(cell)){
cleanRecipients.push(row)
if(!firstValidRow) firstValidRow = rowIdx
}
else rejectedRows.push(rowIdx)
}
rowIdx++
}
// The row before the first valid email row might be headers
let availableColumns
if(firstValidRow>1){
let headersRow = recipientsArray[firstValidRow-2] // rowIdx & firstValidRow start at 1 => row before the first valid is -2 in recipientsArray
availableColumns = headersRow.map( (item, idx) => ({value: idx, hint: ((item.length > 150) ? item.slice(0, 20) + '...' : item ), isEmail: (idx==emailCol) }) )
} else {
availableColumns = recipientsArray[firstValidRow].map( (item, idx) => ({value: idx, hint: '', isEmail: (idx==emailCol) }) )
}
let nodups=[]
const uniqueRecipients = cleanRecipients.filter(item=>{ if(!nodups.includes(item[emailCol])) { nodups.push(item[emailCol]); return(true) } return(false) })
let finalRecipients
if(this.removeDups.value=='yes'){
finalRecipients = uniqueRecipients
} else {
finalRecipients = cleanRecipients
}
let recipientsList=[]
for(let recipient of finalRecipients) {
recipientsList.push({
sourceType: "Excel",
sourceName: file.name,
email:recipient[emailCol],
meta:{}, //Empty mappings
})
}
return({
err: false,
recipientsList: recipientsList,
importedData: finalRecipients,
rejectedRows: rejectedRows,
nbDups: cleanRecipients.length - uniqueRecipients.length,
emailColIdx: emailCol,
emailColName: XLSX.utils.encode_col(emailCol),
availableColumns: availableColumns,
})
}
onFileRemoved(fileIdx, file){
if(this.fileUpload.filesList.length==0){ // normally the case as maxFiles=1
this.reportTitle.innerHTML = ''
this.reportDetails.innerHTML = ''
ui.hide(this.report)
this.recipientsToImport = {}
this.actions.find(o => o.role == 'add').button.disabled = true
} else {
this.actions.find(o => o.role == 'add').button.disabled = false
}
}
async add() {
this.actions.find(o => o.role == 'add').button.loading = true
this.recipientsToImport.importDate = (new Date()).toISOString()
await this.model.saveImport(this.mid, {
sourceName: this.recipientsToImport.sourceName,
sourceType: this.recipientsToImport.sourceType,
data: this.recipientsToImport.importedData,
availableColumns: this.recipientsToImport.availableColumns,
})
this.commit(true)
}
onFailed() {
this.abort()
}
}
app.registerClass('MailingImportDialog',MailingImportDialog);