├── .gitignore ├── LICENSE ├── README.md ├── cpboredbutton ├── __init__.py └── cf_recommend.py ├── pip_push.sh ├── requirements.txt ├── scripts └── cpboredbutton └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /cpboredbutton.egg-info 3 | /dist 4 | /.idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vishal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bored Button 2 | A python script which randomly opens a CP problem according to user difficulty 3 | 4 | ## Requirements 5 | pip3 6 | 7 | ## Installing the package 8 | ``` 9 | sudo pip3 install cpboredbutton 10 | ``` 11 | 12 | ## Running the File 13 | Run the script with 14 | ``` 15 | cpboredbutton [-cf Codeforces_Handle] 16 | ``` 17 | 18 | ## Completed 19 | * Implemented for Codeforces 20 | 21 | ## Next Steps 22 | * Move Codeforces functions to class 23 | * Expand to Codechef, Hackerrank & SPOJ 24 | * Front-End service 25 | * API Service 26 | 27 | ## Current Version 28 | 1.0 29 | 30 | -------------------------------------------------------------------------------- /cpboredbutton/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vishalananth07/CP-Bored-Button/4c8678857fd785f69b1c71be9002adb63316d130/cpboredbutton/__init__.py -------------------------------------------------------------------------------- /cpboredbutton/cf_recommend.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import random 3 | import requests 4 | 5 | CONST_RATING_UPPERBOUND = 300 6 | CONST_RATING_LOWERBOUND = 100 7 | 8 | CONST_MIN_SOLVED_PROBLEM = 13 9 | CONST_AVERAGE_LIMIT = 100 10 | CONST_MIN_RATING_AVERAGE = 1000 11 | 12 | 13 | def get_problem_url(user_rating, problem_list): 14 | filtered_problem = [i for i in problem_list if i['rating'] >= user_rating - CONST_RATING_LOWERBOUND and i[ 15 | 'rating'] <= user_rating + CONST_RATING_UPPERBOUND] 16 | if len(filtered_problem) == 0: 17 | greater_than_upper = [i for i in problem_list if i['rating'] > user_rating + CONST_RATING_UPPERBOUND] 18 | less_than_lower = [i for i in problem_list if i['rating'] < user_rating - CONST_RATING_LOWERBOUND] 19 | if len(greater_than_upper) > 0: 20 | min_of_max = min(greater_than_upper, key=lambda x: x['rating']) 21 | filtered_problem.append(min_of_max) 22 | if len(less_than_lower) > 0: 23 | max_of_min = max(less_than_lower, key=lambda x: x['rating']) 24 | filtered_problem.append(max_of_min) 25 | if len(filtered_problem) == 0: 26 | print("You've Solved Everything") 27 | random_problem = random.choice(filtered_problem) 28 | return "https://codeforces.com/problemset/problem/" + str(random_problem['contestId']) + "/" + random_problem[ 29 | 'index'] 30 | 31 | 32 | def get_limit(x): 33 | if x > CONST_MIN_SOLVED_PROBLEM: 34 | x = (x / 5) + 10 35 | return int(min(x, CONST_AVERAGE_LIMIT)) 36 | 37 | 38 | def calculate_rating_average(solved_problems): 39 | limit = get_limit(len(solved_problems)) 40 | if limit == 0: 41 | return CONST_MIN_RATING_AVERAGE 42 | rating_average = 0 43 | for i in range(0, limit): 44 | rating_average += solved_problems[i]['rating'] 45 | rating_average /= limit 46 | return rating_average 47 | 48 | 49 | def remove_duplicate(solved_problems): 50 | n = len(solved_problems) 51 | s_problems = [] 52 | for i in range(1, n): 53 | if solved_problems[i] != solved_problems[i - 1]: 54 | s_problems.append(solved_problems[i - 1]) 55 | return s_problems 56 | 57 | 58 | def sort_rating_comparator(problem1, problem2): 59 | if problem1['rating'] < problem2['rating']: 60 | return 1 61 | else: 62 | return -1 63 | 64 | 65 | def sort_problem_comparator(problem1, problem2): 66 | if problem1['contestId'] != problem2['contestId']: 67 | if problem1['contestId'] < problem2['contestId']: 68 | return 1 69 | else: 70 | return -1 71 | else: 72 | if problem1['index'] < problem2['index']: 73 | return 1 74 | else: 75 | return -1 76 | 77 | 78 | def remove_solved(solved_problems, all_problems_wr): 79 | unsolved_problems = [] 80 | i = 0 81 | for problem in all_problems_wr: 82 | if i == len(solved_problems) or problem != solved_problems[i]: 83 | unsolved_problems.append(problem) 84 | else: 85 | i += 1 86 | return unsolved_problems 87 | 88 | 89 | def get_random_problem(user_handle): 90 | # Fetching handle 91 | resp = requests.get("https://codeforces.com/api/user.status?handle=" + user_handle + "&from=1") 92 | if resp.status_code < 200 or resp.status_code > 299: 93 | print("Error " + resp.status_code + "\nCould not fetch handle") 94 | quit() 95 | 96 | # Fetching solved problems 97 | all_attempted_problems = resp.json()['result'] 98 | solved_problems = [] 99 | for problem in all_attempted_problems: 100 | if "rating" in problem['problem'] and problem['verdict'] == "OK": 101 | solved_problems.append({"contestId": problem['problem']['contestId'], "index": problem['problem']['index'], 102 | "rating": problem['problem']['rating']}) 103 | 104 | # Fetching all problems 105 | resp = requests.get("https://codeforces.com/api/problemset.problems") 106 | if resp.status_code < 200 or resp.status_code > 299: 107 | print("Error " + resp.status_code + "\nCould not fetch problems") 108 | quit() 109 | all_problems = resp.json()['result']['problems'] 110 | all_problems_wr = [] 111 | for problem in all_problems: 112 | if "rating" in problem: 113 | all_problems_wr.append( 114 | {"contestId": problem['contestId'], "index": problem['index'], "rating": problem['rating']}) 115 | 116 | solved_problems = sorted(solved_problems, key=functools.cmp_to_key(sort_problem_comparator)) 117 | all_problems_wr = sorted(all_problems_wr, key=functools.cmp_to_key(sort_problem_comparator)) 118 | 119 | unsolved_problems = remove_solved(solved_problems, all_problems_wr) 120 | 121 | solved_problems = remove_duplicate(solved_problems) 122 | solved_problems = sorted(solved_problems, key=functools.cmp_to_key(sort_rating_comparator)) 123 | 124 | rating_average = calculate_rating_average(solved_problems) 125 | 126 | cf_url = get_problem_url(rating_average, unsolved_problems) 127 | 128 | return cf_url -------------------------------------------------------------------------------- /pip_push.sh: -------------------------------------------------------------------------------- 1 | python3 setup.py sdist bdist_wheel 2 | twine upload dist/* -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.20.0 2 | -------------------------------------------------------------------------------- /scripts/cpboredbutton: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from argparse import ArgumentParser 4 | import cpboredbutton.cf_recommend as cf_recommend 5 | import random 6 | import os 7 | 8 | 9 | def get_input(): 10 | parser = ArgumentParser() 11 | parser.add_argument("-cf", "--codeforces", required=True) 12 | args = parser.parse_args() 13 | user_handles = { 14 | "codeforces": args.codeforces 15 | } 16 | return user_handles 17 | 18 | 19 | def main(): 20 | parameter = get_input() 21 | # Append all available OJs 22 | open_url = [] 23 | if parameter["codeforces"] is not None: 24 | open_url.append("codeforces") 25 | 26 | random_oj = random.choice(open_url) 27 | url = "" 28 | if random_oj == "codeforces": 29 | url = cf_recommend.get_random_problem(parameter["codeforces"]) 30 | print("Problem url:", url) 31 | os.system("xdg-open " + url + " 2>/dev/null") 32 | 33 | 34 | if __name__ == "__main__": 35 | main() 36 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="cpboredbutton", 8 | version="1.0", 9 | scripts=['scripts/cpboredbutton'], 10 | author="vishalananth, anishbadri", 11 | author_email="vishalananth98@gmail.com, anishbadhri@gmail.com", 12 | description="Competitive programming problem recommendation", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | url="https://github.com/vishalananth07/CP-Bored-Button", 16 | packages=setuptools.find_packages(), 17 | classifiers=[ 18 | "Programming Language :: Python :: 3", 19 | "License :: OSI Approved :: MIT License", 20 | "Operating System :: OS Independent", 21 | ], 22 | python_requires=">=3", 23 | ) 24 | --------------------------------------------------------------------------------