pycityagent 2.0.0a13__py3-none-any.whl → 2.0.0a15__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.
- pycityagent/__init__.py +14 -0
- pycityagent/agent.py +164 -63
- pycityagent/economy/econ_client.py +2 -0
- pycityagent/environment/simulator.py +5 -4
- pycityagent/memory/const.py +1 -0
- pycityagent/memory/memory.py +8 -7
- pycityagent/memory/memory_base.py +6 -4
- pycityagent/message/messager.py +8 -7
- pycityagent/simulation/agentgroup.py +136 -14
- pycityagent/simulation/simulation.py +212 -42
- pycityagent/survey/manager.py +58 -0
- pycityagent/survey/models.py +120 -0
- pycityagent/utils/__init__.py +7 -0
- pycityagent/utils/avro_schema.py +110 -0
- pycityagent/utils/survey_util.py +53 -0
- pycityagent/workflow/tool.py +0 -3
- {pycityagent-2.0.0a13.dist-info → pycityagent-2.0.0a15.dist-info}/METADATA +3 -1
- {pycityagent-2.0.0a13.dist-info → pycityagent-2.0.0a15.dist-info}/RECORD +20 -21
- pycityagent/simulation/interview.py +0 -40
- pycityagent/simulation/survey/manager.py +0 -68
- pycityagent/simulation/survey/models.py +0 -52
- pycityagent/simulation/ui/__init__.py +0 -3
- pycityagent/simulation/ui/interface.py +0 -602
- /pycityagent/{simulation/survey → survey}/__init__.py +0 -0
- {pycityagent-2.0.0a13.dist-info → pycityagent-2.0.0a15.dist-info}/WHEEL +0 -0
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            def process_survey_for_llm(survey_dict: dict) -> str:
         | 
| 2 | 
            +
                """
         | 
| 3 | 
            +
                将问卷字典转换为LLM可以逐题处理的格式,使用英文提示
         | 
| 4 | 
            +
                """
         | 
| 5 | 
            +
                prompt = f"""Survey Title: {survey_dict['title']}
         | 
| 6 | 
            +
            Survey Description: {survey_dict['description']}
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            Please answer each question in the following format:
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            """
         | 
| 11 | 
            +
                
         | 
| 12 | 
            +
                question_count = 1
         | 
| 13 | 
            +
                for page in survey_dict['pages']:
         | 
| 14 | 
            +
                    for question in page['elements']:
         | 
| 15 | 
            +
                        prompt += f"Question {question_count}: {question['title']}\n"
         | 
| 16 | 
            +
                        
         | 
| 17 | 
            +
                        # 根据不同类型的问题生成不同的提示
         | 
| 18 | 
            +
                        if question['type'] == 'radiogroup':
         | 
| 19 | 
            +
                            prompt += "Options: " + ", ".join(question['choices']) + "\n"
         | 
| 20 | 
            +
                            prompt += "Please select ONE option\n"
         | 
| 21 | 
            +
                        
         | 
| 22 | 
            +
                        elif question['type'] == 'checkbox':
         | 
| 23 | 
            +
                            prompt += "Options: " + ", ".join(question['choices']) + "\n"
         | 
| 24 | 
            +
                            prompt += "You can select MULTIPLE options\n"
         | 
| 25 | 
            +
                            
         | 
| 26 | 
            +
                        elif question['type'] == 'rating':
         | 
| 27 | 
            +
                            prompt += f"Rating range: {question.get('min_rating', 1)} - {question.get('max_rating', 5)}\n"
         | 
| 28 | 
            +
                            prompt += "Please provide a rating within the range\n"
         | 
| 29 | 
            +
                            
         | 
| 30 | 
            +
                        elif question['type'] == 'matrix':
         | 
| 31 | 
            +
                            prompt += "Rows: " + ", ".join(question['rows']) + "\n"
         | 
