chuvsu-js 5.2.0 → 5.2.2
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/README.md +3 -0
- package/dist/common/certs.d.ts +4 -0
- package/dist/common/certs.js +67 -0
- package/dist/tt/domain/repository.js +0 -3
- package/dist/tt/domain/types.d.ts +3 -0
- package/dist/tt/parse/audience.js +10 -8
- package/dist/tt/parse/entry-parts.d.ts +5 -0
- package/dist/tt/parse/entry-parts.js +25 -0
- package/dist/tt/parse/full-schedule.js +10 -8
- package/dist/tt/parse/patterns.d.ts +2 -2
- package/dist/tt/parse/patterns.js +1 -1
- package/dist/tt/parse/teacher.js +10 -8
- package/dist/tt/types.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# chuvsu-js
|
|
2
2
|
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> Ооочень плохой вайбкод, библиотека будет переделана и не раз. Есть очень много пространства для упрощения использования и сохранения крутой архитектуры.
|
|
5
|
+
|
|
3
6
|
Типизированная библиотека для сервисов ЧГУ им. И. Н. Ульянова:
|
|
4
7
|
|
|
5
8
|
- `tt.chuvsu.ru` — расписания групп, преподавателей, аудиторий и вебинары;
|
package/dist/common/certs.d.ts
CHANGED
|
@@ -2,4 +2,8 @@
|
|
|
2
2
|
export declare const GLOBALSIGN_GCC_R3_DV_TLS_CA_2020 = "-----BEGIN CERTIFICATE-----\nMIIEsDCCA5igAwIBAgIQd70OB0LV2enQSdd00CpvmjANBgkqhkiG9w0BAQsFADBM\nMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv\nYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMDA3MjgwMDAwMDBaFw0y\nOTAzMTgwMDAwMDBaMFMxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu\nIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIEdDQyBSMyBEViBUTFMgQ0EgMjAy\nMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKxnlJV/de+OpwyvCXAJ\nIcxPCqkFPh1lttW2oljS3oUqPKq8qX6m7K0OVKaKG3GXi4CJ4fHVUgZYE6HRdjqj\nhhnuHY6EBCBegcUFgPG0scB12Wi8BHm9zKjWxo3Y2bwhO8Fvr8R42pW0eINc6OTb\nQXC0VWFCMVzpcqgz6X49KMZowAMFV6XqtItcG0cMS//9dOJs4oBlpuqX9INxMTGp\n6EASAF9cnlAGy/RXkVS9nOLCCa7pCYV+WgDKLTF+OK2Vxw3RUJ/p8009lQeUARv2\nUCcNNPCifYX1xIspvarkdjzLwzOdLahDdQbJON58zN4V+lMj0msg+c0KnywPIRp3\nBMkCAwEAAaOCAYUwggGBMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEF\nBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUDZjA\nc3+rvb3ZR0tJrQpKDKw+x3wwHwYDVR0jBBgwFoAUj/BLf6guRSSuTVD6Y5qL3uLd\nG7wwewYIKwYBBQUHAQEEbzBtMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcDIuZ2xv\nYmFsc2lnbi5jb20vcm9vdHIzMDsGCCsGAQUFBzAChi9odHRwOi8vc2VjdXJlLmds\nb2JhbHNpZ24uY29tL2NhY2VydC9yb290LXIzLmNydDA2BgNVHR8ELzAtMCugKaAn\nhiVodHRwOi8vY3JsLmdsb2JhbHNpZ24uY29tL3Jvb3QtcjMuY3JsMEcGA1UdIARA\nMD4wPAYEVR0gADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWdu\nLmNvbS9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAy8j/c550ea86oCkf\nr2W+ptTCYe6iVzvo7H0V1vUEADJOWelTv07Obf+YkEatdN1Jg09ctgSNv2h+LMTk\nKRZdAXmsE3N5ve+z1Oa9kuiu7284LjeS09zHJQB4DJJJkvtIbjL/ylMK1fbMHhAW\ni0O194TWvH3XWZGXZ6ByxTUIv1+kAIql/Mt29PmKraTT5jrzcVzQ5A9jw16yysuR\nXRrLODlkS1hyBjsfyTNZrmL1h117IFgntBA5SQNVl9ckedq5r4RSAU85jV8XK5UL\nREjRZt2I6M9Po9QL7guFLu4sPFJpwR1sPJvubS2THeo7SxYoNDtdyBHs7euaGcMa\nD/fayQ==\n-----END CERTIFICATE-----";
|
|
3
3
|
/** GlobalSign Root CA - R3 */
|
|
4
4
|
export declare const GLOBALSIGN_ROOT_R3 = "-----BEGIN CERTIFICATE-----\nMIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G\nA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp\nZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4\nMTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG\nA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8\nRgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT\ngHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm\nKPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd\nQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ\nXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw\nDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o\nLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU\nRUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp\njjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK\n6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX\nmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs\nMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH\nWD9f\n-----END CERTIFICATE-----";
|
|
5
|
+
/** GlobalSign GCC R46 DV TLS CA 2025 (intermediate) */
|
|
6
|
+
export declare const GLOBALSIGN_GCC_R46_DV_TLS_CA_2025 = "-----BEGIN CERTIFICATE-----\nMIIFfDCCA2SgAwIBAgIRAIRDWIEh21qyhJn/roBnC6QwDQYJKoZIhvcNAQELBQAw\nRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNV\nBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwHhcNMjUwOTE3MDI1NTQ0WhcNMjkwNjIz\nMDAwMDAwWjBUMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1z\nYTEqMCgGA1UEAxMhR2xvYmFsU2lnbiBHQ0MgUjQ2IERWIFRMUyBDQSAyMDI1MIIB\nIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApT+6pung92y3k3sp2FQMmKv2\nOqcOBNr9uk8IJUf/EyIHKfYRcIdgRuUu5T2PxIsvGYCAHQ3uSG/p6E1a16aMu9LV\nEG9DMXcW+rx6Why5IDOIOvRHjfPyQzAGurD4V/e71U4fW81n7pKIDtcWDFeSXoai\nt1cemN088vJH/L4iCSeQQ1qWsmo9VV4F8jrwWJEjOSrLLYBGHvvuteMlXzpjX2A7\npKcjOooEYU78ygS2K0YOdyN7W2hIUvScVv5yG3sA/q4/tWXY1kyUC/Bb70IgL0/n\nwAYMia+OgDo5RPb+U8RUsBN4QH9qxgM75nBiOlqh/aTrrMYfYrbWOR3vioZQDQID\nAQABo4IBVTCCAVEwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMB\nMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAhp6X+lq/TDYeXNjntH74sp\nZI3sMB8GA1UdIwQYMBaAFANcq3OBh6jMsKbVlOI2lkn/BZksMHsGCCsGAQUFBwEB\nBG8wbTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuZ2xvYmFsc2lnbi5jb20vcm9v\ndHI0NjA7BggrBgEFBQcwAoYvaHR0cDovL3NlY3VyZS5nbG9iYWxzaWduLmNvbS9j\nYWNlcnQvcm9vdHI0Ni5jcnQwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n\nbG9iYWxzaWduLmNvbS9yb290cjQ2LmNybDAhBgNVHSAEGjAYMAgGBmeBDAECATAM\nBgorBgEEAaAyCgEDMA0GCSqGSIb3DQEBCwUAA4ICAQAqoA/ThPjFFFZKSDkoKTKx\nKA/K5/1469Z9wYkqd2P5btK/Hi+pUOpuJo39cHmXaNJrmVgYwTxo4n3VAB7DQWsm\nOXYOz5wOGLl6Aj5eR8aJ0GXGz1zS2iXsWwFjakS2WfLC25HE2t6XI2pQgNG0HyEY\noS90ddxMW51dsm5fUDy9AMGitsgHFjzibI7UbHr4i3srXk08E4Br8N7Sz0YAxjhC\nPANw4A8FaB9Q17CpGZI47CRLJSr4kzxk7sIB39FoqPYd5gtfL6NugWt/x2oRQ1xM\nNVa5QQDWRbiVxYoWkBmXwAWLYir/eRiEB195/CbzTDCEtqQBaId9jTFONPjkm0TZ\nryomaCABk93+GUYgeZzb/ovJ4QS6wxXTzq+ZDxHCTPF+dksaqD/CW6jmCJvEZqPy\nNhgQ96lPYc7ejqY9yET/4CtYMNPupjVbfepeLzE3n/wnTLKlR3n9V0dIhCjcrlJl\nm7WCAJy9nxkx0xmfUFzhFweDOCDsxfsdJ8GnOcFD2b0hyEIDnC+OSVF2G8MCwfn9\n0yfY/6Vg8dmStmLCpfmlndG9aFhxQkuXTBn/5Ojy/mJaT2mt8Idr2m3pxVSpGL1h\nqfCsCW+4rC3XQhQlmfaBc0205o3G8jVadKn/1yNrraM/BOBGESALCWJZTca/Z3+z\nzTYbjxDvBHjVJOwXrqfjyQ==\n-----END CERTIFICATE-----";
|
|
7
|
+
/** GlobalSign Root R46 */
|
|
8
|
+
export declare const GLOBALSIGN_ROOT_R46 = "-----BEGIN CERTIFICATE-----\nMIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA\nMEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD\nVQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy\nMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt\nc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ\nOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG\nvGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud\n316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo\n0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE\ny132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF\nzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE\n+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN\nI/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs\nx2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa\nByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC\n4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\nHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4\n7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg\nJuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti\n2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk\npnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF\nFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt\nrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk\nZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5\nu+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP\n4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6\nN3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3\nvouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6\n-----END CERTIFICATE-----";
|
|
5
9
|
export declare const CHUVSU_CA_CERTS: string[];
|
package/dist/common/certs.js
CHANGED
|
@@ -49,7 +49,74 @@ mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
|
|
|
49
49
|
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
|
|
50
50
|
WD9f
|
|
51
51
|
-----END CERTIFICATE-----`;
|
|
52
|
+
/** GlobalSign GCC R46 DV TLS CA 2025 (intermediate) */
|
|
53
|
+
export const GLOBALSIGN_GCC_R46_DV_TLS_CA_2025 = `-----BEGIN CERTIFICATE-----
|
|
54
|
+
MIIFfDCCA2SgAwIBAgIRAIRDWIEh21qyhJn/roBnC6QwDQYJKoZIhvcNAQELBQAw
|
|
55
|
+
RjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNV
|
|
56
|
+
BAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwHhcNMjUwOTE3MDI1NTQ0WhcNMjkwNjIz
|
|
57
|
+
MDAwMDAwWjBUMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1z
|
|
58
|
+
YTEqMCgGA1UEAxMhR2xvYmFsU2lnbiBHQ0MgUjQ2IERWIFRMUyBDQSAyMDI1MIIB
|
|
59
|
+
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApT+6pung92y3k3sp2FQMmKv2
|
|
60
|
+
OqcOBNr9uk8IJUf/EyIHKfYRcIdgRuUu5T2PxIsvGYCAHQ3uSG/p6E1a16aMu9LV
|
|
61
|
+
EG9DMXcW+rx6Why5IDOIOvRHjfPyQzAGurD4V/e71U4fW81n7pKIDtcWDFeSXoai
|
|
62
|
+
t1cemN088vJH/L4iCSeQQ1qWsmo9VV4F8jrwWJEjOSrLLYBGHvvuteMlXzpjX2A7
|
|
63
|
+
pKcjOooEYU78ygS2K0YOdyN7W2hIUvScVv5yG3sA/q4/tWXY1kyUC/Bb70IgL0/n
|
|
64
|
+
wAYMia+OgDo5RPb+U8RUsBN4QH9qxgM75nBiOlqh/aTrrMYfYrbWOR3vioZQDQID
|
|
65
|
+
AQABo4IBVTCCAVEwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMB
|
|
66
|
+
MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAhp6X+lq/TDYeXNjntH74sp
|
|
67
|
+
ZI3sMB8GA1UdIwQYMBaAFANcq3OBh6jMsKbVlOI2lkn/BZksMHsGCCsGAQUFBwEB
|
|
68
|
+
BG8wbTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuZ2xvYmFsc2lnbi5jb20vcm9v
|
|
69
|
+
dHI0NjA7BggrBgEFBQcwAoYvaHR0cDovL3NlY3VyZS5nbG9iYWxzaWduLmNvbS9j
|
|
70
|
+
YWNlcnQvcm9vdHI0Ni5jcnQwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
|
|
71
|
+
bG9iYWxzaWduLmNvbS9yb290cjQ2LmNybDAhBgNVHSAEGjAYMAgGBmeBDAECATAM
|
|
72
|
+
BgorBgEEAaAyCgEDMA0GCSqGSIb3DQEBCwUAA4ICAQAqoA/ThPjFFFZKSDkoKTKx
|
|
73
|
+
KA/K5/1469Z9wYkqd2P5btK/Hi+pUOpuJo39cHmXaNJrmVgYwTxo4n3VAB7DQWsm
|
|
74
|
+
OXYOz5wOGLl6Aj5eR8aJ0GXGz1zS2iXsWwFjakS2WfLC25HE2t6XI2pQgNG0HyEY
|
|
75
|
+
oS90ddxMW51dsm5fUDy9AMGitsgHFjzibI7UbHr4i3srXk08E4Br8N7Sz0YAxjhC
|
|
76
|
+
PANw4A8FaB9Q17CpGZI47CRLJSr4kzxk7sIB39FoqPYd5gtfL6NugWt/x2oRQ1xM
|
|
77
|
+
NVa5QQDWRbiVxYoWkBmXwAWLYir/eRiEB195/CbzTDCEtqQBaId9jTFONPjkm0TZ
|
|
78
|
+
ryomaCABk93+GUYgeZzb/ovJ4QS6wxXTzq+ZDxHCTPF+dksaqD/CW6jmCJvEZqPy
|
|
79
|
+
NhgQ96lPYc7ejqY9yET/4CtYMNPupjVbfepeLzE3n/wnTLKlR3n9V0dIhCjcrlJl
|
|
80
|
+
m7WCAJy9nxkx0xmfUFzhFweDOCDsxfsdJ8GnOcFD2b0hyEIDnC+OSVF2G8MCwfn9
|
|
81
|
+
0yfY/6Vg8dmStmLCpfmlndG9aFhxQkuXTBn/5Ojy/mJaT2mt8Idr2m3pxVSpGL1h
|
|
82
|
+
qfCsCW+4rC3XQhQlmfaBc0205o3G8jVadKn/1yNrraM/BOBGESALCWJZTca/Z3+z
|
|
83
|
+
zTYbjxDvBHjVJOwXrqfjyQ==
|
|
84
|
+
-----END CERTIFICATE-----`;
|
|
85
|
+
/** GlobalSign Root R46 */
|
|
86
|
+
export const GLOBALSIGN_ROOT_R46 = `-----BEGIN CERTIFICATE-----
|
|
87
|
+
MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA
|
|
88
|
+
MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD
|
|
89
|
+
VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy
|
|
90
|
+
MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt
|
|
91
|
+
c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB
|
|
92
|
+
AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ
|
|
93
|
+
OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG
|
|
94
|
+
vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud
|
|
95
|
+
316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo
|
|
96
|
+
0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE
|
|
97
|
+
y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF
|
|
98
|
+
zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE
|
|
99
|
+
+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN
|
|
100
|
+
I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs
|
|
101
|
+
x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa
|
|
102
|
+
ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC
|
|
103
|
+
4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
|
|
104
|
+
HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4
|
|
105
|
+
7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg
|
|
106
|
+
JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti
|
|
107
|
+
2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk
|
|
108
|
+
pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF
|
|
109
|
+
FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt
|
|
110
|
+
rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk
|
|
111
|
+
ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5
|
|
112
|
+
u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP
|
|
113
|
+
4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6
|
|
114
|
+
N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3
|
|
115
|
+
vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6
|
|
116
|
+
-----END CERTIFICATE-----`;
|
|
52
117
|
export const CHUVSU_CA_CERTS = [
|
|
53
118
|
GLOBALSIGN_GCC_R3_DV_TLS_CA_2020,
|
|
54
119
|
GLOBALSIGN_ROOT_R3,
|
|
120
|
+
GLOBALSIGN_GCC_R46_DV_TLS_CA_2025,
|
|
121
|
+
GLOBALSIGN_ROOT_R46,
|
|
55
122
|
];
|
|
@@ -597,9 +597,6 @@ export class TimetableRepository {
|
|
|
597
597
|
if (!observation.subject.trim()) {
|
|
598
598
|
throw new Error(`Empty lesson subject in ${source.sourceKey}/${observation.key}`);
|
|
599
599
|
}
|
|
600
|
-
if (!observation.type.trim()) {
|
|
601
|
-
throw new Error(`Empty lesson type in ${source.sourceKey}/${observation.key}`);
|
|
602
|
-
}
|
|
603
600
|
const relations = [
|
|
604
601
|
["group", observation.groups, observation.groups.values.map((value) => value.group)],
|
|
605
602
|
["teacher", observation.teachers, observation.teachers.values],
|
|
@@ -62,6 +62,7 @@ export interface LessonSeries {
|
|
|
62
62
|
academicYearStartYear: number;
|
|
63
63
|
period: AcademicPeriod;
|
|
64
64
|
subject: string;
|
|
65
|
+
/** Free-form portal label; empty when no source exposes one. */
|
|
65
66
|
type: string;
|
|
66
67
|
slotNumber?: number;
|
|
67
68
|
time?: TimeRange;
|
|
@@ -84,6 +85,7 @@ export interface LessonOccurrence {
|
|
|
84
85
|
nominalDate: LocalDate;
|
|
85
86
|
scheduledDate: LocalDate;
|
|
86
87
|
subject: string;
|
|
88
|
+
/** Free-form portal label; empty when no source exposes one. */
|
|
87
89
|
type: string;
|
|
88
90
|
slotNumber?: number;
|
|
89
91
|
time?: TimeRange;
|
|
@@ -105,6 +107,7 @@ interface ObservationBase {
|
|
|
105
107
|
/** Stable only inside one source page projection. */
|
|
106
108
|
key: string;
|
|
107
109
|
subject: string;
|
|
110
|
+
/** Free-form portal label; empty when this source does not expose one. */
|
|
108
111
|
type: string;
|
|
109
112
|
slotNumber?: number;
|
|
110
113
|
time?: TimeRange;
|
|
@@ -2,8 +2,8 @@ import { parseHtml, parseTeacher, parseTime, parseWeekParity, parseWeeks, text,
|
|
|
2
2
|
import { parseSemesterScheduleWith, parseSessionScheduleWith, } from "./full-schedule.js";
|
|
3
3
|
import { parseGroupsString } from "./groups.js";
|
|
4
4
|
import { parseSubstituteForDiv, parseSubstitutionDiv, parseTransferDiv, } from "./overlays.js";
|
|
5
|
-
import { LESSON_TYPE_GLOBAL_RE,
|
|
6
|
-
import { containsGroupCode, hasDistanceMarker, linesAfterSubject, stripDistanceMarker, } from "./entry-parts.js";
|
|
5
|
+
import { LESSON_TYPE_GLOBAL_RE, SUBGROUP_ANNOTATION_RE, SUBGROUP_RE, WEEKS_GLOBAL_RE, WEEKS_RE, } from "./patterns.js";
|
|
6
|
+
import { containsGroupCode, hasDistanceMarker, linesAfterSubject, parseLessonTypeAfterSubject, stripDistanceMarker, } from "./entry-parts.js";
|
|
7
7
|
export function parseRoomInfo(html) {
|
|
8
8
|
const doc = parseHtml(html);
|
|
9
9
|
// Name: <span class="htext"><nobr>Аудитория <span style="color: blue;">NAME</span></nobr></span>
|
|
@@ -104,10 +104,11 @@ function parseRoomSemesterEntry(el) {
|
|
|
104
104
|
cleanText = cleanText.replace(text(div), "");
|
|
105
105
|
}
|
|
106
106
|
const subjectEl = td.querySelector('span[style*="color: blue"]');
|
|
107
|
-
|
|
107
|
+
if (!subjectEl)
|
|
108
|
+
return null;
|
|
109
|
+
const subject = text(subjectEl);
|
|
108
110
|
if (!subject)
|
|
109
111
|
return null;
|
|
110
|
-
const typeMatch = cleanText.match(LESSON_TYPE_RE);
|
|
111
112
|
const weeksMatch = cleanText.match(WEEKS_RE);
|
|
112
113
|
const subgroupMatch = cleanText.match(SUBGROUP_RE);
|
|
113
114
|
const weekParity = parseWeekParity(cleanHtml);
|
|
@@ -127,7 +128,7 @@ function parseRoomSemesterEntry(el) {
|
|
|
127
128
|
const groups = parseGroupsString(groupsLine);
|
|
128
129
|
return {
|
|
129
130
|
subject,
|
|
130
|
-
type:
|
|
131
|
+
type: parseLessonTypeAfterSubject(subjectEl),
|
|
131
132
|
weeks: parseWeeks(weeksMatch?.[1] ?? ""),
|
|
132
133
|
teacher: teacherLine ? parseTeacher(teacherLine) : undefined,
|
|
133
134
|
groups: groups.length > 0 ? groups : undefined,
|
|
@@ -149,13 +150,14 @@ function parseRoomSessionEntry(td) {
|
|
|
149
150
|
const fullHtml = td.innerHTML ?? "";
|
|
150
151
|
const plainText = text(td);
|
|
151
152
|
const subjectEl = td.querySelector('span[style*="color: blue"]');
|
|
152
|
-
|
|
153
|
+
if (!subjectEl)
|
|
154
|
+
return null;
|
|
155
|
+
const subject = text(subjectEl);
|
|
153
156
|
if (!plainText || !subject)
|
|
154
157
|
return null;
|
|
155
158
|
const timeMatch = fullHtml.match(/<br\s*\/?>\s*(\d{2}:\d{2})\s*-\s*(\d{2}:\d{2})/);
|
|
156
159
|
if (!timeMatch)
|
|
157
160
|
return null;
|
|
158
|
-
const typeMatch = plainText.match(FLEXIBLE_LESSON_TYPE_RE_I);
|
|
159
161
|
const parts = linesAfterSubject(fullHtml, subject);
|
|
160
162
|
const teacherLine = parts.find((part) => !/^\d{2}:\d{2}\s*-\s*\d{2}:\d{2}$/.test(part) &&
|
|
161
163
|
!containsGroupCode(stripDistanceMarker(part)));
|
|
@@ -165,7 +167,7 @@ function parseRoomSessionEntry(td) {
|
|
|
165
167
|
return {
|
|
166
168
|
lesson: {
|
|
167
169
|
subject,
|
|
168
|
-
type:
|
|
170
|
+
type: parseLessonTypeAfterSubject(subjectEl).toLowerCase(),
|
|
169
171
|
teacher: teacherLine ? parseTeacher(stripDistanceMarker(teacherLine)) : undefined,
|
|
170
172
|
groups: groups.length > 0 ? groups : undefined,
|
|
171
173
|
subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
|
|
@@ -6,3 +6,8 @@ export declare function linesAfterSubject(html: string, subject: string): string
|
|
|
6
6
|
export declare function parseEntryRoom(html: string, subject: string): string;
|
|
7
7
|
export declare function stripDistanceMarker(value: string): string;
|
|
8
8
|
export declare function containsGroupCode(value: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Lesson type is free-form portal data. Its stable marker is position: the first
|
|
11
|
+
* parenthesized value immediately after the blue subject span.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseLessonTypeAfterSubject(subjectElement: Element): string;
|
|
@@ -2,6 +2,9 @@ const BR_RE = /<br\s*\/?>/i;
|
|
|
2
2
|
const TAG_RE = /<[^>]*>/g;
|
|
3
3
|
const DISTANCE_RE = /(?:^|[^\p{L}\p{N}])(?:дистанционно|ДОТ)(?=$|[^\p{L}\p{N}])/iu;
|
|
4
4
|
const GROUP_TOKEN_RE = /^[A-ZА-ЯЁ]{1,}(?:-[A-ZА-ЯЁa-zа-яё0-9]+)+$/u;
|
|
5
|
+
const LEADING_PARENTHESIZED_RE = /^\s*\(([^()]*)\)/u;
|
|
6
|
+
const WEEK_METADATA_RE = /(?:^|\s)нед\.?(?:\s|$)/iu;
|
|
7
|
+
const SUBGROUP_METADATA_RE = /^\d+\s*подгруппа$/iu;
|
|
5
8
|
export function entryHtmlLines(html) {
|
|
6
9
|
return html.split(BR_RE).map((line) => line.trim());
|
|
7
10
|
}
|
|
@@ -50,3 +53,25 @@ export function stripDistanceMarker(value) {
|
|
|
50
53
|
export function containsGroupCode(value) {
|
|
51
54
|
return value.split(/\s+/).some((token) => GROUP_TOKEN_RE.test(token));
|
|
52
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Lesson type is free-form portal data. Its stable marker is position: the first
|
|
58
|
+
* parenthesized value immediately after the blue subject span.
|
|
59
|
+
*/
|
|
60
|
+
export function parseLessonTypeAfterSubject(subjectElement) {
|
|
61
|
+
let suffix = "";
|
|
62
|
+
for (let sibling = subjectElement.nextSibling; sibling; sibling = sibling.nextSibling) {
|
|
63
|
+
if (sibling.tagName?.toLowerCase() === "br")
|
|
64
|
+
break;
|
|
65
|
+
suffix += sibling.textContent ?? "";
|
|
66
|
+
}
|
|
67
|
+
const candidate = suffix.match(LEADING_PARENTHESIZED_RE)?.[1]
|
|
68
|
+
?.trim()
|
|
69
|
+
.replace(/\.$/, "") ?? "";
|
|
70
|
+
if (!candidate ||
|
|
71
|
+
WEEK_METADATA_RE.test(candidate) ||
|
|
72
|
+
SUBGROUP_METADATA_RE.test(candidate) ||
|
|
73
|
+
candidate.toUpperCase() === "ДОТ") {
|
|
74
|
+
return "";
|
|
75
|
+
}
|
|
76
|
+
return candidate;
|
|
77
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseHtml, parseTeacher, parseTime, parseWeekParity, parseWeeks, text, } from "../../common/parse.js";
|
|
2
2
|
import { parseSubstitutionDiv, parseTransferDiv, } from "./overlays.js";
|
|
3
|
-
import {
|
|
4
|
-
import { hasDistanceMarker, containsGroupCode, linesAfterSubject, parseEntryRoom, stripDistanceMarker, } from "./entry-parts.js";
|
|
3
|
+
import { SUBGROUP_RE, WEEKS_RE } from "./patterns.js";
|
|
4
|
+
import { hasDistanceMarker, containsGroupCode, linesAfterSubject, parseLessonTypeAfterSubject, parseEntryRoom, stripDistanceMarker, } from "./entry-parts.js";
|
|
5
5
|
export function parseGroupSchedule(html) {
|
|
6
6
|
const doc = parseHtml(html);
|
|
7
7
|
// Session layout has date-based cells with ids like "trd20251224"
|
|
@@ -99,10 +99,11 @@ function parseSemesterEntry(el) {
|
|
|
99
99
|
cleanText = cleanText.replace(text(div), "");
|
|
100
100
|
}
|
|
101
101
|
const subjectEl = td.querySelector('span[style*="color: blue"]');
|
|
102
|
-
|
|
102
|
+
if (!subjectEl)
|
|
103
|
+
return null;
|
|
104
|
+
const subject = text(subjectEl);
|
|
103
105
|
if (!subject)
|
|
104
106
|
return null;
|
|
105
|
-
const typeMatch = cleanText.match(LESSON_TYPE_RE);
|
|
106
107
|
const weeksMatch = cleanText.match(WEEKS_RE);
|
|
107
108
|
const room = parseEntryRoom(cleanHtml, subject);
|
|
108
109
|
const teacherLine = linesAfterSubject(cleanHtml, subject).find((line) => {
|
|
@@ -114,7 +115,7 @@ function parseSemesterEntry(el) {
|
|
|
114
115
|
return {
|
|
115
116
|
room: room || undefined,
|
|
116
117
|
subject,
|
|
117
|
-
type:
|
|
118
|
+
type: parseLessonTypeAfterSubject(subjectEl),
|
|
118
119
|
weeks: parseWeeks(weeksMatch?.[1] ?? ""),
|
|
119
120
|
teacher: teacherLine ? parseTeacher(teacherLine) : undefined,
|
|
120
121
|
subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
|
|
@@ -170,13 +171,14 @@ function parseSessionEntry(td) {
|
|
|
170
171
|
return null;
|
|
171
172
|
const possibleChanges = (td.getAttribute("class") ?? "").includes("want") || undefined;
|
|
172
173
|
const subjectEl = td.querySelector('span[style*="color: blue"]');
|
|
173
|
-
|
|
174
|
+
if (!subjectEl)
|
|
175
|
+
return null;
|
|
176
|
+
const subject = text(subjectEl);
|
|
174
177
|
if (!subject)
|
|
175
178
|
return null;
|
|
176
179
|
const room = parseEntryRoom(fullHtml, subject);
|
|
177
180
|
// Type: parenthesized text after </span>, case-insensitive
|
|
178
|
-
const
|
|
179
|
-
const type = typeMatch ? typeMatch[1].replace(/\.$/, "").toLowerCase() : "";
|
|
181
|
+
const type = parseLessonTypeAfterSubject(subjectEl).toLowerCase();
|
|
180
182
|
const subgroupMatch = plainText.match(SUBGROUP_RE);
|
|
181
183
|
const parts = linesAfterSubject(fullHtml, subject);
|
|
182
184
|
const teacherPart = parts.find((part) => stripDistanceMarker(part).length > 0 &&
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export declare const LESSON_TYPE_PATTERN = "\u043B\u043A|\u043F\u0440|\u043B\u0431|\u0437\u0430\u0447\u043E|\u0437\u0430\u0447|\u044D\u043A\u0437|\u043A\u043E\u043D\u0441|\u043A\u043F|\u0438\u0437|\u0433\u0437|\u043A\u0440\u043F";
|
|
2
|
-
export declare const FLEXIBLE_LESSON_TYPE_PATTERN = "(?:\u043B\u043A|\u043F\u0440|\u043B\u0431|\u0437\u0430\u0447\u043E|\u0437\u0430\u0447|\u044D\u043A\u0437|\u043A\u043E\u043D\u0441|\u043A\u043F|\u0438\u0437|\u0433\u0437|\u043A\u0440\u043F)\\.?|\u042D\u043A\u0437";
|
|
1
|
+
export declare const LESSON_TYPE_PATTERN = "\u043B\u043A|\u043F\u0440|\u043B\u0431|\u0437\u0430\u0447\u043E|\u0437\u0430\u0447|\u044D\u043A\u0437|\u043A\u043E\u043D\u0441|\u043A\u043F|\u0438\u0437|\u0433\u0437|\u043A\u0440\u043F|\u043A\u0440";
|
|
2
|
+
export declare const FLEXIBLE_LESSON_TYPE_PATTERN = "(?:\u043B\u043A|\u043F\u0440|\u043B\u0431|\u0437\u0430\u0447\u043E|\u0437\u0430\u0447|\u044D\u043A\u0437|\u043A\u043E\u043D\u0441|\u043A\u043F|\u0438\u0437|\u0433\u0437|\u043A\u0440\u043F|\u043A\u0440)\\.?|\u042D\u043A\u0437";
|
|
3
3
|
export declare const LESSON_TYPE_RE: RegExp;
|
|
4
4
|
export declare const LESSON_TYPE_RE_I: RegExp;
|
|
5
5
|
export declare const FLEXIBLE_LESSON_TYPE_RE_I: RegExp;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const LESSON_TYPE_PATTERN = "
|
|
1
|
+
export const LESSON_TYPE_PATTERN = "лк|пр|лб|зачо|зач|экз|конс|кп|из|гз|крп|кр";
|
|
2
2
|
export const FLEXIBLE_LESSON_TYPE_PATTERN = `(?:${LESSON_TYPE_PATTERN})\\.?|Экз`;
|
|
3
3
|
export const LESSON_TYPE_RE = new RegExp(`\\((${LESSON_TYPE_PATTERN})\\)`, "i");
|
|
4
4
|
export const LESSON_TYPE_RE_I = new RegExp(`\\((${LESSON_TYPE_PATTERN})\\)`, "i");
|
package/dist/tt/parse/teacher.js
CHANGED
|
@@ -2,8 +2,8 @@ import { parseHtml, parseTime, parseWeekParity, parseWeeks, text, } from "../../
|
|
|
2
2
|
import { parseSemesterScheduleWith, parseSessionScheduleWith, } from "./full-schedule.js";
|
|
3
3
|
import { parseGroupsString } from "./groups.js";
|
|
4
4
|
import { parseSubstituteForDiv, parseSubstitutionDiv, parseTransferDiv, } from "./overlays.js";
|
|
5
|
-
import {
|
|
6
|
-
import { containsGroupCode, hasDistanceMarker, linesAfterSubject, parseEntryRoom, stripDistanceMarker, } from "./entry-parts.js";
|
|
5
|
+
import { SUBGROUP_RE, WEEKS_RE } from "./patterns.js";
|
|
6
|
+
import { containsGroupCode, hasDistanceMarker, linesAfterSubject, parseEntryRoom, parseLessonTypeAfterSubject, stripDistanceMarker, } from "./entry-parts.js";
|
|
7
7
|
export function parseTeacherSchedule(html) {
|
|
8
8
|
const doc = parseHtml(html);
|
|
9
9
|
if (doc.querySelector('td[id^="trd2"]')) {
|
|
@@ -49,10 +49,11 @@ function parseTeacherSemesterEntry(el) {
|
|
|
49
49
|
cleanText = cleanText.replace(text(div), "");
|
|
50
50
|
}
|
|
51
51
|
const subjectEl = td.querySelector('span[style*="color: blue"]');
|
|
52
|
-
|
|
52
|
+
if (!subjectEl)
|
|
53
|
+
return null;
|
|
54
|
+
const subject = text(subjectEl);
|
|
53
55
|
if (!subject)
|
|
54
56
|
return null;
|
|
55
|
-
const typeMatch = cleanText.match(LESSON_TYPE_RE);
|
|
56
57
|
const weeksMatch = cleanText.match(WEEKS_RE);
|
|
57
58
|
const room = parseEntryRoom(cleanHtml, subject);
|
|
58
59
|
const groupsLine = linesAfterSubject(cleanHtml, subject).find((line) => containsGroupCode(stripDistanceMarker(line))) ?? "";
|
|
@@ -62,7 +63,7 @@ function parseTeacherSemesterEntry(el) {
|
|
|
62
63
|
return {
|
|
63
64
|
room: room || undefined,
|
|
64
65
|
subject,
|
|
65
|
-
type:
|
|
66
|
+
type: parseLessonTypeAfterSubject(subjectEl),
|
|
66
67
|
weeks: parseWeeks(weeksMatch?.[1] ?? ""),
|
|
67
68
|
groups: groups.length > 0 ? groups : undefined,
|
|
68
69
|
subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
|
|
@@ -79,12 +80,13 @@ function parseTeacherSessionEntry(td) {
|
|
|
79
80
|
return null;
|
|
80
81
|
const possibleChanges = (td.getAttribute("class") ?? "").includes("want") || undefined;
|
|
81
82
|
const subjectEl = td.querySelector('span[style*="color: blue"]');
|
|
82
|
-
|
|
83
|
+
if (!subjectEl)
|
|
84
|
+
return null;
|
|
85
|
+
const subject = text(subjectEl);
|
|
83
86
|
if (!subject)
|
|
84
87
|
return null;
|
|
85
88
|
const room = parseEntryRoom(fullHtml, subject);
|
|
86
|
-
const
|
|
87
|
-
const type = typeMatch ? typeMatch[1].replace(/\.$/, "").toLowerCase() : "";
|
|
89
|
+
const type = parseLessonTypeAfterSubject(subjectEl).toLowerCase();
|
|
88
90
|
const subgroupMatch = plainText.match(SUBGROUP_RE);
|
|
89
91
|
const timeMatch = fullHtml.match(/<br\s*\/?>\s*(\d{2}:\d{2})\s*-\s*(\d{2}:\d{2})/);
|
|
90
92
|
if (!timeMatch)
|
package/dist/tt/types.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ export interface ParsedLesson {
|
|
|
71
71
|
/** Missing means this source projection does not expose room information. */
|
|
72
72
|
room?: string | null;
|
|
73
73
|
subject: string;
|
|
74
|
+
/** Free-form portal label; empty when this source does not expose one. */
|
|
74
75
|
type: string;
|
|
75
76
|
/** Recurrence range; absent for concrete session lessons or unknown evidence. */
|
|
76
77
|
weeks?: WeekRange;
|