def router_option_extender(options): from wheezy.routing import PathRouter from wheezy.web.middleware.routing import PathRoutingMiddleware path_router = PathRouter() path_router.add_routes(urls() if callable(urls) else urls) options['path_router'] = path_router options['path_for'] = path_router.path_for return PathRoutingMiddleware(path_router)
class PicroscopyWsgiApp(object): def __init__(self, **kwargs): super().__init__() self.library = PicroscopyLibrary(**kwargs) self.helpers = WebHelpers(self.library) self.clients = kwargs.get('clients', IPv4Network('0.0.0.0/0')) logging.info('Clients must be on network %s', self.clients) self.static_dir = os.path.abspath( os.path.normpath( kwargs.get('static_dir', os.path.join(HERE, 'static')))) logging.info('Static files: %s', self.static_dir) self.templates_dir = os.path.abspath( os.path.normpath( kwargs.get('templates_dir', os.path.join(HERE, 'templates')))) logging.info('Chameleon templates: %s', self.templates_dir) self.templates = PageTemplateLoader(self.templates_dir, default_extension='.pt') self.layout = self.templates['layout'] # No need to make flashes a per-session thing - it's a single user app! self.flashes = [] self.router = PathRouter() # XXX Add handler for exiting system # XXX Make exit code conditional? (upgrade/reboot/shutdown/etc.) self.router.add_routes([ url('/', self.do_template, kwargs={'page': 'library'}, name='home'), url('/{page}.html', self.do_template, name='template'), url('/view/{image}.html', self.do_template, kwargs={'page': 'image'}, name='view'), url('/static/{path:any}', self.do_static, name='static'), url('/images/{image}', self.do_image, name='image'), url('/thumbs/{image}', self.do_thumb, name='thumb'), url('/delete/{image}', self.do_delete, name='delete'), url('/config', self.do_config, name='config'), url('/reset', self.do_reset, name='reset'), url('/capture', self.do_capture, name='capture'), url('/download', self.do_download, name='download'), url('/send', self.do_send, name='send'), url('/logout', self.do_logout, name='logout'), ]) def __call__(self, environ, start_response): req = Request(environ) try: if not IPv4Address(req.remote_addr) in self.clients: raise exc.HTTPForbidden() handler, kwargs = self.router.match(req.path_info) if handler: # XXX Why does route_name only appear in kwargs sometimes?! if 'route_name' in kwargs: del kwargs['route_name'] resp = handler(req, **kwargs) else: self.not_found(req) except exc.HTTPException as e: # The exception itself is a WSGI response resp = e return resp(environ, start_response) def not_found(self, req): """ Handler for unknown locations (404) """ raise exc.HTTPNotFound('The resource at %s could not be found' % req.path_info) def do_reset(self, req): """ Reset all settings to their defaults """ self.library.camera_reset() self.flashes.append('Camera settings reset to defaults') raise exc.HTTPFound( location=self.router.path_for('template', page='library')) def do_config(self, req): """ Configure the library and camera settings """ # Resolution is handled specially as the camera needs to stop the # preview in order to change it try: new_resolution = tuple( int(i) for i in req.params['resolution'].split('x', 1)) if len(new_resolution) != 2: raise ValueError() except ValueError: self.flashes.append('Invalid resolution: %s' % req.params['resolution']) if self.library.camera.resolution != new_resolution: try: self.library.camera.stop_preview() try: self.library.camera.resolution = new_resolution finally: self.library.camera.start_preview() except PiCameraError: self.flashes.append('Unable to change camera resolution ' 'to %s' % req.params['resolution']) # Everything else is handled generically... for setting in ( 'sharpness', 'contrast', 'brightness', 'saturation', #'ISO', 'exposure-compensation'): try: setattr(self.library.camera, setting.replace('-', '_'), int(req.params[setting])) except ValueError: self.flashes.append('Invalid %s: %s' % (setting, req.params[setting])) for setting in ('hflip', 'vflip'): try: setattr(self.library.camera, setting.replace('-', '_'), bool(req.params.get(setting, 0))) except ValueError: self.flashes.append('Invalid %s: %s' % (setting, req.params[setting])) for setting in ('meter-mode', 'awb-mode', 'exposure-mode'): try: setattr(self.library.camera, setting.replace('-', '_'), req.params[setting]) except ValueError: self.flashes.append('Invalid %s: %s' % (setting, req.params[setting])) for setting in ('artist', 'email', 'copyright', 'description', 'filename-template', 'format'): try: setattr(self.library, setting.replace('-', '_'), req.params[setting]) except ValueError: self.flashes.append('Invalid %s: %s' % (setting, req.params[setting])) # If any settings failed, re-render the settings form if self.flashes: return self.do_template(req, 'settings') raise exc.HTTPFound( location=self.router.path_for('template', page='library')) def do_capture(self, req): """ Take a new image with the camera and add it to the library """ self.library.capture() raise exc.HTTPFound( location=self.router.path_for('template', page='library')) def do_download(self, req): """ Send the library as a .zip archive """ archive = self.library.archive() size = archive.seek(0, io.SEEK_END) archive.seek(0) resp = Response() resp.content_type = 'application/zip' resp.content_length = size resp.content_disposition = 'attachment; filename=images.zip' resp.app_iter = FileWrapper(archive) return resp def do_send(self, req): """ Send the library as a set of attachments to an email """ self.library.send() self.flashes.append('Email sent to %s' % library.email) raise exc.HTTPFound( location=self.router.path_for('template', page='library')) def do_delete(self, req, image): """ Delete the selected images from library """ self.library.remove(image) raise exc.HTTPFound( location=self.router.path_for('template', page='library')) def do_logout(self, req): """ Clear the library of all images, reset all settings """ self.library.clear() self.library.user_reset() self.library.camera_reset() raise exc.HTTPFound( location=self.router.path_for('template', page='settings')) def do_image(self, req, image): """ Serve an image from the library """ if not image in self.library: self.not_found(req) resp = Response() resp.content_type, resp.content_encoding = mimetypes.guess_type( image, strict=False) resp.content_length = self.library.stat_image(image).st_size resp.app_iter = FileWrapper(self.library.open_image(image)) return resp def do_thumb(self, req, image): """ Serve a thumbnail of an image from the library """ if not image in self.library: self.not_found(req) resp = Response() resp.content_type = 'image/jpeg' resp.content_length = self.library.stat_thumbnail(image).st_size resp.app_iter = FileWrapper(self.library.open_thumbnail(image)) return resp def do_static(self, req, path): """ Serve static files from disk """ path = os.path.normpath(os.path.join(self.static_dir, path)) if not path.startswith(self.static_dir): self.not_found(req) resp = Response() resp.content_type, resp.content_encoding = mimetypes.guess_type( path, strict=False) if resp.content_type is None: resp.content_type = 'application/octet-stream' resp.content_length = os.stat(path).st_size resp.app_iter = FileWrapper(io.open(path, 'rb')) return resp def do_template(self, req, page, image=None): """ Serve a Chameleon template-based page """ resp = Response() resp.content_type = 'text/html' resp.content_encoding = 'utf-8' try: template = self.templates[page] except ValueError: self.not_found(req) resp.text = template(req=req, page=page, image=image, helpers=self.helpers, layout=self.layout, flashes=self.flashes, library=self.library, camera=self.library.camera, router=self.router) del self.flashes[:] return resp
class PololuRobotWebControlApp(object): def __init__(self, **kwargs): self.form = WebControlFormHelper(**kwargs) self.logger = kwargs.get('logger',) self.robot = kwargs.get('robot') #initialize speed in form with robot speed self.form.speed = self.robot.setDriveSpeed #create routes to web pages self.router = PathRouter() self.router.add_routes([ url('/', self.do_main_page, name='home'), url('/doRobotControl', self.do_process_form, name='execute'), url('/showRobotControlForm', self.do_display_form, name='view') ]) def __call__(self, environ, start_response): req = Request(environ) try: handler, kwargs = self.router.match(req.path_info) if handler: # XXX Why does route_name only appear in kwargs sometimes?! if 'route_name' in kwargs: del kwargs['route_name'] resp = handler(req, **kwargs) else: self.not_found(req) except exc.HTTPException as e: # The exception itself is a WSGI response resp = e return resp(environ, start_response) def not_found(self, req): """Handler for unknown locations (404)""" raise exc.HTTPNotFound('The resource at %s could not be found' % req.path_info) #------------------------------------------------------------------------------# # do_main_page: load the mainpage. The main page contains an iFrame which # # triggers the consecutive loading of the control form # # The main page contains a link to mjpgStreamServer, which shows # # a video stream from the attached raspicam. The url for this # # server is configured in etc/config.ini # #------------------------------------------------------------------------------# # version who when description # # 1.00 hta 15.05.2014 Initial version # #------------------------------------------------------------------------------# def do_main_page(self, req): res = Response() res.content_type = 'text/html' res.text = self.form.mainPage % {'mjpgStreamServer':self.form.mjpgStreamServer} return res #------------------------------------------------------------------------------# # do_display_form: the control form, and set the color of the button text and # # set the message to be shown to the user (if any) # # parameters: message: Text message to be shown to user # # speed : Speed, value for the slider bar # # toggleRovingButtonText: Roving OFF, Roving ON # # backwardButtonColor, forwardButtonColor, leftButtonColor, # # rightButtonColor, stopButtonColor, toggleRovingButtonColor: # # Color for the text on the control buttons. The purpose # # is to highlight the currently active action suchs as # # forward, left etc. # #------------------------------------------------------------------------------# # version who when description # # 1.00 hta 15.05.2014 Initial version # #------------------------------------------------------------------------------# def do_display_form(self, req): res = Response() res.content_type = 'text/html' res.text = self.form.controlForm % {'message':self.form.message, 'speed': self.form.speed, 'backwardButtonColor': self.form.backwardButtonColor, 'forwardButtonColor': self.form.forwardButtonColor, 'leftButtonColor': self.form.leftButtonColor, 'rightButtonColor': self.form.rightButtonColor, 'stopButtonColor': self.form.stopButtonColor, 'toggleRovingButtonColor': self.form.toggleRovingButtonColor, 'toggleRovingButtonText': self.form.toggleRovingButtonText} return res #------------------------------------------------------------------------------# # process_form: Processes the user input from the control form, executes the # # desired action (e.g. forward, backward) and give feedback to # # the user indicating what action has been taken # # is highlighted # # parameters: params: dictionary object containing parameters and their values # # from the form. # # action: possbile values: forward, backward, left, right, # # stop, setSpeed # # speed: possible values: 0-127 #------------------------------------------------------------------------------# # version who when description # # 1.00 hta 15.05.2014 Initial version # #------------------------------------------------------------------------------# def do_process_form(self, req): action = req.params['action'] speedSliderValue = req.params['speed'] self.logger.debug('action['+action+'] speedSliderValue['+speedSliderValue+']') if action == 'forward': self.form.message='Going '+action self.form.setButtonColors(backward=False, forward=True, left=False, right=False, stop=False, roving=False) self.robot.stopRoving() self.robot.driveForwards() elif action == 'backward': self.form.message='Going '+action self.form.setButtonColors(backward=True, forward=False, left=False, right=False, stop=False, roving=False) self.robot.stopRoving() self.robot.driveBackwards() elif action == 'left': self.form.message='Turning '+action self.form.setButtonColors(backward=False, forward=False, left=True, right=False, stop=False, roving=False) self.robot.stopRoving() self.robot.turnLeft() elif action == 'right': self.form.message='Turning '+action self.form.setButtonColors(backward=False, forward=False, left=False, right=True, stop=False, roving=False) self.robot.stopRoving() self.robot.turnRight() elif action == 'setSpeed': self.form.message='Setting speed to '+speedSliderValue self.form.speed = int(speedSliderValue) self.robot.setSpeed(self.form.speed) elif action == 'stop': self.form.message='Stopping' self.form.setButtonColors(backward=False, forward=False, left=False, right=False, stop=True, roving=False) self.robot.stop() if self.robot.isRoving: self.robot.stopRoving() elif action == 'roving': #Toggle roving, of roving on then turn it of and vice versa if self.robot.isRoving: self.form.message='End roving' self.form.setButtonColors(backward=False, forward=False, left=False, right=False, stop=False, roving=False) self.robot.stopRoving() else: self.form.message='Start roving' self.form.setButtonColors(backward=False, forward=False, left=False, right=False, stop=False, roving=True) self.robot.runRoving() #return updated control form to web client return self.do_display_form (req)
class PicroscopyWsgiApp(object): def __init__(self, **kwargs): super().__init__() self.library = PicroscopyLibrary(**kwargs) self.helpers = WebHelpers(self.library) self.clients = kwargs.get('clients', IPv4Network('0.0.0.0/0')) logging.info('Clients must be on network %s', self.clients) self.static_dir = os.path.abspath(os.path.normpath(kwargs.get( 'static_dir', os.path.join(HERE, 'static') ))) logging.info('Static files: %s', self.static_dir) self.templates_dir = os.path.abspath(os.path.normpath(kwargs.get( 'templates_dir', os.path.join(HERE, 'templates') ))) logging.info('Chameleon templates: %s', self.templates_dir) self.templates = PageTemplateLoader( self.templates_dir, default_extension='.pt') self.layout = self.templates['layout'] # No need to make flashes a per-session thing - it's a single user app! self.flashes = [] self.router = PathRouter() # XXX Add handler for exiting system # XXX Make exit code conditional? (upgrade/reboot/shutdown/etc.) self.router.add_routes([ url('/', self.do_template, kwargs={'page': 'library'}, name='home'), url('/{page}.html', self.do_template, name='template'), url('/view/{image}.html', self.do_template, kwargs={'page': 'image'}, name='view'), url('/static/{path:any}', self.do_static, name='static'), url('/images/{image}', self.do_image, name='image'), url('/thumbs/{image}', self.do_thumb, name='thumb'), url('/delete/{image}', self.do_delete, name='delete'), url('/config', self.do_config, name='config'), url('/reset', self.do_reset, name='reset'), url('/capture', self.do_capture, name='capture'), url('/download', self.do_download, name='download'), url('/send', self.do_send, name='send'), url('/logout', self.do_logout, name='logout'), ]) def __call__(self, environ, start_response): req = Request(environ) try: if not IPv4Address(req.remote_addr) in self.clients: raise exc.HTTPForbidden() handler, kwargs = self.router.match(req.path_info) if handler: # XXX Why does route_name only appear in kwargs sometimes?! if 'route_name' in kwargs: del kwargs['route_name'] resp = handler(req, **kwargs) else: self.not_found(req) except exc.HTTPException as e: # The exception itself is a WSGI response resp = e return resp(environ, start_response) def not_found(self, req): """ Handler for unknown locations (404) """ raise exc.HTTPNotFound( 'The resource at %s could not be found' % req.path_info) def do_reset(self, req): """ Reset all settings to their defaults """ self.library.camera_reset() self.flashes.append('Camera settings reset to defaults') raise exc.HTTPFound( location=self.router.path_for('template', page='library')) def do_config(self, req): """ Configure the library and camera settings """ # Resolution is handled specially as the camera needs to stop the # preview in order to change it try: new_resolution = tuple( int(i) for i in req.params['resolution'].split('x', 1)) if len(new_resolution) != 2: raise ValueError() except ValueError: self.flashes.append( 'Invalid resolution: %s' % req.params['resolution']) if self.library.camera.resolution != new_resolution: try: self.library.camera.stop_preview() try: self.library.camera.resolution = new_resolution finally: self.library.camera.start_preview() except PiCameraError: self.flashes.append( 'Unable to change camera resolution ' 'to %s' % req.params['resolution']) # Everything else is handled generically... for setting in ( 'sharpness', 'contrast', 'brightness', 'saturation', #'ISO', 'exposure-compensation'): try: setattr( self.library.camera, setting.replace('-', '_'), int(req.params[setting]) ) except ValueError: self.flashes.append( 'Invalid %s: %s' % (setting, req.params[setting])) for setting in ('hflip', 'vflip'): try: setattr( self.library.camera, setting.replace('-', '_'), bool(req.params.get(setting, 0)) ) except ValueError: self.flashes.append( 'Invalid %s: %s' % (setting, req.params[setting])) for setting in ('meter-mode', 'awb-mode', 'exposure-mode'): try: setattr( self.library.camera, setting.replace('-', '_'), req.params[setting] ) except ValueError: self.flashes.append( 'Invalid %s: %s' % (setting, req.params[setting])) for setting in ( 'artist', 'email', 'copyright', 'description', 'filename-template', 'format'): try: setattr( self.library, setting.replace('-', '_'), req.params[setting] ) except ValueError: self.flashes.append( 'Invalid %s: %s' % (setting, req.params[setting])) # If any settings failed, re-render the settings form if self.flashes: return self.do_template(req, 'settings') raise exc.HTTPFound( location=self.router.path_for('template', page='library')) def do_capture(self, req): """ Take a new image with the camera and add it to the library """ self.library.capture() raise exc.HTTPFound( location=self.router.path_for('template', page='library')) def do_download(self, req): """ Send the library as a .zip archive """ archive = self.library.archive() size = archive.seek(0, io.SEEK_END) archive.seek(0) resp = Response() resp.content_type = 'application/zip' resp.content_length = size resp.content_disposition = 'attachment; filename=images.zip' resp.app_iter = FileWrapper(archive) return resp def do_send(self, req): """ Send the library as a set of attachments to an email """ self.library.send() self.flashes.append('Email sent to %s' % library.email) raise exc.HTTPFound( location=self.router.path_for('template', page='library')) def do_delete(self, req, image): """ Delete the selected images from library """ self.library.remove(image) raise exc.HTTPFound( location=self.router.path_for('template', page='library')) def do_logout(self, req): """ Clear the library of all images, reset all settings """ self.library.clear() self.library.user_reset() self.library.camera_reset() raise exc.HTTPFound( location=self.router.path_for('template', page='settings')) def do_image(self, req, image): """ Serve an image from the library """ if not image in self.library: self.not_found(req) resp = Response() resp.content_type, resp.content_encoding = mimetypes.guess_type( image, strict=False) resp.content_length = self.library.stat_image(image).st_size resp.app_iter = FileWrapper(self.library.open_image(image)) return resp def do_thumb(self, req, image): """ Serve a thumbnail of an image from the library """ if not image in self.library: self.not_found(req) resp = Response() resp.content_type = 'image/jpeg' resp.content_length = self.library.stat_thumbnail(image).st_size resp.app_iter = FileWrapper(self.library.open_thumbnail(image)) return resp def do_static(self, req, path): """ Serve static files from disk """ path = os.path.normpath(os.path.join(self.static_dir, path)) if not path.startswith(self.static_dir): self.not_found(req) resp = Response() resp.content_type, resp.content_encoding = mimetypes.guess_type( path, strict=False) if resp.content_type is None: resp.content_type = 'application/octet-stream' resp.content_length = os.stat(path).st_size resp.app_iter = FileWrapper(io.open(path, 'rb')) return resp def do_template(self, req, page, image=None): """ Serve a Chameleon template-based page """ resp = Response() resp.content_type = 'text/html' resp.content_encoding = 'utf-8' try: template = self.templates[page] except ValueError: self.not_found(req) resp.text = template( req=req, page=page, image=image, helpers=self.helpers, layout=self.layout, flashes=self.flashes, library=self.library, camera=self.library.camera, router=self.router) del self.flashes[:] return resp
class MancifyWsgiApp(object): def __init__(self, **kwargs): super(MancifyWsgiApp, self).__init__() self.sms = MancifySMSService(kwargs['clockwork_api_key']) self.exec_timeout = kwargs.get('exec_timeout', 10) self.connect_timeout = kwargs.get('connect_timeout', 30) self.session_timeout = kwargs.get('session_timeout', 300) self.output_limit = kwargs.get('output_limit', 1024) self.router = PathRouter() self.router.add_routes([ url('/', self.do_index), url('/ssh', self.do_ssh), url('/translate', self.do_translate), ]) self.lock = threading.Lock() self.sessions = {} self.messages = set() self.terminate = threading.Event() self.reap_thread = threading.Thread(target=self.reap_sessions) self.reap_thread.daemon = True self.reap_thread.start() def close(self): self.terminate.set() self.reap_thread.join(5) def reap_sessions(self): while True: reap_list = [] now = time.time() with self.lock: for recipient, session in self.sessions.iteritems(): if not session.timestamp: reap_list.append((recipient, session)) if (now - session.timestamp) > self.session_timeout: reap_list.append((recipient, session)) for recipient, session in reap_list: try: session.close(quiet=True) finally: del self.sessions[recipient] session = None if self.terminate.wait(10): break def __call__(self, environ, start_response): req = Request(environ) try: handler, kwargs = self.router.match(req.path_info) if handler: # XXX Workaround wheezy bug if 'route_name' in kwargs: del kwargs['route_name'] resp = handler(req, **kwargs) else: self.not_found(req) except exc.HTTPException as e: # The exception is the response resp = e return resp(environ, start_response) def not_found(self, req): raise exc.HTTPNotFound( "The resource at %s could not be found" % req.path_info) def do_index(self, req): resp = Response() resp.content_type = b'text/html' resp.content_encoding = b'utf-8' resp.text = """\ <html> <head><title>Mancify</title></head> <body> <h1>Mancify</h1> <p>Probably the silliest webapp in the world...</p> </body> </html> """ return resp def do_translate(self, req): # Check the request has the required parameters if not 'msg_id' in req.params: raise exc.HTTPBadRequest('Missing msg_id parameter') if not 'from' in req.params: raise exc.HTTPBadRequest('Missing from parameter') if not 'content' in req.params: raise exc.HTTPBadRequest('Missing content parameter') msg_id = req.params['msg_id'] recipient = req.params['from'] sender = req.params['to'] content = req.params['content'] # If we've seen the message before it's a duplicate. Return 200 OK so # the server doesn't keep retrying but otherwise ignore it if msg_id in self.messages: raise exc.HTTPOk('Message already processed') self.messages.add(msg_id) self.sms.send(sender, recipient, translator.translate(content, manc)) raise exc.HTTPOk('Message processed') def do_ssh(self, req): # Check the request has the required parameters if not 'msg_id' in req.params: raise exc.HTTPBadRequest('Missing msg_id parameter') if not 'from' in req.params: raise exc.HTTPBadRequest('Missing from parameter') if not 'content' in req.params: raise exc.HTTPBadRequest('Missing content parameter') msg_id = req.params['msg_id'] recipient = req.params['from'] sender = req.params['to'] content = req.params['content'] # If we've seen the message before it's a duplicate. Return 200 OK so # the server doesn't keep retrying but otherwise ignore it if msg_id in self.messages: raise exc.HTTPOk('Message already processed') self.messages.add(msg_id) try: with self.lock: try: session = self.sessions[recipient] except KeyError: session = MancifySSHSession( self.sms, sender, recipient, self.connect_timeout, self.exec_timeout) self.sessions[recipient] = session session.timestamp = time.time() session.execute(content) except Exception as e: msg = str(e) if len(msg) > 140: msg = msg[:137] + '...' self.sms.send(sender, recipient, msg) raise exc.HTTPOk('Message processed')