/pitz/webapp/handlers/__init__.py

https://github.com/mw44118/pitz · Python · 278 lines · 269 code · 7 blank · 2 comment · 1 complexity · 707de6fd2be9d5aa9da190c5bd7d0230 MD5 · raw file

  1. # vim: set expandtab ts=4 sw=4 filetype=python:
  2. import datetime
  3. import logging
  4. import os
  5. import re
  6. import time
  7. from wsgiref.headers import Headers
  8. import pitz.jinja2templates
  9. log = logging.getLogger('pitz.webapp.handlers')
  10. class Handler(object):
  11. def __init__(self, proj):
  12. self.proj = proj
  13. @staticmethod
  14. def extract_frag(path_info):
  15. """
  16. >>> ByFragHandler.extract_frag('/by_frag/9f1c76')
  17. '9f1c76'
  18. >>> ByFragHandler.extract_frag(
  19. ... '/by_frag/9f1c76/edit-attributes')
  20. '9f1c76'
  21. """
  22. return re.match(
  23. r'^/by_frag/(?P<frag>.{6}).*$',
  24. path_info).groupdict()['frag']
  25. class DispatchingHandler(Handler):
  26. def __init__(self, proj):
  27. super(DispatchingHandler, self).__init__(proj)
  28. self.handlers = list()
  29. def dispatch(self, environ):
  30. """
  31. Return the first handler that wants to handle this environ.
  32. """
  33. log.debug('PATH_INFO is %(PATH_INFO)s' % environ)
  34. log.debug('REQUEST_METHOD is %(REQUEST_METHOD)s' % environ)
  35. log.debug('CONTENT_LENGTH is %s' % environ.get('CONTENT_LENGTH'))
  36. log.debug('wsgi.input is %(wsgi.input)s' % environ)
  37. for h in self.handlers:
  38. log.debug(
  39. 'Asking %s if it wants this request...'
  40. % h.__class__.__name__)
  41. if h.wants_to_handle(environ):
  42. log.debug('And the answer is YES!')
  43. return h
  44. def __call__(self, environ, start_response):
  45. h = self.dispatch(environ)
  46. if h:
  47. return h(environ, start_response)
  48. class HelpHandler(Handler):
  49. """
  50. Handles the GET /help request.
  51. """
  52. def wants_to_handle(self, environ):
  53. if environ['PATH_INFO'] == '/help':
  54. return self
  55. def __call__(self, environ, start_response):
  56. """
  57. Return a screen of help about the web app.
  58. """
  59. t = self.proj.e.get_template('help.html')
  60. status = '200 OK'
  61. headers = [('Content-Type', 'text/html')]
  62. start_response(status, headers)
  63. return [str(t.render(title='Pitz Webapp Help'))]
  64. class StaticHandler(object):
  65. """
  66. Serves files like CSS and javascript.
  67. """
  68. timefmt = '%a, %d %b %Y %H:%M:%S GMT'
  69. def __init__(self, static_files):
  70. self.static_files = static_files
  71. def wants_to_handle(self, environ):
  72. if environ['PATH_INFO'].startswith('/static'):
  73. return self
  74. def __call__(self, environ, start_response):
  75. filename = self.extract_filename(environ['PATH_INFO'])
  76. f = self.find_file(filename)
  77. modified_time = self.figure_out_modified_time(
  78. os.path.join(self.static_files, filename))
  79. modified_time_header = modified_time.strftime(self.timefmt)
  80. if 'HTTP_IF_MODIFIED_SINCE' in environ:
  81. if_modified_since = datetime.datetime.strptime(
  82. environ['HTTP_IF_MODIFIED_SINCE'],
  83. self.timefmt)
  84. if 'HTTP_IF_MODIFIED_SINCE' in environ \
  85. and if_modified_since >= modified_time:
  86. headers = [
  87. ('Content-Type', self.figure_out_content_type(filename)),
  88. ('Last-Modified', modified_time_header),
  89. ]
  90. start_response('304 Not Modified', headers)
  91. return []
  92. else:
  93. headers = [
  94. ('Content-Type', self.figure_out_content_type(filename)),
  95. ('Last-Modified', modified_time_header),
  96. ]
  97. start_response('200 OK', headers)
  98. return [f.read()]
  99. def find_file(self, filename):
  100. """
  101. Return an open file for filename.
  102. """
  103. return open(os.path.join(
  104. self.static_files,
  105. filename))
  106. @staticmethod
  107. def extract_filename(path_info):
  108. """
  109. >>> StaticHandler.extract_filename('/static/pitz.css')
  110. 'pitz.css'
  111. """
  112. return re.match(
  113. r'^/static/(?P<filename>.+)$',
  114. path_info).groupdict()['filename']
  115. @staticmethod
  116. def figure_out_modified_time(favicon_path):
  117. tt = time.gmtime(os.stat(favicon_path).st_mtime)
  118. return datetime.datetime(*tt[:6])
  119. @staticmethod
  120. def figure_out_content_type(filename):
  121. """
  122. >>> StaticHandler.figure_out_content_type('abc.js')
  123. 'application/x-javascript'
  124. >>> StaticHandler.figure_out_content_type('abc.css')
  125. 'text/css'
  126. >>> StaticHandler.figure_out_content_type('x.pdf')
  127. 'text/plain'
  128. """
  129. if filename.endswith('.js'):
  130. content_type = 'application/x-javascript'
  131. elif filename.endswith('.css'):
  132. content_type = 'text/css'
  133. else:
  134. content_type = 'text/plain'
  135. return content_type
  136. class FaviconHandler(StaticHandler):
  137. def __init__(self, static_dir):
  138. super(FaviconHandler, self).__init__(static_dir)
  139. self.favicon_path = os.path.join(
  140. self.static_files, 'favicon.ico')
  141. self.favicon_guts = open(self.favicon_path).read()
  142. self.last_modified = self.figure_out_modified_time(
  143. self.favicon_path).strftime(self.timefmt)
  144. def wants_to_handle(self, environ):
  145. if environ['PATH_INFO'] == '/favicon.ico':
  146. return self
  147. def __call__(self, environ, start_response):
  148. status = '200 OK'
  149. headers = [
  150. ('Content-Type', 'image/x-icon'),
  151. ('Last-Modified', self.last_modified),
  152. ]
  153. start_response(status, headers)
  154. return [self.favicon_guts]
  155. class ByFragHandler(Handler):
  156. def __init__(self, proj):
  157. super(ByFragHandler, self).__init__(proj)
  158. self.pattern = re.compile(r'^/by_frag/......$')
  159. def wants_to_handle(self, environ):
  160. if environ['REQUEST_METHOD'] == 'GET' \
  161. and self.pattern.match(environ['PATH_INFO']) :
  162. return self
  163. def __call__(self, environ, start_response):
  164. results = self.proj.by_frag(
  165. self.extract_frag(environ['PATH_INFO']))
  166. status = '200 OK'
  167. headers = [('Content-type', 'text/html')]
  168. start_response(status, headers)
  169. return [str(results.html)]
  170. class Project(Handler):
  171. def wants_to_handle(self, environ):
  172. if environ['PATH_INFO'] in ('/', '/Project'):
  173. return self
  174. def __call__(self, environ, start_response):
  175. status = '200 OK'
  176. headers = [('Content-type', 'text/html')]
  177. start_response(status, headers)
  178. return [str(self.proj.html)]
  179. from pitz.webapp.handlers.team import Team
  180. from pitz.webapp.handlers.editattributes import EditAttributes
  181. from pitz.webapp.handlers.update import Update