roadmap 2.2 + add 单测用例

This commit is contained in:
程茂宇 2023-07-12 11:49:31 +08:00
parent d3d249e844
commit f45b7176b5
4 changed files with 452 additions and 0 deletions

View file

@ -0,0 +1,41 @@
#!/usr/bin/env python
# coding: utf-8
"""
@Time : 2023/7/11 10:03
@Author : chengmaoyu
@File : action_output
"""
from pydantic import create_model, validator, root_validator, BaseModel
from typing import Dict, Type
class ActionOutput:
content: str
instruct_content: BaseModel
def __init__(self, content: str, instruct_content: BaseModel):
self.content = content
self.instruct_content = instruct_content
@classmethod
def create_model_class(cls, class_name: str, mapping: Dict[str, Type]):
new_class = create_model(class_name, **mapping)
@validator('*', allow_reuse=True)
def check_name(v, field):
if field.name not in mapping.keys():
raise ValueError(f'Unrecognized block: {field.name}')
return v
@root_validator(pre=True, allow_reuse=True)
def check_missing_fields(values):
required_fields = set(mapping.keys())
missing_fields = required_fields - set(values.keys())
if missing_fields:
raise ValueError(f'Missing fields: {missing_fields}')
return values
new_class.__validator_check_name = classmethod(check_name)
new_class.__root_validator_check_missing_fields = classmethod(check_missing_fields)
return new_class

View file

@ -0,0 +1,49 @@
#!/usr/bin/env python
# coding: utf-8
"""
@Time : 2023/7/11 10:49
@Author : chengmaoyu
@File : test_action_output
"""
from metagpt.actions import ActionOutput
from typing import List, Tuple
t_dict = {"Required Python third-party packages": "\"\"\"\nflask==1.1.2\npygame==2.0.1\n\"\"\"\n",
"Required Other language third-party packages": "\"\"\"\nNo third-party packages required for other languages.\n\"\"\"\n",
"Full API spec": "\"\"\"\nopenapi: 3.0.0\ninfo:\n title: Web Snake Game API\n version: 1.0.0\npaths:\n /game:\n get:\n summary: Get the current game state\n responses:\n '200':\n description: A JSON object of the game state\n post:\n summary: Send a command to the game\n requestBody:\n required: true\n content:\n application/json:\n schema:\n type: object\n properties:\n command:\n type: string\n responses:\n '200':\n description: A JSON object of the updated game state\n\"\"\"\n",
"Logic Analysis": [
["app.py", "Main entry point for the Flask application. Handles HTTP requests and responses."],
["game.py", "Contains the Game and Snake classes. Handles the game logic."],
["static/js/script.js", "Handles user interactions and updates the game UI."],
["static/css/styles.css", "Defines the styles for the game UI."],
["templates/index.html", "The main page of the web application. Displays the game UI."]],
"Task list": ["game.py", "app.py", "static/css/styles.css", "static/js/script.js", "templates/index.html"],
"Shared Knowledge": "\"\"\"\n'game.py' contains the Game and Snake classes which are responsible for the game logic. The Game class uses an instance of the Snake class.\n\n'app.py' is the main entry point for the Flask application. It creates an instance of the Game class and handles HTTP requests and responses.\n\n'static/js/script.js' is responsible for handling user interactions and updating the game UI based on the game state returned by 'app.py'.\n\n'static/css/styles.css' defines the styles for the game UI.\n\n'templates/index.html' is the main page of the web application. It displays the game UI and loads 'static/js/script.js' and 'static/css/styles.css'.\n\"\"\"\n",
"Anything UNCLEAR": "We need clarification on how the high score should be stored. Should it persist across sessions (stored in a database or a file) or should it reset every time the game is restarted? Also, should the game speed increase as the snake grows, or should it remain constant throughout the game?"}
WRITE_TASKS_OUTPUT_MAPPING = {
"Required Python third-party packages": (str, ...),
"Required Other language third-party packages": (str, ...),
"Full API spec": (str, ...),
"Logic Analysis": (List[Tuple[str, str]], ...),
"Task list": (List[str], ...),
"Shared Knowledge": (str, ...),
"Anything UNCLEAR": (str, ...),
}
def test_create_model_class():
test_class = ActionOutput.create_model_class("test_class", WRITE_TASKS_OUTPUT_MAPPING)
assert test_class.__name__ == "test_class"
def test_create_model_class_with_mapping():
t = ActionOutput.create_model_class("test_class_1", WRITE_TASKS_OUTPUT_MAPPING)
t1 = t(**t_dict)
value = t1.dict()["Task list"]
assert value == ["game.py", "app.py", "static/css/styles.css", "static/js/script.js", "templates/index.html"]
if __name__ == '__main__':
test_create_model_class()
test_create_model_class_with_mapping()

View file

