Manage the state of a VerseMatch session.
If VerseMatch is the heart of the program, then state is the brain. All user interactions are processed by the State class listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | #! /usr/bin/env python
"""Manage the state of a VerseMatch session.
If VerseMatch is the heart of the program, then state is the brain.
All user interactions are processed by the State class listed below."""
################################################################################
__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>'
__date__ = '11 February 2010'
__version__ = '$Revision: 3 $'
################################################################################
def enum(names):
"""Generate an enumeration with the given names.
The names should be separated with commas and/or whitespace.
A class is dynamically generated, and an instance is returned.
Python does not support enumerations, so this is an alternative."""
names = names.replace(',', ' ').split()
space = dict((reversed(pair) for pair in enumerate(names)), __slots__=())
return type('enum', (object,), space)()
################################################################################
class State:
"""Oversee state transitions in a VerseMatch session.
This class controls clients' movements from state to state while
navigating through their sessions. Commands are verified along with
any arguments that they take. The VerseMatch servlet automatically
creates State objects and adds them as attributes to Session objects."""
# These are different states instances may be in.
OPTIONS = enum('GET_QUIZ, GET_VERSE, TEACH, CHECK')
def __init__(self, session, library, bib_svr):
"""Initialize resources to be used by this instance."""
self.__session = session
self.__library = library
self.__bib_svr = bib_svr
self.__state = self.OPTIONS.GET_QUIZ
# These will be set again later on.
self.__quiz_id = ''
self.__verses = []
def load_quiz(self, quiz_id):
"""Transition from getting quiz to getting verse.
The first screen of the verse quiz allows the client to select what
category of verses he would liked to be quizzed from. This method
verifies that selection and moves on to the next phase if possible."""
if self.__state == self.OPTIONS.GET_QUIZ:
if quiz_id in self.__library:
self.__state = self.OPTIONS.GET_VERSE
self.__quiz_id = quiz_id
def pick_verse(self, verse_id):
"""Move from picking the verse to teaching the verse.
The second page that pops up in this application provides a menu
to choose what verse(s) should be used for a quiz. The selected
reference is verified; and if the verse could be found, a state
change occurs. Othewise, the reference is removed from the list."""
if self.__state == self.OPTIONS.GET_VERSE:
file = self.__library[self.__quiz_id]
if verse_id in file:
bk, ch, v1, v2 = file[verse_id]
if bk is None:
verses = None
elif v1 is None:
verses = self.__bib_svr.fetch_chapter(bk, ch)
elif v1 == v2:
verses = self.__bib_svr.fetch_verse(bk, ch, v1)
else:
verses = self.__bib_svr.fetch_range(bk, ch, v1, v2)
if verses is None:
del file[verse_id]
else:
self.__verses = tuple(verses)
for verse in self.__verses:
verse.show_hint = False
self.__state = self.OPTIONS.TEACH
def check_text(self, verses):
"""Begin checking the verses that were submitted.
The text from the verse entry boxes is sent here for immediate
grading. A verification engine is automatically started for
each verse, and status messages are shown for boxes with content."""
if self.__state == self.OPTIONS.TEACH:
if self.__check_arg(verses):
for text, verse in zip(verses, self.__verses):
verse.check(text, 15, self.__session.ip)
verse.show_hint = bool(text)
self.__state = self.OPTIONS.CHECK
def __check_arg(self, verses):
"""Verify that the argument given to check_text is valid."""
if len(verses) != len(self.__verses):
return False
for verse in verses:
if not isinstance(verse, str):
return False
return True
def check_status(self):
"""Sum up each status from the different verse objects.
Find out how many verses are being checked and how many
have finished their checking procedure. If some are still
being checked, the total that have finished is returned.
Otherwise, the program goes back into its teaching mode."""
if self.__state == self.OPTIONS.CHECK:
checking = complete = 0
for verse in self.__verses:
status = verse.ready
if status is False:
checking += 1
elif status is True:
complete += 1
if checking == 0:
self.__state = self.OPTIONS.TEACH
return complete
def go_back(self):
"""Go back to a previous state if possible.
It is impossible to go forward through the states without
meeting certain requirements. Going backward is relatively
easy with this method. Negative states are not allowed,
and the verse-checking process may not be interrupted."""
if self.__state != self.OPTIONS.CHECK:
self.__state = max(0, self.__state - 1)
def reset_session(self):
"""Destroy this state and start over from scratch.
In actuality, the session that keeps this state instance is
removed from the session manager (if it exists there). Note
that a new state is generated before a response is sent out."""
with self.__session.manager:
if self.__session.ip in self.__session.manager:
del self.__session.manager[self.__session.ip]
################################################################################
@property
def current(self):
"""Read-only current-state property for VerseMatch class."""
return self.__state
@property
def verse_file(self):
"""Read-only verse-file property for VerseMatch class."""
return self.__library[self.__quiz_id]
@property
def verse_total(self):
"""Read-only verse-total property for VerseMatch class."""
return len(self.__verses)
@property
def verse_list(self):
"""Read-only verse-list property for VerseMatch class."""
return tuple(self.__verses)
|