| 32 | 
            +
                            prompt += "Columns: " + ", ".join(question['columns']) + "\n"
         | 
| 33 | 
            +
                            prompt += "Please select ONE column option for EACH row\n"
         | 
| 34 | 
            +
                            
         | 
| 35 | 
            +
                        elif question['type'] == 'text':
         | 
| 36 | 
            +
                            prompt += "Please provide a text response\n"
         | 
| 37 | 
            +
                            
         | 
| 38 | 
            +
                        elif question['type'] == 'boolean':
         | 
| 39 | 
            +
                            prompt += "Options: Yes, No\n"
         | 
| 40 | 
            +
                            prompt += "Please select either Yes or No\n"
         | 
| 41 | 
            +
                            
         | 
| 42 | 
            +
                        prompt += "\nAnswer: [Your response here]\n\n---\n\n"
         | 
| 43 | 
            +
                        question_count += 1
         | 
| 44 | 
            +
                        
         | 
| 45 | 
            +
                # 添加总结提示
         | 
| 46 | 
            +
                prompt += """Please ensure:
         | 
| 47 | 
            +
            1. All required questions are answered
         | 
| 48 | 
            +
            2. Responses match the question type requirements
         | 
| 49 | 
            +
            3. Answers are clear and specific
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            Format your responses exactly as requested above."""
         | 
| 52 | 
            +
                
         | 
| 53 | 
            +
                return prompt
         | 
    
        pycityagent/workflow/tool.py
    CHANGED
    
    
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            Metadata-Version: 2.1
         | 
| 2 2 | 
             
            Name: pycityagent
         | 
| 3 | 
            -
            Version: 2.0. | 
| 3 | 
            +
            Version: 2.0.0a15
         | 
| 4 4 | 
             
            Summary: LLM-based城市环境agent构建库
         | 
| 5 5 | 
             
            License: MIT
         | 
| 6 6 | 
             
            Author: Yuwei Yan
         | 
| @@ -20,6 +20,7 @@ Requires-Dist: aiohttp (==3.10.10) | |
| 20 20 | 
             
            Requires-Dist: aiomqtt (>=2.3.0,<3.0.0)
         | 
| 21 21 | 
             
            Requires-Dist: citystreetview (==1.2.4)
         | 
| 22 22 | 
             
            Requires-Dist: dashscope (==1.14.1)
         | 
| 23 | 
            +
            Requires-Dist: fastavro (>=1.10.0,<2.0.0)
         | 
| 23 24 | 
             
            Requires-Dist: geojson (==3.1.0)
         | 
| 24 25 | 
             
            Requires-Dist: gradio (>=5.7.1,<6.0.0)
         | 
| 25 26 | 
             
            Requires-Dist: grpcio (==1.67.1)
         | 
| @@ -28,6 +29,7 @@ Requires-Dist: mosstool (==1.0.24) | |
| 28 29 | 
             
            Requires-Dist: networkx (==3.2.1)
         | 
| 29 30 | 
             
            Requires-Dist: numpy (>=1.20.0,<2.0.0)
         | 
| 30 31 | 
             
            Requires-Dist: openai (>=1.58.1,<2.0.0)
         | 
| 32 | 
            +
            Requires-Dist: pandavro (>=1.8.0,<2.0.0)
         | 
| 31 33 | 
             
            Requires-Dist: poetry (>=1.2.2)
         | 
| 32 34 | 
             
            Requires-Dist: protobuf (<=4.24.0)
         | 
| 33 35 | 
             
            Requires-Dist: pycitydata (==1.0.0)
         | 
| @@ -1,7 +1,7 @@ | |
| 1 | 
            -
            pycityagent/__init__.py,sha256= | 
| 2 | 
            -
            pycityagent/agent.py,sha256= | 
| 1 | 
            +
            pycityagent/__init__.py,sha256=EDxt3Su3lH1IMh9suNw7GeGL7UrXeWiZTw5KWNznDzc,637
         | 
