@obe-loms/coms-parser 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle.js +32 -0
- package/helper/matchTarget.d.ts +4 -0
- package/main.d.ts +22 -0
- package/package.json +3 -2
- package/parser/assessmentdata.d.ts +4 -0
- package/parser/classList.d.ts +6 -0
- package/parser/coaep.d.ts +4 -0
- package/parser/courseOffering.d.ts +2 -0
- package/parser/curriculum.d.ts +2 -0
- package/parser/xls.d.ts +1 -0
- package/relay/uploadAssessmentData.d.ts +1 -0
- package/relay/uploadCOAEP.d.ts +1 -0
- package/relay/uploadClassList.d.ts +1 -0
- package/relay/uploadCourseOffering.d.ts +1 -0
- package/relay/uploadCurriculum.d.ts +1 -0
- package/relay/uploadEnrolledStudent.d.ts +1 -0
- package/types/assessmentdata.d.ts +25 -0
- package/types/classList.d.ts +6 -0
- package/types/coaep.d.ts +19 -0
- package/types/courseOffering.d.ts +12 -0
- package/types/curriculum.d.ts +12 -0
- package/helper/matchTarget.js +0 -9
- package/index.d.ts +0 -1
- package/index.js +0 -10
- package/main.js +0 -50
- package/parser/assessmentdata.js +0 -149
- package/parser/classList.js +0 -36
- package/parser/coaep.js +0 -73
- package/parser/courseOffering.js +0 -84
- package/parser/curriculum.js +0 -83
- package/parser/xls.js +0 -42
- package/relay/uploadAssessmentData.js +0 -24
- package/relay/uploadCOAEP.js +0 -24
- package/relay/uploadClassList.js +0 -25
- package/relay/uploadCourseOffering.js +0 -26
- package/relay/uploadCurriculum.js +0 -26
- package/relay/uploadEnrolledStudent.js +0 -20
- package/types/assessmentdata.js +0 -1
- package/types/classList.js +0 -1
- package/types/coaep.js +0 -1
- package/types/courseOffering.js +0 -1
- package/types/curriculum.js +0 -1
package/bundle.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import _0x3a8d60 from 'papaparse';
|
|
2
|
+
import * as _0x172711 from 'xlsx';
|
|
3
|
+
|
|
4
|
+
function parseCurriculum(_0xa25ca6){let _0x69443b='',_0x10bf1b='',_0x3c87c1=0x0,_0x2502ce=null;const _0x977af2=[];return _0x3a8d60['parse'](_0xa25ca6,{'skipEmptyLines':!![],'complete':_0x5c5a6a=>{_0x5c5a6a['data']['forEach'](_0x47b204=>{const _0xeb00bf=_0x47b204['map'](_0x445591=>(_0x445591??'')['toString']()['trim']()),_0x1d7a6a=_0xeb00bf[0x0]??'';if(_0x1d7a6a['includes']('Rev#')){const _0x4aaa05=_0x1d7a6a['match'](/([A-Z0-9]+)\s*-\s*.*:\s*(.*?)\s+Rev#\s*(\d+)/i);_0x4aaa05&&(_0x69443b=_0x4aaa05[0x1]?.['trim']()??'',_0x10bf1b=_0x4aaa05[0x2]?.['trim']()??'',_0x3c87c1=parseInt(_0x4aaa05[0x3]??'0',0xa));return;}if(/FIRST YEAR/i['test'](_0x1d7a6a)){_0x2502ce=0x1;return;}if(/SECOND YEAR/i['test'](_0x1d7a6a)){_0x2502ce=0x2;return;}if(/THIRD YEAR/i['test'](_0x1d7a6a)){_0x2502ce=0x3;return;}if(/FOURTH YEAR/i['test'](_0x1d7a6a)){_0x2502ce=0x4;return;}if(/FIFTH YEAR/i['test'](_0x1d7a6a)){_0x2502ce=0x5;return;}if(/First Semester/i['test'](_0x1d7a6a)||/Second Semester/i['test'](_0x1d7a6a)||/Summer/i['test'](_0x1d7a6a))return;const _0x5274f0=[{'sem':0x1,'offset':0x0},{'sem':0x2,'offset':0x6},{'sem':0x3,'offset':0xc}];_0x5274f0['forEach'](({sem:_0x22acf3,offset:_0x201941})=>{const _0xbb79c8=_0xeb00bf[_0x201941]??'',_0x3e4049=_0xeb00bf[_0x201941+0x1]??'',_0x12fceb=_0xeb00bf[_0x201941+0x2]??'',_0x1d5664=_0xeb00bf[_0x201941+0x3]??'',_0x20e05a=_0xeb00bf[_0x201941+0x4]??'';_0xbb79c8&&_0x2502ce&&_0x977af2['push']({'curr_id':_0x69443b,'program_name':_0x10bf1b,'revision_no':_0x3c87c1,'year_level':_0x2502ce,'sem':_0x22acf3,'course_id':_0xbb79c8,'course_desc':_0x3e4049,'total_units':_0x12fceb,'lec_unit':_0x1d5664,'lab_unit':_0x20e05a});});});}}),_0x977af2;}
|
|
5
|
+
|
|
6
|
+
function convertToCSVFile(_0x241f6f){return new Promise((_0x36a424,_0x17af4c)=>{const _0x419f2d=new FileReader();_0x419f2d['onload']=_0x2090e7=>{const _0x1dda48=_0x2090e7['target']?.['result'];if(!_0x1dda48){_0x17af4c(new Error('Failed\x20to\x20read\x20file'));return;}try{const _0x31afbf=_0x172711['read'](_0x1dda48,{'type':'array'}),_0x4e3068=_0x31afbf['SheetNames'][0x0];if(!_0x4e3068){_0x17af4c(new Error('No\x20sheets\x20found\x20in\x20workbook'));return;}const _0x3d3e7f=_0x31afbf['Sheets'][_0x4e3068];if(!_0x3d3e7f){_0x17af4c(new Error('No\x20sheets\x20found\x20in\x20worksheet'));return;}const _0x430cbe=_0x172711['utils']['sheet_to_csv'](_0x3d3e7f),_0x6337df=_0x241f6f['name']['replace'](/\.[^/.]+$/,'.csv'),_0x12cba0=new File([_0x430cbe],_0x6337df,{'type':'text/csv','lastModified':Date['now']()});_0x36a424(_0x12cba0);}catch(_0x47ec36){_0x17af4c(_0x47ec36);}},_0x419f2d['onerror']=()=>{_0x17af4c(new Error('File\x20reading\x20failed'));},_0x419f2d['readAsArrayBuffer'](_0x241f6f);});}
|
|
7
|
+
|
|
8
|
+
async function uploadCurriculum(_0x1c3f29,_0x430deb){try{const _0x54583b=await convertToCSVFile(_0x430deb),_0x23a011=await _0x54583b['text'](),_0x1aae4b=parseCurriculum(_0x23a011),_0x53697c=_0x3a8d60['unparse'](_0x1aae4b),_0x300d6a=new File([_0x53697c],_0x54583b['name'],{'type':'text/csv'}),_0x1567d1=new FormData();_0x1567d1['append']('csvFile',_0x300d6a);const _0xb32da3=await fetch(_0x1c3f29+'/curr-courses/upload',{'method':'POST','body':_0x1567d1});if(!_0xb32da3['ok']){const _0x145ed8=await _0xb32da3['json']();throw _0x145ed8;}return _0xb32da3['json']();}catch(_0x19fc76){throw _0x19fc76;}}
|
|
9
|
+
|
|
10
|
+
function parseCourseOffering(_0x14e887){let _0xc0fbc3='',_0x3ee946='';const _0x1dfb75=[],_0x181db5=_0x3a8d60['parse'](_0x14e887,{'header':![],'skipEmptyLines':!![],'delimiter':','}),_0x3f7833=_0x181db5['data'];for(let _0xa5b466=0x0;_0xa5b466<Math['min'](0x5,_0x3f7833['length']);_0xa5b466++){const _0x2937bc=_0x3f7833[_0xa5b466];if(_0x2937bc&&_0x2937bc['length']>0x0){const _0x3f53b6=_0x2937bc[0x0]?.['toString']()||'',_0xa03256=_0x3f53b6['match'](/(First|Second)\s+Sem\s+S\/Y\s+(\d{4}-\d{4})/i);if(_0xa03256){_0xc0fbc3=_0xa03256[0x1]?.['toLowerCase']()==='first'?'1':'2';if(_0xa03256[0x2]){const _0x58d5e7=_0xa03256[0x2]['match'](/(\d{2})(\d{2})-(\d{2})(\d{2})/);_0x58d5e7&&_0x58d5e7[0x2]&&_0x58d5e7[0x4]&&(_0x3ee946=_0x58d5e7[0x2]+_0x58d5e7[0x4]);}}}}let _0xd1d713=-1;for(let _0x49f950=0x0;_0x49f950<_0x3f7833['length'];_0x49f950++){const _0x1878a4=_0x3f7833[_0x49f950];if(_0x1878a4&&_0x1878a4['some'](_0x16dda0=>_0x16dda0?.['toString']()['toUpperCase']()['includes']('CODE'))){_0xd1d713=_0x49f950;break;}}if(_0xd1d713===-1)throw new Error('Could\x20not\x20find\x20header\x20row\x20in\x20CSV\x20data');for(let _0xfbe871=_0xd1d713+0x1;_0xfbe871<_0x3f7833['length'];_0xfbe871++){const _0x482efa=_0x3f7833[_0xfbe871];if(!_0x482efa||_0x482efa['length']<0x8)continue;const _0x5d51e4=_0x482efa[0x0]?.['toString']()['trim']()||'',_0x586f21=_0x482efa[0x1]?.['toString']()['trim']()||'',_0xd8d0da=_0x482efa[0x2]?.['toString']()['trim']()||'',_0x213ee4=_0x482efa[0x3]?.['toString']()['trim']()||'',_0x6c25ef=_0x482efa[0x4]?.['toString']()['trim']()||'',_0x159a87=_0x482efa[0x5]?.['toString']()['trim']()||'',_0x59b728=_0x482efa[0x6]?.['toString']()['trim']()||'',_0xc81b8=_0x482efa[0x7]?.['toString']()['trim']()||'';if(!_0x5d51e4)continue;let _0x1dbf13=0x0;const _0x3c8a06=_0x213ee4['match'](/(\d+)/);_0x3c8a06&&_0x3c8a06[0x1]&&(_0x1dbf13=parseInt(_0x3c8a06[0x1],0xa));const _0x375eb1={'sem':_0xc0fbc3,'school_year':_0x3ee946,'code':_0x5d51e4,'course_no':_0x586f21,'course_desc':_0xd8d0da,'unit':_0x1dbf13,'time':_0x6c25ef,'days':_0x159a87,'faculty':_0x59b728,'room':_0xc81b8};_0x1dfb75['push'](_0x375eb1);}return _0x1dfb75;}
|
|
11
|
+
|
|
12
|
+
async function uploadCourseOffering(_0x2f0be0,_0x30bbe2){try{const _0x35e2b8=await convertToCSVFile(_0x30bbe2),_0x3448b6=await _0x35e2b8['text'](),_0x2d0f3b=parseCourseOffering(_0x3448b6),_0x5dcd3b=_0x3a8d60['unparse'](_0x2d0f3b),_0x36db9f=new File([_0x5dcd3b],_0x35e2b8['name'],{'type':'text/csv'}),_0x203ec1=new FormData();_0x203ec1['append']('csvFile',_0x36db9f);const _0x41bc8d=await fetch(_0x2f0be0+'/course-offerings/upload',{'method':'POST','body':_0x203ec1});if(!_0x41bc8d['ok']){const _0x6040d7=await _0x41bc8d['json']();throw _0x6040d7;}return _0x41bc8d['json']();}catch(_0x310b23){throw _0x310b23;}}
|
|
13
|
+
|
|
14
|
+
function performaceTarget(_0x382505){if(!_0x382505)return {'performance_target':null,'passing_score':null};const _0x5aa7f0=_0x382505['match'](/\d+/g);return {'performance_target':_0x5aa7f0?.[0x0]?parseInt(_0x5aa7f0[0x0],0xa):null,'passing_score':_0x5aa7f0?.[0x1]?parseInt(_0x5aa7f0[0x1],0xa):null};}
|
|
15
|
+
|
|
16
|
+
function parseCOAEP(_0x1cf2da){const _0x5e0766=_0x3a8d60['parse'](_0x1cf2da,{'skipEmptyLines':![]})['data'],_0x7b5b2={'COAEP':{'faculty':null,'course':null,'sy':null,'semester':null,'co':[]}};_0x5e0766['forEach'](_0x2a86b1=>{_0x2a86b1[0x1]?.['includes']('Name\x20of\x20Faculty:')&&(_0x7b5b2['COAEP']['faculty']=_0x2a86b1[0x2]?.['trim']()||null);if(_0x2a86b1['includes']('School\x20Year')){const _0x2d5dfa=_0x2a86b1['indexOf']('School\x20Year');_0x7b5b2['COAEP']['sy']=_0x2a86b1[_0x2d5dfa+0x1]?.['trim']()||null;}if(_0x2a86b1[0x1]?.['includes']('Course:')){const _0x328e12=_0x2a86b1[0x2]?.['trim']()||'';_0x7b5b2['COAEP']['course']=_0x328e12||null;}if(_0x2a86b1['includes']('Semester')){const _0x2fe795=_0x2a86b1['indexOf']('Semester'),_0x25b7ea=_0x2a86b1[_0x2fe795+0x1]?.['trim']()||'',_0x43e82e=_0x25b7ea['match'](/\d+/)?.[0x0];_0x7b5b2['COAEP']['semester']=_0x43e82e?parseInt(_0x43e82e,0xa):null;}});let _0x2fa035=null;return _0x5e0766['forEach'](_0x376f10=>{const _0x2d8797=_0x376f10[0x0]?.['trim']()||'',_0x55109e=_0x376f10[0x1]?.['trim']()||'',_0x28e2c3=_0x376f10[0x3]?.['trim']()||'';if(_0x2d8797&&/^\d+$/['test'](_0x2d8797)){_0x2fa035&&_0x7b5b2['COAEP']['co']['push'](_0x2fa035);const _0x7ae440=_0x55109e,_0x5bec58=_0x7ae440['trim']()['split'](/\s+/)[0x0]??'';_0x2fa035={'statement':_0x7ae440,'verb':_0x5bec58,'ilo':[]};}if(_0x2fa035&&_0x28e2c3){const _0x2abec5=_0x28e2c3['replace'](/^ILO\d+\s*/,'')['replace'](/\s+/g,'\x20')['trim'](),_0x42b27e=_0x376f10[0x4]?.['replace'](/\s+/g,'\x20')['trim']()||'',_0x113104=_0x376f10[0x5]?.['replace'](/\s+/g,'\x20')['trim']()||'',{performance_target:_0x56ba13,passing_score:_0x18de41}=performaceTarget(_0x113104);_0x2fa035['ilo']['push']({'statement':_0x2abec5,'verb':_0x2abec5['split'](/\s+/)[0x0]??'','assessment_tool':_0x42b27e,'performance_target':_0x56ba13,'passing_score':_0x18de41});}}),_0x2fa035&&_0x7b5b2['COAEP']['co']['push'](_0x2fa035),_0x7b5b2;}
|
|
17
|
+
|
|
18
|
+
async function uploadCOAEP(_0x4128ee,_0x1737dc,_0x16ab1e){try{const _0x1510c5=await convertToCSVFile(_0x1737dc),_0xcf8cca=await _0x1510c5['text'](),_0x2e89d4=parseCOAEP(_0xcf8cca),_0x47d526=await fetch(_0x4128ee+'/coaeps/upload?course_id='+_0x16ab1e,{'method':'POST','headers':{'Content-Type':'application/json'},'body':JSON['stringify'](_0x2e89d4)});if(!_0x47d526['ok']){const _0x20e0d9=await _0x47d526['json']();throw _0x20e0d9;}return _0x47d526['json']();}catch(_0x1493a9){throw _0x1493a9;}}
|
|
19
|
+
|
|
20
|
+
async function uploadEnrolledStudent(_0x307e5c,_0x171df9){try{const _0xfcb6de=await convertToCSVFile(_0x171df9),_0x185c4d=new FormData();_0x185c4d['append']('csvFile',_0xfcb6de);const _0x402a74=await fetch(_0x307e5c+'/enrolled-students/upload',{'method':'POST','body':_0x185c4d});if(!_0x402a74['ok']){const _0x319840=await _0x402a74['json']();throw _0x319840;}return _0x402a74['json']();}catch(_0x30c3ec){throw _0x30c3ec;}}
|
|
21
|
+
|
|
22
|
+
function parseClassList(_0x2041cf){const _0x57a078=_0x3a8d60['parse'](_0x2041cf,{'skipEmptyLines':!![]})['data'];let _0x214960='',_0x42634f='',_0x22b8a4='';const _0x38a2cf=[];return _0x57a078['forEach'](_0x56d98a=>{const _0x56912e=_0x56d98a['map'](_0x3e5af5=>(_0x3e5af5??'')['toString']()['trim']()),_0x2cd7ba=_0x56912e[0x0]??'';if(_0x2cd7ba['startsWith']('Course\x20No:')){const _0x420f7b=_0x2cd7ba['replace']('Course\x20No:','')['trim'](),_0xcff7d9=_0x420f7b['split'](/\s+/);_0x22b8a4=_0xcff7d9[0x0]??'';const [_0x414d25,_0x10b5a6]=(_0xcff7d9[0x1]??'')['split']('-');_0x42634f=_0x414d25??'';const _0x362a24=(_0x10b5a6??'')['match'](/[a-z]$/);_0x214960=_0x362a24?_0x362a24[0x0]:'';}if(/^\d+$/['test'](_0x2cd7ba)){const _0x4ac3e3=_0x56912e[0x2]??'';_0x38a2cf['push']({'subj_code':_0x22b8a4,'student_no':_0x4ac3e3,'course_id':_0x42634f,'section':_0x214960});}}),{'enrolledCourses':_0x38a2cf};}
|
|
23
|
+
|
|
24
|
+
async function uploadClassList(_0x1582a3,_0x5340e6,_0xaaa19,_0x2bc303){try{const _0x5e059c=await convertToCSVFile(_0x5340e6),_0x3f457d=await _0x5e059c['text'](),_0x345b94=parseClassList(_0x3f457d),_0x4be92f='subj_code='+_0xaaa19+'&period_id='+_0x2bc303,_0xe3ab56=await fetch(_0x1582a3+'/enrolled-courses/upload?'+_0x4be92f,{'method':'POST','headers':{'Content-Type':'application/json'},'body':JSON['stringify'](_0x345b94)});if(!_0xe3ab56['ok']){const _0x2152e9=await _0xe3ab56['json']();throw _0x2152e9;}return _0xe3ab56['json']();}catch(_0x2367f2){throw _0x2367f2;}}
|
|
25
|
+
|
|
26
|
+
function parseAssessmentCsv(_0xf86a96){const {data:_0x5f0a0c}=_0x3a8d60['parse'](_0xf86a96,{'skipEmptyLines':!![]}),_0x4c6cac=_0x5f0a0c['filter'](_0x26c0e2=>_0x26c0e2['length']>0x0),_0x6edf8d=_0x4c6cac['find'](_0x5eb043=>_0x5eb043['some'](_0x166c0f=>_0x166c0f?.['includes']('Faculty'))),_0x48dfd8=_0x6edf8d?_0x6edf8d['findIndex'](_0x4a7f5d=>_0x4a7f5d?.['includes']('Faculty')):-1,_0x42998c=_0x48dfd8!==-1&&_0x6edf8d?_0x6edf8d[_0x48dfd8+0x2]?.['replace'](/"/g,'')['trim']()??'':'',_0x43037b=_0x4c6cac['find'](_0x1028b5=>_0x1028b5['some'](_0xf87cd5=>_0xf87cd5?.['includes']('Semester'))),_0x1bcb2f=_0x43037b?_0x43037b['findIndex'](_0x3451f0=>_0x3451f0?.['includes']('Semester')):-1,_0x57ffe1=_0x1bcb2f!==-1&&_0x43037b?_0x43037b[_0x1bcb2f+0x2]?.['trim']()??'':'',_0x42efd4=_0x57ffe1['includes']('1st')?0x1:_0x57ffe1['includes']('2nd')?0x2:0x0,_0x12c358=_0x4c6cac['find'](_0x537275=>_0x537275['some'](_0x210012=>_0x210012?.['includes']('Course\x20&\x20Sec'))),_0x4f7e41=_0x12c358?_0x12c358['findIndex'](_0x2c3269=>_0x2c3269?.['includes']('Course\x20&\x20Sec')):-1,_0x19dc9c=_0x4f7e41!==-1&&_0x12c358?_0x12c358[_0x4f7e41+0x2]?.['trim']()??'':'';let _0xcc7dcf='',_0x15e60c='';if(_0x19dc9c){const _0x56a4a6=_0x19dc9c['match'](/^([A-Za-z0-9]+)-?([A-Za-z]+)?/);_0x56a4a6&&(_0xcc7dcf=_0x56a4a6[0x1]??'',_0x56a4a6[0x2]&&(_0x15e60c=_0x56a4a6[0x2]['replace'](/^OC/i,'')));}const _0xf434a3=_0x4c6cac['find'](_0x50d5bd=>_0x50d5bd['some'](_0x489de6=>_0x489de6?.['includes']('School\x20Year'))),_0x6d796d=_0xf434a3?_0xf434a3['findIndex'](_0x2a57fe=>_0x2a57fe?.['includes']('School\x20Year')):-1,_0x357cf2=_0x6d796d!==-1&&_0xf434a3?_0xf434a3[_0x6d796d+0x2]?.['trim']()??'':'';let _0x2ebdba=0x0;if(_0x357cf2){const _0x3c689d=_0x357cf2['match'](/(\d{4})-(\d{4})/);if(_0x3c689d){const _0x11e4d2=(_0x3c689d[0x1]??'')['slice'](0x2),_0x5e16db=(_0x3c689d[0x2]??'')['slice'](0x2);_0x2ebdba=parseInt(_0x11e4d2+_0x5e16db,0xa);}}const _0x4eb3ab={'faculty':_0x42998c,'course':_0xcc7dcf,'section':_0x15e60c,'semester':_0x42efd4,'sy':_0x2ebdba},_0x4a8837=_0x4c6cac['findIndex'](_0xdd21bd=>_0xdd21bd['some'](_0x3a58d3=>_0x3a58d3?.['trim']()==='CO\x20#'));if(_0x4a8837===-1)throw new Error('CO\x20header\x20row\x20not\x20found\x20in\x20CSV');const _0x32bfb6=_0x4c6cac[_0x4a8837+0x1];if(!_0x32bfb6)throw new Error('ILO\x20header\x20row\x20not\x20found\x20in\x20CSV');const _0x3267bd={};let _0x5dabbb=0x1,_0x22ae24=0x1;for(let _0x1f8db4=0x3;_0x1f8db4<_0x32bfb6['length'];_0x1f8db4++){const _0x106e97=_0x32bfb6[_0x1f8db4];if(!_0x106e97)continue;const _0x5bb735='co'+_0x5dabbb;if(!_0x3267bd[_0x5bb735])_0x3267bd[_0x5bb735]=[];_0x3267bd[_0x5bb735]['push']('ilo'+_0x22ae24),_0x22ae24++,_0x22ae24>0x3&&(_0x5dabbb++,_0x22ae24=0x1);}const _0x1030fe=_0x4c6cac['findIndex'](_0x586f98=>_0x586f98['includes']('Name\x20of\x20Students'));if(_0x1030fe===-1)throw new Error('Student\x20header\x20row\x20not\x20found\x20in\x20CSV');const _0x4adab1=_0x4c6cac[_0x1030fe];if(!_0x4adab1)throw new Error('Student\x20header\x20row\x20is\x20missing');const _0x5642a2=_0x4adab1['findIndex'](_0x5a20ef=>_0x5a20ef?.['includes']('Name\x20of\x20Students'));if(_0x5642a2===-1)throw new Error('Name\x20of\x20Students\x20column\x20not\x20found\x20in\x20CSV');const _0x368639=_0x5642a2+0x2,_0x284de4=[];for(let _0x191955=_0x1030fe+0x1;_0x191955<_0x4c6cac['length'];_0x191955++){const _0x57089c=_0x4c6cac[_0x191955];if(!_0x57089c)continue;if(_0x57089c['some'](_0x9a6b9a=>_0x9a6b9a?.['toUpperCase']()['includes']('TOTAL\x20STUDENTS')||_0x9a6b9a?.['toUpperCase']()['includes']('ACHIEVED\x20THE\x20MINIMUM')||_0x9a6b9a?.['toUpperCase']()['includes']('INACTIVE')||_0x9a6b9a?.['toUpperCase']()['includes']('AVERAGE')))continue;if(!_0x57089c[_0x5642a2])continue;const _0x10a773=_0x57089c[_0x5642a2]['replace'](/"/g,'')['trim'](),_0x54d2f7=_0x57089c['slice'](_0x368639)['map'](_0x5c9f70=>_0x5c9f70===null?null:!isNaN(Number(_0x5c9f70))?parseFloat(_0x5c9f70):0x0);let _0x210df8=0x0;const _0x565268={};Object['entries'](_0x3267bd)['forEach'](([_0x2b09af,_0x3d2266])=>{const _0x4e924c={};_0x3d2266['forEach'](_0x459296=>{_0x4e924c[_0x459296]=_0x54d2f7[_0x210df8]??0x0,_0x210df8++;}),_0x565268[_0x2b09af]={'transmuted_score':_0x4e924c};}),_0x284de4['push']({'student_name':_0x10a773,'coaep':_0x565268});}const _0x3cb27a=_0x4c6cac['find'](_0xe3e190=>_0xe3e190['some'](_0x2dece6=>_0x2dece6?.['includes']('ACHIEVED\x20THE\x20MINIMUM'))),_0x2f4ed1=_0x4c6cac['find'](_0x4de0fa=>_0x4de0fa['some'](_0x12bfd4=>_0x12bfd4?.['includes']('AVERAGE'))),_0x4c0f39=_0x3cb27a?_0x3cb27a['slice'](_0x368639)['map'](_0x23ba6d=>_0x23ba6d&&!isNaN(Number(_0x23ba6d))?parseInt(_0x23ba6d):0x0):[],_0x39dac5=_0x2f4ed1?_0x2f4ed1['slice'](_0x368639)['map'](_0x1776ee=>_0x1776ee&&_0x1776ee!=='#DIV/0!'&&!isNaN(Number(_0x1776ee))?parseInt(_0x1776ee):0x0):[],_0x2206db={};let _0x593ab2=0x0;return Object['entries'](_0x3267bd)['forEach'](([_0x2c783a,_0x33f3ab])=>{if(!_0x2206db[_0x2c783a])_0x2206db[_0x2c783a]={};_0x33f3ab['forEach'](_0x4ab880=>{_0x2206db[_0x2c783a][_0x4ab880]={'achievedMinimum':_0x4c0f39[_0x593ab2]??0x0,'average':_0x39dac5[_0x593ab2]??0x0},_0x593ab2++;});}),{'assessmentData':{'classAssignment':_0x4eb3ab,'student':_0x284de4,'total':_0x2206db}};}
|
|
27
|
+
|
|
28
|
+
async function uploadAssessmentData(_0x5dd3e2,_0x52162d){try{const _0x13e194=await convertToCSVFile(_0x52162d),_0x34d726=await _0x13e194['text'](),_0x1ddfcd=parseAssessmentCsv(_0x34d726),_0x141e86=await fetch(_0x5dd3e2+'/assessment-data/upload',{'method':'POST','headers':{'Content-Type':'application/json'},'body':JSON['stringify'](_0x1ddfcd)});if(!_0x141e86['ok']){const _0x5bec96=await _0x141e86['json']();throw _0x5bec96;}return _0x141e86['json']();}catch(_0x169759){throw _0x169759;}}
|
|
29
|
+
|
|
30
|
+
class Client{['BASE_URL'];constructor(_0x54e724){this['BASE_URL']=_0x54e724;}['Parser'](){return {'curriculum':async _0x1f3b14=>{const _0x55c49e=await uploadCurriculum(this['BASE_URL'],_0x1f3b14);return _0x55c49e;},'courseOffering':async _0x2570b3=>{const _0x5e5f2e=await uploadCourseOffering(this['BASE_URL'],_0x2570b3);return _0x5e5f2e;},'coaep':async(_0x5303e2,_0x308c32)=>{const _0x1e85ca=await uploadCOAEP(this['BASE_URL'],_0x5303e2,_0x308c32);return _0x1e85ca;},'enrolledStudent':async _0x1c011e=>{const _0x51013c=await uploadEnrolledStudent(this['BASE_URL'],_0x1c011e);return _0x51013c;},'classlist':async(_0x365ba9,_0x29dd23,_0x38e95f)=>{const _0x3984b1=await uploadClassList(this['BASE_URL'],_0x365ba9,_0x29dd23,_0x38e95f);return _0x3984b1;},'assessmentData':async _0x3fa740=>{const _0x3c2a05=await uploadAssessmentData(this['BASE_URL'],_0x3fa740);return _0x3c2a05;}};}}
|
|
31
|
+
|
|
32
|
+
export { Client as default };
|
package/main.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export default class Client {
|
|
2
|
+
private BASE_URL;
|
|
3
|
+
constructor(url: string);
|
|
4
|
+
Parser(): {
|
|
5
|
+
/**
|
|
6
|
+
* Parse the official curriculum data from the registrar to a digestible format
|
|
7
|
+
* @param curriculum file from registrar
|
|
8
|
+
* @returns status 201
|
|
9
|
+
*/
|
|
10
|
+
curriculum: (xls: File) => Promise<Record<string, unknown>>;
|
|
11
|
+
/**
|
|
12
|
+
* Parse the course offering data
|
|
13
|
+
* @param offering file from registrar
|
|
14
|
+
* @returns status 201
|
|
15
|
+
*/
|
|
16
|
+
courseOffering: (xls: File) => Promise<Record<string, unknown>>;
|
|
17
|
+
coaep: (xls: File, course_id: string) => Promise<Record<string, unknown>>;
|
|
18
|
+
enrolledStudent: (xls: File) => Promise<Record<string, unknown>>;
|
|
19
|
+
classlist: (xls: File, subj_code: number, period_id: number) => Promise<Record<string, unknown>>;
|
|
20
|
+
assessmentData: (xls: File) => Promise<Record<string, unknown>>;
|
|
21
|
+
};
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obe-loms/coms-parser",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"papaparse": "^5.5.3",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"ts-node": "^10.9.2",
|
|
18
18
|
"typescript": "^5.9.2"
|
|
19
19
|
},
|
|
20
|
-
"main": "./
|
|
20
|
+
"main": "./bundle.js",
|
|
21
|
+
"types": "./main.d.ts",
|
|
21
22
|
"publishConfig": {
|
|
22
23
|
"access": "public"
|
|
23
24
|
}
|
package/parser/xls.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function convertToCSVFile(xls: File): Promise<File>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function uploadAssessmentData(url: string, xls: File): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function uploadCOAEP(url: string, xls: File, course_id: string): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function uploadClassList(url: string, xls: File, subj_code: number, period_id: number): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function uploadCourseOffering(url: string, xls: File): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function uploadCurriculum(url: string, xls: File): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function uploadEnrolledStudent(url: string, xls: File): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type TransmutedScore = Record<string, number | null>;
|
|
2
|
+
export type Coaep = Record<string, {
|
|
3
|
+
transmuted_score: TransmutedScore;
|
|
4
|
+
}>;
|
|
5
|
+
export type Student = {
|
|
6
|
+
student_name: string;
|
|
7
|
+
coaep: Coaep;
|
|
8
|
+
};
|
|
9
|
+
export type ClassAssignment = {
|
|
10
|
+
faculty: string;
|
|
11
|
+
course: string;
|
|
12
|
+
section: string;
|
|
13
|
+
semester: number;
|
|
14
|
+
sy: number;
|
|
15
|
+
};
|
|
16
|
+
export type TotalIlo = {
|
|
17
|
+
achievedMinimum: number;
|
|
18
|
+
average: number;
|
|
19
|
+
};
|
|
20
|
+
export type Total = Record<string, Record<string, TotalIlo>>;
|
|
21
|
+
export type AssessmentData = {
|
|
22
|
+
classAssignment: ClassAssignment;
|
|
23
|
+
student: Student[];
|
|
24
|
+
total: Total;
|
|
25
|
+
};
|
package/types/coaep.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface ILO {
|
|
2
|
+
statement: string;
|
|
3
|
+
verb: string;
|
|
4
|
+
assessment_tool: string;
|
|
5
|
+
performance_target: number | null;
|
|
6
|
+
passing_score: number | null;
|
|
7
|
+
}
|
|
8
|
+
export interface CO {
|
|
9
|
+
statement: string;
|
|
10
|
+
verb: string;
|
|
11
|
+
ilo: ILO[];
|
|
12
|
+
}
|
|
13
|
+
export interface COAEP {
|
|
14
|
+
faculty: string | null;
|
|
15
|
+
course: string | null;
|
|
16
|
+
sy: string | null;
|
|
17
|
+
semester: number | null;
|
|
18
|
+
co: CO[];
|
|
19
|
+
}
|
package/helper/matchTarget.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export function performaceTarget(target) {
|
|
2
|
-
if (!target)
|
|
3
|
-
return { performance_target: null, passing_score: null };
|
|
4
|
-
const matches = target.match(/\d+/g);
|
|
5
|
-
return {
|
|
6
|
-
performance_target: matches?.[0] ? parseInt(matches[0], 10) : null,
|
|
7
|
-
passing_score: matches?.[1] ? parseInt(matches[1], 10) : null
|
|
8
|
-
};
|
|
9
|
-
}
|
package/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/index.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import Client from "./main";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
const client = new Client('http://localhost:3000/api/v1');
|
|
5
|
-
const filePath = path.join(__dirname, "../Test.csv");
|
|
6
|
-
const buffer = fs.readFileSync(filePath);
|
|
7
|
-
const file = new File([buffer], 'Test.csv', { type: 'text/csv' });
|
|
8
|
-
client.Parser().assessmentData(file)
|
|
9
|
-
.then(result => console.log(result))
|
|
10
|
-
.catch(err => console.error("Error uploading COAEP:", err));
|
package/main.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { uploadCurriculum } from "./relay/uploadCurriculum";
|
|
2
|
-
import { uploadCourseOffering } from "./relay/uploadCourseOffering";
|
|
3
|
-
import { uploadCOAEP } from "./relay/uploadCOAEP";
|
|
4
|
-
import { uploadEnrolledStudent } from "./relay/uploadEnrolledStudent";
|
|
5
|
-
import { uploadClassList } from "./relay/uploadClassList";
|
|
6
|
-
import { uploadAssessmentData } from "./relay/uploadAssessmentData";
|
|
7
|
-
export default class Client {
|
|
8
|
-
BASE_URL;
|
|
9
|
-
constructor(url) {
|
|
10
|
-
this.BASE_URL = url;
|
|
11
|
-
}
|
|
12
|
-
Parser() {
|
|
13
|
-
return {
|
|
14
|
-
/**
|
|
15
|
-
* Parse the official curriculum data from the registrar to a digestible format
|
|
16
|
-
* @param curriculum file from registrar
|
|
17
|
-
* @returns status 201
|
|
18
|
-
*/
|
|
19
|
-
curriculum: async (xls) => {
|
|
20
|
-
const res = await uploadCurriculum(this.BASE_URL, xls);
|
|
21
|
-
return res;
|
|
22
|
-
},
|
|
23
|
-
/**
|
|
24
|
-
* Parse the course offering data
|
|
25
|
-
* @param offering file from registrar
|
|
26
|
-
* @returns status 201
|
|
27
|
-
*/
|
|
28
|
-
courseOffering: async (xls) => {
|
|
29
|
-
const res = await uploadCourseOffering(this.BASE_URL, xls);
|
|
30
|
-
return res;
|
|
31
|
-
},
|
|
32
|
-
coaep: async (xls, course_id) => {
|
|
33
|
-
const res = await uploadCOAEP(this.BASE_URL, xls, course_id);
|
|
34
|
-
return res;
|
|
35
|
-
},
|
|
36
|
-
enrolledStudent: async (xls) => {
|
|
37
|
-
const res = await uploadEnrolledStudent(this.BASE_URL, xls);
|
|
38
|
-
return res;
|
|
39
|
-
},
|
|
40
|
-
classlist: async (xls, subj_code, period_id) => {
|
|
41
|
-
const res = await uploadClassList(this.BASE_URL, xls, subj_code, period_id);
|
|
42
|
-
return res;
|
|
43
|
-
},
|
|
44
|
-
assessmentData: async (xls) => {
|
|
45
|
-
const res = await uploadAssessmentData(this.BASE_URL, xls);
|
|
46
|
-
return res;
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
}
|
package/parser/assessmentdata.js
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import Papa from "papaparse";
|
|
2
|
-
export function parseAssessmentCsv(csv) {
|
|
3
|
-
const { data } = Papa.parse(csv, { skipEmptyLines: true });
|
|
4
|
-
const rows = data.filter((r) => r.length > 0);
|
|
5
|
-
const facultyRow = rows.find((r) => r.some((c) => c?.includes("Faculty")));
|
|
6
|
-
const facultyIdx = facultyRow
|
|
7
|
-
? facultyRow.findIndex((c) => c?.includes("Faculty"))
|
|
8
|
-
: -1;
|
|
9
|
-
const faculty = facultyIdx !== -1 && facultyRow
|
|
10
|
-
? facultyRow[facultyIdx + 2]?.replace(/"/g, "").trim() ?? ""
|
|
11
|
-
: "";
|
|
12
|
-
const semRow = rows.find((r) => r.some((c) => c?.includes("Semester")));
|
|
13
|
-
const semIdx = semRow ? semRow.findIndex((c) => c?.includes("Semester")) : -1;
|
|
14
|
-
const semesterStr = semIdx !== -1 && semRow ? semRow[semIdx + 2]?.trim() ?? "" : "";
|
|
15
|
-
const semester = semesterStr.includes("1st")
|
|
16
|
-
? 1
|
|
17
|
-
: semesterStr.includes("2nd")
|
|
18
|
-
? 2
|
|
19
|
-
: 0;
|
|
20
|
-
const courseRow = rows.find((r) => r.some((c) => c?.includes("Course & Sec")));
|
|
21
|
-
const courseIdx = courseRow
|
|
22
|
-
? courseRow.findIndex((c) => c?.includes("Course & Sec"))
|
|
23
|
-
: -1;
|
|
24
|
-
const rawCourseSection = courseIdx !== -1 && courseRow ? courseRow[courseIdx + 2]?.trim() ?? "" : "";
|
|
25
|
-
let courseCode = "";
|
|
26
|
-
let section = "";
|
|
27
|
-
if (rawCourseSection) {
|
|
28
|
-
const match = rawCourseSection.match(/^([A-Za-z0-9]+)-?([A-Za-z]+)?/);
|
|
29
|
-
if (match) {
|
|
30
|
-
courseCode = match[1] ?? "";
|
|
31
|
-
if (match[2]) {
|
|
32
|
-
section = match[2].replace(/^OC/i, "");
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
const syRow = rows.find((r) => r.some((c) => c?.includes("School Year")));
|
|
37
|
-
const syIdx = syRow ? syRow.findIndex((c) => c?.includes("School Year")) : -1;
|
|
38
|
-
const syStr = syIdx !== -1 && syRow ? syRow[syIdx + 2]?.trim() ?? "" : "";
|
|
39
|
-
let sy = 0;
|
|
40
|
-
if (syStr) {
|
|
41
|
-
const match = syStr.match(/(\d{4})-(\d{4})/);
|
|
42
|
-
if (match) {
|
|
43
|
-
const first = (match[1] ?? "").slice(2);
|
|
44
|
-
const second = (match[2] ?? "").slice(2);
|
|
45
|
-
sy = parseInt(first + second, 10);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const classAssignment = {
|
|
49
|
-
faculty,
|
|
50
|
-
course: courseCode,
|
|
51
|
-
section: section,
|
|
52
|
-
semester: semester,
|
|
53
|
-
sy,
|
|
54
|
-
};
|
|
55
|
-
const coRowIndex = rows.findIndex((r) => r.some((c) => c?.trim() === "CO #"));
|
|
56
|
-
if (coRowIndex === -1)
|
|
57
|
-
throw new Error("CO header row not found in CSV");
|
|
58
|
-
const iloRow = rows[coRowIndex + 1];
|
|
59
|
-
if (!iloRow)
|
|
60
|
-
throw new Error("ILO header row not found in CSV");
|
|
61
|
-
const iloGroups = {};
|
|
62
|
-
let coCounter = 1;
|
|
63
|
-
let iloCounter = 1;
|
|
64
|
-
for (let i = 3; i < iloRow.length; i++) {
|
|
65
|
-
const val = iloRow[i];
|
|
66
|
-
if (!val)
|
|
67
|
-
continue;
|
|
68
|
-
const coKey = `co${coCounter}`;
|
|
69
|
-
if (!iloGroups[coKey])
|
|
70
|
-
iloGroups[coKey] = [];
|
|
71
|
-
iloGroups[coKey].push(`ilo${iloCounter}`);
|
|
72
|
-
iloCounter++;
|
|
73
|
-
if (iloCounter > 3) {
|
|
74
|
-
coCounter++;
|
|
75
|
-
iloCounter = 1;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const studentHeaderIndex = rows.findIndex((r) => r.includes("Name of Students"));
|
|
79
|
-
if (studentHeaderIndex === -1)
|
|
80
|
-
throw new Error("Student header row not found in CSV");
|
|
81
|
-
const headerRow = rows[studentHeaderIndex];
|
|
82
|
-
if (!headerRow)
|
|
83
|
-
throw new Error("Student header row is missing");
|
|
84
|
-
const nameColIndex = headerRow.findIndex((c) => c?.includes("Name of Students"));
|
|
85
|
-
if (nameColIndex === -1)
|
|
86
|
-
throw new Error("Name of Students column not found in CSV");
|
|
87
|
-
const firstScoreColIndex = nameColIndex + 2;
|
|
88
|
-
const students = [];
|
|
89
|
-
for (let i = studentHeaderIndex + 1; i < rows.length; i++) {
|
|
90
|
-
const row = rows[i];
|
|
91
|
-
if (!row)
|
|
92
|
-
continue;
|
|
93
|
-
if (row.some((c) => c?.toUpperCase().includes("TOTAL STUDENTS") ||
|
|
94
|
-
c?.toUpperCase().includes("ACHIEVED THE MINIMUM") ||
|
|
95
|
-
c?.toUpperCase().includes("INACTIVE") ||
|
|
96
|
-
c?.toUpperCase().includes("AVERAGE"))) {
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
if (!row[nameColIndex])
|
|
100
|
-
continue;
|
|
101
|
-
const name = row[nameColIndex].replace(/"/g, "").trim();
|
|
102
|
-
const scores = row
|
|
103
|
-
.slice(firstScoreColIndex)
|
|
104
|
-
.map((s) => (s === null ? null : !isNaN(Number(s)) ? parseFloat(s) : 0));
|
|
105
|
-
let scoreIndex = 0;
|
|
106
|
-
const coaep = {};
|
|
107
|
-
Object.entries(iloGroups).forEach(([coKey, iloKeys]) => {
|
|
108
|
-
const transmuted = {};
|
|
109
|
-
iloKeys.forEach((iloKey) => {
|
|
110
|
-
transmuted[iloKey] = scores[scoreIndex] ?? 0;
|
|
111
|
-
scoreIndex++;
|
|
112
|
-
});
|
|
113
|
-
coaep[coKey] = { transmuted_score: transmuted };
|
|
114
|
-
});
|
|
115
|
-
students.push({ student_name: name, coaep });
|
|
116
|
-
}
|
|
117
|
-
const achievedRow = rows.find((r) => r.some((c) => c?.includes("ACHIEVED THE MINIMUM")));
|
|
118
|
-
const avgRow = rows.find((r) => r.some((c) => c?.includes("AVERAGE")));
|
|
119
|
-
const achieved = achievedRow
|
|
120
|
-
? achievedRow
|
|
121
|
-
.slice(firstScoreColIndex)
|
|
122
|
-
.map((s) => (s && !isNaN(Number(s)) ? parseInt(s) : 0))
|
|
123
|
-
: [];
|
|
124
|
-
const averages = avgRow
|
|
125
|
-
? avgRow
|
|
126
|
-
.slice(firstScoreColIndex)
|
|
127
|
-
.map((s) => s && s !== "#DIV/0!" && !isNaN(Number(s)) ? parseInt(s) : 0)
|
|
128
|
-
: [];
|
|
129
|
-
const total = {};
|
|
130
|
-
let totalIndex = 0;
|
|
131
|
-
Object.entries(iloGroups).forEach(([coKey, iloKeys]) => {
|
|
132
|
-
if (!total[coKey])
|
|
133
|
-
total[coKey] = {};
|
|
134
|
-
iloKeys.forEach((iloKey) => {
|
|
135
|
-
total[coKey][iloKey] = {
|
|
136
|
-
achievedMinimum: achieved[totalIndex] ?? 0,
|
|
137
|
-
average: averages[totalIndex] ?? 0,
|
|
138
|
-
};
|
|
139
|
-
totalIndex++;
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
return {
|
|
143
|
-
assessmentData: {
|
|
144
|
-
classAssignment,
|
|
145
|
-
student: students,
|
|
146
|
-
total,
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
}
|
package/parser/classList.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import Papa from "papaparse";
|
|
2
|
-
export function parseClassList(csvData) {
|
|
3
|
-
const rows = Papa.parse(csvData, {
|
|
4
|
-
skipEmptyLines: true,
|
|
5
|
-
}).data;
|
|
6
|
-
let section = "";
|
|
7
|
-
let course_id = "";
|
|
8
|
-
let subj_code = "";
|
|
9
|
-
const enrolledCourses = [];
|
|
10
|
-
rows.forEach((row) => {
|
|
11
|
-
const cells = row.map((c) => (c ?? "").toString().trim());
|
|
12
|
-
const firstCell = cells[0] ?? "";
|
|
13
|
-
if (firstCell.startsWith("Course No:")) {
|
|
14
|
-
// "Course No: 3093 BIT324L-OBa"
|
|
15
|
-
const courseLine = firstCell.replace("Course No:", "").trim();
|
|
16
|
-
const parts = courseLine.split(/\s+/);
|
|
17
|
-
subj_code = parts[0] ?? ""; // 3093
|
|
18
|
-
const [course, sec] = (parts[1] ?? "").split("-");
|
|
19
|
-
course_id = course ?? ""; // BIT999L
|
|
20
|
-
const sectionMatch = (sec ?? "").match(/[a-z]$/);
|
|
21
|
-
section = sectionMatch ? sectionMatch[0] : "";
|
|
22
|
-
}
|
|
23
|
-
if (/^\d+$/.test(firstCell)) {
|
|
24
|
-
const student_no = cells[2] ?? "";
|
|
25
|
-
enrolledCourses.push({
|
|
26
|
-
subj_code,
|
|
27
|
-
student_no,
|
|
28
|
-
course_id,
|
|
29
|
-
section,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
return {
|
|
34
|
-
enrolledCourses
|
|
35
|
-
};
|
|
36
|
-
}
|
package/parser/coaep.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import Papa from "papaparse";
|
|
2
|
-
import { performaceTarget } from "../helper/matchTarget";
|
|
3
|
-
export function parseCOAEP(csvString) {
|
|
4
|
-
const rows = Papa.parse(csvString, {
|
|
5
|
-
skipEmptyLines: false
|
|
6
|
-
}).data;
|
|
7
|
-
const data = {
|
|
8
|
-
COAEP: {
|
|
9
|
-
faculty: null,
|
|
10
|
-
course: null,
|
|
11
|
-
sy: null,
|
|
12
|
-
semester: null,
|
|
13
|
-
co: []
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
rows.forEach(row => {
|
|
17
|
-
if (row[1]?.includes("Name of Faculty:")) {
|
|
18
|
-
data.COAEP.faculty = row[2]?.trim() || null;
|
|
19
|
-
}
|
|
20
|
-
if (row.includes("School Year")) {
|
|
21
|
-
const idx = row.indexOf("School Year");
|
|
22
|
-
data.COAEP.sy = row[idx + 1]?.trim() || null;
|
|
23
|
-
}
|
|
24
|
-
if (row[1]?.includes("Course:")) {
|
|
25
|
-
const courseStr = row[2]?.trim() || "";
|
|
26
|
-
data.COAEP.course = courseStr || null;
|
|
27
|
-
}
|
|
28
|
-
if (row.includes("Semester")) {
|
|
29
|
-
const idx = row.indexOf("Semester");
|
|
30
|
-
const semStr = row[idx + 1]?.trim() || "";
|
|
31
|
-
const semNum = semStr.match(/\d+/)?.[0];
|
|
32
|
-
data.COAEP.semester = semNum ? parseInt(semNum, 10) : null;
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
let currentCO = null;
|
|
36
|
-
rows.forEach(row => {
|
|
37
|
-
const col0 = row[0]?.trim() || "";
|
|
38
|
-
const col1 = row[1]?.trim() || "";
|
|
39
|
-
const col3 = row[3]?.trim() || "";
|
|
40
|
-
if (col0 && /^\d+$/.test(col0)) {
|
|
41
|
-
if (currentCO) {
|
|
42
|
-
data.COAEP.co.push(currentCO);
|
|
43
|
-
}
|
|
44
|
-
const stmt = col1;
|
|
45
|
-
const verb = stmt.trim().split(/\s+/)[0] ?? "";
|
|
46
|
-
currentCO = {
|
|
47
|
-
statement: stmt,
|
|
48
|
-
verb: verb,
|
|
49
|
-
ilo: []
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
if (currentCO && col3) {
|
|
53
|
-
const iloStatement = col3
|
|
54
|
-
.replace(/^ILO\d+\s*/, "")
|
|
55
|
-
.replace(/\s+/g, " ")
|
|
56
|
-
.trim();
|
|
57
|
-
const assessmentTool = (row[4]?.replace(/\s+/g, " ").trim()) || "";
|
|
58
|
-
const perfTargetStr = (row[5]?.replace(/\s+/g, " ").trim()) || "";
|
|
59
|
-
const { performance_target, passing_score } = performaceTarget(perfTargetStr);
|
|
60
|
-
currentCO.ilo.push({
|
|
61
|
-
statement: iloStatement,
|
|
62
|
-
verb: iloStatement.split(/\s+/)[0] ?? "",
|
|
63
|
-
assessment_tool: assessmentTool,
|
|
64
|
-
performance_target,
|
|
65
|
-
passing_score
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
if (currentCO) {
|
|
70
|
-
data.COAEP.co.push(currentCO);
|
|
71
|
-
}
|
|
72
|
-
return data;
|
|
73
|
-
}
|
package/parser/courseOffering.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import Papa from "papaparse";
|
|
2
|
-
export function parseCourseOffering(csvData) {
|
|
3
|
-
let semester = "";
|
|
4
|
-
let school_year = "";
|
|
5
|
-
const cleanRows = [];
|
|
6
|
-
// Parse CSV data
|
|
7
|
-
const parsed = Papa.parse(csvData, {
|
|
8
|
-
header: false,
|
|
9
|
-
skipEmptyLines: true,
|
|
10
|
-
delimiter: ","
|
|
11
|
-
});
|
|
12
|
-
const rows = parsed.data;
|
|
13
|
-
// Extract semester and school year from header rows
|
|
14
|
-
for (let i = 0; i < Math.min(5, rows.length); i++) {
|
|
15
|
-
const row = rows[i];
|
|
16
|
-
if (row && row.length > 0) {
|
|
17
|
-
const cellContent = row[0]?.toString() || "";
|
|
18
|
-
// Look for semester and school year pattern like "Second Sem S/Y 2024-2025"
|
|
19
|
-
const semesterMatch = cellContent.match(/(First|Second)\s+Sem\s+S\/Y\s+(\d{4}-\d{4})/i);
|
|
20
|
-
if (semesterMatch) {
|
|
21
|
-
// Convert semester to number: First = "1", Second = "2"
|
|
22
|
-
semester = semesterMatch[1]?.toLowerCase() === "first" ? "1" : "2";
|
|
23
|
-
// Convert school year format: "2024-2025" -> "2425"
|
|
24
|
-
if (semesterMatch[2]) {
|
|
25
|
-
const yearMatch = semesterMatch[2].match(/(\d{2})(\d{2})-(\d{2})(\d{2})/);
|
|
26
|
-
if (yearMatch && yearMatch[2] && yearMatch[4]) {
|
|
27
|
-
school_year = yearMatch[2] + yearMatch[4]; // Extract last 2 digits of each year
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
// Find the header row (should contain "CODE", "COURSE NO", etc.)
|
|
34
|
-
let headerRowIndex = -1;
|
|
35
|
-
for (let i = 0; i < rows.length; i++) {
|
|
36
|
-
const row = rows[i];
|
|
37
|
-
if (row && row.some(cell => cell?.toString().toUpperCase().includes("CODE"))) {
|
|
38
|
-
headerRowIndex = i;
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (headerRowIndex === -1) {
|
|
43
|
-
throw new Error("Could not find header row in CSV data");
|
|
44
|
-
}
|
|
45
|
-
// Process data rows (starting after header)
|
|
46
|
-
for (let i = headerRowIndex + 1; i < rows.length; i++) {
|
|
47
|
-
const row = rows[i];
|
|
48
|
-
if (!row || row.length < 8)
|
|
49
|
-
continue; // Skip incomplete rows
|
|
50
|
-
// Extract data from each column
|
|
51
|
-
const code = row[0]?.toString().trim() || "";
|
|
52
|
-
const course_no = row[1]?.toString().trim() || "";
|
|
53
|
-
const course_desc = row[2]?.toString().trim() || "";
|
|
54
|
-
const unitStr = row[3]?.toString().trim() || "";
|
|
55
|
-
const time = row[4]?.toString().trim() || "";
|
|
56
|
-
const days = row[5]?.toString().trim() || "";
|
|
57
|
-
const faculty = row[6]?.toString().trim() || "";
|
|
58
|
-
const room = row[7]?.toString().trim() || "";
|
|
59
|
-
// Skip rows without essential data (must have subject code)
|
|
60
|
-
if (!code)
|
|
61
|
-
continue;
|
|
62
|
-
// Parse unit as number
|
|
63
|
-
let unit = 0;
|
|
64
|
-
const unitMatch = unitStr.match(/(\d+)/);
|
|
65
|
-
if (unitMatch && unitMatch[1]) {
|
|
66
|
-
unit = parseInt(unitMatch[1], 10);
|
|
67
|
-
}
|
|
68
|
-
// Create course offering object
|
|
69
|
-
const offering = {
|
|
70
|
-
sem: semester,
|
|
71
|
-
school_year: school_year,
|
|
72
|
-
code: code,
|
|
73
|
-
course_no: course_no,
|
|
74
|
-
course_desc: course_desc,
|
|
75
|
-
unit: unit,
|
|
76
|
-
time: time,
|
|
77
|
-
days: days,
|
|
78
|
-
faculty: faculty,
|
|
79
|
-
room: room
|
|
80
|
-
};
|
|
81
|
-
cleanRows.push(offering);
|
|
82
|
-
}
|
|
83
|
-
return cleanRows;
|
|
84
|
-
}
|
package/parser/curriculum.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import Papa from "papaparse";
|
|
2
|
-
export function parseCurriculum(csvData) {
|
|
3
|
-
let curr_id = "";
|
|
4
|
-
let program_name = "";
|
|
5
|
-
let revision_no = 0;
|
|
6
|
-
let year_level = null;
|
|
7
|
-
const cleanRows = [];
|
|
8
|
-
Papa.parse(csvData, {
|
|
9
|
-
skipEmptyLines: true,
|
|
10
|
-
complete: (result) => {
|
|
11
|
-
result.data.forEach((row) => {
|
|
12
|
-
const cells = row.map((c) => (c ?? "").toString().trim());
|
|
13
|
-
const firstCell = cells[0] ?? "";
|
|
14
|
-
//Metadata
|
|
15
|
-
if (firstCell.includes("Rev#")) {
|
|
16
|
-
const metaMatch = firstCell.match(/([A-Z0-9]+)\s*-\s*.*:\s*(.*?)\s+Rev#\s*(\d+)/i);
|
|
17
|
-
if (metaMatch) {
|
|
18
|
-
curr_id = metaMatch[1]?.trim() ?? "";
|
|
19
|
-
program_name = metaMatch[2]?.trim() ?? "";
|
|
20
|
-
revision_no = parseInt(metaMatch[3] ?? "0", 10);
|
|
21
|
-
}
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
//Year level
|
|
25
|
-
if (/FIRST YEAR/i.test(firstCell)) {
|
|
26
|
-
year_level = 1;
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
if (/SECOND YEAR/i.test(firstCell)) {
|
|
30
|
-
year_level = 2;
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
if (/THIRD YEAR/i.test(firstCell)) {
|
|
34
|
-
year_level = 3;
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (/FOURTH YEAR/i.test(firstCell)) {
|
|
38
|
-
year_level = 4;
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (/FIFTH YEAR/i.test(firstCell)) {
|
|
42
|
-
year_level = 5;
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
//skip semester header
|
|
46
|
-
if (/First Semester/i.test(firstCell) || /Second Semester/i.test(firstCell) || /Summer/i.test(firstCell)) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
// CSV columns:
|
|
50
|
-
// First Sem: 0=id, 1=desc, 2=units?, 3=lec, 4=lab
|
|
51
|
-
// Second Sem: 6=id, 7=desc, 8=lec, 9=lab
|
|
52
|
-
// Summer Sem: 12=id, 13=desc, 14=lec, 15=lab
|
|
53
|
-
const semesterBlocks = [
|
|
54
|
-
{ sem: 1, offset: 0 }, // first semester columns start at index 0
|
|
55
|
-
{ sem: 2, offset: 6 }, // second semester columns start at index 6
|
|
56
|
-
{ sem: 3, offset: 12 }, // summer semester columns start at index 12
|
|
57
|
-
];
|
|
58
|
-
semesterBlocks.forEach(({ sem, offset }) => {
|
|
59
|
-
const course_id = cells[offset] ?? "";
|
|
60
|
-
const course_desc = cells[offset + 1] ?? "";
|
|
61
|
-
const total_units = cells[offset + 2] ?? "";
|
|
62
|
-
const lec_unit = cells[offset + 3] ?? "";
|
|
63
|
-
const lab_unit = cells[offset + 4] ?? "";
|
|
64
|
-
if (course_id && year_level) {
|
|
65
|
-
cleanRows.push({
|
|
66
|
-
curr_id,
|
|
67
|
-
program_name,
|
|
68
|
-
revision_no,
|
|
69
|
-
year_level,
|
|
70
|
-
sem,
|
|
71
|
-
course_id,
|
|
72
|
-
course_desc,
|
|
73
|
-
total_units,
|
|
74
|
-
lec_unit,
|
|
75
|
-
lab_unit,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
return cleanRows;
|
|
83
|
-
}
|
package/parser/xls.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import * as XLSX from "xlsx";
|
|
2
|
-
export function convertToCSVFile(xls) {
|
|
3
|
-
return new Promise((resolve, reject) => {
|
|
4
|
-
const reader = new FileReader();
|
|
5
|
-
reader.onload = (evt) => {
|
|
6
|
-
const data = evt.target?.result;
|
|
7
|
-
if (!data) {
|
|
8
|
-
reject(new Error("Failed to read file"));
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
try {
|
|
12
|
-
const workbook = XLSX.read(data, { type: "array" });
|
|
13
|
-
const sheetName = workbook.SheetNames[0];
|
|
14
|
-
if (!sheetName) {
|
|
15
|
-
reject(new Error("No sheets found in workbook"));
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
const workSheet = workbook.Sheets[sheetName];
|
|
19
|
-
if (!workSheet) {
|
|
20
|
-
reject(new Error("No sheets found in worksheet"));
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
const csvString = XLSX.utils.sheet_to_csv(workSheet);
|
|
24
|
-
// Create a new File from the CSV string
|
|
25
|
-
// Use the original filename but replace extension with .csv
|
|
26
|
-
const csvFileName = xls.name.replace(/\.[^/.]+$/, ".csv");
|
|
27
|
-
const csvFile = new File([csvString], csvFileName, {
|
|
28
|
-
type: "text/csv",
|
|
29
|
-
lastModified: Date.now(),
|
|
30
|
-
});
|
|
31
|
-
resolve(csvFile);
|
|
32
|
-
}
|
|
33
|
-
catch (error) {
|
|
34
|
-
reject(error);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
reader.onerror = () => {
|
|
38
|
-
reject(new Error("File reading failed"));
|
|
39
|
-
};
|
|
40
|
-
reader.readAsArrayBuffer(xls);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { parseAssessmentCsv } from "../parser/assessmentdata";
|
|
2
|
-
import { convertToCSVFile } from "../parser/xls";
|
|
3
|
-
export async function uploadAssessmentData(url, xls) {
|
|
4
|
-
try {
|
|
5
|
-
const csv = await convertToCSVFile(xls);
|
|
6
|
-
const data = await csv.text();
|
|
7
|
-
const parsed = parseAssessmentCsv(data);
|
|
8
|
-
const res = await fetch(`${url}/assessment-data/upload`, {
|
|
9
|
-
method: "POST",
|
|
10
|
-
headers: {
|
|
11
|
-
"Content-Type": "application/json"
|
|
12
|
-
},
|
|
13
|
-
body: JSON.stringify(parsed)
|
|
14
|
-
});
|
|
15
|
-
if (!res.ok) {
|
|
16
|
-
const errorData = await res.json();
|
|
17
|
-
throw errorData;
|
|
18
|
-
}
|
|
19
|
-
return res.json();
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
throw error;
|
|
23
|
-
}
|
|
24
|
-
}
|
package/relay/uploadCOAEP.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { parseCOAEP } from "../parser/coaep";
|
|
2
|
-
import { convertToCSVFile } from "../parser/xls";
|
|
3
|
-
export async function uploadCOAEP(url, xls, course_id) {
|
|
4
|
-
try {
|
|
5
|
-
const csv = await convertToCSVFile(xls);
|
|
6
|
-
const data = await csv.text();
|
|
7
|
-
const parsed = parseCOAEP(data);
|
|
8
|
-
const res = await fetch(`${url}/coaeps/upload?course_id=${course_id}`, {
|
|
9
|
-
method: "POST",
|
|
10
|
-
headers: {
|
|
11
|
-
"Content-Type": "application/json"
|
|
12
|
-
},
|
|
13
|
-
body: JSON.stringify(parsed)
|
|
14
|
-
});
|
|
15
|
-
if (!res.ok) {
|
|
16
|
-
const errorData = await res.json();
|
|
17
|
-
throw errorData;
|
|
18
|
-
}
|
|
19
|
-
return res.json();
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
throw error;
|
|
23
|
-
}
|
|
24
|
-
}
|
package/relay/uploadClassList.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { parseClassList } from "../parser/classList";
|
|
2
|
-
import { convertToCSVFile } from "../parser/xls";
|
|
3
|
-
export async function uploadClassList(url, xls, subj_code, period_id) {
|
|
4
|
-
try {
|
|
5
|
-
const csv = await convertToCSVFile(xls);
|
|
6
|
-
const data = await csv.text();
|
|
7
|
-
const parsed = parseClassList(data);
|
|
8
|
-
const query = `subj_code=${subj_code}&period_id=${period_id}`;
|
|
9
|
-
const res = await fetch(`${url}/enrolled-courses/upload?${query}`, {
|
|
10
|
-
method: "POST",
|
|
11
|
-
headers: {
|
|
12
|
-
"Content-Type": "application/json",
|
|
13
|
-
},
|
|
14
|
-
body: JSON.stringify(parsed),
|
|
15
|
-
});
|
|
16
|
-
if (!res.ok) {
|
|
17
|
-
const errorData = await res.json();
|
|
18
|
-
throw errorData;
|
|
19
|
-
}
|
|
20
|
-
return res.json();
|
|
21
|
-
}
|
|
22
|
-
catch (error) {
|
|
23
|
-
throw error;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import Papa from "papaparse";
|
|
2
|
-
import { convertToCSVFile } from "../parser/xls";
|
|
3
|
-
import { parseCourseOffering } from "../parser/courseOffering";
|
|
4
|
-
export async function uploadCourseOffering(url, xls) {
|
|
5
|
-
try {
|
|
6
|
-
const csv = await convertToCSVFile(xls);
|
|
7
|
-
const data = await csv.text();
|
|
8
|
-
const parsed = parseCourseOffering(data);
|
|
9
|
-
const sanitized = Papa.unparse(parsed);
|
|
10
|
-
const sanitizedCsv = new File([sanitized], csv.name, { type: "text/csv" });
|
|
11
|
-
const formData = new FormData();
|
|
12
|
-
formData.append("csvFile", sanitizedCsv);
|
|
13
|
-
const res = await fetch(`${url}/course-offerings/upload`, {
|
|
14
|
-
method: "POST",
|
|
15
|
-
body: formData
|
|
16
|
-
});
|
|
17
|
-
if (!res.ok) {
|
|
18
|
-
const errorData = await res.json();
|
|
19
|
-
throw errorData;
|
|
20
|
-
}
|
|
21
|
-
return res.json();
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
throw error;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { parseCurriculum } from "../parser/curriculum";
|
|
2
|
-
import Papa from "papaparse";
|
|
3
|
-
import { convertToCSVFile } from "../parser/xls";
|
|
4
|
-
export async function uploadCurriculum(url, xls) {
|
|
5
|
-
try {
|
|
6
|
-
const csv = await convertToCSVFile(xls);
|
|
7
|
-
const data = await csv.text();
|
|
8
|
-
const parsed = parseCurriculum(data);
|
|
9
|
-
const sanitized = Papa.unparse(parsed);
|
|
10
|
-
const sanitizedCsv = new File([sanitized], csv.name, { type: "text/csv" });
|
|
11
|
-
const formData = new FormData();
|
|
12
|
-
formData.append("csvFile", sanitizedCsv);
|
|
13
|
-
const res = await fetch(`${url}/curr-courses/upload`, {
|
|
14
|
-
method: "POST",
|
|
15
|
-
body: formData
|
|
16
|
-
});
|
|
17
|
-
if (!res.ok) {
|
|
18
|
-
const errorData = await res.json();
|
|
19
|
-
throw errorData;
|
|
20
|
-
}
|
|
21
|
-
return res.json();
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
throw error;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { convertToCSVFile } from "../parser/xls";
|
|
2
|
-
export async function uploadEnrolledStudent(url, xls) {
|
|
3
|
-
try {
|
|
4
|
-
const csv = await convertToCSVFile(xls);
|
|
5
|
-
const formData = new FormData();
|
|
6
|
-
formData.append("csvFile", csv);
|
|
7
|
-
const res = await fetch(`${url}/enrolled-students/upload`, {
|
|
8
|
-
method: "POST",
|
|
9
|
-
body: formData
|
|
10
|
-
});
|
|
11
|
-
if (!res.ok) {
|
|
12
|
-
const errorData = await res.json();
|
|
13
|
-
throw errorData;
|
|
14
|
-
}
|
|
15
|
-
return res.json();
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
throw error;
|
|
19
|
-
}
|
|
20
|
-
}
|
package/types/assessmentdata.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/types/classList.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/types/coaep.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/types/courseOffering.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/types/curriculum.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|