# encoding=utf-8 import collections; import functools import os.path from tornado.web import URLSpec as U import tornado.web from terroroftinytown.client.alphabet import str_to_int, int_to_str from terroroftinytown.services.registry import registry from terroroftinytown.tracker import account, admin, project, api from terroroftinytown.tracker import model from terroroftinytown.tracker.base import BaseHandler from terroroftinytown.tracker.errors import UserIsBanned from terroroftinytown.tracker.form import CalculatorForm from terroroftinytown.tracker.model import GlobalSetting, ErrorReport from terroroftinytown.tracker.stats import Stats from terroroftinytown.tracker.ui import FormUIModule ProjectStatus = collections.namedtuple( '_ProjectStatus', ['git_hash', 'projects', 'project_stats']) class Application(tornado.web.Application): GIT_HASH = model.get_git_hash() def __init__(self, database, redis=None, **kwargs): self.db = database self.redis = redis handlers = [ U(r'/', IndexHandler, name='index'), U(r'/admin/', admin.AdminHandler, name='admin.overview'), U(r'/admin/banned', admin.BannedHandler, name='admin.banned'), U(r'/admin/login', account.LoginHandler, name='admin.login'), U(r'/admin/logout', account.LogoutHandler, name='admin.logout'), U(r'/admin/results', admin.ResultsHandler, name='admin.results'), U(r'/admin/error_reports', admin.ErrorReportsListHandler, name='admin.error_reports'), U(r'/admin/error_reports/delete_all', admin.ErrorReportsDeleteAllHandler, name='admin.error_reports.delete_all'), U(r'/admin/error_reports/delete_one/(.+)', admin.ErrorReportsDeleteOneHandler, name='admin.error_reports.delete_one'), U(r'/admin/error_reports/auto_delete_setting', admin.AutoDeleteErrorReportsSettingHandler, name='admin.error_reports.auto_delete_setting'), U(r'/users/', account.AllUsersHandler, name='users.overview'), U(r'/user/([a-z0-9_-]*)', account.UserHandler, name='user.overview'), U(r'/projects/overview', project.AllProjectsHandler, name='projects.overview'), U(r'/project/([a-z0-9_-]*)', project.ProjectHandler, name='project.overview'), U(r'/project/([a-z0-9_-]*)/queue', project.QueueHandler, name='project.queue'), U(r'/project/([a-z0-9_-]*)/claims', project.ClaimsHandler, name='project.claims'), U(r'/project/([a-z0-9_-]*)/settings', project.SettingsHandler, name='project.settings'), U(r'/project/([a-z0-9_-]*)/delete', project.DeleteHandler, name='project.delete'), U(r'/api/live_stats', api.LiveStatsHandler, name='api.live_stats'), U(r'/api/stats/([A-Za-z0-9_-]+)', api.UserStatsHandler, name='api.user_stats'), U(r'/api/project_settings', api.ProjectSettingsHandler, name='api.project_settings'), U(r'/api/get', api.GetHandler, name='api.get'), U(r'/api/done', api.DoneHandler, name='api.done'), U(r'/api/error', api.ErrorHandler, name='api.error'), U(r'/api/health', api.HealthHandler, name='api.health'), U(r'/status', StatusHandler, name='index.status'), U(r'/calculator', CalculatorHandler, name='index.calculator'), ] static_path = os.path.join( os.path.dirname(__file__), 'static' ) template_path = os.path.join( os.path.dirname(__file__), 'template' ) ui_modules = { 'Form': FormUIModule, } super(Application, self).__init__( handlers, static_path=static_path, template_path=template_path, login_url='/admin/login', ui_modules=ui_modules, **kwargs ) def job_task(): if self.is_maintenance_in_progress(): return model.Item.release_old(autoqueue_only=True) model.Budget.calculate_budgets() job_task() self._job_timer = tornado.ioloop.PeriodicCallback( job_task, 60 * 1000 ) self._job_timer.start() def clean_error_reports(): if self.is_maintenance_in_progress(): return enabled = GlobalSetting.get_value( GlobalSetting.AUTO_DELETE_ERROR_REPORTS) if enabled: ErrorReport.delete_orphaned() clean_error_reports() self._clean_error_reports_timer = tornado.ioloop.PeriodicCallback( clean_error_reports, 300 * 1000 ) self._clean_error_reports_timer.start() def checkout_item(self, username, ip_address=None, version=-1, client_version=-1): if model.BlockedUser.is_username_blocked(username, ip_address): raise UserIsBanned() return model.checkout_item(username, ip_address, version, client_version) def checkin_item(self, item_id, tamper_key, results): return model.checkin_item(item_id, tamper_key, results) def report_error(self, item_id, tamper_key, message): model.report_error(item_id, tamper_key, message) def is_maintenance_in_progress(self): sentinel_path = self.settings.get('maintenance_sentinel') return sentinel_path and os.path.exists(sentinel_path) def is_deadman_safety_tripped(self): return model.deadman_checks() def get_project_status(self): projects = list([ model.Project.get_plain(name) for name in model.Project.all_project_names()]) project_stats = Stats.instance.get_project() return ProjectStatus( git_hash=self.GIT_HASH, projects=projects, project_stats=project_stats ) class IndexHandler(BaseHandler): def get(self): lifetime_list = [ (username, found, scanned) for username, (found, scanned) in Stats.instance.get_lifetime().items() ] lifetime_list = sorted(lifetime_list, key=lambda item: item[2], reverse=True) stats = { 'global': Stats.instance.get_global(), 'lifetime': lifetime_list[:300], 'live': Stats.instance.get_live(), } self.render('index.html', stats=stats) class StatusHandler(BaseHandler): def get(self): status = self.application.get_project_status() self.render('status.html', projects=status.projects, services=registry, project_stats=status.project_stats, git_hash=status.git_hash) class CalculatorHandler(BaseHandler): def get_current_user(self): # No need for database access pass def _show_maintenance_page(self): pass def get(self): form = CalculatorForm(self.request.arguments) message = None convert_direction = self.get_argument('convert', None) if convert_direction and form.validate(): try: if convert_direction == 'up': source_number = self.get_argument('number_2') source_alphabet = self.get_argument('alphabet_2') target_alphabet = self.get_argument('alphabet_1') num = str_to_int(source_number, source_alphabet) form.number_1.data = int_to_str(num, target_alphabet) else: source_number = self.get_argument('number_1') source_alphabet = self.get_argument('alphabet_1') target_alphabet = self.get_argument('alphabet_2') num = str_to_int(source_number, source_alphabet) form.number_2.data = int_to_str(num, target_alphabet) except ValueError as error: message = str(error) self.render('calculator.html', form=form, message=message)