forked from mergeos-bounties/PoseGuide
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathai_solution.py
More file actions
3 lines (3 loc) · 8.37 KB
/
Copy pathai_solution.py
File metadata and controls
3 lines (3 loc) · 8.37 KB
1
2
3
```json
{
"solution_code": "# File: poseguide/cli.py\n\nimport click\nfrom rich.console import Console\nfrom rich.table import Table\nfrom typing import Optional, List\nimport json\nfrom pathlib import Path\n\nfrom poseguide.core import PoseDatabase\n\nconsole = Console()\n\n\n@click.group()\ndef cli():\n \"\"\"PoseGuide CLI - Photography pose coach.\"\"\"\n pass\n\n\n@cli.group()\ndef poses():\n \"\"\"Manage and search poses.\"\"\"\n pass\n\n\n@poses.command(\"search\")\n@click.option(\n \"--tag\",\n \"-t\",\n multiple=True,\n help=\"Filter by tag(s). Can be specified multiple times for AND filtering.\",\n)\n@click.option(\n \"--difficulty\",\n \"-d\",\n type=click.Choice([\"beginner\", \"intermediate\", \"advanced\"], case_sensitive=False),\n help=\"Filter by difficulty level.\",\n)\n@click.option(\n \"--format\",\n \"-f\",\n type=click.Choice([\"table\", \"json\"], case_sensitive=False),\n default=\"table\",\n help=\"Output format (default: table).\",\n)\ndef search_poses(\n tag: tuple,\n difficulty: Optional[str],\n format: str,\n):\n \"\"\"Search poses with optional tag and difficulty filters.\n \n Examples:\n \n poseguide poses search\n \n poseguide poses search --tag portrait\n \n poseguide poses search --tag portrait --tag outdoor\n \n poseguide poses search --difficulty beginner\n \n poseguide poses search --tag portrait --difficulty intermediate\n \"\"\"\n db = PoseDatabase()\n poses = db.search(\n tags=list(tag) if tag else None,\n difficulty=difficulty,\n )\n \n if format == \"json\":\n output = [\n {\n \"id\": pose.id,\n \"name\": pose.name,\n \"difficulty\": pose.difficulty,\n \"tags\": pose.tags,\n \"description\": pose.description,\n }\n for pose in poses\n ]\n console.print_json(json.dumps(output, indent=2))\n else:\n # Rich table output\n table = Table(title=\"Poses\", show_header=True, header_style=\"bold magenta\")\n table.add_column(\"ID\", style=\"cyan\", width=8)\n table.add_column(\"Name\", style=\"green\", width=25)\n table.add_column(\"Difficulty\", style=\"yellow\", width=15)\n table.add_column(\"Tags\", style=\"blue\", width=30)\n table.add_column(\"Description\", width=40)\n \n for pose in poses:\n table.add_row(\n str(pose.id),\n pose.name,\n pose.difficulty,\n \", \".join(pose.tags),\n pose.description[:60] + \"...\" if len(pose.description) > 60 else pose.description,\n )\n \n console.print(table)\n console.print(f\"\\n[bold]Total:[/bold] {len(poses)} pose(s) found\")\n\n\nif __name__ == \"__main__\":\n cli()\n\n\n# File: poseguide/core.py\n\nfrom dataclasses import dataclass\nfrom typing import List, Optional\nimport json\nfrom pathlib import Path\n\n\n@dataclass\nclass Pose:\n \"\"\"Represents a photography pose.\"\"\"\n id: int\n name: str\n difficulty: str\n tags: List[str]\n description: str\n svg_path: Optional[str] = None\n\n\nclass PoseDatabase:\n \"\"\"Manages the pose database.\"\"\"\n \n def __init__(self, data_path: Optional[Path] = None):\n \"\"\"Initialize the pose database.\n \n Args:\n data_path: Path to poses.json. If None, uses default location.\n \"\"\"\n if data_path is None:\n data_path = Path(__file__).parent / \"data\" / \"poses.json\"\n self.data_path = data_path\n self._poses = self._load_poses()\n \n def _load_poses(self) -> List[Pose]:\n \"\"\"Load poses from JSON file.\"\"\"\n if not self.data_path.exists():\n return self._get_default_poses()\n \n with open(self.data_path, \"r\") as f:\n data = json.load(f)\n \n return [\n Pose(\n id=p[\"id\"],\n name=p[\"name\"],\n difficulty=p[\"difficulty\"],\n tags=p[\"tags\"],\n description=p[\"description\"],\n svg_path=p.get(\"svg_path\"),\n )\n for p in data[\"poses\"]\n ]\n \n def _get_default_poses(self) -> List[Pose]:\n \"\"\"Return default poses if no data file exists.\"\"\"\n return [\n Pose(\n id=1,\n name=\"Classic Portrait\",\n difficulty=\"beginner\",\n tags=[\"portrait\", \"indoor\", \"standing\"],\n description=\"Standing straight, facing camera, hands relaxed at sides.\",\n ),\n Pose(\n id=2,\n name=\"Crossed Arms\",\n difficulty=\"beginner\",\n tags=[\"portrait\", \"indoor\", \"standing\", \"confident\"],\n description=\"Arms crossed over chest, slight lean to one side.\",\n ),\n Pose(\n id=3,\n name=\"Walking Pose\",\n difficulty=\"intermediate\",\n tags=[\"outdoor\", \"dynamic\", \"casual\"],\n description=\"Mid-stride walking pose, looking at camera over shoulder.\",\n ),\n Pose(\n id=4,\n name=\"Leaning Against Wall\",\n difficulty=\"intermediate\",\n tags=[\"urban\", \"casual\", \"standing\"],\n description=\"Leaning against wall with one foot up, arms crossed or in pockets.\",\n ),\n Pose(\n id=5,\n name=\"Action Jump\",\n difficulty=\"advanced\",\n tags=[\"outdoor\", \"dynamic\", \"energetic\"],\n description=\"Mid-air jump with arms and legs extended dynamically.\",\n ),\n Pose(\n id=6,\n name=\"Sitting on Chair\",\n difficulty=\"beginner\",\n tags=[\"portrait\", \"indoor\", \"sitting\"],\n description=\"Sitting on chair with good posture, hands on knees or armrests.\",\n ),\n Pose(\n id=7,\n name=\"Silhouette Pose\",\n difficulty=\"advanced\",\n tags=[\"outdoor\", \"artistic\", \"creative\"],\n description=\"Backlit pose creating dramatic silhouette, arms extended.\",\n ),\n ]\n \n def search(\n self,\n tags: Optional[List[str]] = None,\n difficulty: Optional[str] = None,\n ) -> List[Pose]:\n \"\"\"Search poses with filters.\n \n Args:\n tags: List of tags to filter by (AND logic - pose must have all tags).\n difficulty: Difficulty level to filter by.\n \n Returns:\n List of matching poses.\n \"\"\"\n results = self._poses\n \n # Filter by tags (AND logic)\n if tags:\n results = [\n pose for pose in results\n if all(tag.lower() in [t.lower() for t in pose.tags] for tag in tags)\n ]\n \n # Filter by difficulty\n if difficulty:\n results = [\n pose for pose in results\n if pose.difficulty.lower() == difficulty.lower()\n ]\n \n return results\n \n def get_all_tags(self) -> List[str]:\n \"\"\"Get all unique tags from poses.\"\"\"\n tags = set()\n for pose in self._poses:\n tags.update(pose.tags)\n return sorted(list(tags))\n\n\n# File: tests/test_cli.py\n\nimport pytest\nfrom click.testing import CliRunner\nfrom poseguide.cli import cli\nfrom pathlib import Path\nimport json\n\n\nclass TestPosesSearch:\n \"\"\"Test suite for poses search command.\"\"\"\n \n def setup_method(self):\n \"\"\"Set up test fixtures.\"\"\"\n self.runner = CliRunner()\n \n def test_search_no_filters(self):\n \"\"\"Test search without any filters returns all poses.\"\"\"\n result = self.runner.invoke(cli, [\"poses\", \"search\"])\n assert result.exit_code == 0\n assert \"Poses\" in result.output\n assert \"Total:\" in result.output\n \n def test_search_with_single_tag(self):\n