Changeset - 6644c5267fb5
[Not reviewed]
default
0 2 1
Laman - 3 years ago 2021-10-14 18:07:25

added tests
3 files changed with 64 insertions and 6 deletions:
0 comments (0 inline, 0 general)
.hgignore
Show inline comments
 
.idea/
 
__pycache__/
rank_progress.py
Show inline comments
 
import re
 
from itertools import groupby
 
from datetime import datetime
 
import argparse
 

	
 

	
 
class Record:
 
	def __init__(self, tokens, source):
 
		self.source = source
 

	
 
		self.pin = int(tokens[0])
 
		self.country_code = tokens[2]
 
		self.rating_before = int(tokens[8])
 
		self.rounded_before = round_rating(self.rating_before)
 
		self.rating_after = int(tokens[9])
 
		self.rounded_after = round_rating(self.rating_after)
 

	
 
		tournament_code = tokens[5]
 
		self.date = datetime.strptime(tournament_code[1:7], "%y%m%d")
 

	
 
		self.rank_change = (rating_to_rank(self.rating_before), rating_to_rank(self.rating_after))
 

	
 
	@classmethod
 
	def parse(cls, line):
 
		tokens = re.split(r" {2,}", line.strip())
 

	
 
		if len(tokens) != 10:
 
			return None
 

	
 
		return cls(tokens, line)
 

	
 
	def __str__(self):
 
		s = self.source + "  {} -> {}".format(*self.rank_change)
 
		return s
 

	
 

	
 
class RankTracker:
 
	def __init__(self, rating):
 
		assert rating >= -900
 
		self._rounded_rating = round_rating(rating)
 

	
 
	@property
 
	def rank(self):
 
		return rating_to_rank(self._rounded_rating)
 

	
 
	def update(self, rating):
 
		assert rating >= -900
 
		rounded_rating = round_rating(rating)
 
		old_rank = self.rank
 

	
 
		if rounded_rating == self._rounded_rating:
 
			return False
 
			pass
 
		elif rounded_rating > self._rounded_rating:  # promotion
 
			self._rounded_rating = rounded_rating
 
			return self.rank
 
		else:  # demotion
 
			if rounded_rating >= 1500 and self._rounded_rating - rating > 100:
 
			if self._rounded_rating >= 1600 and self._rounded_rating - rating > 100:
 
				self._rounded_rating = rounded_rating
 
				return self.rank
 
			elif self._rounded_rating - rating > 150:
 
				self._rounded_rating = rounded_rating+100
 
				return self.rank
 
			else:
 
				return False
 
				pass
 

	
 
		new_rank = self.rank
 
		return new_rank if new_rank != old_rank else False
 

	
 

	
 
def parse_record(record):
 
	types = [int, str, str, str, str, str, int, int, int, int]
 
	columns = [f(token) for (f, token) in zip(types, record)] + [datetime.strptime(record[5][1:7], "%y%m%d")]
 
	return tuple(columns)
 

	
 

	
 
def round_rating(r):
 
	return (r+50)//100*100
 

	
 

	
 
def rating_to_rank(rating):
 
	rank_list = [str(i)+"k" for i in range(30, 0, -1)] + [str(i)+"d" for i in range(1, 8)]
 
	key = round_rating(rating)//100
 
	return rank_list[min(key+9, 36)]
 

	
 

	
 
if __name__ == "__main__":
 
	parser = argparse.ArgumentParser()
 
	parser.add_argument("since", help="a date in YYYYMMDD format")
 
	parser.add_argument("to", nargs="?", help="a date in YYYYMMDD format")
 
	parser.add_argument("-c", "--country-code", default="CZ", help="a two letter country code, default=CZ")
 

	
 
	args = parser.parse_args()
 
	
 
	since = datetime.strptime(args.since, "%Y%m%d")
 
	to = datetime.strptime(args.to, "%Y%m%d") if args.to else datetime.now()
 

	
 
	with open("/tmp/all.hst") as f:
 
		s = f.read()
 

	
 
	records = [Record.parse(line) for line in s.splitlines()]
 
	records = filter(lambda rec: rec is not None, records)
 
	national_records = filter(lambda rec: rec.country_code == args.country_code, records)
 
	player_records = groupby(national_records, lambda rec: rec.pin)
 

	
 
	for (pin, recs) in player_records:
 
		tourneys = list(recs)
 
		rounded_rating = tourneys[0].rounded_before
 

	
 
		steps = []
 

	
 
		for r in tourneys:
 
			# omit reset ratings
 
			if rounded_rating != r.rounded_before:
 
				rounded_rating = r.rounded_before
 

	
test.py
Show inline comments
 
new file 100644
 
from unittest import TestCase
 

	
 
from rank_progress import RankTracker
 

	
 

	
 
class TestRankTracker(TestCase):
 
	def test_no_change(self):
 
		t = RankTracker(1945)  # 2k
 
		for r in [1945, 1949, 1900, 1850]:
 
			self.assertFalse(t.update(r), r)
 

	
 
	def test_promotion(self):
 
		# basic
 
		t = RankTracker(1945)
 
		for (rating, rank) in [(1950, "1k"), (1945, False), (2000, False), (2049, False), (2051, "1d")]:
 
			self.assertEqual(t.update(rating), rank, (rating, rank))
 

	
 
		# 2+ ranks leaps
 

	
 
		# limited when pushing the personal best
 
		self.skipTest("not yet implemented")
 
		t = RankTracker(1100)
 
		self.assertEqual(t.update(1299), "9k")
 
		self.assertEqual(t.update(1250), "8k")
 
		self.assertEqual(t.update(1500), "6k")
 

	
 
		# regular below the personal best
 
		t = RankTracker(1201)  # 9k
 
		t.update(1000)
 
		t.update(900)
 
		t.update(849)  # 11k
 
		self.assertEqual(t.update(1250), "8k")
 

	
 
	def test_demotion(self):
 
		t = RankTracker(2069)
 
		self.assertEqual(t.update(1995), "1k")
 

	
 
		t = RankTracker(1590)
 
		self.assertEqual(t.update(1387), "7k")
 

	
 
		t = RankTracker(1087)
 
		self.assertFalse(t.update(995))
 
		self.assertEqual(t.update(940), "11k")
 

	
 
	def test_edges_cases(self):
 
		t = RankTracker(-900)
 
		self.assertEqual(t.rank, "30k")
 
		with self.assertRaises(AssertionError):
 
			t.update(-901)
 

	
 
		t = RankTracker(2700)
 
		self.assertEqual(t.rank, "7d")
 
		self.assertFalse(t.update(2750))
 
		self.assertFalse(t.update(2800))
0 comments (0 inline, 0 general)