feat: import class sessions, attendance, and payments from JSON
Match students by name when national ID or phone is missing, parse Jalali dates, and allow sessions without a professor.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
const { toEnglishDigits } = require('../../utils/jalaliDate');
|
||||
|
||||
const TITLE_PREFIX = /^(آقای|اقای|خانم|آقا)\s+/;
|
||||
const HONORIFIC_MIDDLE = /\s+خ\s+/g;
|
||||
|
||||
const normalizePersonName = (value) => {
|
||||
const text = String(value ?? '')
|
||||
.replace(/ي/g, 'ی')
|
||||
.replace(/ك/g, 'ک')
|
||||
.replace(/[\u200c\u200d]/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
.replace(TITLE_PREFIX, '')
|
||||
.replace(HONORIFIC_MIDDLE, ' ')
|
||||
.replace(/^خ\s+/, '')
|
||||
.trim();
|
||||
return text;
|
||||
};
|
||||
|
||||
const namesMatch = (left, right) => {
|
||||
const a = normalizePersonName(left);
|
||||
const b = normalizePersonName(right);
|
||||
return Boolean(a) && a === b;
|
||||
};
|
||||
|
||||
const normalizePhone = (raw) => {
|
||||
if (raw == null || raw === '') return '';
|
||||
let digits = toEnglishDigits(raw).replace(/\D/g, '');
|
||||
if (digits.startsWith('98') && digits.length === 12) digits = `0${digits.slice(2)}`;
|
||||
if (digits.length === 10 && digits.startsWith('9')) digits = `0${digits}`;
|
||||
return digits;
|
||||
};
|
||||
|
||||
const normalizeNationalId = (raw) => {
|
||||
if (raw == null || raw === '') return '';
|
||||
return toEnglishDigits(raw).replace(/\D/g, '');
|
||||
};
|
||||
|
||||
const mapAttendanceStatus = (raw) => {
|
||||
const value = String(raw ?? '').trim();
|
||||
if (!value) return null;
|
||||
if (value === '*' || value === 'ح' || value === 'حاضر' || /^present$/i.test(value) || /^p$/i.test(value)) {
|
||||
return 'present';
|
||||
}
|
||||
if (value === 'غ' || value === 'غایب' || /^absent$/i.test(value) || /^a$/i.test(value)) {
|
||||
return 'absent';
|
||||
}
|
||||
if (value === 'ت' || value === 'تأخیر' || value === 'تاخیر' || /^late$/i.test(value)) {
|
||||
return 'late';
|
||||
}
|
||||
if (value === 'م' || value === 'موجه' || /^excused$/i.test(value)) {
|
||||
return 'excused';
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const projectSessionDates = (existingIsoDates, totalCount) => {
|
||||
const dates = [...existingIsoDates].filter(Boolean).sort();
|
||||
if (!dates.length || dates.length >= totalCount) return dates.slice(0, totalCount);
|
||||
|
||||
const weekdaySet = new Set(dates.map((iso) => new Date(`${iso}T12:00:00.000Z`).getUTCDay()));
|
||||
const last = new Date(`${dates[dates.length - 1]}T12:00:00.000Z`);
|
||||
const projected = [...dates];
|
||||
const cursor = new Date(last);
|
||||
|
||||
while (projected.length < totalCount) {
|
||||
cursor.setUTCDate(cursor.getUTCDate() + 1);
|
||||
if (weekdaySet.has(cursor.getUTCDay())) {
|
||||
projected.push(cursor.toISOString().slice(0, 10));
|
||||
}
|
||||
}
|
||||
return projected;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
normalizePersonName,
|
||||
namesMatch,
|
||||
normalizePhone,
|
||||
normalizeNationalId,
|
||||
mapAttendanceStatus,
|
||||
projectSessionDates
|
||||
};
|
||||
Reference in New Issue
Block a user