From 11fde0636f56d77e54bd21518db33a16f0cf55aa Mon Sep 17 00:00:00 2001 From: buenosairesam Date: Wed, 24 Dec 2025 07:36:36 -0300 Subject: [PATCH] remove leftovers --- station/tools/modelgen/__init__.py | 25 +- station/tools/modelgen/code_generator.py | 418 ------------------ station/tools/modelgen/orchestrator.py | 128 ------ station/tools/modelgen/structure_generator.py | 127 ------ 4 files changed, 22 insertions(+), 676 deletions(-) delete mode 100644 station/tools/modelgen/code_generator.py delete mode 100644 station/tools/modelgen/orchestrator.py delete mode 100644 station/tools/modelgen/structure_generator.py diff --git a/station/tools/modelgen/__init__.py b/station/tools/modelgen/__init__.py index 5d01838..84ab456 100644 --- a/station/tools/modelgen/__init__.py +++ b/station/tools/modelgen/__init__.py @@ -1,8 +1,27 @@ """ -Framework Generator System +Modelgen - Generic Model Generation Tool -Generates complete framework instances (pawprint, soleprint, etc.) -from configuration files. +Generates typed models from various sources to various output formats. + +Input sources: +- Configuration files (soleprint.config.json style) +- JSON Schema (planned) +- Existing codebases: Django, SQLAlchemy, Prisma (planned - for databrowse) + +Output formats: +- pydantic: Pydantic BaseModel classes +- django: Django ORM models (planned) +- prisma: Prisma schema (planned) +- sqlalchemy: SQLAlchemy models (planned) + +Usage: + python -m station.tools.modelgen from-config -c config.json -o models.py -f pydantic + python -m station.tools.modelgen list-formats """ __version__ = "0.1.0" + +from .config_loader import ConfigLoader, load_config +from .model_generator import WRITERS, ModelGenerator + +__all__ = ["ModelGenerator", "ConfigLoader", "load_config", "WRITERS"] diff --git a/station/tools/modelgen/code_generator.py b/station/tools/modelgen/code_generator.py deleted file mode 100644 index f8d747d..0000000 --- a/station/tools/modelgen/code_generator.py +++ /dev/null @@ -1,418 +0,0 @@ -""" -Code Generator - -Generates Python code files (main.py, data layer, system main files). -""" - -from pathlib import Path -from .config_loader import ConfigLoader - - -class CodeGenerator: - """Generates Python code from configuration""" - - def __init__(self, config: ConfigLoader, output_dir: Path): - self.config = config - self.output_dir = Path(output_dir) - - def generate(self): - """Generate all code files""" - - # Generate hub main.py - self._generate_hub_main() - - # Generate data layer - self._generate_data_layer() - - # Generate system main files - for system in self.config.systems: - self._generate_system_main(system) - - print(f"Generated code in {self.output_dir}") - - def _generate_hub_main(self): - """Generate hub main.py file""" - - fw = self.config.framework - systems = self.config.systems - - # Build system URL mappings - system_urls = "\n".join([ - f'{s.name.upper()}_URL = os.getenv("{s.name.upper()}_URL", "http://localhost:{s.port}")' - for s in systems - ]) - - system_external_urls = "\n".join([ - f'{s.name.upper()}_EXTERNAL_URL = os.getenv("{s.name.upper()}_EXTERNAL_URL", {s.name.upper()}_URL)' - for s in systems - ]) - - system_health = ",\n ".join([ - f'"{s.name}": {s.name.upper()}_URL' - for s in systems - ]) - - system_routes = "\n".join([ - f' "{s.name}": {s.name.upper()}_EXTERNAL_URL,' - for s in systems - ]) - - system_redirects = "\n\n".join([ - f'''@app.get("/{s.name}") -@app.get("/{s.name}/{{path:path}}") -def {s.name}_redirect(path: str = ""): - """Redirect to {s.name} service.""" - target = os.getenv("{s.name.upper()}_URL") - if target: - return RedirectResponse(url=f"{{target}}/{{path}}") - return {{"error": "{s.name.upper()}_URL not configured"}}''' - for s in systems - ]) - - content = f'''""" -{fw.name.capitalize()} - Overview and routing hub. - -{fw.description} -{fw.icon} {fw.tagline} - -Systems: -''' - - # Add system documentation - for s in systems: - content += f' {s.icon} {s.title} ({s.name}) - {s.tagline}\n' - - content += f''' -Routes: - / → index - /health → health check -''' - - # Add data routes - for s in systems: - content += f' /api/data/{s.name} → {s.name} data\n' - - # Add system redirects - for s in systems: - content += f' /{s.name}/* → proxy to {s.name} service\n' - - content += f'''""" - -import os -from pathlib import Path -from fastapi import FastAPI, Request -from fastapi.responses import RedirectResponse -from fastapi.templating import Jinja2Templates - -# Import data functions -from data import get_{systems[0].name}_data, get_{systems[1].name}_data, get_{systems[2].name}_data - -app = FastAPI(title="{fw.name.capitalize()}", version="{fw.version}") - -templates = Jinja2Templates(directory=Path(__file__).parent) - -# Service URLs (internal for API calls) -{system_urls} - -# External URLs (for frontend links, falls back to internal) -{system_external_urls} - - -@app.get("/health") -def health(): - return {{ - "status": "ok", - "service": "{fw.name}", - "subsystems": {{ - {system_health}, - }} - }} - - -# === Data API === - -@app.get("/api/data/{systems[0].name}") -def api_{systems[0].name}_data(): - """Data for {systems[0].name} service.""" - return get_{systems[0].name}_data() - - -@app.get("/api/data/{systems[1].name}") -def api_{systems[1].name}_data(): - """Data for {systems[1].name} service.""" - return get_{systems[1].name}_data() - - -@app.get("/api/data/{systems[2].name}") -def api_{systems[2].name}_data(): - """Data for {systems[2].name} service.""" - return get_{systems[2].name}_data() - - -@app.get("/") -def index(request: Request): - return templates.TemplateResponse("index.html", {{ - "request": request, -{system_routes} - }}) - - -# === Cross-system redirects === -# These allow {fw.name} to act as a hub, redirecting to subsystem routes - -{system_redirects} - - -if __name__ == "__main__": - import uvicorn - uvicorn.run( - "main:app", - host="0.0.0.0", - port=int(os.getenv("PORT", "{fw.hub_port}")), - reload=os.getenv("DEV", "").lower() in ("1", "true"), - ) -''' - - (self.output_dir / "main.py").write_text(content) - - def _generate_data_layer(self): - """Generate data/__init__.py file""" - - # Get all component names for imports - connector = self.config.get_component('data_flow', 'connector') - pattern = self.config.get_component('documentation', 'pattern') - tool = self.config.get_component('execution', 'utility') - monitor = self.config.get_component('execution', 'watcher') - cabinet = self.config.get_component('execution', 'container') - config_comp = self.config.get_shared_component('config') - data_comp = self.config.get_shared_component('data') - - pulse = self.config.get_component('data_flow', 'composed') - doc_composed = self.config.get_component('documentation', 'composed') - exec_composed = self.config.get_component('execution', 'composed') - - systems = self.config.systems - - # Build imports - imports = f'''from models.pydantic import ( - {connector.title}, {config_comp.title}, {data_comp.title}, {pattern.title}, {tool.title}, - {pulse.title}, {doc_composed.title}, {exec_composed.title}, - {connector.title}Collection, {config_comp.title}Collection, {data_comp.title}Collection, - {pattern.title}Collection, {tool.title}Collection, - {pulse.title}Collection, {doc_composed.title}Collection, {exec_composed.title}Collection, - Status -)''' - - # Build loader functions - loaders = f''' -def get_{connector.plural}() -> List[{connector.title}]: - data = _load_json("{connector.plural}.json") - return {connector.title}Collection(**data).items - - -def get_{config_comp.plural}() -> List[{config_comp.title}]: - data = _load_json("{config_comp.plural}.json") - return {config_comp.title}Collection(**data).items - - -def get_{data_comp.plural}() -> List[{data_comp.title}]: - data = _load_json("{data_comp.plural}.json") - return {data_comp.title}Collection(**data).items - - -def get_{pattern.plural}() -> List[{pattern.title}]: - data = _load_json("{pattern.plural}.json") - return {pattern.title}Collection(**data).items - - -def get_{tool.plural}() -> List[{tool.title}]: - data = _load_json("{tool.plural}.json") - return {tool.title}Collection(**data).items - - -def get_{cabinet.plural}() -> list: - """Load {cabinet.plural} (simple dict, no pydantic yet).""" - data = _load_json("{cabinet.plural}.json") - return data.get("items", []) - - -def get_{monitor.plural}() -> list: - """Load {monitor.plural} (simple dict, no pydantic yet).""" - data = _load_json("{monitor.plural}.json") - return data.get("items", []) - - -def get_{pulse.plural}() -> List[{pulse.title}]: - data = _load_json("{pulse.plural}.json") - return {pulse.title}Collection(**data).items - - -def get_{doc_composed.plural}() -> List[{doc_composed.title}]: - data = _load_json("{doc_composed.plural}.json") - return {doc_composed.title}Collection(**data).items - - -def get_{exec_composed.plural}() -> List[{exec_composed.title}]: - data = _load_json("{exec_composed.plural}.json") - return {exec_composed.title}Collection(**data).items -''' - - # Build system data functions - data_flow_sys = systems[0] - doc_sys = systems[1] - exec_sys = systems[2] - - system_data = f''' -def get_{data_flow_sys.name}_data() -> dict: - """Data for {data_flow_sys.name} frontend.""" - return {{ - "{connector.plural}": [v.model_dump() for v in get_{connector.plural}()], - "{config_comp.plural}": [n.model_dump() for n in get_{config_comp.plural}()], - "{data_comp.plural}": [l.model_dump() for l in get_{data_comp.plural}()], - "{pulse.plural}": [p.model_dump() for p in get_{pulse.plural}()], - }} - - -def get_{doc_sys.name}_data() -> dict: - """Data for {doc_sys.name} frontend.""" - return {{ - "{pattern.plural}": [t.model_dump() for t in get_{pattern.plural}()], - "{data_comp.plural}": [l.model_dump() for l in get_{data_comp.plural}()], - "{doc_composed.plural}": [b.model_dump() for b in get_{doc_composed.plural}()], - }} - - -def get_{exec_sys.name}_data() -> dict: - """Data for {exec_sys.name} frontend.""" - return {{ - "{tool.plural}": [t.model_dump() for t in get_{tool.plural}()], - "{monitor.plural}": get_{monitor.plural}(), - "{cabinet.plural}": get_{cabinet.plural}(), - "{config_comp.plural}": [n.model_dump() for n in get_{config_comp.plural}()], - "{data_comp.plural}": [l.model_dump() for l in get_{data_comp.plural}()], - "{exec_composed.plural}": [t.model_dump() for t in get_{exec_composed.plural}()], - }} -''' - - content = f'''""" -{self.config.framework.name.capitalize()} Data Layer - -JSON file storage (future: MongoDB) -""" - -import json -from pathlib import Path -from typing import List, Optional - -# Add parent to path for models import -import sys -sys.path.insert(0, str(Path(__file__).parent.parent)) - -{imports} - -DATA_DIR = Path(__file__).parent.resolve() - - -def _load_json(filename: str) -> dict: - filepath = DATA_DIR / filename - if filepath.exists(): - with open(filepath) as f: - return json.load(f) - return {{"items": []}} - - -def _save_json(filename: str, data: dict): - filepath = DATA_DIR / filename - with open(filepath, 'w') as f: - json.dump(data, f, indent=2) - - -# === Loaders === -{loaders} - -# === For frontend rendering === -{system_data} -''' - - (self.output_dir / "data" / "__init__.py").write_text(content) - - def _generate_system_main(self, system): - """Generate main.py for a system""" - - fw = self.config.framework - - content = f'''""" -{system.title} - {system.tagline} -""" - -import os -import httpx -from pathlib import Path -from fastapi import FastAPI, Request -from fastapi.templating import Jinja2Templates - -app = FastAPI(title="{system.title}", version="{fw.version}") - -templates = Jinja2Templates(directory=Path(__file__).parent) - -# {fw.name.capitalize()} URL for data fetching -{fw.name.upper()}_URL = os.getenv("{fw.name.upper()}_URL", "http://localhost:{fw.hub_port}") - - -def get_data(): - """Fetch data from {fw.name} hub.""" - try: - resp = httpx.get(f"{{{fw.name.upper()}_URL}}/api/data/{system.name}", timeout=5.0) - if resp.status_code == 200: - return resp.json() - except Exception as e: - print(f"Failed to fetch data from {fw.name}: {{e}}") - return {{"items": []}} - - -@app.get("/health") -def health(): - return {{"status": "ok", "service": "{system.name}"}} - - -@app.get("/") -def index(request: Request): - data = get_data() - return templates.TemplateResponse("index.html", {{ - "request": request, - "{fw.name}_url": os.getenv("{fw.name.upper()}_EXTERNAL_URL", {fw.name.upper()}_URL), - **data, - }}) - - -@app.get("/api/data") -def api_data(): - """API endpoint for frontend data (proxied from {fw.name}).""" - return get_data() - - -if __name__ == "__main__": - import uvicorn - uvicorn.run( - "main:app", - host="0.0.0.0", - port=int(os.getenv("PORT", "{system.port}")), - reload=os.getenv("DEV", "").lower() in ("1", "true"), - ) -''' - - (self.output_dir / system.name / "main.py").write_text(content) - - -if __name__ == "__main__": - from .config_loader import load_config - - # Test with soleprint config - config_path = Path(__file__).parent.parent / "soleprint.config.json" - config = load_config(config_path) - - output_dir = Path(__file__).parent.parent - generator = CodeGenerator(config, output_dir) - generator.generate() - - print("Code generated successfully!") diff --git a/station/tools/modelgen/orchestrator.py b/station/tools/modelgen/orchestrator.py deleted file mode 100644 index 66e9c7a..0000000 --- a/station/tools/modelgen/orchestrator.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python3 -""" -Framework Generator Orchestrator - -Generates complete framework from configuration file. - -Usage: - python generators/orchestrator.py [--config CONFIG_PATH] - -Example: - python generators/orchestrator.py - python generators/orchestrator.py --config custom.config.json -""" - -import argparse -import logging -import shutil -from pathlib import Path - -from .config_loader import load_config -from .structure_generator import StructureGenerator -from .model_generator import ModelGenerator -from .code_generator import CodeGenerator - -# Setup logging -logging.basicConfig( - level=logging.INFO, - format='%(message)s' -) -logger = logging.getLogger(__name__) - - -def generate_framework(config_path: Path, output_dir: Path): - """Generate complete framework from configuration""" - - logger.info("="*60) - logger.info("Framework Generator") - logger.info("="*60) - - # Load configuration - logger.info(f"Loading configuration from {config_path}...") - config = load_config(config_path) - - logger.info(f"\nGenerating {config.framework.name.capitalize()}") - logger.info(f" {config.framework.tagline}") - logger.info(f" Version: {config.framework.version}") - - logger.info(f"\nOutput directory: {output_dir}") - - logger.info(f"\nSystems:") - for sys in config.systems: - logger.info(f" {sys.title} ({sys.name}) - {sys.tagline}") - - # Generate structure - logger.info(f"\n[1/4] Generating folder structure...") - struct_gen = StructureGenerator(config, output_dir) - struct_gen.generate() - - # Generate models - logger.info(f"\n[2/4] Generating Pydantic models...") - model_gen = ModelGenerator(config, output_dir) - model_gen.generate() - - # Generate code - logger.info(f"\n[3/4] Generating Python code...") - code_gen = CodeGenerator(config, output_dir) - code_gen.generate() - - # Copy templates - logger.info(f"\n[4/4] Copying templates...") - templates_dir = Path(__file__).parent.parent / "templates" - if (templates_dir / "index.html").exists(): - shutil.copy(templates_dir / "index.html", output_dir / "index.html") - logger.info(f" Copied index.html") - if (templates_dir / "requirements.txt").exists(): - shutil.copy(templates_dir / "requirements.txt", output_dir / "requirements.txt") - logger.info(f" Copied requirements.txt") - - logger.info(f"\n{'='*60}") - logger.info(f"Framework generated successfully!") - logger.info(f"{'='*60}\n") - - logger.info(f"Next steps:") - logger.info(f" 1. Review generated files in {output_dir}") - logger.info(f" 2. Install dependencies: pip install -r requirements.txt") - logger.info(f" 3. Run hub: python {output_dir}/main.py") - logger.info(f" 4. Visit http://localhost:{config.framework.hub_port}") - - -def main(): - parser = argparse.ArgumentParser( - description="Generate framework from configuration" - ) - parser.add_argument( - "--config", - default="soleprint.config.json", - help="Path to configuration file (default: soleprint.config.json)" - ) - parser.add_argument( - "--output", - default=None, - help="Output directory (default: same as config directory)" - ) - - args = parser.parse_args() - - config_path = Path(args.config) - if not config_path.exists(): - # Try relative to script location - script_dir = Path(__file__).parent.parent - config_path = script_dir / args.config - - if not config_path.exists(): - logger.error(f"Configuration file not found: {args.config}") - return 1 - - # Output directory defaults to config directory - if args.output: - output_dir = Path(args.output) - else: - output_dir = config_path.parent - - generate_framework(config_path, output_dir) - return 0 - - -if __name__ == "__main__": - exit(main()) diff --git a/station/tools/modelgen/structure_generator.py b/station/tools/modelgen/structure_generator.py deleted file mode 100644 index e232b08..0000000 --- a/station/tools/modelgen/structure_generator.py +++ /dev/null @@ -1,127 +0,0 @@ -""" -Structure Generator - -Creates folder structure for framework instance. -""" - -from pathlib import Path -from .config_loader import ConfigLoader - - -class StructureGenerator: - """Generates folder structure from configuration""" - - def __init__(self, config: ConfigLoader, output_dir: Path): - self.config = config - self.output_dir = Path(output_dir) - - def generate(self): - """Generate complete folder structure""" - - # Note: output_dir is the framework root (spr/), not soleprint-room/ - # soleprint-room/ is generated separately as Docker orchestration - - # Create models/ - (self.output_dir / "models").mkdir(parents=True, exist_ok=True) - (self.output_dir / "models" / "pydantic").mkdir(exist_ok=True) - - # Create data/ - data_dir = self.output_dir / "data" - data_dir.mkdir(exist_ok=True) - - # Create ctrl/ (for local scripts) - (self.output_dir / "ctrl").mkdir(exist_ok=True) - - # Get component names - connector = self.config.get_component('data_flow', 'connector') - pattern = self.config.get_component('documentation', 'pattern') - tool = self.config.get_component('execution', 'utility') - monitor = self.config.get_component('execution', 'watcher') - config_comp = self.config.get_shared_component('config') - data_comp = self.config.get_shared_component('data') - - # Create system directories - for system in self.config.systems: - sys_dir = self.output_dir / system.name - sys_dir.mkdir(exist_ok=True) - - # Create __init__.py markers - (sys_dir / "__init__.py").touch() - - # System-specific structure - if system.key == 'data_flow': - # artery/vein/, artery/pulse/, artery/room/, artery/depot/ - (sys_dir / connector.plural).mkdir(exist_ok=True) - (sys_dir / self.config.get_component('data_flow', 'composed').plural).mkdir(exist_ok=True) - (sys_dir / config_comp.plural).mkdir(exist_ok=True) - (sys_dir / data_comp.plural).mkdir(exist_ok=True) - - elif system.key == 'documentation': - # atlas/template/, atlas/maps/, atlas/depot/ - (sys_dir / pattern.plural).mkdir(exist_ok=True) - (sys_dir / self.config.get_component('documentation', 'library').name).mkdir(exist_ok=True) - (sys_dir / data_comp.plural).mkdir(exist_ok=True) - - elif system.key == 'execution': - # station/tools/, station/monitors/, station/desk/, station/room/, station/depot/ - (sys_dir / tool.plural).mkdir(exist_ok=True) - (sys_dir / monitor.plural).mkdir(exist_ok=True) - exec_composed = self.config.get_component('execution', 'composed') - (sys_dir / exec_composed.plural).mkdir(exist_ok=True) - (sys_dir / config_comp.plural).mkdir(exist_ok=True) - (sys_dir / data_comp.plural).mkdir(exist_ok=True) - - # Create data JSON files - self._create_data_files(data_dir) - - print(f"Generated structure in {self.output_dir}") - - def _create_data_files(self, data_dir: Path): - """Create empty data JSON files""" - - # Get component names for plurals - connector = self.config.get_component('data_flow', 'connector') - pattern = self.config.get_component('documentation', 'pattern') - tool = self.config.get_component('execution', 'utility') - monitor = self.config.get_component('execution', 'watcher') - cabinet = self.config.get_component('execution', 'container') - config_comp = self.config.get_shared_component('config') - data_comp = self.config.get_shared_component('data') - - pulse = self.config.get_component('data_flow', 'composed') - doc_composed = self.config.get_component('documentation', 'composed') - exec_composed = self.config.get_component('execution', 'composed') - - # Create JSON files with empty items arrays - files = [ - f"{connector.plural}.json", - f"{pattern.plural}.json", - f"{tool.plural}.json", - f"{monitor.plural}.json", - f"{cabinet.plural}.json", - f"{config_comp.plural}.json", - f"{data_comp.plural}.json", - f"{pulse.plural}.json", - f"{doc_composed.plural}.json", - f"{exec_composed.plural}.json", - ] - - for filename in files: - filepath = data_dir / filename - if not filepath.exists(): - filepath.write_text('{\n "items": []\n}\n') - - -if __name__ == "__main__": - from .config_loader import load_config - - # Test with soleprint config - config_path = Path(__file__).parent.parent / "soleprint.config.json" - config = load_config(config_path) - - # Output to framework root (spr/), not soleprint-room/ - output_dir = Path(__file__).parent.parent - generator = StructureGenerator(config, output_dir) - generator.generate() - - print("Structure generated successfully!")