| 2 | 
            +
            pycityagent/agent.py,sha256=fcuKX6FtMzjNP8lVep9pG-9KHzHQwJ8IymJbmLKMfu0,23109
         | 
| 3 3 | 
             
            pycityagent/economy/__init__.py,sha256=aonY4WHnx-6EGJ4WKrx4S-2jAkYNLtqUA04jp6q8B7w,75
         | 
| 4 | 
            -
            pycityagent/economy/econ_client.py,sha256= | 
| 4 | 
            +
            pycityagent/economy/econ_client.py,sha256=wcuNtcpkSijJwNkt2mXw3SshYy4SBy6qbvJ0VQ7Aovo,10854
         | 
| 5 5 | 
             
            pycityagent/environment/__init__.py,sha256=awHxlOud-btWbk0FCS4RmGJ13W84oVCkbGfcrhKqihA,240
         | 
| 6 6 | 
             
            pycityagent/environment/interact/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 7 7 | 
             
            pycityagent/environment/interact/interact.py,sha256=ifxPPzuHeqLHIZ_6zvfXMoBOnBsXNIP4bYp7OJ7pnEQ,6588
         | 
| @@ -21,7 +21,7 @@ pycityagent/environment/sim/person_service.py,sha256=nIvOsoBoqOTDYtsiThg07-4ZBgk | |
| 21 21 | 
             
            pycityagent/environment/sim/road_service.py,sha256=phKTwTyhc_6Ht2mddEXpdENfl-lRXIVY0CHAlw1yHjI,1264
         | 
| 22 22 | 
             
            pycityagent/environment/sim/sim_env.py,sha256=HI1LcS_FotDKQ6vBnx0e49prXSABOfA20aU9KM-ZkCY,4625
         | 
| 23 23 | 
             
            pycityagent/environment/sim/social_service.py,sha256=6Iqvq6dz8H2jhLLdtaITc6Js9QnQw-Ylsd5AZgUj3-E,1993
         | 
| 24 | 
            -
            pycityagent/environment/simulator.py,sha256= | 
| 24 | 
            +
            pycityagent/environment/simulator.py,sha256=K7IyhiGC9BxanW28bpML4M0YREdMp1h7yMoWBlbf3RY,12504
         | 
| 25 25 | 
             
            pycityagent/environment/utils/__init__.py,sha256=1m4Q1EfGvNpUsa1bgQzzCyWhfkpElnskNImjjFD3Znc,237
         | 
| 26 26 | 
             
            pycityagent/environment/utils/base64.py,sha256=hoREzQo3FXMN79pqQLO2jgsDEvudciomyKii7MWljAM,374
         | 
| 27 27 | 
             
            pycityagent/environment/utils/const.py,sha256=3RMNy7_bE7-23K90j9DFW_tWEzu8s7hSTgKbV-3BFl4,5327
         | 
| @@ -36,35 +36,34 @@ pycityagent/llm/llm.py,sha256=BtxBvPK4tb8QlZIfxO5XJ73lKXwF8L31LqVbejWB8eo,15121 | |
| 36 36 | 
             
            pycityagent/llm/llmconfig.py,sha256=4Ylf4OFSBEFy8jrOneeX0HvPhWEaF5jGvy1HkXK08Ro,436
         | 
| 37 37 | 
             
            pycityagent/llm/utils.py,sha256=hoNPhvomb1u6lhFX0GctFipw74hVKb7bvUBDqwBzBYw,160
         | 
| 38 38 | 
             
            pycityagent/memory/__init__.py,sha256=Hs2NhYpIG-lvpwPWwj4DydB1sxtjz7cuA4iDAzCXnjI,243
         | 
| 39 | 
            -
            pycityagent/memory/const.py,sha256= | 
| 40 | 
            -
            pycityagent/memory/memory.py,sha256= | 
| 41 | 
            -
            pycityagent/memory/memory_base.py,sha256= | 
