{{breadcrumbs}}
HUB / lib_be_project_service.py
lib_be_project_service.py
- Runtime
- Python
- Category
- System
- Path
- /storage/emulated/0/Projects/Management/Libraries/py/System/lib_be_project_service.py
FILE // lib_be_project_service.py
"""
Library: lib_be_project_service.py
MFDB Version: 1.3.1
Format_Creator: Elton Boehnen
Status: OFFICIAL - v1.3.1
Date: 2026-05-06
"""
"""
Library: lib_be_project_service.py
Family: System
Jurisdiction: ["PYTHON", "BEJSON_LIBRARIES"]
Status: OFFICIAL — BEJSON/Lib (v1.4)
Author: Elton Boehnen
Version: 1.3 OFFICIAL
Date: 2026-05-01
Description: Core-Command library component.
"""
import os
import sys
import json
import time
import shutil
import subprocess
from datetime import datetime
import tempfile
from pathlib import Path
from typing import List, Optional, Dict
_DEFAULT_BEC_ROOT = str(Path(__file__).resolve().parent.parent.parent)
def get_bec_root():
root_env = os.environ.get("BEC_ROOT")
if root_env:
return root_env
root_file = os.path.join(_DEFAULT_BEC_ROOT, "data/state/BEC_ROOT.txt")
if os.path.exists(root_file):
with open(root_file, 'r') as f:
return f.read().strip()
return _DEFAULT_BEC_ROOT
BEC_ROOT = get_bec_root()
LIB_DIR = os.path.join(BEC_ROOT, 'Lib/py')
if LIB_DIR not in sys.path:
sys.path.insert(0, LIB_DIR)
from lib_bejson_core import bejson_core_acquire_lock, bejson_core_release_lock
_DEFAULT_CC_DB = os.path.join(BEC_ROOT, 'data/centralized')
DB_FILE = os.path.join(os.environ.get('CC_DB', _DEFAULT_CC_DB), 'BE_Tracking.json')
PROJECTS_ROOT = os.environ.get('CC_PROJECTS', os.path.join(BEC_ROOT, 'projects'))
class ProjectService:
@staticmethod
def _load_db():
if not os.path.exists(DB_FILE):
return None
with open(DB_FILE, 'r') as f:
try:
return json.load(f)
except json.JSONDecodeError:
return None
@staticmethod
def _save_db(doc):
if not doc: return
with tempfile.NamedTemporaryFile('w', dir=os.path.dirname(DB_FILE), delete=False) as tf:
json.dump(doc, tf, indent=2)
tmp_name = tf.name
os.replace(tmp_name, DB_FILE)
@staticmethod
def get_projects(project_type: str = None, include_archived: bool = False) -> List:
doc = ProjectService._load_db()
if not doc: return []
projects = [v for v in doc['Values'] if v[0] == 'Project']
if project_type:
projects = [v for v in projects if v[6] == project_type]
if not include_archived:
projects = [v for v in projects if len(v) > 20 and v[20] == False]
return projects
@staticmethod
def add_project(name: str, p_type: str, path: str = None) -> bool:
if not path:
path = os.path.join(PROJECTS_ROOT, name.replace(" ", "_"))
bejson_core_acquire_lock(DB_FILE)
try:
doc = ProjectService._load_db()
if not doc: return False
# Deduplicate by name or normalized path
norm_path = os.path.normpath(path)
exists = any(v[2] == name or os.path.normpath(v[3]) == norm_path
for v in doc['Values'] if v[0] == 'Project')
if exists:
print(f"Project '{name}' already tracked at {path}")
return False
if not os.path.exists(path):
os.makedirs(path, exist_ok=True)
proj_id = str(int(time.time() * 1000))
created = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# Schema v1.4.0 (22 fields)
record = [
'Project', proj_id, name, path, '0.0.1', created, p_type,
True, True, False,
None, None, None, None, None, None, None, None, None, None, False, False
]
doc['Values'].append(record)
ProjectService._save_db(doc)
print(f"Tracked new project: {name}")
return True
finally:
bejson_core_release_lock(DB_FILE)
@staticmethod
def scan_and_sync():
print(f"--- Project Service: Syncing {PROJECTS_ROOT} ---")
bejson_core_acquire_lock(DB_FILE)
try:
doc = ProjectService._load_db()
if not doc: return
# 1. Discovery: Find new folders in Projects_ROOT
if os.path.exists(PROJECTS_ROOT):
for item in os.listdir(PROJECTS_ROOT):
full_path = os.path.join(PROJECTS_ROOT, item)
if not os.path.isdir(full_path): continue
norm_path = os.path.normpath(full_path)
exists = any(os.path.normpath(v[3]) == norm_path
for v in doc['Values'] if v[0] == 'Project')
if not exists:
# Infer type
p_type = "bash"
try:
if any(f.endswith('.py') for f in os.listdir(full_path)):
p_type = "python"
except PermissionError: pass
proj_id = str(int(time.time() * 1000))
created = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
record = [
'Project', proj_id, item, full_path, '0.0.1', created, p_type,
True, True, False,
None, None, None, None, None, None, None, None, None, None, False, False
]
print(f"Discovered: {item} ({p_type})")
doc['Values'].append(record)
# 2. Audit: Check for missing files and update flags
for v in doc['Values']:
if v[0] == 'Project':
p_path = v[3]
if not os.path.exists(p_path):
if not v[9]:
print(f"Flagging missing project: {v[2]}")
v[9] = True
else:
if v[9]:
print(f"Project restored: {v[2]}")
v[9] = False
ProjectService._save_db(doc)
print("Sync complete.")
finally:
bejson_core_release_lock(DB_FILE)
@staticmethod
def archive_project(name: str) -> bool:
bejson_core_acquire_lock(DB_FILE)
try:
doc = ProjectService._load_db()
if not doc: return False
for v in doc['Values']:
if v[0] == 'Project' and v[2] == name:
v[20] = True
ProjectService._save_db(doc)
return True
finally:
bejson_core_release_lock(DB_FILE)
@staticmethod
def expel_project(name: str, p_type: str) -> bool:
doc = ProjectService._load_db()
if not doc: return False
proj = next((v for v in doc['Values'] if v[0] == 'Project' and v[2] == name), None)
if not proj: return False
p_path = proj[3]
expelled_root = os.path.join(BEC_ROOT, f"management/expelled/expelled_{datetime.now().strftime('%Y%m%d_%H%M%S')}")
target_dir = os.path.join(expelled_root, p_type, name)
os.makedirs(os.path.dirname(target_dir), exist_ok=True)
print(f"Expelling {name} to {target_dir}...")
if os.path.exists(p_path):
try:
shutil.move(p_path, target_dir)
except Exception as e:
print(f"Move failed: {e}")
bejson_core_acquire_lock(DB_FILE)
try:
doc = ProjectService._load_db()
doc['Values'] = [v for v in doc['Values'] if not (v[0] == 'Project' and v[2] == name)]
ProjectService._save_db(doc)
return True
finally:
bejson_core_release_lock(DB_FILE)
@staticmethod
def toggle_reset_protection(name: str) -> bool:
bejson_core_acquire_lock(DB_FILE)
try:
doc = ProjectService._load_db()
if not doc: return False
for v in doc['Values']:
if v[0] == 'Project' and v[2] == name:
if len(v) <= 21: v.append(True)
else: v[21] = not v[21]
ProjectService._save_db(doc)
return True
finally:
bejson_core_release_lock(DB_FILE)
@staticmethod
def get_reset_protection(name: str) -> str:
doc = ProjectService._load_db()
if not doc: return "OFF"
for v in doc['Values']:
if v[0] == 'Project' and v[2] == name:
return "ON" if (len(v) > 21 and v[21]) else "OFF"
return "OFF"
@staticmethod
def get_project_path(name: str) -> Optional[str]:
doc = ProjectService._load_db()
if not doc: return None
for v in doc['Values']:
if v[0] == 'Project' and v[2] == name:
return v[3]
return None
@staticmethod
def get_project_type(name: str) -> Optional[str]:
doc = ProjectService._load_db()
if not doc: return None
for v in doc['Values']:
if v[0] == 'Project' and v[2] == name:
return v[6]
return None
@staticmethod
def delete_record(name: str) -> bool:
bejson_core_acquire_lock(DB_FILE)
try:
doc = ProjectService._load_db()
if not doc: return False
doc['Values'] = [v for v in doc['Values'] if not (v[0] == 'Project' and v[2] == name)]
ProjectService._save_db(doc)
return True
finally:
bejson_core_release_lock(DB_FILE)
# Legacy Wrappers for backward compatibility during transition
def track_project(name, p_type, path=None): return ProjectService.add_project(name, p_type, path)
def list_projects(p_type=None, include_archived=False): return ProjectService.get_projects(p_type, include_archived)
def get_project_path(name): return ProjectService.get_project_path(name)
def track_project_archive(name): return ProjectService.archive_project(name)
def track_project_expel(name, p_type): return ProjectService.expel_project(name, p_type)
def track_project_toggle_reset_protection(name): return ProjectService.toggle_reset_protection(name)
def track_project_get_reset_protection(name): return ProjectService.get_reset_protection(name)
def track_project_delete_record(name): return ProjectService.delete_record(name)
if __name__ == "__main__":
if len(sys.argv) > 1:
if sys.argv[1] == "--sync":
ProjectService.scan_and_sync()
elif sys.argv[1] == "--list":
projects = ProjectService.get_projects()
for p in projects:
print(f"{p[2]} ({p[6]})")