source: trunk/game/scripts/dialoguevalidator.py @ 369

Revision 369, 7.4 KB checked in by eliedebrauwer, 10 years ago (diff)

Ticket #147. Patch by eliedebrauwer: Added dialogue-validator code and unittests, already capable of structure checking. Unittest fail atm. Still pending for ticket 147 is a standalone validator. comment[s:trac, t:147]

Line 
1#!/usr/bin/python
2
3#   This file is part of PARPG.
4
5#   PARPG is free software: you can redistribute it and/or modify
6#   it under the terms of the GNU General Public License as published by
7#   the Free Software Foundation, either version 3 of the License, or
8#   (at your option) any later version.
9
10#   PARPG is distributed in the hope that it will be useful,
11#   but WITHOUT ANY WARRANTY; without even the implied warranty of
12#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13#   GNU General Public License for more details.
14
15#   You should have received a copy of the GNU General Public License
16#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>.
17
18import yaml
19import types
20import os
21
22class DialogueFormatException(Exception):
23    """ Exception thrown when the DialogueValidator has encountered an error. """
24    pass
25
26class DialogueValidator(object):
27
28    def validateDialogue(self, file_name, topdir="."):
29        """ Checks whether a yaml file is containing a valid dialogue
30        @type file_name: string
31        @param file_name: File name of the yaml file to validate
32        @type topdir: string
33        @param topdir: Top directory where to start searching for files.
34        @raises: DialogueFormatException on error
35        @return: True on success"""
36
37        self.tree = yaml.load(file(file_name))
38        self.topdir = topdir
39       
40        # Test if the required top nodes are present
41        for node in ("NPC", "AVATAR", "START"):
42            if not node in self.tree or not isinstance(self.tree[node],\
43                                                       types.StringType):
44                raise DialogueFormatException("Node: " + node + \
45                                              " not found or invalid type")
46       
47       
48        self.__validateAvatar()
49
50        # Test if the sections node is present
51        if not "SECTIONS" in self.tree or not isinstance(self.tree["SECTIONS"],\
52                                                         types.DictionaryType):
53            raise DialogueFormatException("Node: SECTIONS not found or invalid type")
54
55        # Start node should be valid
56        if not self.tree["START"] in self.tree["SECTIONS"]:
57            raise DialogueFormatException("Main section " + self.tree["START"] \
58                                          + " could not be found")
59
60        # Validate all sections
61        for section in self.tree["SECTIONS"]:
62            self.__validateSection(self.tree["SECTIONS"][section], section)
63           
64        return True 
65
66    def __validateAvatar(self):
67        """ Check that the avatar is an existing file.
68        @raises: DialogueFormatException on error. """
69       
70        fname = os.path.join(self.topdir,self.tree["AVATAR"])
71        if not os.path.isfile(fname):
72            raise DialogueFormatException("File containing avatar could not " +\
73                                          "be found")
74
75       
76    def __validateSection(self, section, section_name):
77        """ Checks whether a section is a valid section.
78        @type section: dictionary
79        @type section_name: string
80        @param section: Section to validate
81        @param section_name: Name of the section to validate
82        @raises: DialogueFormatException on error """
83
84        for entry in section:
85            for action in entry:
86                # Verify if the commands are known and if their parameters have
87                # the correct type.
88                if action == "say":
89                    if not isinstance(entry[action], types.StringType):
90                        raise DialogueFormatException(
91                            "Section: " + section_name + " has an invalid " +\
92                            action + " node")
93                elif action == "responses":
94                    if not isinstance(entry[action], types.ListType):
95                        raise DialogueFormatException(                       
96                            "Section: " + section_name + " has an invalid " +\
97                            action + " node")
98                    self.__validateResponses(entry["responses"])
99                elif action == "meet":
100                    if not isinstance(entry[action], types.StringType):
101                        raise DialogueFormatException(\
102                            "Section: " + section_name + " has an invalid " +\
103                            action + " node")
104                    #TODO: verify person
105                elif action in ("complete_quest", "start_quest", "delete_quest"):
106                    if not isinstance(entry[action], types.StringType):
107                        raise DialogueFormatException(\
108                            "Section: " + section_name + " has an invalid " +\
109                            action + " node")
110                    #TODO: verify quest
111                elif action in ("get_stuff", "take_stuff"):
112                    if not isinstance(entry[action], types.StringType):
113                        raise DialogueFormatException(\
114                            "Section: " + section_name + " has an invalid " +\
115                            action + " node")
116                    #TODO: verify object
117                elif action in ("increase_value", "decrease_value", "set_value"):
118                    if not isinstance(entry[action], types.DictionaryType):
119                        raise DialogueFormatException(\
120                            "Section: " + section_name + " has an invalid " +\
121                            action + " node")                   
122                    #TODO: verify value checks
123                elif action == "dialogue":
124                    if not isinstance(entry[action], types.StringType) \
125                       or not self.__isValidSectionName(entry[action]):
126                        raise DialogueFormatException(\
127                            "Section: " + section_name + " has an invalid " +\
128                            action + " node")
129                else :
130                    raise DialogueFormatException("Section: " + section_name + \
131                                                  " has an unknown action: " +\
132                                                  action)
133           
134
135    def __validateResponses(self, responses):
136        """ Checks if the list of responses is a valid list.
137        @type responses: List
138        @param respones: A list of response
139        @raise DialogueFormatException on error
140        @return True When the lists is a valid list"""
141       
142        for option in responses:
143            if not isinstance(option[0], types.StringType):
144                raise DialogueFormatException("Response should be a string")
145           
146            if not self.__isValidSectionName(option[1]):
147                raise DialogueFormatException("Invalid target section given " +\
148                                              option[1])
149           
150            #TODO: option[2] might be a conditional
151       
152    def __isValidSectionName(self, name):
153        """ Checks if a given name is valid section
154        @type name: string
155        @param name: Name of the section to check
156        @return True when name is a valid section name, False otherwise"""
157       
158        if name=="back" or name=="end":
159            return True
160
161        if name in self.tree["SECTIONS"]:
162            return True;
163
164        # Handle 'back name' used to go back 'n' in the stack.
165        if name.startswith("back "):
166            return self.__isValidSectionName(name.split(" ")[1])
167       
168        return False;
169
Note: See TracBrowser for help on using the repository browser.