| 39 | 
            +
            pycityagent/memory/const.py,sha256=6zpJPJXWoH9-yf4RARYYff586agCoud9BRn7sPERB1g,932
         | 
| 40 | 
            +
            pycityagent/memory/memory.py,sha256=FjKVL_MgNBnSc0sox2tuxLqXg9_MQQr9vYdRDHMdDL4,18183
         | 
| 41 | 
            +
            pycityagent/memory/memory_base.py,sha256=euKZRCs4dbcKxjlZzpLCTnH066DAtRjj5g1JFKD40qQ,5633
         | 
| 42 42 | 
             
            pycityagent/memory/profile.py,sha256=s4LnxSPGSjIGZXHXkkd8mMa6uYYZrytgyQdWjcaqGf4,5182
         | 
| 43 43 | 
             
            pycityagent/memory/self_define.py,sha256=poPiexNhOLq_iTgK8s4mK_xoL_DAAcB8kMvInj7iE5E,5179
         | 
| 44 44 | 
             
            pycityagent/memory/state.py,sha256=5W0c1yJ-aaPpE74B2LEcw3Ygpm77tyooHv8NylyrozE,5113
         | 
| 45 45 | 
             
            pycityagent/memory/utils.py,sha256=wLNlNlZ-AY9VB8kbUIy0UQSYh26FOQABbhmKQkit5o8,850
         | 
| 46 46 | 
             
            pycityagent/message/__init__.py,sha256=TCjazxqb5DVwbTu1fF0sNvaH_EPXVuj2XQ0p6W-QCLU,55
         | 
| 47 | 
            -
            pycityagent/message/messager.py,sha256= | 
| 47 | 
            +
            pycityagent/message/messager.py,sha256=W_OVlNGcreHSBf6v-DrEnfNCXExB78ySr0w26MSncfU,2541
         | 
| 48 48 | 
             
            pycityagent/simulation/__init__.py,sha256=jYaqaNpzM5M_e_ykISS_M-mIyYdzJXJWhgpfBpA6l5k,111
         | 
| 49 | 
            -
            pycityagent/simulation/agentgroup.py,sha256= | 
| 50 | 
            -
            pycityagent/simulation/ | 
| 51 | 
            -
            pycityagent/ | 
| 52 | 
            -
            pycityagent/ | 
| 53 | 
            -
            pycityagent/ | 
| 54 | 
            -
            pycityagent/ | 
| 55 | 
            -
            pycityagent/ | 
| 56 | 
            -
            pycityagent/simulation/ui/interface.py,sha256=cSQRQhuA5Gj-X7anuN9tmRpG6-I8QYOuswjIWUeSvio,21636
         | 
| 57 | 
            -
            pycityagent/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 49 | 
            +
            pycityagent/simulation/agentgroup.py,sha256=JwfssUtVrOgSnJCan4jcIcSHLjWBCwYxqOPT-AXA2sE,12514
         | 
| 50 | 
            +
            pycityagent/simulation/simulation.py,sha256=G68P1EJ3JceA3zID2O6AGd_KdhhYy5XVZVUgkfJHypc,18897
         | 
| 51 | 
            +
            pycityagent/survey/__init__.py,sha256=rxwou8U9KeFSP7rMzXtmtp2fVFZxK4Trzi-psx9LPIs,153
         | 
| 52 | 
            +
            pycityagent/survey/manager.py,sha256=N4-Q8vve4L-PLaFikAgVB4Z8BNyFDEd6WjEk3AyMTgs,1764
         | 
| 53 | 
            +
            pycityagent/survey/models.py,sha256=Z2gHEazQRj0TkTz5qbh4Uy_JrU_FZGWpOLwjN0RoUrY,3547
         | 
| 54 | 
            +
            pycityagent/utils/__init__.py,sha256=xXEMhVfFeOJUXjczaHv9DJqYNp57rc6FibtS7CfrVbA,305
         | 
| 55 | 
            +
            pycityagent/utils/avro_schema.py,sha256=DHM3bOo8m0dJf8oSwyOWzVeXrH6OERmzA_a5vS4So4M,4255
         | 
| 58 56 | 
             
            pycityagent/utils/decorators.py,sha256=Gk3r41hfk6awui40tbwpq3C7wC7jHaRmLRlcJFlLQCE,3160
         | 
| 59 57 | 
             
            pycityagent/utils/parsers/__init__.py,sha256=AN2xgiPxszWK4rpX7zrqRsqNwfGF3WnCA5-PFTvbaKk,281
         | 
| 60 58 | 
             
            pycityagent/utils/parsers/code_block_parser.py,sha256=Cs2Z_hm9VfNCpPPll1TwteaJF-HAQPs-3RApsOekFm4,1173
         | 
| 61 59 | 
             
            pycityagent/utils/parsers/json_parser.py,sha256=FZ3XN1g8z4Dr2TFraUOoah1oQcze4fPd2m01hHoX0Mo,2917
         | 
| 62 60 | 
             
            pycityagent/utils/parsers/parser_base.py,sha256=k6DVqwAMK3jJdOP4IeLE-aFPm3V2F-St5qRBuRdx4aU,1742
         | 
| 61 | 
            +
            pycityagent/utils/survey_util.py,sha256=Be9nptmu2JtesFNemPgORh_2GsN7rcDYGQS9Zfvc5OI,2169
         | 
| 63 62 | 
             
            pycityagent/workflow/__init__.py,sha256=EyCcjB6LyBim-5iAOPe4m2qfvghEPqu1ZdGfy4KPeZ8,551
         | 
| 64 63 | 
             
            pycityagent/workflow/block.py,sha256=6EmiRMLdOZC1wMlmLMIjfrp9TuiI7Gw4s3nnXVMbrnw,6031
         | 
| 65 64 | 
             
            pycityagent/workflow/prompt.py,sha256=tY69nDO8fgYfF_dOA-iceR8pAhkYmCqoox8uRPqEuGY,2956
         | 
| 66 | 
            -
            pycityagent/workflow/tool.py,sha256= | 
| 65 | 
            +
            pycityagent/workflow/tool.py,sha256=zMvz3BV4QBs5TqyQ3ziJxj4pCfL2uqUI3A1FbT1gd3Q,6626
         | 
| 67 66 | 
             
            pycityagent/workflow/trigger.py,sha256=t5X_i0WtL32bipZSsq_E3UUyYYudYLxQUpvxbgClp2s,5683
         | 
| 68 | 
            -
            pycityagent-2.0. | 
| 69 | 
            -
            pycityagent-2.0. | 
| 70 | 
            -
            pycityagent-2.0. | 
| 67 | 
            +
            pycityagent-2.0.0a15.dist-info/METADATA,sha256=8ONKHaTIPOGja6FKJDE9tZ-pLZL2aeXQufB-LWkElR8,7705
         | 
| 68 | 
            +
            pycityagent-2.0.0a15.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
         | 
| 69 | 
            +
            pycityagent-2.0.0a15.dist-info/RECORD,,
         | 
| @@ -1,40 +0,0 @@ | |
| 1 | 
            -
            from dataclasses import dataclass
         | 
| 2 | 
            -
            from datetime import datetime
         | 
| 3 | 
            -
            from typing import List, Optional
         | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
            @dataclass
         | 
| 7 | 
            -
            class InterviewRecord:
         | 
| 8 | 
            -
                """采访记录"""
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                timestamp: datetime
         | 
| 11 | 
            -
                agent_name: str
         | 
| 12 | 
            -
                question: str
         | 
| 13 | 
            -
                response: str
         | 
| 14 | 
            -
                blocking: bool
         | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
            class InterviewManager:
         | 