@ -0,0 +1,139 @@
#!/usr/bin/env python
# coding: utf-8
"""
@Time : 2023/7/10 17:14
@Author : chengmaoyu
@File : test_code_parser.py
"""
import pytest
from metagpt.utils.common import CodeParser
t_text = '''
## Required Python third-party packages
```python
"""
flask==1.1.2
pygame==2.0.1
"""
```
## Required Other language third-party packages
```python
"""
No third-party packages required for other languages.
"""
```
## Full API spec
```python
"""
openapi: 3.0.0
info:
title: Web Snake Game API
version: 1.0.0
paths:
/game:
get:
summary: Get the current game state
responses:
'200':
description: A JSON object of the game state
post:
summary: Send a command to the game
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
command:
type: string
responses:
'200':
description: A JSON object of the updated game state
"""
```
## Logic Analysis
```python
[
("app.py", "Main entry point for the Flask application. Handles HTTP requests and responses."),
("game.py", "Contains the Game and Snake classes. Handles the game logic."),
("static/js/script.js", "Handles user interactions and updates the game UI."),
("static/css/styles.css", "Defines the styles for the game UI."),
("templates/index.html", "The main page of the web application. Displays the game UI.")
]
```
## Task list
```python
[
"game.py",
"app.py",
"static/css/styles.css",
"static/js/script.js",
"templates/index.html"
]
```
## Shared Knowledge
```python
"""
'game.py' contains the Game and Snake classes which are responsible for the game logic. The Game class uses an instance of the Snake class.
'app.py' is the main entry point for the Flask application. It creates an instance of the Game class and handles HTTP requests and responses.
'static/js/script.js' is responsible for handling user interactions and updating the game UI based on the game state returned by 'app.py'.
'static/css/styles.css' defines the styles for the game UI.
'templates/index.html' is the main page of the web application. It displays the game UI and loads 'static/js/script.js' and 'static/css/styles.css'.
"""
```
## Anything UNCLEAR
We need clarification on how the high score should be stored. Should it persist across sessions (stored in a database or a file) or should it reset every time the game is restarted? Also, should the game speed increase as the snake grows, or should it remain constant throughout the game?
'''
class TestCodeParser:
@pytest.fixture
def parser(self):
return CodeParser()
@pytest.fixture
def text(self):
return t_text
def test_parse_blocks(self, parser, text):
result = parser.parse_blocks(text)
print(result)
assert result == {"title": "content", "title2": "content2"}
def test_parse_block(self, parser, text):
result = parser.parse_block("title", text)
print(result)
assert result == "content"
def test_parse_code(self, parser, text):
result = parser.parse_code("title", text, "python")
print(result)
assert result == "print('hello world')"
def test_parse_str(self, parser, text):
result = parser.parse_str("title", text, "python")
print(result)
assert result == "hello world"
def test_parse_file_list(self, parser, text):
result = parser.parse_file_list("Task list", text)
print(result)
assert result == ['task1', 'task2']
if __name__ == '__main__':
t = TestCodeParser()
t.test_parse_file_list(CodeParser(), t_text)
# TestCodeParser.test_parse_file_list()

View file

