📄 userinterface.py
字号:
"""Web User InterfaceClasses: UserInterfaceServer - Implements the web server component via a Dibbler plugin. BaseUserInterface - Just has utilities for creating boxes and so forth. (Does not include any pages) UserInterface - A base class for Spambayes web user interfaces.Abstract:This module implements a browser based Spambayes user interface. Users can*not* use this class (there is no 'home' page), but developments shouldsub-class it to provide an appropriate interface for their application.Functions deemed appropriate for all application interfaces are included.These currently include: onClassify - classify a given message onWordquery - query a word from the database onTrain - train a message or mbox onSave - save the database and possibly shutdown onConfig - present the appropriate configuration page onAdvancedconfig - present the appropriate advanced configuration page onExperimentalconfig - present the experimental options configuration page onHelp - present the help page onStats - present statistics information onBugreport - help the user fill out a bug reportTo Do:Web training interface: o Functional tests. o Keyboard navigation (David Ascher). But aren't Tab and left/right arrow enough?User interface improvements: o Once the pieces are on separate pages, make the paste box bigger. o Deployment: Windows executable? atlaxwin and ctypes? Or just webbrowser? o Save the stats (num classified, etc.) between sessions. o "Reload database" button. o Displaying options should be done with the locale format function rather than str(). o Suggestions?"""# This module is part of the spambayes project, which is Copyright 2002# The Python Software Foundation and is covered by the Python Software# Foundation license.# This module was once part of pop3proxy.py; if you are looking through# the history of the file, you may need to go back there.# The options/configuration section started life in OptionConfig.py.# You can find this file in the cvs attic if you want to trawl through# its history.__author__ = """Richie Hindle <richie@entrian.com>, Tim Stone <tim@fourstonesExpressions.com>"""__credits__ = "Tim Peters, Neale Pickett, Tony Meyer, all the Spambayes folk."try: True, Falseexcept NameError: # Maintain compatibility with Python 2.2 True, False = 1, 0import reimport osimport sysimport timeimport emailimport smtplibimport binasciiimport cgiimport mailboximport typesimport StringIOimport oe_mailboximport PyMeldLiteimport Dibblerimport tokenizerfrom spambayes import Statsfrom spambayes import Versionfrom spambayes import storagefrom spambayes import FileCorpusfrom Options import options, optionsPathname, defaults, OptionsClass, _IMAGES = ('helmet', 'status', 'config', 'help', 'message', 'train', 'classify', 'query')experimental_ini_map = ( ('Experimental Options', None),)# Dynamically add any current experimental options.# (Don't add deprecated options, or, more specifically, any# options whose description starts with (DEPRECATED)).for opt in options.options(True): sect, opt = opt[1:].split(']', 1) if opt[:2].lower() == "x-" and \ not options.doc(sect, opt).lower().startswith(_("(deprecated)")): experimental_ini_map += ((sect, opt),)class UserInterfaceServer(Dibbler.HTTPServer): """Implements the web server component via a Dibbler plugin.""" def __init__(self, uiPort): Dibbler.HTTPServer.__init__(self, uiPort) print _('User interface url is http://localhost:%d/') % (uiPort) def requestAuthenticationMode(self): return options["html_ui", "http_authentication"] def getRealm(self): return _("SpamBayes Web Interface") def isValidUser(self, name, password): return (name == options["html_ui", "http_user_name"] and password == options["html_ui", "http_password"]) def getPasswordForUser(self, name): # There is only one login available in the web interface. return options["html_ui", "http_password"] def getCancelMessage(self): return _("You must login to use SpamBayes.")class BaseUserInterface(Dibbler.HTTPPlugin): def __init__(self, lang_manager=None): Dibbler.HTTPPlugin.__init__(self) self.lang_manager = lang_manager htmlSource, self._images = self.readUIResources() self.html = PyMeldLite.Meld(htmlSource, readonly=True) self.app_for_version = "SpamBayes" def onIncomingConnection(self, clientSocket): """Checks the security settings.""" remoteIP = clientSocket.getpeername()[0] trustedIPs = options["html_ui", "allow_remote_connections"] if trustedIPs == "*" or remoteIP == clientSocket.getsockname()[0]: return True trustedIPs = trustedIPs.replace('.', '\.').replace('*', '([01]?\d\d?|2[04]\d|25[0-5])') for trusted in trustedIPs.split(','): if re.search("^" + trusted + "$", remoteIP): return True return False def _getHTMLClone(self, help_topic=None): """Gets a clone of the HTML, with the footer timestamped, and version information added, ready to be modified and sent to the browser.""" clone = self.html.clone() timestamp = time.strftime('%H:%M on %A %B %d %Y', time.localtime()) clone.footer.timestamp = timestamp v = Version.get_current_version() clone.footer.version = v.get_long_version(self.app_for_version) if help_topic: clone.helplink.href = "help?topic=%s" % (help_topic,) return clone def _writePreamble(self, name, parent=None, showImage=True): """Writes the HTML for the beginning of a page - time-consuming methlets use this and `_writePostamble` to write the page in pieces, including progress messages. `parent` (if given) should be a pair: `(url, label)`, eg. `('review', 'Review')`.""" # Take the whole palette and remove the content and the footer, # leaving the header and an empty body. html = self._getHTMLClone() html.mainContent = " " del html.footer # Add in the name of the page and remove the link to Home if this # *is* Home. html.title = name if name == _('Home'): del html.homelink html.pagename = _("Home") elif parent: html.pagename = "> <a href='%s'>%s</a> > %s" % \ (parent[0], parent[1], name) else: html.pagename = "> " + name # Remove the helmet image if we're not showing it - this happens on # shutdown because the browser might ask for the image after we've # exited. if not showImage: del html.helmet # Strip the closing tags, so we push as far as the start of the main # content. We'll push the closing tags at the end. self.writeOKHeaders('text/html') self.write(re.sub(r'</div>\s*</body>\s*</html>', '', str(html))) def _writePostamble(self, help_topic=None): """Writes the end of time-consuming pages - see `_writePreamble`.""" self.write("</div>" + self._getHTMLClone(help_topic).footer) self.write("</body></html>") def _trimHeader(self, field, limit, quote=False): """Trims a string, adding an ellipsis if necessary and HTML-quoting on request. Also pumps it through email.Header.decode_header, which understands charset sections in email headers - I suspect this will only work for Latin character sets, but hey, it works for Francois Granger's name. 8-)""" try: sections = email.Header.decode_header(field) except (binascii.Error, email.Errors.HeaderParseError): sections = [(field, None)] field = ' '.join([text for text, unused in sections]) if len(field) > limit: field = field[:limit-3] + "..." if quote: field = cgi.escape(field) return field def onHome(self): """Serve up the homepage.""" raise NotImplementedError def _writeImage(self, image): self.writeOKHeaders('image/gif') self.write(self._images[image]) # If you are easily offended, look away now... for imageName in IMAGES: exec "def %s(self): self._writeImage('%s')" % \ ("on%sGif" % imageName.capitalize(), imageName) def _buildBox(self, heading, icon, content): """Builds a yellow-headed HTML box.""" box = self.html.headedBox.clone() box.heading = heading if icon: box.icon.src = icon else: del box.iconCell box.boxContent = content return box def readUIResources(self): """Returns ui.html and a dictionary of Gifs.""" if self.lang_manager: ui_html = self.lang_manager.import_ui_html() else: from spambayes.resources import ui_html images = {} for baseName in IMAGES: moduleName = '%s.%s_gif' % ('spambayes.resources', baseName) module = __import__(moduleName, {}, {}, ('spambayes', 'resources')) images[baseName] = module.data return ui_html.data, imagesclass UserInterface(BaseUserInterface): """Serves the HTML user interface.""" def __init__(self, bayes, config_parms=(), adv_parms=(), lang_manager=None, stats=None): """Load up the necessary resources: ui.html and helmet.gif.""" BaseUserInterface.__init__(self, lang_manager) self.classifier = bayes self.parm_ini_map = config_parms self.advanced_options_map = adv_parms self.stats = stats self.app_for_version = None # subclasses must fill this in def onClassify(self, file, text, which): """Classify an uploaded or pasted message.""" # XXX This doesn't get recorded in the session counts # XXX for messages classified. That seems right to me (Tony), # XXX but is easily changed if it isn't. self._writePreamble(_("Classify")) message = file or text message = message.replace('\r\n', '\n').replace('\r', '\n') # For Macs results = self._buildCluesTable(message) results.classifyAnother = self._buildClassifyBox() self.write(results) self._writePostamble() ev_re = re.compile("%s:(.*?)(?:\n\S|\n\n)" % \ re.escape(options["Headers", "evidence_header_name"]), re.DOTALL) sc_re = re.compile("%s:\s*([\d.]+)" % \ re.escape(options["Headers", "score_header_name"])) def _fillCluesTable(self, clues): accuracy = 6 cluesTable = self.html.cluesTable.clone() cluesRow = cluesTable.cluesRow.clone() del cluesTable.cluesRow # Delete dummy row to make way for real ones fetchword = self.classifier._wordinfoget for word, wordProb in clues: record = fetchword(word) if record: nham = record.hamcount nspam = record.spamcount if wordProb is None: wordProb = self.classifier.probability(record) elif word != "*H*" and word != "*S*": nham = nspam = 0
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -