PageRenderTime 69ms CodeModel.GetById 40ms RepoModel.GetById 1ms app.codeStats 0ms

/main.py

https://github.com/cesarkawakami/competitionprintserver
Python | 311 lines | 304 code | 4 blank | 3 comment | 0 complexity | d4a125851995d782485a94c850847630 MD5 | raw file
  1. from datetime import datetime
  2. import os
  3. import re
  4. import time
  5. from sqlalchemy import create_engine, desc
  6. from sqlalchemy import Column, Integer, String, Enum, DateTime, ForeignKey
  7. from sqlalchemy.ext.declarative import declarative_base
  8. from sqlalchemy.orm import sessionmaker, relationship, backref
  9. from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
  10. from sqlalchemy.schema import UniqueConstraint
  11. import tornado.ioloop
  12. import tornado.web
  13. ##########
  14. # Helpers
  15. #
  16. def sanitize_name(name):
  17. return re.sub(r"[^a-zA-Z0-9]", "", name)
  18. def get_app_abs_path(path):
  19. return os.path.join(os.path.dirname(__file__), path)
  20. def get_store():
  21. return Store(settings["database"])
  22. ##########
  23. # Mapping
  24. #
  25. BaseModel = declarative_base()
  26. class User(BaseModel):
  27. __tablename__ = "users"
  28. id = Column(Integer, primary_key=True)
  29. username = Column(String)
  30. teamname = Column(String)
  31. __table_args__ = (
  32. UniqueConstraint('username'),
  33. )
  34. class Code(BaseModel):
  35. __tablename__ = "codes"
  36. id = Column(Integer, primary_key=True)
  37. user_id = Column(Integer, ForeignKey("users.id"))
  38. timestamp = Column(DateTime)
  39. tags = Column(String)
  40. lines = Column(Integer)
  41. status = Column(Enum("new", "printing", "delivered"))
  42. code = Column(String)
  43. user = relationship("User", backref=backref("codes", order_by=id))
  44. ##########
  45. # Handlers
  46. #
  47. class BaseHandler(tornado.web.RequestHandler):
  48. __session = None
  49. def get_current_user(self):
  50. session = self.get_session()
  51. username = self.get_secure_cookie("username")
  52. try:
  53. user = session.query(User).filter_by(username=username).one()
  54. return user
  55. except NoResultFound:
  56. return None
  57. def get_session(self):
  58. if self.__session is None:
  59. self.__session = Session()
  60. return self.__session
  61. class MainHandler(BaseHandler):
  62. @tornado.web.authenticated
  63. def get(self):
  64. self.render("main.html")
  65. class LoginHandler(BaseHandler):
  66. def get(self):
  67. self.render("login.html", errors=[])
  68. def post(self):
  69. session = self.get_session()
  70. teamname = self.get_argument("teamname", "")
  71. username = sanitize_name(teamname)
  72. if len(username) < 3:
  73. self.render("login.html", errors=["Nome de time muito curto."])
  74. return
  75. try:
  76. user = session.query(User).filter_by(username=username).one()
  77. except NoResultFound:
  78. user = User()
  79. user.username = username
  80. user.teamname = teamname
  81. session.add(user)
  82. session.commit()
  83. self.set_secure_cookie("username", user.username)
  84. if username == settings["super_secret"]:
  85. self.redirect("/super")
  86. else:
  87. self.redirect("/")
  88. class LogoutHandler(BaseHandler):
  89. def get(self):
  90. self.set_secure_cookie("username", "")
  91. self.redirect("/")
  92. class SubmitHandler(BaseHandler):
  93. @tornado.web.authenticated
  94. def post(self):
  95. session = self.get_session()
  96. code = Code()
  97. code.user = self.current_user
  98. code.timestamp = datetime.now()
  99. code.code = self.get_argument("code")
  100. code.tags = self.get_argument("tags", "")
  101. code.lines = code.code.count("\n") + 1
  102. code.status = "new"
  103. session.add(code)
  104. session.commit()
  105. Notifier.notify()
  106. class UpdateHandler(BaseHandler):
  107. @tornado.web.authenticated
  108. @tornado.web.asynchronous
  109. def get(self):
  110. cursor = int(self.get_argument("cursor", -1))
  111. Notifier.add_callback(self.callback, cursor)
  112. def callback(self, cursor):
  113. if self.request.connection.stream.closed():
  114. return
  115. self.finish({"cursor": cursor})
  116. class SubmissionsHandler(BaseHandler):
  117. @tornado.web.authenticated
  118. def get(self):
  119. session = self.get_session()
  120. self.render(
  121. "submissions.html",
  122. submissions=session.query(Code)\
  123. .filter_by(user_id=self.current_user.id)\
  124. .order_by(desc(Code.id))\
  125. .all()
  126. )
  127. class SeeHandler(BaseHandler):
  128. @tornado.web.authenticated
  129. def get(self, id):
  130. session = self.get_session()
  131. code = session.query(Code)\
  132. .filter_by(id=id)\
  133. .filter_by(user_id=self.current_user.id)\
  134. .scalar()
  135. if code is None:
  136. raise tornado.web.HTTPError(404)
  137. self.set_header("Content-Type", "text/plain")
  138. self.write(code.code)
  139. class SuperBaseHandler(BaseHandler):
  140. def check_super(self):
  141. if self.current_user.username != settings["super_secret"]:
  142. raise tornado.web.HTTPError(404)
  143. class SuperMainHandler(SuperBaseHandler):
  144. @tornado.web.authenticated
  145. def get(self):
  146. self.check_super()
  147. self.render("super_main.html")
  148. class SuperSubmissionsHandler(SuperBaseHandler):
  149. @tornado.web.authenticated
  150. def get(self):
  151. self.check_super()
  152. session = self.get_session()
  153. self.render(
  154. "super_submissions.html",
  155. submissions=session.query(Code)\
  156. .order_by(desc(Code.id))\
  157. .all()
  158. )
  159. class SuperSeeHandler(SuperBaseHandler):
  160. @tornado.web.authenticated
  161. def get(self, id):
  162. self.check_super()
  163. session = self.get_session()
  164. code = session.query(Code)\
  165. .filter_by(id=id)\
  166. .scalar()
  167. if code is None:
  168. raise tornado.web.HTTPError(404)
  169. self.render("super_see.html", code=code)
  170. class SuperActionHandler(SuperBaseHandler):
  171. @tornado.web.authenticated
  172. def get(self, id, action):
  173. self.check_super()
  174. session = self.get_session()
  175. code = session.query(Code).filter_by(id=id).one()
  176. if action == "delete":
  177. session.delete(code)
  178. else:
  179. code.status = action
  180. session.commit()
  181. Notifier.notify()
  182. class SuperNotifyHandler(SuperBaseHandler):
  183. @tornado.web.authenticated
  184. def get(self):
  185. self.check_super()
  186. Notifier.notify()
  187. ##########
  188. # Notifier
  189. #
  190. class Notifier(object):
  191. __cursor = 10
  192. __callbacks = []
  193. @classmethod
  194. def add_callback(cls, callback, cursor):
  195. if cursor < cls.__cursor:
  196. callback(cls.__cursor)
  197. else:
  198. cls.__callbacks.append(callback)
  199. @classmethod
  200. def notify(cls):
  201. cls.__cursor += 1
  202. for callback in cls.__callbacks:
  203. callback(cls.__cursor)
  204. cls.__callbacks = []
  205. ##########
  206. # Main/Settings
  207. #
  208. settings = {
  209. "static_path": get_app_abs_path("static"),
  210. "template_path": get_app_abs_path("tmpl"),
  211. "cookie_secret": "S39Un0HjQLmR7LRMzdg0WDavKI3VkkGJoPlCGMO0tOQ=",
  212. "super_secret": "sHCxUCh7MH28HBTxBcSftQCNMlVBy894zUztTOPn1kCnHAbDD2",
  213. "login_url": "/login",
  214. "debug": True,
  215. }
  216. application = tornado.web.Application([
  217. (r"/", MainHandler),
  218. (r"/login", LoginHandler),
  219. (r"/logout", LogoutHandler),
  220. (r"/submit", SubmitHandler),
  221. (r"/update", UpdateHandler),
  222. (r"/submissions", SubmissionsHandler),
  223. (r"/see/([0-9]+)", SeeHandler),
  224. (r"/super", SuperMainHandler),
  225. (r"/super/submissions", SuperSubmissionsHandler),
  226. (r"/super/see/([0-9]+)", SuperSeeHandler),
  227. (r"/super/set/([0-9]+)/([a-z]+)", SuperActionHandler),
  228. (r"/super/notify", SuperNotifyHandler),
  229. ], **settings)
  230. engine = create_engine("sqlite:///" + get_app_abs_path("var/db.db"))
  231. # BaseModel.metadata.drop_all(engine)
  232. BaseModel.metadata.create_all(engine)
  233. Session = sessionmaker(bind=engine)
  234. if __name__ == "__main__":
  235. application.listen(80)
  236. tornado.ioloop.IOLoop.instance().start()