app.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. # encoding=utf-8
  2. import collections;
  3. import functools
  4. import os.path
  5. from tornado.web import URLSpec as U
  6. import tornado.web
  7. from terroroftinytown.client.alphabet import str_to_int, int_to_str
  8. from terroroftinytown.services.registry import registry
  9. from terroroftinytown.tracker import account, admin, project, api
  10. from terroroftinytown.tracker import model
  11. from terroroftinytown.tracker.base import BaseHandler
  12. from terroroftinytown.tracker.errors import UserIsBanned
  13. from terroroftinytown.tracker.form import CalculatorForm
  14. from terroroftinytown.tracker.model import GlobalSetting, ErrorReport
  15. from terroroftinytown.tracker.stats import Stats
  16. from terroroftinytown.tracker.ui import FormUIModule
  17. ProjectStatus = collections.namedtuple(
  18. '_ProjectStatus',
  19. ['git_hash', 'projects', 'project_stats'])
  20. class Application(tornado.web.Application):
  21. GIT_HASH = model.get_git_hash()
  22. def __init__(self, database, redis=None, **kwargs):
  23. self.db = database
  24. self.redis = redis
  25. handlers = [
  26. U(r'/', IndexHandler, name='index'),
  27. U(r'/admin/', admin.AdminHandler, name='admin.overview'),
  28. U(r'/admin/banned', admin.BannedHandler, name='admin.banned'),
  29. U(r'/admin/login', account.LoginHandler, name='admin.login'),
  30. U(r'/admin/logout', account.LogoutHandler, name='admin.logout'),
  31. U(r'/admin/results', admin.ResultsHandler, name='admin.results'),
  32. U(r'/admin/error_reports', admin.ErrorReportsListHandler,
  33. name='admin.error_reports'),
  34. U(r'/admin/error_reports/delete_all',
  35. admin.ErrorReportsDeleteAllHandler,
  36. name='admin.error_reports.delete_all'),
  37. U(r'/admin/error_reports/delete_one/(.+)',
  38. admin.ErrorReportsDeleteOneHandler,
  39. name='admin.error_reports.delete_one'),
  40. U(r'/admin/error_reports/auto_delete_setting',
  41. admin.AutoDeleteErrorReportsSettingHandler,
  42. name='admin.error_reports.auto_delete_setting'),
  43. U(r'/users/', account.AllUsersHandler, name='users.overview'),
  44. U(r'/user/([a-z0-9_-]*)', account.UserHandler, name='user.overview'),
  45. U(r'/projects/overview', project.AllProjectsHandler, name='projects.overview'),
  46. U(r'/project/([a-z0-9_-]*)', project.ProjectHandler, name='project.overview'),
  47. U(r'/project/([a-z0-9_-]*)/queue', project.QueueHandler, name='project.queue'),
  48. U(r'/project/([a-z0-9_-]*)/claims', project.ClaimsHandler, name='project.claims'),
  49. U(r'/project/([a-z0-9_-]*)/settings', project.SettingsHandler, name='project.settings'),
  50. U(r'/project/([a-z0-9_-]*)/delete', project.DeleteHandler, name='project.delete'),
  51. U(r'/api/live_stats', api.LiveStatsHandler, name='api.live_stats'),
  52. U(r'/api/stats/([A-Za-z0-9_-]+)', api.UserStatsHandler, name='api.user_stats'),
  53. U(r'/api/project_settings', api.ProjectSettingsHandler, name='api.project_settings'),
  54. U(r'/api/get', api.GetHandler, name='api.get'),
  55. U(r'/api/done', api.DoneHandler, name='api.done'),
  56. U(r'/api/error', api.ErrorHandler, name='api.error'),
  57. U(r'/api/health', api.HealthHandler, name='api.health'),
  58. U(r'/status', StatusHandler, name='index.status'),
  59. U(r'/calculator', CalculatorHandler, name='index.calculator'),
  60. ]
  61. static_path = os.path.join(
  62. os.path.dirname(__file__), 'static'
  63. )
  64. template_path = os.path.join(
  65. os.path.dirname(__file__), 'template'
  66. )
  67. ui_modules = {
  68. 'Form': FormUIModule,
  69. }
  70. super(Application, self).__init__(
  71. handlers,
  72. static_path=static_path,
  73. template_path=template_path,
  74. login_url='/admin/login',
  75. ui_modules=ui_modules,
  76. **kwargs
  77. )
  78. def job_task():
  79. if self.is_maintenance_in_progress():
  80. return
  81. model.Item.release_old(autoqueue_only=True)
  82. model.Budget.calculate_budgets()
  83. job_task()
  84. self._job_timer = tornado.ioloop.PeriodicCallback(
  85. job_task,
  86. 60 * 1000
  87. )
  88. self._job_timer.start()
  89. def clean_error_reports():
  90. if self.is_maintenance_in_progress():
  91. return
  92. enabled = GlobalSetting.get_value(
  93. GlobalSetting.AUTO_DELETE_ERROR_REPORTS)
  94. if enabled:
  95. ErrorReport.delete_orphaned()
  96. clean_error_reports()
  97. self._clean_error_reports_timer = tornado.ioloop.PeriodicCallback(
  98. clean_error_reports,
  99. 300 * 1000
  100. )
  101. self._clean_error_reports_timer.start()
  102. def checkout_item(self, username, ip_address=None, version=-1, client_version=-1):
  103. if model.BlockedUser.is_username_blocked(username, ip_address):
  104. raise UserIsBanned()
  105. return model.checkout_item(username, ip_address, version, client_version)
  106. def checkin_item(self, item_id, tamper_key, results):
  107. return model.checkin_item(item_id, tamper_key, results)
  108. def report_error(self, item_id, tamper_key, message):
  109. model.report_error(item_id, tamper_key, message)
  110. def is_maintenance_in_progress(self):
  111. sentinel_path = self.settings.get('maintenance_sentinel')
  112. return sentinel_path and os.path.exists(sentinel_path)
  113. def is_deadman_safety_tripped(self):
  114. return model.deadman_checks()
  115. def get_project_status(self):
  116. projects = list([
  117. model.Project.get_plain(name)
  118. for name in model.Project.all_project_names()])
  119. project_stats = Stats.instance.get_project()
  120. return ProjectStatus(
  121. git_hash=self.GIT_HASH,
  122. projects=projects,
  123. project_stats=project_stats
  124. )
  125. class IndexHandler(BaseHandler):
  126. def get(self):
  127. lifetime_list = [
  128. (username, found, scanned)
  129. for username, (found, scanned)
  130. in Stats.instance.get_lifetime().items()
  131. ]
  132. lifetime_list = sorted(lifetime_list, key=lambda item: item[2],
  133. reverse=True)
  134. stats = {
  135. 'global': Stats.instance.get_global(),
  136. 'lifetime': lifetime_list[:300],
  137. 'live': Stats.instance.get_live(),
  138. }
  139. self.render('index.html', stats=stats)
  140. class StatusHandler(BaseHandler):
  141. def get(self):
  142. status = self.application.get_project_status()
  143. self.render('status.html', projects=status.projects, services=registry,
  144. project_stats=status.project_stats,
  145. git_hash=status.git_hash)
  146. class CalculatorHandler(BaseHandler):
  147. def get_current_user(self):
  148. # No need for database access
  149. pass
  150. def _show_maintenance_page(self):
  151. pass
  152. def get(self):
  153. form = CalculatorForm(self.request.arguments)
  154. message = None
  155. convert_direction = self.get_argument('convert', None)
  156. if convert_direction and form.validate():
  157. try:
  158. if convert_direction == 'up':
  159. source_number = self.get_argument('number_2')
  160. source_alphabet = self.get_argument('alphabet_2')
  161. target_alphabet = self.get_argument('alphabet_1')
  162. num = str_to_int(source_number, source_alphabet)
  163. form.number_1.data = int_to_str(num, target_alphabet)
  164. else:
  165. source_number = self.get_argument('number_1')
  166. source_alphabet = self.get_argument('alphabet_1')
  167. target_alphabet = self.get_argument('alphabet_2')
  168. num = str_to_int(source_number, source_alphabet)
  169. form.number_2.data = int_to_str(num, target_alphabet)
  170. except ValueError as error:
  171. message = str(error)
  172. self.render('calculator.html', form=form, message=message)