From c835c556889f7f32d15b968c1b2d3da2415fe55c Mon Sep 17 00:00:00 2001 From: Mohab Yaser Date: Mon, 6 Nov 2023 00:18:47 +0200 Subject: [PATCH] Initializing the repo --- index.html | 95 +++++++++++++++++++++++++++++++++++++ main.js | 59 +++++++++++++++++++++++ main.py | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++ style.css | 101 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 391 insertions(+) create mode 100644 index.html create mode 100644 main.js create mode 100644 main.py create mode 100644 style.css diff --git a/index.html b/index.html new file mode 100644 index 0000000..7415d03 --- /dev/null +++ b/index.html @@ -0,0 +1,95 @@ + + + + + + + Codeforces Problem Selector + + +

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+ + + +
+ +
+
+ * to remove a handle, click on it +
+
+
+
+

+

+

+

+

+

+

+

+

+

+

+

+ + + + \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..449796a --- /dev/null +++ b/main.js @@ -0,0 +1,59 @@ +// const Swal = require('sweetalert2') +// import Swal from 'sweetalert2' + + +let btns = document.getElementsByClassName('tag-btn'); + +for (let i = 0; i < btns.length; i++) { + let element = btns[i]; + let green = "rgb(51, 172, 113)"; + let blue = "rgb(0, 188, 212)"; + + element.style.backgroundColor = blue; + + element.addEventListener("click", + () => { + if (element.style.backgroundColor == blue) { + // Include + element.style.backgroundColor = green; + } else { + // Exclude + element.style.backgroundColor = blue; + } + } + ); +} + +let rating_slider = document.getElementById('rating'); + +rating_slider.addEventListener("change", + () => { + // console.log(rating_slider.value); + }); + +function valid_handle(handle) { + if (handle == '') + return false; + + let url = `https://codeforces.com/api/user.status?handle=${handle}`; + return true; +} + +let add_handle_btn = document.getElementsByClassName('add-handle-btn')[0]; + +add_handle_btn.addEventListener('click', + () => { + let handle = document.getElementById('handles').value; + + if (valid_handle(handle)) { + + } else { + // Swal.fire({ + // title: 'Error!', + // text: 'Invalid Codefroces Handle', + // icon: 'error', + // confirmButtonText: 'Try again' + // }); + alert('Invalid Codeforces Handle'); + } + }); \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..3ec1c35 --- /dev/null +++ b/main.py @@ -0,0 +1,136 @@ +import requests +import random +import webbrowser +import argparse + +# handles to choose problems not problems not solved by any one of them +not_solved_by = ['wrong_handle'] + +# problems not solved by any handle in "not_solved_by" +problems_out_of_scope = set([]) + + +def remove_solved_problems(): + # extracting all the problems solved by all handles in "not_solved_by" + for handle in not_solved_by: + user = f'https://codeforces.com/api/user.status?handle={handle}' + submissions = requests.get(user).json() + if submissions.get('status') != 'FAILED': + for submission in submissions['result']: + if submission.get('verdict') == 'OK': + problems_out_of_scope.add( + submission.get('problem').get('name')) + + +DEFAULT_MIN_RATE = 1300 +DEFAULT_MAX_RATE = 1500 +DEFAULT_PROBLEMS_CNT = 1 + + +def get_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("-o", "--open", + help="Whether you want the problems to be opened directly in your browser or not [y/n].", + type=str, default=False) + + parser.add_argument("-c", "--count", + help="The number of problems you wanna get.", + type=int, default=DEFAULT_PROBLEMS_CNT) + + parser.add_argument("-mn", "--minRate", + help="The rate that you don't want to get any problems with rate less than it.", + type=int, default=DEFAULT_MIN_RATE) + + parser.add_argument("-mx", "--maxRate", + help="The rate that you don't want to get any problems with rate more than it.", + type=int, default=DEFAULT_MAX_RATE) + + args = parser.parse_args() + + return args + + +available_problems = [] + +url = 'https://codeforces.com/api/problemset.problems' +problemset = requests.get(url).json() + +# tags that I want to see one or more of them in all problems (and only them) +desired_tags = ['binary search', + 'bitmasks', + 'brute force', + 'combinatorics', + 'constructive algorithms', + 'data structures', + 'dfs and similar', + 'dp', + 'dsu', + 'graphs', + 'greedy', + 'implementation', + 'math', + 'number theory', + 'ternary search', + 'trees', + 'two pointers', + 'sortings' + ] + + +def valid(problem, min, max): + # if solved by any user in "not_solved_by" + if problem.get('name') in problems_out_of_scope: + return False + + rate = problem.get('rating', 0) + + # if the rate is not in the desired rage + if rate < min or rate > max: + return False + + # if it have any tag that I don't want + for tag in problem.get('tags'): + if tag not in desired_tags: + return False + return True + + +def filter_problems(min=DEFAULT_MIN_RATE, max=DEFAULT_MAX_RATE): + for problem in problemset['result']['problems']: + if valid(problem, min, max): + available_problems.append(problem) + + +def get_the_problems(problems_cnt=DEFAULT_PROBLEMS_CNT, open=False): + random.shuffle(available_problems) + + for i in range(problems_cnt): + index = str(available_problems[i].get('index')) + contestID = available_problems[i].get('contestId') + + link = f'https://codeforces.com/contest/{contestID}/problem/{index}' + + print(link) + + if open: + webbrowser.open(link) + + +if __name__ == '__main__': + remove_solved_problems() + args = get_args() + + mn = args.__dict__['minRate'] + mx = args.__dict__['maxRate'] + cnt = args.__dict__['count'] + opn = args.__dict__['open'] + + assert cnt > 0, 'Enter a positive number of problems' + assert mn >= 800 and mn <= 3500, 'Enter a min rating between 800 and 3500' + assert mx >= 800 and mx <= 3500, 'Enter a max rating between 800 and 3500' + assert mn <= mx, 'The max is less than min' + assert opn == 'y' or opn == 'n', "Enter \'y\' or \'n\'" + + filter_problems(mn, mx) + get_the_problems(cnt, True if opn == 'y' else False) diff --git a/style.css b/style.css new file mode 100644 index 0000000..27ac35d --- /dev/null +++ b/style.css @@ -0,0 +1,101 @@ +body { + display: flex; + flex-direction: column; +} + +.tags-container { + border: solid 2.5px; + border-color: lightgrey; + border-radius: 5px; + padding: 7px; +} + +.tag-btn { + background-color: rgb(0, 188, 212); + border: 0px; + padding: 8px; + color: white; + font-weight: bold; + font-size: 18px; + border-radius: 5px; + font-family: arial; + cursor: pointer; + margin: 4px; + transition: 0.5s; +} + +/*.tag-btn:hover { + background-color: rgb(215 208 54); +}*/ + +.slider-container{ + margin-top: 10px; + margin-bottom: 10px; +} + +.rating-label { + font-weight: bold; + font-family: sans-serif; + font-size: 18px; +} + +.handles-label { + font-weight: bold; + font-family: sans-serif; + font-size: 18px; +} + +.add-handle-btn { + background-color: #00ff4c; + border: 0px; + border-radius: 3px; + padding: 7px 12px; + color: white; + font-weight: bold; + font-family: sans-serif; + font-size: 16px; + cursor: pointer; + transition: 0.5s; +} + +.add-handle-btn:hover { + background-color: #16a942; +} + +#handles { + height: 25px; + border: 3px solid lightgrey; + border-radius: 6px; +} + +input { + font-size: 20px; +} + +.handles-container { + +} + +.accepted-handles { + margin-top: 10px; + border: 4px solid grey; + border-radius: 10px; + padding: 10px; +} + +.accepted-handles-container { + margin-top: 10px; + border: 4px solid grey; + border-radius: 10px; + padding: 10px; + width: 120px; + height: 15px; +} + +.problems-container { + +} + +.problem { + +}