File size: 7,766 Bytes
1a96169 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | #!/usr/bin/env python3
"""
Create a deployment bundle for an edge node.
This script packages everything an edge node needs to perform face recognition
for a specific section: FAISS index, student map, config, and model files.
Usage:
python scripts/create_deploy_bundle.py --section AIML-3-A
python scripts/create_deploy_bundle.py --section AIML-3-A --output data/deploy/
python scripts/create_deploy_bundle.py --section AIML-3-A --include-models
"""
import argparse
import hashlib
import json
import os
import shutil
import sys
import zipfile
from datetime import datetime
from pathlib import Path
# Add project root to path
PROJECT_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(PROJECT_ROOT / "src"))
def compute_checksum(filepath: Path) -> str:
"""Compute SHA-256 checksum of a file."""
sha256 = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
sha256.update(chunk)
return sha256.hexdigest()
def create_bundle(
section_key: str,
output_dir: Path,
data_dir: Path,
models_dir: Path,
config_dir: Path,
include_models: bool = False,
) -> Path:
"""Create a deployment bundle zip for the given section."""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
bundle_name = f"bundle_{section_key}_{timestamp}"
bundle_dir = output_dir / bundle_name
print(f"π¦ Creating deployment bundle for section: {section_key}")
print(f" Output directory: {output_dir}")
# Create temporary bundle directory
bundle_dir.mkdir(parents=True, exist_ok=True)
# βββ 1. FAISS Index ββββββββββββββββββββββββββββββββββββββββββββββ
faiss_source = data_dir / "faiss_indices" / f"{section_key}.index"
if faiss_source.exists():
shutil.copy2(faiss_source, bundle_dir / "faiss_index.bin")
print(f" β
FAISS index: {faiss_source.stat().st_size / 1024:.1f} KB")
else:
print(f" β οΈ FAISS index not found: {faiss_source}")
print(f" Run face enrollment first for section {section_key}")
sys.exit(1)
# βββ 2. Student Map ββββββββββββββββββββββββββββββββββββββββββββββ
student_map_source = data_dir / "student_maps" / f"{section_key}_map.json"
if student_map_source.exists():
shutil.copy2(student_map_source, bundle_dir / "student_map.json")
with open(student_map_source) as f:
student_map = json.load(f)
print(f" β
Student map: {len(student_map)} students")
else:
print(f" β οΈ Student map not found: {student_map_source}")
sys.exit(1)
# βββ 3. Edge Configuration βββββββββββββββββββββββββββββββββββββββ
edge_config_source = config_dir / "edge_config.yaml"
if edge_config_source.exists():
shutil.copy2(edge_config_source, bundle_dir / "edge_config.yaml")
print(f" β
Edge config included")
else:
print(f" β οΈ Edge config not found: {edge_config_source}")
# Create a minimal config
minimal_config = f"""# SmartClass Edge Configuration
section_key: "{section_key}"
recognition:
confidence_threshold: 0.65
min_face_size: 80
max_faces: 30
pipeline:
fps_target: 15
frame_skip: 2
metrics:
port: 9100
enabled: true
"""
with open(bundle_dir / "edge_config.yaml", "w") as f:
f.write(minimal_config)
print(f" β
Generated minimal edge config")
# βββ 4. Model Files (optional) βββββββββββββββββββββββββββββββββββ
if include_models:
model_files = [
"face_detection.onnx",
"face_recognition.onnx",
"anti_spoof.onnx",
]
models_included = 0
model_bundle_dir = bundle_dir / "models"
model_bundle_dir.mkdir(exist_ok=True)
for model_file in model_files:
model_path = models_dir / model_file
if model_path.exists():
shutil.copy2(model_path, model_bundle_dir / model_file)
models_included += 1
print(f" β
Model: {model_file} ({model_path.stat().st_size / 1024 / 1024:.1f} MB)")
if models_included == 0:
print(f" β οΈ No model files found in {models_dir}")
shutil.rmtree(model_bundle_dir)
# βββ 5. Create Manifest ββββββββββββββββββββββββββββββββββββββββββ
manifest = {
"version": "1.0",
"section_key": section_key,
"created_at": datetime.now().isoformat(),
"student_count": len(student_map) if "student_map" in dir() else 0,
"includes_models": include_models,
"files": {},
}
for file_path in bundle_dir.rglob("*"):
if file_path.is_file() and file_path.name != "manifest.json":
rel_path = str(file_path.relative_to(bundle_dir))
manifest["files"][rel_path] = {
"size": file_path.stat().st_size,
"checksum": compute_checksum(file_path),
}
with open(bundle_dir / "manifest.json", "w") as f:
json.dump(manifest, f, indent=2)
# βββ 6. Create ZIP βββββββββββββββββββββββββββββββββββββββββββββββ
zip_path = output_dir / f"{bundle_name}.zip"
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
for file_path in bundle_dir.rglob("*"):
if file_path.is_file():
arcname = file_path.relative_to(bundle_dir)
zf.write(file_path, arcname)
# Also create a "latest" symlink/copy
latest_path = output_dir / "latest_bundle.zip"
if latest_path.exists():
latest_path.unlink()
shutil.copy2(zip_path, latest_path)
# Cleanup temporary directory
shutil.rmtree(bundle_dir)
bundle_checksum = compute_checksum(zip_path)
print(f"\nβ
Bundle created successfully!")
print(f" Path: {zip_path}")
print(f" Size: {zip_path.stat().st_size / 1024:.1f} KB")
print(f" Checksum: {bundle_checksum}")
print(f" Latest: {latest_path}")
return zip_path
def main():
parser = argparse.ArgumentParser(
description="Create deployment bundle for SmartClass edge node"
)
parser.add_argument(
"--section", required=True, help="Section key (e.g., AIML-3-A)"
)
parser.add_argument(
"--output", default="data/deploy", help="Output directory (default: data/deploy)"
)
parser.add_argument(
"--data-dir", default="data", help="Data directory (default: data)"
)
parser.add_argument(
"--models-dir", default="models", help="Models directory (default: models)"
)
parser.add_argument(
"--config-dir", default="config", help="Config directory (default: config)"
)
parser.add_argument(
"--include-models", action="store_true", help="Include model files in bundle"
)
args = parser.parse_args()
output_dir = Path(args.output)
output_dir.mkdir(parents=True, exist_ok=True)
create_bundle(
section_key=args.section,
output_dir=output_dir,
data_dir=Path(args.data_dir),
models_dir=Path(args.models_dir),
config_dir=Path(args.config_dir),
include_models=args.include_models,
)
if __name__ == "__main__":
main()
|