rhylthyme-cli-runner 0.1.0a0__py3-none-any.whl

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.
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Rhylthyme CLI Runner Package
4
+
5
+ This package provides the command-line interface for running and validating
6
+ Rhylthyme real-time program schedules.
7
+ """
8
+
9
+ __version__ = "0.1.0-alpha"
10
+ __author__ = "Rhylthyme Team"
11
+ __description__ = "CLI runner for Rhylthyme real-time program schedules"
12
+
13
+ from .cli import cli, main
14
+
15
+ __all__ = ['cli', 'main']
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Command-line interface for Rhylthyme
4
+
5
+ This module provides the command-line interface for the Rhylthyme package,
6
+ allowing users to validate and run real-time program schedules.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ import click
12
+ import subprocess
13
+ import pkg_resources
14
+ from pathlib import Path
15
+ from .validate_program import validate_program_file
16
+ from .program_runner import run_program
17
+ from .program_planner import plan_program
18
+ from .environment_loader import EnvironmentLoader
19
+
20
+ # Global environment loader instance
21
+ _environment_loader = None
22
+
23
+ def get_environment_loader(environments_dir=None):
24
+ """Get the environment loader instance, creating it if necessary."""
25
+ global _environment_loader
26
+
27
+ if _environment_loader is None:
28
+ if environments_dir is None:
29
+ # Check for environment variable first
30
+ env_dir = os.environ.get('RHYLTHYME_ENVIRONMENTS_DIR')
31
+ if env_dir:
32
+ environments_dir = env_dir
33
+ else:
34
+ # Check for environments directory in current working directory
35
+ cwd_environments = Path.cwd() / "environments"
36
+ if cwd_environments.exists():
37
+ environments_dir = str(cwd_environments)
38
+
39
+ _environment_loader = EnvironmentLoader(environments_dir)
40
+
41
+ return _environment_loader
42
+
43
+ # Set up the main CLI group
44
+ @click.group()
45
+ @click.option('--environments-dir', type=click.Path(exists=True),
46
+ help='Directory containing environment files (default: check current directory, then package default)')
47
+ @click.version_option()
48
+ @click.pass_context
49
+ def cli(ctx, environments_dir):
50
+ """
51
+ Rhylthyme - A tool for working with real-time program schedules.
52
+
53
+ This CLI tool provides commands for validating and running real-time
54
+ program schedules defined using the Rhylthyme JSON or YAML schema.
55
+ """
56
+ # Store the environments directory in the context
57
+ ctx.ensure_object(dict)
58
+ ctx.obj['environments_dir'] = environments_dir
59
+
60
+ # Initialize the environment loader
61
+ get_environment_loader(environments_dir)
62
+
63
+ # Validate command
64
+ @cli.command()
65
+ @click.argument('program_file', type=click.Path(exists=True))
66
+ @click.option('--schema', type=click.Path(exists=True),
67
+ default=lambda: pkg_resources.resource_filename('rhylthyme_spec', 'schemas/program_schema_0.1.0-alpha.json'),
68
+ help='Path to the schema file (default: built-in schema)')
69
+ @click.option('--verbose', '-v', is_flag=True, help='Show detailed validation information')
70
+ @click.option('--json', '-j', 'json_output', is_flag=True, help='Print machine-readable JSON result')
71
+ @click.option('--strict', '-s', is_flag=True, help='Enforce all tasks must be defined in resourceConstraints (strict mode)')
72
+ def validate(program_file, schema, verbose, json_output, strict):
73
+ """
74
+ Validate a program file against the schema.
75
+
76
+ This command checks if the provided program file (JSON or YAML) conforms to the
77
+ Rhylthyme schema and performs additional semantic validations.
78
+
79
+ Use --json to get machine-readable output for CI or scripting.
80
+ Use --strict to require all tasks used in steps/buffers to be defined in resourceConstraints.
81
+ """
82
+ success = validate_program_file(program_file, schema, verbose, json_output, strict)
83
+ if not success:
84
+ sys.exit(1)
85
+
86
+ # Run command
87
+ @cli.command()
88
+ @click.argument('program_file', type=click.Path(exists=True))
89
+ @click.option('--schema', type=click.Path(exists=True),
90
+ default=lambda: pkg_resources.resource_filename('rhylthyme_spec', 'schemas/program_schema_0.1.0-alpha.json'),
91
+ help='Path to the schema file (default: built-in schema)')
92
+ @click.option('-e', '--environment', type=str,
93
+ help='Environment file path or ID to use (overrides program environment setting)')
94
+ @click.option('--time-scale', type=float, default=1.0,
95
+ help='Time scale factor (default: 1.0)')
96
+ @click.option('--validate/--no-validate', default=True,
97
+ help='Validate the program before running (default: True)')
98
+ @click.option('--auto-start', is_flag=True,
99
+ help='Automatically start the program without waiting for manual trigger')
100
+ def run(program_file, schema, environment, time_scale, validate, auto_start):
101
+ """
102
+ Run a program file with the interactive UI.
103
+
104
+ This command executes the provided program file (JSON or YAML) according to the
105
+ Rhylthyme schema and displays an interactive terminal UI for
106
+ monitoring and controlling the execution.
107
+
108
+ Use -e/--environment to specify which environment to use when running the program.
109
+ This overrides any environment specified in the program file.
110
+ """
111
+ run_program(program_file, schema, time_scale, validate, auto_start, environment)
112
+
113
+ # Plan command
114
+ @cli.command()
115
+ @click.argument('input_file', type=click.Path(exists=True))
116
+ @click.argument('output_file', type=click.Path())
117
+ @click.option('-e', '--environment', type=str, help='Environment file path to use for planning')
118
+ @click.option('--verbose', '-v', is_flag=True, help='Show detailed planning information')
119
+ def plan(input_file, output_file, environment, verbose):
120
+ """
121
+ Optimize a program schedule to reduce resource contention.
122
+
123
+ This command analyzes the provided program file (JSON or YAML) for resource
124
+ bottlenecks and creates an optimized version by staggering track and step
125
+ starts to reduce contention at critical junctures.
126
+
127
+ The optimized program is saved to the specified output file.
128
+ """
129
+ success = plan_program(input_file, output_file, verbose, environment_file=environment)
130
+ if not success:
131
+ sys.exit(1)
132
+
133
+ click.echo(f"Optimized program saved to {output_file}")
134
+ click.echo("Run the optimized program with:")
135
+ click.echo(f" rhylthyme run {output_file}")
136
+
137
+ # Environments command
138
+ @cli.command()
139
+ @click.option('--format', '-f', type=click.Choice(['table', 'json', 'yaml']), default='table',
140
+ help='Output format (default: table)')
141
+ def environments(format):
142
+ """
143
+ List all available environment catalogs.
144
+
145
+ This command displays all environment catalogs that can be referenced
146
+ by programs. Each environment defines resource constraints for different
147
+ settings like restaurants, bakeries, laboratories, etc.
148
+ """
149
+ loader = get_environment_loader()
150
+ envs = loader.list_environments()
151
+
152
+ if not envs:
153
+ click.echo("No environment catalogs found.")
154
+ return
155
+
156
+ if format == 'json':
157
+ import json
158
+ click.echo(json.dumps(envs, indent=2))
159
+ elif format == 'yaml':
160
+ import yaml
161
+ click.echo(yaml.dump(envs, default_flow_style=False))
162
+ else: # table format
163
+ # Calculate column widths
164
+ id_width = max(len(env['id']) for env in envs) + 2
165
+ name_width = max(len(env['name']) for env in envs) + 2
166
+ type_width = max(len(env['type']) for env in envs) + 2
167
+ icon_width = max(len(env.get('icon', '')) for env in envs) + 2
168
+
169
+ # Print header
170
+ click.echo(f"{'ID':<{id_width}}{'Name':<{name_width}}{'Type':<{type_width}}{'Icon':<{icon_width}}Description")
171
+ click.echo(f"{'-' * id_width}{'-' * name_width}{'-' * type_width}{'-' * icon_width}{'-' * 40}")
172
+
173
+ # Print environments
174
+ for env in envs:
175
+ description = env['description']
176
+ if len(description) > 40:
177
+ description = description[:37] + "..."
178
+ icon = env.get('icon', 'fa-building')
179
+ click.echo(f"{env['id']:<{id_width}}{env['name']:<{name_width}}{env['type']:<{type_width}}{icon:<{icon_width}}{description}")
180
+
181
+ # Validate environments command
182
+ @cli.command('validate-environments')
183
+ @click.option('--environments-dir', type=click.Path(exists=True), default='environments',
184
+ help='Directory containing environment files (default: environments)')
185
+ @click.option('--verbose', '-v', is_flag=True, help='Show detailed validation information')
186
+ def validate_environments(environments_dir, verbose):
187
+ """
188
+ Validate all environment catalog files against their schemas.
189
+
190
+ This command checks if environment files conform to the base environment
191
+ schema and validates type-specific requirements (e.g., kitchen environments
192
+ should have appropriate kitchen tasks and equipment).
193
+ """
194
+ try:
195
+ from .environment_schemas import validate_all_environments, EnvironmentValidator
196
+ except ImportError:
197
+ click.echo("Error: Environment validation not available. Missing dependencies.")
198
+ sys.exit(1)
199
+
200
+ click.echo(f"Validating environments in: {environments_dir}")
201
+ validation_results = validate_all_environments(environments_dir)
202
+
203
+ if not validation_results:
204
+ click.echo("✓ All environment files are valid!")
205
+ return
206
+
207
+ # Count errors vs warnings
208
+ total_errors = 0
209
+ total_warnings = 0
210
+
211
+ for filename, errors in validation_results.items():
212
+ click.echo(f"\n{filename}:")
213
+ for error in errors:
214
+ if error.startswith("Warning:"):
215
+ total_warnings += 1
216
+ if verbose:
217
+ click.echo(f" ⚠️ {error}")
218
+ else:
219
+ total_errors += 1
220
+ click.echo(f" ❌ {error}")
221
+
222
+ if total_errors > 0:
223
+ click.echo(f"\n❌ Validation failed: {total_errors} errors found")
224
+ if total_warnings > 0:
225
+ click.echo(f"⚠️ {total_warnings} warnings found")
226
+ sys.exit(1)
227
+ elif total_warnings > 0:
228
+ click.echo(f"\n⚠️ Validation passed with {total_warnings} warnings")
229
+ if not verbose:
230
+ click.echo("Use --verbose to see warning details")
231
+ else:
232
+ click.echo("✓ All environment files are valid!")
233
+
234
+ # Environment info command
235
+ @cli.command('environment-info')
236
+ @click.argument('environment_type')
237
+ def environment_info(environment_type):
238
+ """
239
+ Show information about a specific environment type.
240
+
241
+ This command displays the required tasks, common tasks, and suggested
242
+ actor types for a given environment type (e.g., kitchen, laboratory, bakery).
243
+ """
244
+ try:
245
+ from .environment_schemas import EnvironmentValidator
246
+ from .environment_icons import get_environment_icon
247
+ except ImportError:
248
+ click.echo("Error: Environment schemas not available.")
249
+ sys.exit(1)
250
+
251
+ validator = EnvironmentValidator()
252
+ info = validator.get_environment_type_info(environment_type)
253
+
254
+ if not info:
255
+ click.echo(f"Unknown environment type: {environment_type}")
256
+ click.echo(f"Supported types: {', '.join(sorted(validator.list_supported_types()))}")
257
+ sys.exit(1)
258
+
259
+ # Get icon
260
+ icon = get_environment_icon(environment_type)
261
+
262
+ click.echo(f"Environment Type: {environment_type}")
263
+ click.echo(f"Icon: {icon}")
264
+ click.echo(f"Required Tasks: {', '.join(info.get('required_tasks', []))}")
265
+ click.echo(f"Common Tasks: {', '.join(info.get('common_tasks', []))}")
266
+ click.echo(f"Actor Types: {', '.join(info.get('actor_types', []))}")
267
+
268
+ def main():
269
+ """Entry point for the CLI."""
270
+ cli()
271
+
272
+ if __name__ == '__main__':
273
+ main()
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Environment Icons Index
4
+
5
+ This module provides a mapping of environment types to appropriate FontAwesome icons
6
+ for use in visualizations and user interfaces.
7
+ """
8
+
9
+ from typing import Dict, List, Optional
10
+
11
+ # Environment Types to FontAwesome Icons Mapping
12
+ ENVIRONMENT_ICONS: Dict[str, str] = {
13
+ # Kitchen environments
14
+ "kitchen": "fa-utensils",
15
+ "home": "fa-house",
16
+ "restaurant": "fa-utensils",
17
+ "commercial-kitchen": "fa-fire-burner",
18
+
19
+ # Laboratory environments
20
+ "laboratory": "fa-flask",
21
+ "lab": "fa-flask",
22
+ "research": "fa-microscope",
23
+ "biotech": "fa-dna",
24
+ "pharma": "fa-pills",
25
+ "medical": "fa-user-doctor",
26
+
27
+ # Bakery environments
28
+ "bakery": "fa-bread-slice",
29
+ "artisan": "fa-wheat-awn",
30
+ "pastry": "fa-cake-candles",
31
+
32
+ # Airport environments
33
+ "airport": "fa-plane",
34
+ "aviation": "fa-plane-departure",
35
+ "runway": "fa-plane-arrival",
36
+ "terminal": "fa-building",
37
+
38
+ # Additional environment types
39
+ "manufacturing": "fa-industry",
40
+ "warehouse": "fa-warehouse",
41
+ "office": "fa-building",
42
+ "hospital": "fa-hospital",
43
+ "school": "fa-graduation-cap",
44
+ "retail": "fa-store",
45
+ "farm": "fa-tractor",
46
+ "datacenter": "fa-server",
47
+ "factory": "fa-gear",
48
+ "workshop": "fa-screwdriver-wrench",
49
+ "garage": "fa-car",
50
+ "gym": "fa-dumbbell",
51
+ "studio": "fa-microphone",
52
+ "theater": "fa-masks-theater",
53
+ "library": "fa-book",
54
+ "garden": "fa-seedling",
55
+ "greenhouse": "fa-leaf",
56
+ "clinic": "fa-stethoscope",
57
+ "spa": "fa-spa",
58
+ "hotel": "fa-bed",
59
+ }
60
+
61
+ # Default icon for unknown environment types
62
+ DEFAULT_ENVIRONMENT_ICON = "fa-building"
63
+
64
+ # Icon categories for organization
65
+ ICON_CATEGORIES: Dict[str, List[str]] = {
66
+ "food_service": [
67
+ "kitchen", "restaurant", "commercial-kitchen", "bakery",
68
+ "artisan", "pastry", "home"
69
+ ],
70
+ "research_science": [
71
+ "laboratory", "lab", "research", "biotech", "pharma", "medical"
72
+ ],
73
+ "transportation": [
74
+ "airport", "aviation", "runway", "terminal"
75
+ ],
76
+ "industrial": [
77
+ "manufacturing", "warehouse", "factory", "workshop", "garage"
78
+ ],
79
+ "commercial": [
80
+ "office", "retail", "store", "hotel"
81
+ ],
82
+ "healthcare": [
83
+ "hospital", "clinic", "spa"
84
+ ],
85
+ "educational": [
86
+ "school", "library"
87
+ ],
88
+ "entertainment": [
89
+ "gym", "studio", "theater"
90
+ ],
91
+ "agricultural": [
92
+ "farm", "garden", "greenhouse"
93
+ ],
94
+ "technology": [
95
+ "datacenter"
96
+ ]
97
+ }
98
+
99
+
100
+ def get_environment_icon(environment_type: str) -> str:
101
+ """
102
+ Get the FontAwesome icon class for a given environment type.
103
+
104
+ Args:
105
+ environment_type: The type of environment (e.g., 'kitchen', 'laboratory')
106
+
107
+ Returns:
108
+ FontAwesome icon class string (e.g., 'fa-utensils')
109
+ """
110
+ if not environment_type:
111
+ return DEFAULT_ENVIRONMENT_ICON
112
+
113
+ # Normalize the environment type (lowercase, handle common variations)
114
+ normalized_type = environment_type.lower().strip()
115
+
116
+ # Direct lookup
117
+ if normalized_type in ENVIRONMENT_ICONS:
118
+ return ENVIRONMENT_ICONS[normalized_type]
119
+
120
+ # Try partial matches for compound names
121
+ for env_type, icon in ENVIRONMENT_ICONS.items():
122
+ if env_type in normalized_type or normalized_type in env_type:
123
+ return icon
124
+
125
+ # Return default if no match found
126
+ return DEFAULT_ENVIRONMENT_ICON
127
+
128
+
129
+ def get_environment_icon_with_prefix(environment_type: str, prefix: str = "fas") -> str:
130
+ """
131
+ Get the complete FontAwesome icon class with prefix for a given environment type.
132
+
133
+ Args:
134
+ environment_type: The type of environment
135
+ prefix: FontAwesome prefix (e.g., 'fas', 'far', 'fab')
136
+
137
+ Returns:
138
+ Complete FontAwesome icon class string (e.g., 'fas fa-utensils')
139
+ """
140
+ icon = get_environment_icon(environment_type)
141
+ return f"{prefix} {icon}"
142
+
143
+
144
+ def get_icon_category(environment_type: str) -> Optional[str]:
145
+ """
146
+ Get the category that an environment type belongs to.
147
+
148
+ Args:
149
+ environment_type: The type of environment
150
+
151
+ Returns:
152
+ Category name or None if not found
153
+ """
154
+ normalized_type = environment_type.lower().strip() if environment_type else ""
155
+
156
+ for category, types in ICON_CATEGORIES.items():
157
+ if normalized_type in types:
158
+ return category
159
+
160
+ return None
161
+
162
+
163
+ def list_environment_types() -> List[str]:
164
+ """
165
+ Get a list of all supported environment types.
166
+
167
+ Returns:
168
+ List of environment type strings
169
+ """
170
+ return list(ENVIRONMENT_ICONS.keys())
171
+
172
+
173
+ def list_environment_types_by_category() -> Dict[str, List[str]]:
174
+ """
175
+ Get all environment types organized by category.
176
+
177
+ Returns:
178
+ Dictionary mapping category names to lists of environment types
179
+ """
180
+ return ICON_CATEGORIES.copy()
181
+
182
+
183
+ def search_environment_types(query: str) -> List[str]:
184
+ """
185
+ Search for environment types that match a query string.
186
+
187
+ Args:
188
+ query: Search query string
189
+
190
+ Returns:
191
+ List of matching environment types
192
+ """
193
+ query_lower = query.lower().strip()
194
+ matches = []
195
+
196
+ for env_type in ENVIRONMENT_ICONS.keys():
197
+ if query_lower in env_type.lower():
198
+ matches.append(env_type)
199
+
200
+ return sorted(matches)
201
+
202
+
203
+ # Validation function to ensure all referenced icons exist in FontAwesome
204
+ def validate_icons() -> Dict[str, bool]:
205
+ """
206
+ Validate that all icons in the mapping are valid FontAwesome icons.
207
+ Note: This is a basic check based on common FontAwesome patterns.
208
+ For complete validation, you would need to check against the FontAwesome icon list.
209
+
210
+ Returns:
211
+ Dictionary mapping icon names to validation status
212
+ """
213
+ validation_results = {}
214
+
215
+ # Common FontAwesome icon patterns for basic validation
216
+ valid_patterns = [
217
+ "fa-", "fa-solid", "fa-regular", "fa-light", "fa-thin", "fa-duotone", "fa-brands"
218
+ ]
219
+
220
+ for env_type, icon in ENVIRONMENT_ICONS.items():
221
+ # Basic validation: check if it starts with fa-
222
+ is_valid = icon.startswith("fa-") and len(icon) > 3
223
+ validation_results[icon] = is_valid
224
+
225
+ return validation_results
226
+
227
+
228
+ if __name__ == "__main__":
229
+ # Example usage and testing
230
+ print("Environment Icons Index")
231
+ print("=" * 40)
232
+
233
+ # Test the specific environment types requested
234
+ test_types = ["kitchen", "laboratory", "bakery", "airport"]
235
+
236
+ print("\nRequested Environment Types:")
237
+ for env_type in test_types:
238
+ icon = get_environment_icon(env_type)
239
+ full_icon = get_environment_icon_with_prefix(env_type)
240
+ category = get_icon_category(env_type)
241
+ print(f" {env_type:12} -> {icon:20} ({full_icon}) [Category: {category}]")
242
+
243
+ print(f"\nTotal supported environment types: {len(list_environment_types())}")
244
+
245
+ print("\nEnvironment Types by Category:")
246
+ for category, types in list_environment_types_by_category().items():
247
+ print(f" {category:15}: {', '.join(types)}")
248
+
249
+ print(f"\nDefault icon: {DEFAULT_ENVIRONMENT_ICON}")
250
+
251
+ # Icon validation
252
+ print("\nIcon Validation Results:")
253
+ validation = validate_icons()
254
+ invalid_icons = [icon for icon, valid in validation.items() if not valid]
255
+ if invalid_icons:
256
+ print(f" Invalid icons found: {invalid_icons}")
257
+ else:
258
+ print(" All icons appear to be valid FontAwesome patterns")