def handle(self, *args, **options): self.stdout.write(str(options)) args = AttrDict(options) bot = Bot() if args.dump is not None and os.path.exists(args.dump): with open(args.dump, 'r') as fo: standings = json.load(fo) else: standings = {} problems_info = standings.setdefault('__problems_info', {}) parser_command = ParserCommand() iteration = 1 if args.dump else 0 while True: subprocess.call('clear', shell=True) print(now()) contest = Contest.objects.filter(pk=args.cid) parser_command.parse_statistic(contest, without_contest_filter=True) contest = contest.first() resource = contest.resource statistics = list(Statistics.objects.filter(contest=contest)) for p in problems_info.values(): if p.get('accepted') or not p.get('n_hidden'): p.pop('show_hidden', None) p['n_hidden'] = 0 updated = False has_hidden = False numbered = 0 for stat in sorted(statistics, key=lambda s: s.place_as_int): name_instead_key = resource.info.get( 'standings', {}).get('name_instead_key') name_instead_key = stat.account.info.get( '_name_instead_key', name_instead_key) if name_instead_key: name = stat.account.name else: name = stat.addition.get('name') if not name or not has_season(stat.account.key, name): name = stat.account.key filtered = False if args.query is not None and re.search( args.query, name, re.I): filtered = True message_id = None key = str(stat.account.id) if key in standings: if filtered: print(stat.modified, { k: v for k, v in standings[key].items() if k != 'problems' }, end=' | ') print(stat.place, stat.solving, end=' | ') problems = standings[key]['problems'] message_id = standings[key].get('messageId') def delete_message(): nonlocal message_id if message_id: for iteration in range(1, 5): try: bot.delete_message(chat_id=args.tid, message_id=message_id) message_id = None break except telegram.error.TimedOut as e: logger.warning(str(e)) time.sleep(iteration) continue p = [] has_update = False has_first_ac = False has_try_first_ac = False has_new_accepted = False for k, v in stat.addition.get('problems', {}).items(): p_info = problems_info.setdefault(k, {}) p_result = problems.get(k, {}).get('result') result = v['result'] is_hidden = str(result).startswith('?') is_accepted = str(result).startswith('+') try: is_accepted = is_accepted or float( result) > 0 and not v.get('partial') except Exception: pass if is_hidden: p_info['n_hidden'] = p_info.get('n_hidden', 0) + 1 if p_result != result or is_hidden: has_new_accepted |= is_accepted m = '%s%s %s' % (k, ('. ' + v['name']) if 'name' in v else '', result) if v.get('verdict'): m += ' ' + v['verdict'] if p_result != result: m = '*%s*' % m has_update = True if iteration: if p_info.get('show_hidden') == key: delete_message() if not is_hidden: p_info.pop('show_hidden') if not p_info.get('accepted'): if is_accepted: m += ' FIRST ACCEPTED' has_first_ac = True elif is_hidden and not p_info.get( 'show_hidden'): p_info['show_hidden'] = key m += ' TRY FIRST AC' has_try_first_ac = True if args.top and stat.place_as_int <= args.top: if not filtered: m += f' TOP{args.top}' filtered = True p.append(m) if result.startswith('+'): p_info['accepted'] = True has_hidden = has_hidden or is_hidden prev_place = standings[key].get('place') place = stat.place if has_new_accepted and prev_place: place = '%s->%s' % (prev_place, place) if args.numbered is not None and re.search( args.numbered, stat.account.key, re.I): numbered += 1 place = '%s (%s)' % (place, numbered) msg = '%s. _%s_' % (place, telegram.utils.helpers.escape_markdown( name.replace('_', ' '))) if p: msg = '%s, %s' % (', '.join(p), msg) if abs(standings[key]['solving'] - stat.solving) > 1e-9: msg += ' = %d' % stat.solving if 'penalty' in stat.addition: msg += f' ({stat.addition["penalty"]})' if has_update or has_first_ac or has_try_first_ac: updated = True if filtered: print(stat.place, stat.solving, end=' | ') if filtered: print(msg) if filtered and has_update or has_first_ac or has_try_first_ac: if not args.dryrun: delete_message() for iteration in range(1, 5): try: message = bot.send_message( msg=msg, chat_id=args.tid) message_id = message.message_id break except telegram.error.TimedOut as e: logger.warning(str(e)) time.sleep(iteration * 3) continue except telegram.error.BadRequest as e: logger.error(str(e)) break standings[key] = { 'solving': stat.solving, 'place': stat.place, 'problems': stat.addition.get('problems', {}), 'messageId': message_id, } if args.dump is not None and (updated or not os.path.exists(args.dump)): standings_dump = json.dumps(standings, indent=2) with open(args.dump, 'w') as fo: fo.write(standings_dump) if iteration: is_over = contest.end_time < now() if is_over and not has_hidden: break tick = 60 if is_over else 1 limit = now() + timedelta(seconds=args.delay * tick) size = 1 while now() < limit: value = humanize.naturaldelta(limit - now()) out = f'{value:{size}s}' size = len(value) print(out, end='\r') time.sleep(tick) print() iteration += 1
def handle(self, *args, **options): self.stdout.write(str(options)) args = AttrDict(options) bot = Bot() if args.dump is not None and os.path.exists(args.dump): with open(args.dump, 'r') as fo: standings = json.load(fo) else: standings = {} problems_info = standings.setdefault('__problems_info', {}) parser_command = ParserCommand() iteration = 1 if args.dump else 0 while True: subprocess.call('clear', shell=True) contest = Contest.objects.get(pk=args.cid) parser_command.parse_statistic([contest], with_check=False) statistics = Statistics.objects.filter(contest=contest) updated = False has_hidden = False numbered = 0 for stat in sorted(statistics, key=lambda s: s.place_as_int): filtered = False if args.query is not None and re.search( args.query, stat.account.key, re.I): filtered = True message_id = None key = str(stat.account.id) if key in standings: problems = standings[key]['problems'] message_id = standings[key].get('messageId') p = [] has_update = False has_first_ac = False for k, v in stat.addition.get('problems', {}).items(): p_info = problems_info.setdefault(k, {}) p_result = problems.get(k, {}).get('result') result = v['result'] is_hidden = result.startswith('?') try: is_accepted = result.startswith('+') is_accepted = is_accepted or float(result) > 0 except Exception: pass if p_result != result or is_hidden: m = '%s%s %s' % (k, ('. ' + v['name']) if 'name' in v else '', result) if p_result != result: m = '*%s*' % m has_update = True if iteration and is_accepted and not p_info.get( 'accepted'): m += ' FIRST ACCEPTED' has_first_ac = True if is_accepted and args.top and stat.place_as_int <= args.top: filtered = True p.append(m) if result.startswith('+'): p_info['accepted'] = True has_hidden = has_hidden or is_hidden if args.numbered is not None and re.search( args.numbered, stat.account.key, re.I): numbered += 1 place = '%s (%s)' % (stat.place, numbered) else: place = stat.place msg = '%s. _%s_ %s' % (place, stat.account.key, ', '.join(p)) if standings[key]['solving'] != stat.solving: msg += ' = %d' % stat.solving if has_update or has_first_ac: updated = True if filtered: print(msg) if filtered and has_update or has_first_ac: if not args.dryrun: for _ in range(1, 5): try: if message_id: bot.delete_message( chat_id=args.tid, message_id=message_id) message = bot.send_message( msg=msg, chat_id=args.tid) message_id = message.message_id except telegram.error.TimedOut as e: logger.warning(str(e)) time.sleep(_ * 3) continue except telegram.error.BadRequest as e: logger.error(str(e)) break standings[key] = { 'solving': stat.solving, 'problems': stat.addition.get('problems', {}), 'messageId': message_id, } if args.dump is not None and (updated or not os.path.exists(args.dump)): standings_dump = json.dumps(standings, indent=2) with open(args.dump, 'w') as fo: fo.write(standings_dump) if iteration: is_over = contest.end_time < now() if is_over and not has_hidden: break time.sleep(args.delay * (60 if is_over else 1)) iteration += 1