Prechádzať zdrojové kódy

add: 北方工业大学教务适配(#287)

EnderHCO3 1 mesiac pred
rodič
commit
752fa020b3

+ 5 - 0
index/root_index.yaml

@@ -461,3 +461,8 @@ schools:
     name: "呼和浩特民族学院"
     initial: "H"
     resource_folder: "IMNC"
+
+  - id: "NCUT"
+    name: "北方工业大学"
+    initial: "N"
+    resource_folder: "NCUT"

+ 8 - 0
resources/NCUT/adapters.yaml

@@ -0,0 +1,8 @@
+adapters:
+  - adapter_id: "NCUT_01"
+    adapter_name: "北方工业大学-教务系统"
+    category: "BACHELOR_AND_ASSOCIATE"
+    asset_js_path: "ncut_01.js"
+    import_url: "https://jwxtbk.ncut.edu.cn/jsxsd/"
+    maintainer: "EnderHCO3"
+    description: "适配北方工业大学本科教务管理系统课表导入 请在导入时将'我的课表'后的'第x周'改为全部周次"

+ 132 - 0
resources/NCUT/ncut_01.js

@@ -0,0 +1,132 @@
+(function() {
+    'use strict';
+
+    function timeToMinutes(t) { const [h,m]=t.split(':').map(Number); return h*60+m; }
+    function minutesToTime(m) { const hh=String(Math.floor(m/60)).padStart(2,'0'); const mm=String(m%60).padStart(2,'0'); return hh+':'+mm; }
+    function parseWeeks(weekStr) {
+        const cleaned = weekStr.replace(/[\[\]]/g,'').trim();
+        let parity=null;
+        if(cleaned.includes('(单周)')||cleaned.includes('单周')) parity=1;
+        else if(cleaned.includes('(双周)')||cleaned.includes('双周')) parity=2;
+        const m=cleaned.match(/(\d+)\s*[-~—]\s*(\d+)/);
+        if(!m) return [];
+        let s=parseInt(m[1]), e=parseInt(m[2]), weeks=[];
+        for(let i=s;i<=e;i++) {
+            if(parity===1 && i%2===0) continue;
+            if(parity===2 && i%2!==0) continue;
+            weeks.push(i);
+        }
+        return weeks;
+    }
+
+    function getScheduleDocument() {
+        let dv = document.getElementById('dv1');
+        if (dv && dv.querySelector('table tbody tr')) return document;
+        const iframes = document.querySelectorAll('iframe');
+        for (let f of iframes) {
+            try {
+                const doc = f.contentDocument || f.contentWindow.document;
+                dv = doc.getElementById('dv1');
+                if (dv && dv.querySelector('table tbody tr')) return doc;
+            } catch(e) {}
+        }
+        return null;
+    }
+
+    function parseConfig(doc) {
+        const ws = doc.getElementById('week');
+        if(!ws) return { semesterStartDate:null, semesterTotalWeeks:20 };
+        let sDate=null, total=0;
+        ws.querySelectorAll('option').forEach(o=>{
+            if(o.value && o.value!=='all'){
+                if(!sDate) sDate=o.value;
+                total++;
+            }
+        });
+        return { semesterStartDate:sDate, semesterTotalWeeks:total||20 };
+    }
+
+    function parseTimeSlots(doc) {
+        const slots=[];
+        doc.querySelectorAll('#dv1 tbody tr').forEach(row=>{
+            const first=row.querySelector('td:first-child');
+            if(!first) return;
+            const ps=first.querySelectorAll('p');
+            if(ps.length<2) return;
+            const secMatch=ps[0].textContent.trim().match(/\((\d+),\s*(\d+)小节\)/);
+            if(!secMatch) return;
+            const start=parseInt(secMatch[1]), end=parseInt(secMatch[2]);
+            const timeMatch=ps[1].textContent.trim().match(/(\d{2}:\d{2})\s*[-~—]\s*(\d{2}:\d{2})/);
+            if(!timeMatch) return;
+            const bs=timeToMinutes(timeMatch[1]), be=timeToMinutes(timeMatch[2]);
+            const dur=45;
+            slots.push({number:start,startTime:timeMatch[1],endTime:minutesToTime(bs+dur)});
+            slots.push({number:end,startTime:minutesToTime(be-dur),endTime:timeMatch[2]});
+        });
+        return slots.sort((a,b)=>a.number-b.number);
+    }
+
+    function parseCourses(doc) {
+        const courses=[];
+        doc.querySelectorAll('#dv1 tbody tr').forEach(row=>{
+            const first=row.querySelector('td:first-child');
+            if(!first) return;
+            const ps=first.querySelectorAll('p');
+            if(ps.length<2) return;
+            const secMatch=ps[0].textContent.trim().match(/\((\d+),\s*(\d+)小节\)/);
+            if(!secMatch) return;
+            const startSec=parseInt(secMatch[1]), endSec=parseInt(secMatch[2]);
+            row.querySelectorAll('td.kb-cell').forEach((cell,idx)=>{
+                const day=idx+1;
+                const classDiv=cell.querySelector('.person-class');
+                if(!classDiv) return;
+                const nameEl=classDiv.querySelector('h3 a');
+                if(!nameEl) return;
+                const lis=classDiv.querySelectorAll('ul>li');
+                if(lis.length<4) return;
+                const weeks=parseWeeks(lis[1].textContent.trim());
+                if(!weeks.length) return;
+                courses.push({
+                    name:nameEl.textContent.trim(),
+                    teacher:lis[2].textContent.trim(),
+                    position:lis[3].textContent.trim(),
+                    day, startSection:startSec, endSection:endSec,
+                    weeks, isCustomTime:false
+                });
+            });
+        });
+        return courses;
+    }
+
+    async function saveCourses(c){ try{await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(c));}catch(e){} }
+    async function saveTimeSlots(s){ try{await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(s));}catch(e){} }
+    async function saveConfig(c){ try{await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(c));}catch(e){} }
+
+    function waitForScheduleDoc(maxWait=15000) {
+        const start=Date.now();
+        return new Promise((resolve,reject)=>{
+            function check(){
+                const doc = getScheduleDocument();
+                if(doc) return resolve(doc);
+                if(Date.now()-start > maxWait) return reject(new Error('课表加载超时'));
+                setTimeout(check,300);
+            }
+            check();
+        });
+    }
+
+    async function main() {
+        try {
+            const doc = await waitForScheduleDoc();
+            const config = parseConfig(doc);
+            const timeSlots = parseTimeSlots(doc);
+            const courses = parseCourses(doc);
+            await saveConfig(config);
+            await saveTimeSlots(timeSlots);
+            await saveCourses(courses);
+            AndroidBridge.notifyTaskCompletion();
+        } catch(e) {}
+    }
+
+    setTimeout(main, 500);
+})();