Example #1
0
    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
Example #2
0
    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