| 18 | 
            -
                """采访管理器"""
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                def __init__(self):
         | 
| 21 | 
            -
                    self._history: List[InterviewRecord] = []
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                def add_record(self, agent_name: str, question: str, response: str, blocking: bool):
         | 
| 24 | 
            -
                    """添加采访记录"""
         | 
| 25 | 
            -
                    record = InterviewRecord(
         | 
| 26 | 
            -
                        timestamp=datetime.now(),
         | 
| 27 | 
            -
                        agent_name=agent_name,
         | 
| 28 | 
            -
                        question=question,
         | 
| 29 | 
            -
                        response=response,
         | 
| 30 | 
            -
                        blocking=blocking,
         | 
| 31 | 
            -
                    )
         | 
| 32 | 
            -
                    self._history.append(record)
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                def get_agent_history(self, agent_name: str) -> List[InterviewRecord]:
         | 
| 35 | 
            -
                    """获取指定智能体的采访历史"""
         | 
| 36 | 
            -
                    return [r for r in self._history if r.agent_name == agent_name]
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                def get_recent_history(self, limit: int = 10) -> List[InterviewRecord]:
         | 
| 39 | 
            -
                    """获取最近的采访记录"""
         | 
| 40 | 
            -
                    return sorted(self._history, key=lambda x: x.timestamp, reverse=True)[:limit]
         | 
| @@ -1,68 +0,0 @@ | |
| 1 | 
            -
            from typing import List, Dict, Optional
         | 
| 2 | 
            -
            from datetime import datetime
         | 
| 3 | 
            -
            import uuid
         | 
| 4 | 
            -
            import json
         | 
| 5 | 
            -
            from .models import Survey, Question, QuestionType
         | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
            class SurveyManager:
         | 
| 9 | 
            -
                def __init__(self):
         | 
| 10 | 
            -
                    self._surveys: Dict[str, Survey] = {}
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                def create_survey(
         | 
| 13 | 
            -
                    self, title: str, description: str, questions: List[dict]
         | 
| 14 | 
            -
                ) -> Survey:
         | 
| 15 | 
            -
                    """创建新问卷"""
         | 
| 16 | 
            -
                    survey_id = str(uuid.uuid4())
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                    # 转换问题数据
         | 
| 19 | 
            -
                    survey_questions = []
         | 
| 20 | 
            -
                    for q in questions:
         | 
| 21 | 
            -
                        question = Question(
         | 
| 22 | 
            -
                            content=q["content"],
         | 
| 23 | 
            -
                            type=QuestionType(q["type"]),
         | 
| 24 | 
            -
                            required=q.get("required", True),
         | 
| 25 | 
            -
                            options=q.get("options", []),
         | 
| 26 | 
            -
                            min_rating=q.get("min_rating", 1),
         | 
| 27 | 
            -
                            max_rating=q.get("max_rating", 5),
         | 
| 28 | 
            -
                        )
         | 
| 29 | 
            -
                        survey_questions.append(question)
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                    survey = Survey(
         | 
| 32 | 
            -
                        id=survey_id,
         | 
| 33 | 
            -
                        title=title,
         | 
| 34 | 
            -
                        description=description,
         | 
| 35 | 
            -
                        questions=survey_questions,
         | 
| 36 | 
            -
                    )
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                    self._surveys[survey_id] = survey
         | 
| 39 | 
            -
                    return survey
         | 
| 40 | 
            -
             | 
| 41 | 
            -
                def get_survey(self, survey_id: str) -> Optional[Survey]:
         | 
| 42 | 
            -
                    """获取指定问卷"""
         | 
| 43 | 
            -
                    return self._surveys.get(survey_id)
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                def get_all_surveys(self) -> List[Survey]:
         | 
| 46 | 
            -
                    """获取所有问卷"""
         | 
| 47 | 
            -
                    return list(self._surveys.values())
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                def add_response(self, survey_id: str, agent_name: str, response: dict) -> bool:
         | 
