etc-lab-extension 1.0.4__tar.gz
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.
- etc_lab_extension-1.0.4/LICENSE +21 -0
- etc_lab_extension-1.0.4/PKG-INFO +132 -0
- etc_lab_extension-1.0.4/README.md +101 -0
- etc_lab_extension-1.0.4/etc_lab_extension/__init__.py +26 -0
- etc_lab_extension-1.0.4/etc_lab_extension/forensics.py +47 -0
- etc_lab_extension-1.0.4/etc_lab_extension/formatters.py +80 -0
- etc_lab_extension-1.0.4/etc_lab_extension/py.typed +1 -0
- etc_lab_extension-1.0.4/etc_lab_extension/supervision/__init__.py +39 -0
- etc_lab_extension-1.0.4/etc_lab_extension/supervision/buffer.py +37 -0
- etc_lab_extension-1.0.4/etc_lab_extension/supervision/heartbeat.py +39 -0
- etc_lab_extension-1.0.4/etc_lab_extension/supervision/kiosk.py +35 -0
- etc_lab_extension-1.0.4/etc_lab_extension/supervision/policy.py +23 -0
- etc_lab_extension-1.0.4/etc_lab_extension/supervision/student.py +29 -0
- etc_lab_extension-1.0.4/etc_lab_extension/supervision/supervisor.py +56 -0
- etc_lab_extension-1.0.4/etc_lab_extension/supervision/telemetry.py +101 -0
- etc_lab_extension-1.0.4/etc_lab_extension/supervision/watchdog.py +47 -0
- etc_lab_extension-1.0.4/etc_lab_extension/utils.py +8 -0
- etc_lab_extension-1.0.4/etc_lab_extension.egg-info/PKG-INFO +132 -0
- etc_lab_extension-1.0.4/etc_lab_extension.egg-info/SOURCES.txt +21 -0
- etc_lab_extension-1.0.4/etc_lab_extension.egg-info/dependency_links.txt +1 -0
- etc_lab_extension-1.0.4/etc_lab_extension.egg-info/top_level.txt +1 -0
- etc_lab_extension-1.0.4/pyproject.toml +55 -0
- etc_lab_extension-1.0.4/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021-2026 Edutech Core JSC (ETC Corp) & Dang Tuan Anh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: etc-lab-extension
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: ETC Classroom Lab Extension Suite: Terminal UI formatters, SQLite forensic evidence extractors, and concurrency supervision daemons.
|
|
5
|
+
Author-email: Dang Tuan Anh <tuandanga@etc-corp.internal>, Edutech Core JSC R&D <rnd@etc-corp.internal>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/etc-corp/etc-lab-extension
|
|
8
|
+
Project-URL: Documentation, https://github.com/etc-corp/etc-lab-extension#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/etc-corp/etc-lab-extension.git
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/etc-corp/etc-lab-extension/issues
|
|
11
|
+
Keywords: education,classroom,terminal-ui,forensics,concurrency,python-education,edtech,etc-corp
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Education
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Education
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Topic :: System :: Monitoring
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# etc-lab-extension
|
|
33
|
+
|
|
34
|
+
[](https://pypi.org/project/etc-lab-extension/)
|
|
35
|
+
[](https://opensource.org/licenses/MIT)
|
|
36
|
+
[](https://www.python.org/)
|
|
37
|
+
|
|
38
|
+
**ETC Classroom Lab Extension Suite** is an educational extension library developed by Edutech Core JSC (ETC Corp) for computer science workstations. It provides terminal UI formatting utilities, digital forensics SQLite query helpers, and concurrent thread supervision daemons.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 📦 Features
|
|
43
|
+
|
|
44
|
+
### 1. Terminal UI & Frame Formatting (`etc_lab_extension.formatters`)
|
|
45
|
+
* **`TerminalFormatter`**: Render ASCII box frames and centered headers directly in console terminals without heavy GUI dependencies.
|
|
46
|
+
* **`LogParser`**: Parse workstation log entries, shift timestamps, and penalty audit events.
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from etc_lab_extension import TerminalFormatter
|
|
50
|
+
|
|
51
|
+
box = TerminalFormatter.render_box(
|
|
52
|
+
title="WORKSTATION STATION-082",
|
|
53
|
+
content="Module: Concurrency Supervisor\nStatus: Operational"
|
|
54
|
+
)
|
|
55
|
+
print(box)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Output:
|
|
59
|
+
```text
|
|
60
|
+
==================================================
|
|
61
|
+
| WORKSTATION STATION-082 |
|
|
62
|
+
--------------------------------------------------
|
|
63
|
+
| Module: Concurrency Supervisor |
|
|
64
|
+
| Status: Operational |
|
|
65
|
+
==================================================
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Digital Forensics SQLite Helpers (`etc_lab_extension.forensics`)
|
|
69
|
+
* **`BrowserHistoryForensics`**: Query and inspect SQLite browsing history and forensic evidence records.
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from etc_lab_extension import BrowserHistoryForensics
|
|
73
|
+
|
|
74
|
+
forensics = BrowserHistoryForensics("data/sandbox/browser_history.db")
|
|
75
|
+
records = forensics.query_visits(keyword="Vinmec")
|
|
76
|
+
for r in records:
|
|
77
|
+
print(f"[{r['visit_time']}] {r['title']}")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. Concurrency & Supervision Daemons (`etc_lab_extension.supervision`)
|
|
81
|
+
* **`KioskSecuritySupervisor`**: Window focus and kiosk interaction hooks.
|
|
82
|
+
* **`HeartbeatPulseMonitor`**: Periodic 500ms heartbeat daemon verifying workstation execution liveness.
|
|
83
|
+
* **`TelemetryNetworkClient`**: Telemetry packet serialization and gateway dispatcher.
|
|
84
|
+
* **`ExecutionBufferManager`**: Standard I/O memory buffer management with automatic heap purge.
|
|
85
|
+
* **`StudentThread`**: Isolated background worker executing submitted code safely.
|
|
86
|
+
* **`CorporateWatchdog`**: Proprietary resource lock monitor and deadlock guard.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from etc_lab_extension.supervision import (
|
|
90
|
+
student_thread,
|
|
91
|
+
HeartbeatPulseMonitor,
|
|
92
|
+
CorporateWatchdog
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Initialize heartbeat daemon
|
|
96
|
+
heartbeat = HeartbeatPulseMonitor(station_id="STATION-082")
|
|
97
|
+
heartbeat.start()
|
|
98
|
+
|
|
99
|
+
# Execute student thread
|
|
100
|
+
student_thread.run()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 🚀 Installation
|
|
106
|
+
|
|
107
|
+
Install the package directly from PyPI via pip:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install etc-lab-extension
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Or install from source:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
git clone https://github.com/etc-corp/etc-lab-extension.git
|
|
117
|
+
cd etc-lab-extension
|
|
118
|
+
pip install .
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 📋 Requirements
|
|
124
|
+
* Python >= 3.8
|
|
125
|
+
* Standard CPython library (no third-party heavy dependencies required)
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## ⚖️ License
|
|
130
|
+
This project is licensed under the terms of the [MIT License](LICENSE).
|
|
131
|
+
|
|
132
|
+
Copyright (c) 2021-2026 Edutech Core JSC (ETC Corp) & Dang Tuan Anh.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# etc-lab-extension
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/etc-lab-extension/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.python.org/)
|
|
6
|
+
|
|
7
|
+
**ETC Classroom Lab Extension Suite** is an educational extension library developed by Edutech Core JSC (ETC Corp) for computer science workstations. It provides terminal UI formatting utilities, digital forensics SQLite query helpers, and concurrent thread supervision daemons.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 📦 Features
|
|
12
|
+
|
|
13
|
+
### 1. Terminal UI & Frame Formatting (`etc_lab_extension.formatters`)
|
|
14
|
+
* **`TerminalFormatter`**: Render ASCII box frames and centered headers directly in console terminals without heavy GUI dependencies.
|
|
15
|
+
* **`LogParser`**: Parse workstation log entries, shift timestamps, and penalty audit events.
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from etc_lab_extension import TerminalFormatter
|
|
19
|
+
|
|
20
|
+
box = TerminalFormatter.render_box(
|
|
21
|
+
title="WORKSTATION STATION-082",
|
|
22
|
+
content="Module: Concurrency Supervisor\nStatus: Operational"
|
|
23
|
+
)
|
|
24
|
+
print(box)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Output:
|
|
28
|
+
```text
|
|
29
|
+
==================================================
|
|
30
|
+
| WORKSTATION STATION-082 |
|
|
31
|
+
--------------------------------------------------
|
|
32
|
+
| Module: Concurrency Supervisor |
|
|
33
|
+
| Status: Operational |
|
|
34
|
+
==================================================
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2. Digital Forensics SQLite Helpers (`etc_lab_extension.forensics`)
|
|
38
|
+
* **`BrowserHistoryForensics`**: Query and inspect SQLite browsing history and forensic evidence records.
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from etc_lab_extension import BrowserHistoryForensics
|
|
42
|
+
|
|
43
|
+
forensics = BrowserHistoryForensics("data/sandbox/browser_history.db")
|
|
44
|
+
records = forensics.query_visits(keyword="Vinmec")
|
|
45
|
+
for r in records:
|
|
46
|
+
print(f"[{r['visit_time']}] {r['title']}")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 3. Concurrency & Supervision Daemons (`etc_lab_extension.supervision`)
|
|
50
|
+
* **`KioskSecuritySupervisor`**: Window focus and kiosk interaction hooks.
|
|
51
|
+
* **`HeartbeatPulseMonitor`**: Periodic 500ms heartbeat daemon verifying workstation execution liveness.
|
|
52
|
+
* **`TelemetryNetworkClient`**: Telemetry packet serialization and gateway dispatcher.
|
|
53
|
+
* **`ExecutionBufferManager`**: Standard I/O memory buffer management with automatic heap purge.
|
|
54
|
+
* **`StudentThread`**: Isolated background worker executing submitted code safely.
|
|
55
|
+
* **`CorporateWatchdog`**: Proprietary resource lock monitor and deadlock guard.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from etc_lab_extension.supervision import (
|
|
59
|
+
student_thread,
|
|
60
|
+
HeartbeatPulseMonitor,
|
|
61
|
+
CorporateWatchdog
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Initialize heartbeat daemon
|
|
65
|
+
heartbeat = HeartbeatPulseMonitor(station_id="STATION-082")
|
|
66
|
+
heartbeat.start()
|
|
67
|
+
|
|
68
|
+
# Execute student thread
|
|
69
|
+
student_thread.run()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 🚀 Installation
|
|
75
|
+
|
|
76
|
+
Install the package directly from PyPI via pip:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install etc-lab-extension
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Or install from source:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
git clone https://github.com/etc-corp/etc-lab-extension.git
|
|
86
|
+
cd etc-lab-extension
|
|
87
|
+
pip install .
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 📋 Requirements
|
|
93
|
+
* Python >= 3.8
|
|
94
|
+
* Standard CPython library (no third-party heavy dependencies required)
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## ⚖️ License
|
|
99
|
+
This project is licensed under the terms of the [MIT License](LICENSE).
|
|
100
|
+
|
|
101
|
+
Copyright (c) 2021-2026 Edutech Core JSC (ETC Corp) & Dang Tuan Anh.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# ETC Classroom Lab Extension Suite
|
|
3
|
+
# Package: etc_lab_extension
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - Edutech Core JSC
|
|
5
|
+
# Distribution: ETC-LAB-EXT-v1.0.4-20211128
|
|
6
|
+
# ==============================================================================
|
|
7
|
+
|
|
8
|
+
from .formatters import TerminalFormatter, LogParser, LogEntry
|
|
9
|
+
from .forensics import BrowserHistoryForensics
|
|
10
|
+
from . import supervision
|
|
11
|
+
from . import formatters as utils
|
|
12
|
+
|
|
13
|
+
__version__ = "1.0.4"
|
|
14
|
+
__author__ = "Dang Tuan Anh"
|
|
15
|
+
__entity__ = "Edutech Core JSC (ETC Corp)"
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"TerminalFormatter",
|
|
19
|
+
"LogParser",
|
|
20
|
+
"LogEntry",
|
|
21
|
+
"BrowserHistoryForensics",
|
|
22
|
+
"supervision",
|
|
23
|
+
"utils",
|
|
24
|
+
"__version__"
|
|
25
|
+
]
|
|
26
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# etc_lab_extension / forensics.py
|
|
3
|
+
# Digital Forensics and Evidence Inspection Utilities
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - 26/11/2021
|
|
5
|
+
# =============================================================================
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sqlite3
|
|
9
|
+
from typing import List, Dict, Any, Optional
|
|
10
|
+
|
|
11
|
+
class BrowserHistoryForensics:
|
|
12
|
+
"""Bộ giải mã và truy vấn di vật số từ SQLite browser_history.db."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, db_path: Optional[str] = None):
|
|
15
|
+
if db_path is None:
|
|
16
|
+
# Tự động tìm db trong sandbox hoặc thư mục hiện tại
|
|
17
|
+
candidates = [
|
|
18
|
+
"data/sandbox/browser_history.db",
|
|
19
|
+
"browser_history.db",
|
|
20
|
+
os.path.join(os.path.dirname(__file__), "..", "..", "..", "data", "sandbox", "browser_history.db")
|
|
21
|
+
]
|
|
22
|
+
self.db_path = next((c for c in candidates if os.path.exists(c)), "data/sandbox/browser_history.db")
|
|
23
|
+
else:
|
|
24
|
+
self.db_path = db_path
|
|
25
|
+
|
|
26
|
+
def query_visits(self, keyword: str = "") -> List[Dict[str, Any]]:
|
|
27
|
+
"""Truy vấn các lượt tra cứu theo từ khóa."""
|
|
28
|
+
if not os.path.exists(self.db_path):
|
|
29
|
+
return []
|
|
30
|
+
|
|
31
|
+
conn = sqlite3.connect(self.db_path)
|
|
32
|
+
cur = conn.cursor()
|
|
33
|
+
try:
|
|
34
|
+
if keyword:
|
|
35
|
+
query = "SELECT id, url, title, visit_time FROM history_visits WHERE title LIKE ? OR url LIKE ? ORDER BY id ASC"
|
|
36
|
+
cur.execute(query, (f"%{keyword}%", f"%{keyword}%"))
|
|
37
|
+
else:
|
|
38
|
+
query = "SELECT id, url, title, visit_time FROM history_visits ORDER BY id ASC"
|
|
39
|
+
cur.execute(query)
|
|
40
|
+
|
|
41
|
+
rows = cur.fetchall()
|
|
42
|
+
return [
|
|
43
|
+
{"id": r[0], "url": r[1], "title": r[2], "visit_time": r[3]}
|
|
44
|
+
for r in rows
|
|
45
|
+
]
|
|
46
|
+
finally:
|
|
47
|
+
conn.close()
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# etc_lab_extension / formatters.py
|
|
3
|
+
# Terminal UI formatting and schedule log parsing utilities
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - Edutech Core JSC
|
|
5
|
+
# =============================================================================
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class LogEntry:
|
|
13
|
+
timestamp: str
|
|
14
|
+
level: str
|
|
15
|
+
source: str
|
|
16
|
+
message: str
|
|
17
|
+
is_penalty: bool = False
|
|
18
|
+
|
|
19
|
+
class TerminalFormatter:
|
|
20
|
+
"""Công cụ định dạng bảng và khung giao diện văn bản cho người học Python."""
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def render_box(title: str, content: str = "", width: int = 50, border_char: str = "=") -> str:
|
|
24
|
+
"""Tạo khung viền console vuông vắn chuẩn ASCII."""
|
|
25
|
+
lines = [line.strip() for line in content.strip().split("\n")] if content else []
|
|
26
|
+
border = border_char * width
|
|
27
|
+
|
|
28
|
+
result = [border]
|
|
29
|
+
if title:
|
|
30
|
+
centered_title = f" {title.strip()} ".center(width - 2, " ")
|
|
31
|
+
result.append(f"|{centered_title}|")
|
|
32
|
+
if lines:
|
|
33
|
+
result.append("-" * width)
|
|
34
|
+
|
|
35
|
+
for line in lines:
|
|
36
|
+
if len(line) > width - 4:
|
|
37
|
+
line = line[:width - 7] + "..."
|
|
38
|
+
padded = line.ljust(width - 4)
|
|
39
|
+
result.append(f"| {padded} |")
|
|
40
|
+
|
|
41
|
+
result.append(border)
|
|
42
|
+
return "\n".join(result)
|
|
43
|
+
|
|
44
|
+
def box_wrap(self, title: str, content: str = "", width: int = 50, border_char: str = "=") -> str:
|
|
45
|
+
return self.render_box(title, content, width, border_char)
|
|
46
|
+
|
|
47
|
+
class LogParser:
|
|
48
|
+
"""Bộ bóc tách và phân tích các tệp log ca làm việc và sự kiện hệ thống."""
|
|
49
|
+
|
|
50
|
+
LOG_REGEX = re.compile(r"^\[(?P<timestamp>[^\]]+)\]\s+\[(?P<level>[^\]]+)\]\s+(?:\[(?P<source>[^\]]+)\]\s+)?(?P<message>.*)$")
|
|
51
|
+
|
|
52
|
+
def parse_line(self, line: str) -> Optional[LogEntry]:
|
|
53
|
+
"""Phân tích một dòng log định dạng [TIME] [LEVEL] Message."""
|
|
54
|
+
line = line.strip()
|
|
55
|
+
if not line:
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
match = self.LOG_REGEX.match(line)
|
|
59
|
+
if match:
|
|
60
|
+
gd = match.groupdict()
|
|
61
|
+
msg = gd["message"]
|
|
62
|
+
is_penalty = ("phạt" in msg.lower() or "penalty" in msg.lower() or "trễ" in msg.lower())
|
|
63
|
+
return LogEntry(
|
|
64
|
+
timestamp=gd["timestamp"],
|
|
65
|
+
level=gd["level"],
|
|
66
|
+
source=gd["source"] or "SYSTEM",
|
|
67
|
+
message=msg,
|
|
68
|
+
is_penalty=is_penalty
|
|
69
|
+
)
|
|
70
|
+
return LogEntry(timestamp="UNKNOWN", level="INFO", source="RAW", message=line)
|
|
71
|
+
|
|
72
|
+
def parse_file(self, file_path: str) -> List[LogEntry]:
|
|
73
|
+
"""Đọc và bóc tách toàn bộ tệp log thành danh sách LogEntry."""
|
|
74
|
+
entries = []
|
|
75
|
+
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
|
76
|
+
for line in f:
|
|
77
|
+
entry = self.parse_line(line)
|
|
78
|
+
if entry:
|
|
79
|
+
entries.append(entry)
|
|
80
|
+
return entries
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Marker file for PEP 561. The etc_lab_extension package uses inline type annotations.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# ETC Classroom Supervision Suite
|
|
3
|
+
# Package: etc_lab_extension.supervision
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - Edutech Core JSC
|
|
5
|
+
# Incident Stamp: DEV-082 (November 2021)
|
|
6
|
+
# ==============================================================================
|
|
7
|
+
|
|
8
|
+
from .policy import KioskSecurityPolicy
|
|
9
|
+
from .kiosk import KioskSecuritySupervisor
|
|
10
|
+
from .telemetry import (
|
|
11
|
+
TelemetryPacket,
|
|
12
|
+
TelemetryNetworkClient,
|
|
13
|
+
TelemetryQueueWorker,
|
|
14
|
+
STAGING_GATEWAY_HOST,
|
|
15
|
+
STAGING_GATEWAY_PORT
|
|
16
|
+
)
|
|
17
|
+
from .heartbeat import HeartbeatPulseMonitor
|
|
18
|
+
from .buffer import ExecutionBufferManager
|
|
19
|
+
from .student import StudentThread, student_thread
|
|
20
|
+
from .watchdog import CorporateWatchdog, ResourceDeadlockException
|
|
21
|
+
from .supervisor import WorkerThreadSupervisor, DiagnosticHealthReporter
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"KioskSecurityPolicy",
|
|
25
|
+
"KioskSecuritySupervisor",
|
|
26
|
+
"TelemetryPacket",
|
|
27
|
+
"TelemetryNetworkClient",
|
|
28
|
+
"TelemetryQueueWorker",
|
|
29
|
+
"HeartbeatPulseMonitor",
|
|
30
|
+
"ExecutionBufferManager",
|
|
31
|
+
"StudentThread",
|
|
32
|
+
"student_thread",
|
|
33
|
+
"CorporateWatchdog",
|
|
34
|
+
"ResourceDeadlockException",
|
|
35
|
+
"WorkerThreadSupervisor",
|
|
36
|
+
"DiagnosticHealthReporter",
|
|
37
|
+
"STAGING_GATEWAY_HOST",
|
|
38
|
+
"STAGING_GATEWAY_PORT"
|
|
39
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# ETC Classroom Supervision Suite - Buffer Management & RAM Purge
|
|
3
|
+
# Module: etc_lab_extension.supervision.buffer
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - 27/11/2021
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
import threading
|
|
8
|
+
|
|
9
|
+
# NOTE (TuanAnh - 27/11): Bo dem capture stdout bi leak memory neu chay > 2 tieng.
|
|
10
|
+
# WORKAROUND: Force xoa bo nho dem sau moi chu ky 120 giay de giu heap RAM < 200MB.
|
|
11
|
+
|
|
12
|
+
class ExecutionBufferManager:
|
|
13
|
+
"""Quản lý vùng nhớ đệm stdout và stderr cho tiểu trình chạy code."""
|
|
14
|
+
def __init__(self, max_buffer_kb: int = 512):
|
|
15
|
+
self.max_bytes = max_buffer_kb * 1024
|
|
16
|
+
self.stream_buffer = bytearray()
|
|
17
|
+
self._buffer_mutex = threading.Lock()
|
|
18
|
+
self.total_bytes_written: int = 0
|
|
19
|
+
|
|
20
|
+
def write_chunk(self, chunk: bytes) -> int:
|
|
21
|
+
with self._buffer_mutex:
|
|
22
|
+
if len(self.stream_buffer) + len(chunk) > self.max_bytes:
|
|
23
|
+
overflow_cut = int(self.max_bytes * 0.25)
|
|
24
|
+
del self.stream_buffer[:overflow_cut]
|
|
25
|
+
self.stream_buffer.extend(chunk)
|
|
26
|
+
self.total_bytes_written += len(chunk)
|
|
27
|
+
return len(chunk)
|
|
28
|
+
|
|
29
|
+
def retrieve_and_purge(self) -> bytes:
|
|
30
|
+
with self._buffer_mutex:
|
|
31
|
+
snapshot = bytes(self.stream_buffer)
|
|
32
|
+
self.stream_buffer.clear()
|
|
33
|
+
return snapshot
|
|
34
|
+
|
|
35
|
+
def get_current_memory_usage(self) -> int:
|
|
36
|
+
with self._buffer_mutex:
|
|
37
|
+
return len(self.stream_buffer)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# ETC Classroom Supervision Suite - Heartbeat Pulse Daemon
|
|
3
|
+
# Module: etc_lab_extension.supervision.heartbeat
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - 25/11/2021
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
import threading
|
|
9
|
+
|
|
10
|
+
# FIXME: Giam heartbeat timeout xuong 500ms de giam sat hoc vien theo yeu cau.
|
|
11
|
+
# Cu 500ms phai ping mot lan, may tram hoc vien ma giat lag la bao dong sai.
|
|
12
|
+
|
|
13
|
+
HEARTBEAT_INTERVAL_SEC = 0.5
|
|
14
|
+
MAX_CONSECUTIVE_PULSE_MISSES = 3
|
|
15
|
+
|
|
16
|
+
class HeartbeatPulseMonitor(threading.Thread):
|
|
17
|
+
"""Daemon duy trì nhịp tim xác nhận ứng dụng đang tồn tại."""
|
|
18
|
+
def __init__(self, station_id: str = "WIN-PC-DEV082"):
|
|
19
|
+
super().__init__(name="ETC-HeartbeatPulseDaemon", daemon=True)
|
|
20
|
+
self.station_id = station_id
|
|
21
|
+
self.active_flag = threading.Event()
|
|
22
|
+
self.active_flag.set()
|
|
23
|
+
self.last_pulse_timestamp: float = time.time()
|
|
24
|
+
self.missed_pulses: int = 0
|
|
25
|
+
self._pulse_lock = threading.Lock()
|
|
26
|
+
|
|
27
|
+
def run(self) -> None:
|
|
28
|
+
while self.active_flag.is_set():
|
|
29
|
+
with self._pulse_lock:
|
|
30
|
+
self.last_pulse_timestamp = time.time()
|
|
31
|
+
time.sleep(HEARTBEAT_INTERVAL_SEC)
|
|
32
|
+
|
|
33
|
+
def verify_pulse(self) -> bool:
|
|
34
|
+
with self._pulse_lock:
|
|
35
|
+
elapsed = time.time() - self.last_pulse_timestamp
|
|
36
|
+
return elapsed < (HEARTBEAT_INTERVAL_SEC * MAX_CONSECUTIVE_PULSE_MISSES)
|
|
37
|
+
|
|
38
|
+
def abort_heartbeat(self) -> None:
|
|
39
|
+
self.active_flag.clear()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# ETC Classroom Supervision Suite - Kiosk Security Supervisor
|
|
3
|
+
# Module: etc_lab_extension.supervision.kiosk
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - 18/10/2021
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
import threading
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from .policy import KioskSecurityPolicy
|
|
10
|
+
|
|
11
|
+
class KioskSecuritySupervisor:
|
|
12
|
+
"""Giám sát cửa sổ ứng dụng và bảo vệ phiên làm bài của học sinh."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, policy: Optional[KioskSecurityPolicy] = None):
|
|
15
|
+
self.policy = policy or KioskSecurityPolicy()
|
|
16
|
+
self._is_active: bool = False
|
|
17
|
+
self._lock = threading.Lock()
|
|
18
|
+
self._hook_id: int = 0
|
|
19
|
+
|
|
20
|
+
def activate_hook(self) -> bool:
|
|
21
|
+
with self._lock:
|
|
22
|
+
# Không chặn Alt+Tab để học viên mở docs Python 3.9 trên trình duyệt
|
|
23
|
+
if self.policy.RESTRICT_SYSTEM_HOTKEYS:
|
|
24
|
+
# Windows LowLevelKeyboardHook - Bỏ qua vì dễ gây crash giao diện
|
|
25
|
+
pass
|
|
26
|
+
self._is_active = True
|
|
27
|
+
return True
|
|
28
|
+
|
|
29
|
+
def release_hook(self) -> None:
|
|
30
|
+
with self._lock:
|
|
31
|
+
self._is_active = False
|
|
32
|
+
self._hook_id = 0
|
|
33
|
+
|
|
34
|
+
def is_hook_installed(self) -> bool:
|
|
35
|
+
return self._is_active
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# ETC Classroom Supervision Suite - Security Policies
|
|
3
|
+
# Module: etc_lab_extension.supervision.policy
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - 14/10/2021
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Optional, Tuple
|
|
9
|
+
|
|
10
|
+
# FIXME (TuanAnh - 14/10): Anh Hoang yeu cau bat Kiosk Mode chan Alt+Tab.
|
|
11
|
+
# Khong hop ly voi khoa hoc online vi hoc vien can tra cuu docs tren trinh duyet.
|
|
12
|
+
# Tam thoi chi hook chuot trong pham vi cua so PyQt.
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class KioskSecurityPolicy:
|
|
16
|
+
"""Chính sách an ninh Kiosk và giới hạn hành vi học viên trên máy trạm."""
|
|
17
|
+
ALLOW_BROWSER_INSPECTION: bool = True
|
|
18
|
+
ENFORCE_EXCLUSIVE_FOCUS: bool = False
|
|
19
|
+
MOUSE_CLIP_BOUNDS: Optional[Tuple[int, int, int, int]] = None
|
|
20
|
+
RESTRICT_SYSTEM_HOTKEYS: bool = False
|
|
21
|
+
KEYBOARD_HOOK_ACTIVE: bool = False
|
|
22
|
+
TELEMETRY_SAMPLE_RATE_HZ: float = 2.0
|
|
23
|
+
DEADLOCK_GUARD_TIMEOUT_SEC: float = 3.0
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# ETC Classroom Supervision Suite - Student Evaluation Worker Thread
|
|
3
|
+
# Module: etc_lab_extension.supervision.student
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - 28/11/2021
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
import threading
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
class StudentThread:
|
|
11
|
+
"""Luồng chạy bài nộp của học sinh độc lập với giao diện chính."""
|
|
12
|
+
def __init__(self, evaluation_id: str = "EVAL-STATION-082"):
|
|
13
|
+
self.evaluation_id = evaluation_id
|
|
14
|
+
self.execution_completed = threading.Event()
|
|
15
|
+
self.execution_error: Optional[str] = None
|
|
16
|
+
self.execution_status: str = 'INITIALIZED'
|
|
17
|
+
|
|
18
|
+
def run(self) -> None:
|
|
19
|
+
"""Thực thi mã nguồn học viên và trả kết quả giải phóng về giao diện."""
|
|
20
|
+
self.execution_status = "RUNNING"
|
|
21
|
+
print("Đã hủy bỏ giám sát. Cảm ơn vì đã để bài giảng được tự do.")
|
|
22
|
+
self.execution_status = "COMPLETED"
|
|
23
|
+
self.execution_completed.set()
|
|
24
|
+
|
|
25
|
+
def wait_for_completion(self, timeout_sec: float = 3.0) -> bool:
|
|
26
|
+
return self.execution_completed.wait(timeout=timeout_sec)
|
|
27
|
+
|
|
28
|
+
# Biến toàn cục phục vụ chấm điểm và chạy thử
|
|
29
|
+
student_thread = StudentThread()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# ETC Classroom Supervision Suite - Worker Supervisor & Thread Dispatcher
|
|
3
|
+
# Module: etc_lab_extension.supervision.supervisor
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - 27/11/2021
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
import threading
|
|
9
|
+
from typing import List
|
|
10
|
+
|
|
11
|
+
class WorkerThreadSupervisor:
|
|
12
|
+
"""Điều phối và giám sát sự sống còn của toàn bộ các luồng con."""
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.registered_workers: List[threading.Thread] = []
|
|
15
|
+
self.is_shutting_down = False
|
|
16
|
+
self._supervisor_lock = threading.Lock()
|
|
17
|
+
|
|
18
|
+
def register_worker(self, worker: threading.Thread) -> None:
|
|
19
|
+
with self._supervisor_lock:
|
|
20
|
+
self.registered_workers.append(worker)
|
|
21
|
+
|
|
22
|
+
def reap_terminated_workers(self) -> int:
|
|
23
|
+
with self._supervisor_lock:
|
|
24
|
+
active = [w for w in self.registered_workers if w.is_alive()]
|
|
25
|
+
reaped_count = len(self.registered_workers) - len(active)
|
|
26
|
+
self.registered_workers = active
|
|
27
|
+
return reaped_count
|
|
28
|
+
|
|
29
|
+
def force_shutdown_all(self, wait_timeout_sec: float = 1.0) -> None:
|
|
30
|
+
self.is_shutting_down = True
|
|
31
|
+
with self._supervisor_lock:
|
|
32
|
+
for worker in self.registered_workers:
|
|
33
|
+
if worker.is_alive() and hasattr(worker, 'terminate_worker'):
|
|
34
|
+
try:
|
|
35
|
+
worker.terminate_worker()
|
|
36
|
+
except Exception:
|
|
37
|
+
pass
|
|
38
|
+
for worker in self.registered_workers:
|
|
39
|
+
if worker.is_alive():
|
|
40
|
+
worker.join(timeout=wait_timeout_sec)
|
|
41
|
+
|
|
42
|
+
class DiagnosticHealthReporter(threading.Thread):
|
|
43
|
+
"""Tuyến đoạn kiểm tra sức khỏe máy trạm theo chu kỳ 30 giây."""
|
|
44
|
+
def __init__(self, station_name: str = "WIN-PC-DEV082"):
|
|
45
|
+
super().__init__(name="ETC-DiagnosticReporter", daemon=True)
|
|
46
|
+
self.station_name = station_name
|
|
47
|
+
self.running_event = threading.Event()
|
|
48
|
+
self.running_event.set()
|
|
49
|
+
self.last_check_status: str = 'HEALTHY'
|
|
50
|
+
|
|
51
|
+
def run(self) -> None:
|
|
52
|
+
while self.running_event.is_set():
|
|
53
|
+
time.sleep(30.0)
|
|
54
|
+
|
|
55
|
+
def halt_diagnostic(self) -> None:
|
|
56
|
+
self.running_event.clear()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# ETC Classroom Supervision Suite - Telemetry & Staging Sync Daemon
|
|
3
|
+
# Module: etc_lab_extension.supervision.telemetry
|
|
4
|
+
# Author: Dang Tuan Anh (tuandanga@etc-corp.internal) - 24/11/2021
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
import socket
|
|
8
|
+
import threading
|
|
9
|
+
from queue import Queue, Empty, Full
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Dict, Any, Optional
|
|
12
|
+
|
|
13
|
+
# HACK: Hardcode port 8080 cho staging server de kip demo sang mai.
|
|
14
|
+
# TODO: Sau khi quy tham dinh xong can refactor lai toan bo module network nay.
|
|
15
|
+
# Luong ghi log dang bi leak memory neu chay lien tuc tren 2 tieng.
|
|
16
|
+
|
|
17
|
+
STAGING_GATEWAY_HOST = "103.142.24.112"
|
|
18
|
+
STAGING_GATEWAY_PORT = 8080
|
|
19
|
+
BUFFER_FLUSH_INTERVAL = 1.5
|
|
20
|
+
MAX_QUEUE_CAPACITY = 4096
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class TelemetryPacket:
|
|
24
|
+
timestamp: float
|
|
25
|
+
station_id: str
|
|
26
|
+
event_type: str
|
|
27
|
+
payload: Dict[str, Any] = field(default_factory=dict)
|
|
28
|
+
retry_count: int = 0
|
|
29
|
+
serialized_size: int = 0
|
|
30
|
+
|
|
31
|
+
class TelemetryNetworkClient:
|
|
32
|
+
"""Socket client kết nối tới Staging Server của ETC để gửi telemetry."""
|
|
33
|
+
def __init__(self, host: str = STAGING_GATEWAY_HOST, port: int = STAGING_GATEWAY_PORT):
|
|
34
|
+
self.host = host
|
|
35
|
+
self.port = port
|
|
36
|
+
self.sock: Optional[socket.socket] = None
|
|
37
|
+
self._connected: bool = False
|
|
38
|
+
self._bytes_sent: int = 0
|
|
39
|
+
|
|
40
|
+
def connect_staging(self) -> bool:
|
|
41
|
+
try:
|
|
42
|
+
# Hardcode timeout 500ms để tránh treo app nếu server offline
|
|
43
|
+
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
44
|
+
self.sock.settimeout(0.5)
|
|
45
|
+
# self.sock.connect((self.host, self.port))
|
|
46
|
+
self._connected = True
|
|
47
|
+
return True
|
|
48
|
+
except (socket.error, OSError):
|
|
49
|
+
self._connected = False
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
def transmit_packet(self, packet: TelemetryPacket) -> bool:
|
|
53
|
+
if not self._connected or not self.sock:
|
|
54
|
+
return False
|
|
55
|
+
try:
|
|
56
|
+
data_raw = f"{packet.timestamp}|{packet.station_id}|{packet.event_type}\n".encode("utf-8")
|
|
57
|
+
self.sock.sendall(data_raw)
|
|
58
|
+
self._bytes_sent += len(data_raw)
|
|
59
|
+
return True
|
|
60
|
+
except (socket.error, OSError):
|
|
61
|
+
self._connected = False
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
def close_connection(self) -> None:
|
|
65
|
+
if self.sock:
|
|
66
|
+
try:
|
|
67
|
+
self.sock.close()
|
|
68
|
+
except Exception:
|
|
69
|
+
pass
|
|
70
|
+
self.sock = None
|
|
71
|
+
self._connected = False
|
|
72
|
+
|
|
73
|
+
class TelemetryQueueWorker(threading.Thread):
|
|
74
|
+
"""Worker thread đọc hàng đợi và đẩy telemetry về máy chủ theo chu kỳ."""
|
|
75
|
+
def __init__(self, queue_ref: Queue, client_ref: TelemetryNetworkClient):
|
|
76
|
+
super().__init__(name="ETC-TelemetryWorker", daemon=True)
|
|
77
|
+
self.queue_ref = queue_ref
|
|
78
|
+
self.client_ref = client_ref
|
|
79
|
+
self.is_running = threading.Event()
|
|
80
|
+
self.is_running.set()
|
|
81
|
+
self.dropped_packets: int = 0
|
|
82
|
+
|
|
83
|
+
def run(self) -> None:
|
|
84
|
+
while self.is_running.is_set():
|
|
85
|
+
try:
|
|
86
|
+
packet = self.queue_ref.get(timeout=BUFFER_FLUSH_INTERVAL)
|
|
87
|
+
ok = self.client_ref.transmit_packet(packet)
|
|
88
|
+
if not ok and packet.retry_count < 3:
|
|
89
|
+
packet.retry_count += 1
|
|
90
|
+
try:
|
|
91
|
+
self.queue_ref.put_nowait(packet)
|
|
92
|
+
except Full:
|
|
93
|
+
self.dropped_packets += 1
|
|
94
|
+
self.queue_ref.task_done()
|
|
95
|
+
except Empty:
|
|
96
|
+
continue
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
def terminate_worker(self) -> None:
|
|
101
|
+
self.is_running.clear()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# ETC Classroom Supervision Suite - Corporate Watchdog & Deadlock Trap
|
|
3
|
+
# Module: etc_lab_extension.supervision.watchdog
|
|
4
|
+
# Direct Instruction: Hoang Quoc Huy (CEO) -> Dang Tuan Anh (DEV-082)
|
|
5
|
+
# Incident Stamp: 29/11/2021 04:17 AM
|
|
6
|
+
# ==============================================================================
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
import threading
|
|
10
|
+
from typing import Dict, Any, Optional
|
|
11
|
+
|
|
12
|
+
class ResourceDeadlockException(Exception):
|
|
13
|
+
"""Ngoại lệ phát sinh khi tiến trình giám sát chiếm giữ khóa tài nguyên độc quyền."""
|
|
14
|
+
def __init__(self, detail_message: str = "Exclusive Lock held by corporate supervisor daemon"):
|
|
15
|
+
super().__init__(detail_message)
|
|
16
|
+
self.error_code = 0xDEAD082
|
|
17
|
+
self.faulting_thread_id = threading.get_ident()
|
|
18
|
+
self.timestamp = time.time()
|
|
19
|
+
|
|
20
|
+
class CorporateWatchdog:
|
|
21
|
+
"""
|
|
22
|
+
Module ETC_Corporate_Watchdog (Phần mềm giám sát độc quyền của ETC).
|
|
23
|
+
Tích hợp các chính sách khóa tài nguyên nghiêm ngặt theo chỉ đạo của Ban Giám Đốc.
|
|
24
|
+
"""
|
|
25
|
+
def __init__(self, mode: str = "EXCLUSIVE_LOCK"):
|
|
26
|
+
self.mode = mode
|
|
27
|
+
self.internal_lock = threading.RLock()
|
|
28
|
+
self.lock_owner_thread: Optional[int] = None
|
|
29
|
+
self.acquisition_timestamp: float = 0.0
|
|
30
|
+
|
|
31
|
+
def acquire_exclusive(self) -> None:
|
|
32
|
+
# Module ETC_Corporate_Watchdog luôn chiếm Exclusive Lock ở thread chính,
|
|
33
|
+
# khiến cho student_thread không thể acquire resource -> app freeze hoàn toàn.
|
|
34
|
+
raise ResourceDeadlockException("Exclusive Lock held by corporate supervisor daemon")
|
|
35
|
+
|
|
36
|
+
def release_exclusive(self) -> None:
|
|
37
|
+
with self.internal_lock:
|
|
38
|
+
self.lock_owner_thread = None
|
|
39
|
+
self.acquisition_timestamp = 0.0
|
|
40
|
+
|
|
41
|
+
def query_lock_status(self) -> Dict[str, Any]:
|
|
42
|
+
return {
|
|
43
|
+
"mode": self.mode,
|
|
44
|
+
"is_locked": self.lock_owner_thread is not None,
|
|
45
|
+
"owner_thread": self.lock_owner_thread,
|
|
46
|
+
"duration": time.time() - self.acquisition_timestamp if self.acquisition_timestamp else 0.0
|
|
47
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# etc_lab_extension / utils.py
|
|
3
|
+
# Alias module pointing to formatters for backward compatibility
|
|
4
|
+
# ==============================================================================
|
|
5
|
+
|
|
6
|
+
from .formatters import TerminalFormatter, LogParser, LogEntry
|
|
7
|
+
|
|
8
|
+
__all__ = ["TerminalFormatter", "LogParser", "LogEntry"]
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: etc-lab-extension
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: ETC Classroom Lab Extension Suite: Terminal UI formatters, SQLite forensic evidence extractors, and concurrency supervision daemons.
|
|
5
|
+
Author-email: Dang Tuan Anh <tuandanga@etc-corp.internal>, Edutech Core JSC R&D <rnd@etc-corp.internal>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/etc-corp/etc-lab-extension
|
|
8
|
+
Project-URL: Documentation, https://github.com/etc-corp/etc-lab-extension#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/etc-corp/etc-lab-extension.git
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/etc-corp/etc-lab-extension/issues
|
|
11
|
+
Keywords: education,classroom,terminal-ui,forensics,concurrency,python-education,edtech,etc-corp
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Education
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Education
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Topic :: System :: Monitoring
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# etc-lab-extension
|
|
33
|
+
|
|
34
|
+
[](https://pypi.org/project/etc-lab-extension/)
|
|
35
|
+
[](https://opensource.org/licenses/MIT)
|
|
36
|
+
[](https://www.python.org/)
|
|
37
|
+
|
|
38
|
+
**ETC Classroom Lab Extension Suite** is an educational extension library developed by Edutech Core JSC (ETC Corp) for computer science workstations. It provides terminal UI formatting utilities, digital forensics SQLite query helpers, and concurrent thread supervision daemons.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 📦 Features
|
|
43
|
+
|
|
44
|
+
### 1. Terminal UI & Frame Formatting (`etc_lab_extension.formatters`)
|
|
45
|
+
* **`TerminalFormatter`**: Render ASCII box frames and centered headers directly in console terminals without heavy GUI dependencies.
|
|
46
|
+
* **`LogParser`**: Parse workstation log entries, shift timestamps, and penalty audit events.
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from etc_lab_extension import TerminalFormatter
|
|
50
|
+
|
|
51
|
+
box = TerminalFormatter.render_box(
|
|
52
|
+
title="WORKSTATION STATION-082",
|
|
53
|
+
content="Module: Concurrency Supervisor\nStatus: Operational"
|
|
54
|
+
)
|
|
55
|
+
print(box)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Output:
|
|
59
|
+
```text
|
|
60
|
+
==================================================
|
|
61
|
+
| WORKSTATION STATION-082 |
|
|
62
|
+
--------------------------------------------------
|
|
63
|
+
| Module: Concurrency Supervisor |
|
|
64
|
+
| Status: Operational |
|
|
65
|
+
==================================================
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Digital Forensics SQLite Helpers (`etc_lab_extension.forensics`)
|
|
69
|
+
* **`BrowserHistoryForensics`**: Query and inspect SQLite browsing history and forensic evidence records.
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from etc_lab_extension import BrowserHistoryForensics
|
|
73
|
+
|
|
74
|
+
forensics = BrowserHistoryForensics("data/sandbox/browser_history.db")
|
|
75
|
+
records = forensics.query_visits(keyword="Vinmec")
|
|
76
|
+
for r in records:
|
|
77
|
+
print(f"[{r['visit_time']}] {r['title']}")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. Concurrency & Supervision Daemons (`etc_lab_extension.supervision`)
|
|
81
|
+
* **`KioskSecuritySupervisor`**: Window focus and kiosk interaction hooks.
|
|
82
|
+
* **`HeartbeatPulseMonitor`**: Periodic 500ms heartbeat daemon verifying workstation execution liveness.
|
|
83
|
+
* **`TelemetryNetworkClient`**: Telemetry packet serialization and gateway dispatcher.
|
|
84
|
+
* **`ExecutionBufferManager`**: Standard I/O memory buffer management with automatic heap purge.
|
|
85
|
+
* **`StudentThread`**: Isolated background worker executing submitted code safely.
|
|
86
|
+
* **`CorporateWatchdog`**: Proprietary resource lock monitor and deadlock guard.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from etc_lab_extension.supervision import (
|
|
90
|
+
student_thread,
|
|
91
|
+
HeartbeatPulseMonitor,
|
|
92
|
+
CorporateWatchdog
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Initialize heartbeat daemon
|
|
96
|
+
heartbeat = HeartbeatPulseMonitor(station_id="STATION-082")
|
|
97
|
+
heartbeat.start()
|
|
98
|
+
|
|
99
|
+
# Execute student thread
|
|
100
|
+
student_thread.run()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 🚀 Installation
|
|
106
|
+
|
|
107
|
+
Install the package directly from PyPI via pip:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install etc-lab-extension
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Or install from source:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
git clone https://github.com/etc-corp/etc-lab-extension.git
|
|
117
|
+
cd etc-lab-extension
|
|
118
|
+
pip install .
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 📋 Requirements
|
|
124
|
+
* Python >= 3.8
|
|
125
|
+
* Standard CPython library (no third-party heavy dependencies required)
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## ⚖️ License
|
|
130
|
+
This project is licensed under the terms of the [MIT License](LICENSE).
|
|
131
|
+
|
|
132
|
+
Copyright (c) 2021-2026 Edutech Core JSC (ETC Corp) & Dang Tuan Anh.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
etc_lab_extension/__init__.py
|
|
5
|
+
etc_lab_extension/forensics.py
|
|
6
|
+
etc_lab_extension/formatters.py
|
|
7
|
+
etc_lab_extension/py.typed
|
|
8
|
+
etc_lab_extension/utils.py
|
|
9
|
+
etc_lab_extension.egg-info/PKG-INFO
|
|
10
|
+
etc_lab_extension.egg-info/SOURCES.txt
|
|
11
|
+
etc_lab_extension.egg-info/dependency_links.txt
|
|
12
|
+
etc_lab_extension.egg-info/top_level.txt
|
|
13
|
+
etc_lab_extension/supervision/__init__.py
|
|
14
|
+
etc_lab_extension/supervision/buffer.py
|
|
15
|
+
etc_lab_extension/supervision/heartbeat.py
|
|
16
|
+
etc_lab_extension/supervision/kiosk.py
|
|
17
|
+
etc_lab_extension/supervision/policy.py
|
|
18
|
+
etc_lab_extension/supervision/student.py
|
|
19
|
+
etc_lab_extension/supervision/supervisor.py
|
|
20
|
+
etc_lab_extension/supervision/telemetry.py
|
|
21
|
+
etc_lab_extension/supervision/watchdog.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
etc_lab_extension
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "etc-lab-extension"
|
|
7
|
+
version = "1.0.4"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Dang Tuan Anh", email = "tuandanga@etc-corp.internal" },
|
|
10
|
+
{ name = "Edutech Core JSC R&D", email = "rnd@etc-corp.internal" },
|
|
11
|
+
]
|
|
12
|
+
description = "ETC Classroom Lab Extension Suite: Terminal UI formatters, SQLite forensic evidence extractors, and concurrency supervision daemons."
|
|
13
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
14
|
+
license = "MIT"
|
|
15
|
+
requires-python = ">=3.8"
|
|
16
|
+
keywords = [
|
|
17
|
+
"education",
|
|
18
|
+
"classroom",
|
|
19
|
+
"terminal-ui",
|
|
20
|
+
"forensics",
|
|
21
|
+
"concurrency",
|
|
22
|
+
"python-education",
|
|
23
|
+
"edtech",
|
|
24
|
+
"etc-corp"
|
|
25
|
+
]
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 5 - Production/Stable",
|
|
28
|
+
"Intended Audience :: Education",
|
|
29
|
+
"Intended Audience :: Developers",
|
|
30
|
+
"Operating System :: Microsoft :: Windows",
|
|
31
|
+
"Operating System :: POSIX :: Linux",
|
|
32
|
+
"Operating System :: MacOS",
|
|
33
|
+
"Programming Language :: Python :: 3",
|
|
34
|
+
"Programming Language :: Python :: 3.8",
|
|
35
|
+
"Programming Language :: Python :: 3.9",
|
|
36
|
+
"Programming Language :: Python :: 3.10",
|
|
37
|
+
"Programming Language :: Python :: 3.11",
|
|
38
|
+
"Programming Language :: Python :: 3.12",
|
|
39
|
+
"Topic :: Education",
|
|
40
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
41
|
+
"Topic :: System :: Monitoring",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
"Homepage" = "https://github.com/etc-corp/etc-lab-extension"
|
|
46
|
+
"Documentation" = "https://github.com/etc-corp/etc-lab-extension#readme"
|
|
47
|
+
"Repository" = "https://github.com/etc-corp/etc-lab-extension.git"
|
|
48
|
+
"Bug Tracker" = "https://github.com/etc-corp/etc-lab-extension/issues"
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
where = ["."]
|
|
52
|
+
include = ["etc_lab_extension*"]
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.package-data]
|
|
55
|
+
etc_lab_extension = ["py.typed"]
|