@ -0,0 +1,223 @@
#!/usr/bin/env python
# coding: utf-8
"""
@Time : 2023/7/11 10:25
@Author : chengmaoyu
@File : test_output_parser.py
"""
import pytest
from typing import List, Tuple
import re
import ast
from metagpt.utils.common import OutputParser
def test_parse_blocks():
test_text = "##block1\nThis is block 1.\n##block2\nThis is block 2."
expected_result = {'block1': 'This is block 1.', 'block2': 'This is block 2.'}
assert OutputParser.parse_blocks(test_text) == expected_result
def test_parse_code():
test_text = "```python\nprint('Hello, world!')\n```"
expected_result = "print('Hello, world!')"
assert OutputParser.parse_code(test_text, 'python') == expected_result
with pytest.raises(Exception):
OutputParser.parse_code(test_text, 'java')
def test_parse_str():
test_text = "name = 'Alice'"
expected_result = 'Alice'
assert OutputParser.parse_str(test_text) == expected_result
def test_parse_file_list():
test_text = "files=['file1', 'file2', 'file3']"
expected_result = ['file1', 'file2', 'file3']
assert OutputParser.parse_file_list(test_text) == expected_result
with pytest.raises(Exception):
OutputParser.parse_file_list("wrong_input")
def test_parse_data():
test_data = "##block1\n```python\nprint('Hello, world!')\n```\n##block2\nfiles=['file1', 'file2', 'file3']"
expected_result = {'block1': "print('Hello, world!')", 'block2': ['file1', 'file2', 'file3']}
assert OutputParser.parse_data(test_data) == expected_result
if __name__ == '__main__':
t_text = '''
## Required Python third-party packages
```python
"""
flask==1.1.2
pygame==2.0.1
"""
```
## Required Other language third-party packages
```python
"""
No third-party packages required for other languages.
"""
```
## Full API spec
```python
"""
openapi: 3.0.0
info:
title: Web Snake Game API
version: 1.0.0
paths:
/game:
get:
summary: Get the current game state
responses:
'200':
description: A JSON object of the game state
post:
summary: Send a command to the game
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
command:
type: string
responses:
'200':
description: A JSON object of the updated game state
"""
```
## Logic Analysis
```python
[
("app.py", "Main entry point for the Flask application. Handles HTTP requests and responses."),
("game.py", "Contains the Game and Snake classes. Handles the game logic."),
("static/js/script.js", "Handles user interactions and updates the game UI."),
("static/css/styles.css", "Defines the styles for the game UI."),
("templates/index.html", "The main page of the web application. Displays the game UI.")
]
```
## Task list
```python
[
"game.py",
"app.py",
"static/css/styles.css",
"static/js/script.js",
"templates/index.html"
]
```
## Shared Knowledge
```python
"""
'game.py' contains the Game and Snake classes which are responsible for the game logic. The Game class uses an instance of the Snake class.
'app.py' is the main entry point for the Flask application. It creates an instance of the Game class and handles HTTP requests and responses.
'static/js/script.js' is responsible for handling user interactions and updating the game UI based on the game state returned by 'app.py'.
'static/css/styles.css' defines the styles for the game UI.
'templates/index.html' is the main page of the web application. It displays the game UI and loads 'static/js/script.js' and 'static/css/styles.css'.
"""
```
## Anything UNCLEAR
We need clarification on how the high score should be stored. Should it persist across sessions (stored in a database or a file) or should it reset every time the game is restarted? Also, should the game speed increase as the snake grows, or should it remain constant throughout the game?
'''
OUTPUT_MAPPING = {
"Original Requirements": (str, ...),
"Product Goals": (List[str], ...),
"User Stories": (List[str], ...),
"Competitive Analysis": (List[str], ...),
"Competitive Quadrant Chart": (str, ...),
"Requirement Analysis": (str, ...),
"Requirement Pool": (List[Tuple[str, str]], ...),
"Anything UNCLEAR": (str, ...),
}
t_text1 = '''## Original Requirements:
The boss wants to create a web-based version of the game "Fly Bird".
## Product Goals:
- Create a web-based version of the game "Fly Bird" that is engaging and addictive.
- Provide a seamless and intuitive user experience.
- Optimize the game for different devices and screen sizes.
## User Stories:
- As a user, I want to be able to control the bird's flight by clicking or tapping on the screen.
- As a user, I want to see my score and the highest score achieved in the game.
- As a user, I want the game to be challenging but not frustratingly difficult.
- As a user, I want to be able to pause and resume the game at any time.
- As a user, I want to be able to share my score on social media.
## Competitive Analysis:
- Flappy Bird: A popular mobile game where the player controls a bird's flight through a series of obstacles.
- Angry Birds: A physics-based puzzle game where the player launches birds to destroy structures and defeat pigs.
- Snake Game: A classic game where the player controls a snake to eat food and grow longer without hitting the walls or its own body.
- Temple Run: An endless running game where the player controls a character to avoid obstacles and collect coins.
- Subway Surfers: An endless running game where the player controls a character to avoid obstacles and collect coins while being chased by a guard.
- Doodle Jump: A vertical platform game where the player controls a character to jump on platforms and avoid falling.
- Fruit Ninja: A fruit-slicing game where the player uses their finger to slice flying fruits.
## Competitive Quadrant Chart:
```mermaid
quadrantChart
title Reach and engagement of games
x-axis Low Reach --> High Reach
y-axis Low Engagement --> High Engagement
quadrant-1 We should expand
quadrant-2 Need to promote
quadrant-3 Re-evaluate
quadrant-4 May be improved
"Flappy Bird": [0.8, 0.9]
"Angry Birds": [0.9, 0.8]
"Snake Game": [0.6, 0.6]
"Temple Run": [0.9, 0.7]
"Subway Surfers": [0.9, 0.7]
"Doodle Jump": [0.7, 0.5]
"Fruit Ninja": [0.8, 0.6]
"Our Target Product": [0.7, 0.8]
```
## Requirement Analysis:
The product should be a web-based version of the game "Fly Bird" that is engaging, addictive, and optimized for different devices and screen sizes. It should provide a seamless and intuitive user experience, with controls that allow the user to control the bird's flight by clicking or tapping on the screen. The game should display the user's score and the highest score achieved. It should be challenging but not frustratingly difficult, allowing the user to pause and resume the game at any time. The user should also have the option to share their score on social media.
## Requirement Pool:
```python
[
("Implement bird's flight control using click or tap", "P0"),
("Display user's score and highest score achieved", "P0"),
("Implement challenging but not frustrating difficulty level", "P1"),
("Allow user to pause and resume the game", "P1"),
("Implement social media sharing feature", "P2")
]
```
## Anything UNCLEAR:
There are no unclear points.
'''
d = OutputParser.parse_data_with_mapping(t_text1, OUTPUT_MAPPING)
import json
print(json.dumps(d))