| 50 | 
            -
                    """添加问卷回答"""
         | 
| 51 | 
            -
                    survey = self.get_survey(survey_id)
         | 
| 52 | 
            -
                    if not survey:
         | 
| 53 | 
            -
                        return False
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                    survey.responses[agent_name] = {"timestamp": datetime.now(), **response}
         | 
| 56 | 
            -
                    return True
         | 
| 57 | 
            -
             | 
| 58 | 
            -
                def export_results(self, survey_id: str) -> str:
         | 
| 59 | 
            -
                    """导出问卷结果"""
         | 
| 60 | 
            -
                    survey = self.get_survey(survey_id)
         | 
| 61 | 
            -
                    if not survey:
         | 
| 62 | 
            -
                        return json.dumps({"error": "问卷不存在"})
         | 
| 63 | 
            -
             | 
| 64 | 
            -
                    return json.dumps(
         | 
| 65 | 
            -
                        {"survey": survey.to_dict(), "responses": survey.responses},
         | 
| 66 | 
            -
                        ensure_ascii=False,
         | 
| 67 | 
            -
                        indent=2,
         | 
| 68 | 
            -
                    )
         | 
| @@ -1,52 +0,0 @@ | |
| 1 | 
            -
            from dataclasses import dataclass, field
         | 
| 2 | 
            -
            from typing import List, Dict, Optional
         | 
| 3 | 
            -
            from datetime import datetime
         | 
| 4 | 
            -
            from enum import Enum
         | 
| 5 | 
            -
            import uuid
         | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
            class QuestionType(Enum):
         | 
| 9 | 
            -
                TEXT = "文本"
         | 
| 10 | 
            -
                SINGLE_CHOICE = "单选"
         | 
| 11 | 
            -
                MULTIPLE_CHOICE = "多选"
         | 
| 12 | 
            -
                RATING = "评分"
         | 
| 13 | 
            -
                LIKERT = "李克特量表"
         | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
            @dataclass
         | 
| 17 | 
            -
            class Question:
         | 
| 18 | 
            -
                content: str
         | 
| 19 | 
            -
                type: QuestionType
         | 
| 20 | 
            -
                required: bool = True
         | 
| 21 | 
            -
                options: List[str] = field(default_factory=list)
         | 
| 22 | 
            -
                min_rating: int = 1
         | 
| 23 | 
            -
                max_rating: int = 5
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                def to_dict(self) -> dict:
         | 
| 26 | 
            -
                    return {
         | 
| 27 | 
            -
                        "content": self.content,
         | 
| 28 | 
            -
                        "type": self.type.value,
         | 
| 29 | 
            -
                        "required": self.required,
         | 
| 30 | 
            -
                        "options": self.options,
         | 
| 31 | 
            -
                        "min_rating": self.min_rating,
         | 
| 32 | 
            -
                        "max_rating": self.max_rating,
         | 
| 33 | 
            -
                    }
         | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
            @dataclass
         | 
| 37 | 
            -
            class Survey:
         | 
| 38 | 
            -
                id: str
         | 
| 39 | 
            -
                title: str
         | 
| 40 | 
            -
                description: str
         | 
| 41 | 
            -
                questions: List[Question]
         | 
| 42 | 
            -
                responses: Dict[str, dict] = field(default_factory=dict)
         | 
| 43 | 
            -
                created_at: datetime = field(default_factory=datetime.now)
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                def to_dict(self) -> dict:
         | 
| 46 | 
            -
                    return {
         | 
| 47 | 
            -
                        "id": self.id,
         | 
| 48 | 
            -
                        "title": self.title,
         | 
| 49 | 
            -
                        "description": self.description,
         | 
| 50 | 
            -
                        "questions": [q.to_dict() for q in self.questions],
         | 
| 51 | 
            -
                        "response_count": len(self.responses),
         | 
| 52 | 
            -
                    }
         |