├── .env.example ├── .gitignore ├── Problems_Links.txt ├── README.md ├── Sheet_Generator.py └── requirements.txt /.env.example: -------------------------------------------------------------------------------- 1 | CODEFORCES_HANDLE= 2 | CODEFORCES_PASSWORD= 3 | SHEET_NAME= 4 | SHEET_DURATION= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /Problems_Links.txt: -------------------------------------------------------------------------------- 1 | https://codeforces.com/problemset/problem/1421/A 2 | https://codeforces.com/contest/1594/problem/B 3 | https://codeforces.com/contest/1527/problem/A 4 | https://codeforces.com/contest/467/problem/B 5 | https://codeforces.com/gym/101810/problem/C 6 | https://codeforces.com/contest/1097/problem/B 7 | https://codeforces.com/contest/1567/problem/B 8 | https://codeforces.com/contest/1359/problem/A 9 | https://codeforces.com/contest/766/problem/B 10 | https://codeforces.com/problemset/problem/1593/B 11 | https://codeforces.com/problemset/problem/1579/A 12 | https://codeforces.com/problemset/problem/1501/B 13 | https://codeforces.com/contest/975/problem/B 14 | https://codeforces.com/contest/1420/problem/B 15 | https://codeforces.com/problemset/problem/1635/A 16 | https://codeforces.com/contest/476/problem/B 17 | https://codeforces.com/contest/1632/problem/B 18 | https://codeforces.com/contest/1625/problem/A 19 | https://codeforces.com/problemset/problem/1559/A 20 | https://codeforces.com/contest/1657/problem/C 21 | https://codeforces.com/problemset/problem/579/A 22 | https://codeforces.com/problemset/problem/202/A 23 | https://codeforces.com/problemset/problem/1362/B 24 | https://codeforces.com/contest/743/problem/B 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Codeforces-Sheet-Generator 2 | 3 | It's a simple script to generate a mushup on code forces, the script will accept the public problem urls only or polygon problems. 4 | 5 | ## Requirements 6 | 7 | - Copy the `.env.example` file and rename it to `.env` and provide the required environment variables. 8 | - CODEFORCES_HANDLE 9 | - CODEFORCES_PASSWORD 10 | - SHEET_NAME 11 | - SHEET_DURATION 12 | - Copy the urls of the problems to `Problems_Links.txt` 13 | 14 | ```bash 15 | # install requirements 16 | pip3 install -r requirements.txt 17 | ``` 18 | 19 | ## Running the script 20 | 21 | ```bash 22 | python3 Sheet_Generator.py 23 | ``` 24 | -------------------------------------------------------------------------------- /Sheet_Generator.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | from dotenv import load_dotenv 4 | from selenium import webdriver 5 | from selenium.webdriver.common.by import By 6 | from selenium.webdriver.support import expected_conditions as EC 7 | from selenium.webdriver.support.ui import WebDriverWait 8 | 9 | load_dotenv() 10 | 11 | # Colors to use 12 | red = '\033[38;5;196m' 13 | green = '\033[38;5;40m' 14 | blue = '\033[34m' 15 | 16 | 17 | # To make the problem in the valid format for being added to codeforces 18 | def scrapper(problem): 19 | if 'polygon' in problem: 20 | return problem 21 | parts = problem.split('/') 22 | if 'group' in parts or 'task' in parts or 'leetcode.com' in parts or not problem: 23 | return 'Invalid Problem' 24 | if 'problemset' in parts: 25 | idx = parts.index('problem') 26 | return parts[idx + 1] + '-' + parts[idx + 2] 27 | if 'contest' or 'gym' in parts: 28 | idx = parts.index('problem') 29 | return parts[idx - 1] + '-' + parts[idx + 1] 30 | 31 | 32 | # Get links of the problem from the file 33 | def get_problem_links(file): 34 | f = open(file, "r") 35 | problems = f.read().strip().split('\n') 36 | return problems 37 | 38 | 39 | # Create chrome driver 40 | def create_driver(): 41 | chrome_options = webdriver.ChromeOptions() 42 | chrome_options.add_argument("--headless") 43 | chrome_options.add_argument("--disable-dev-shm-usage") 44 | chrome_options.add_argument("--no-sandbox") 45 | chrome_options.add_argument("--disable-notifications") 46 | chrome_options.add_argument("--disable-popup-blocking") 47 | driver = webdriver.Chrome(options=chrome_options) 48 | return driver 49 | 50 | 51 | # make timeout 30 seconds for command find element 52 | def find_element(driver, by, value, timeout=30): 53 | return WebDriverWait(driver, timeout).until(EC.visibility_of_element_located((by, value)), f'{red}Timeout while trying to reach an element') 54 | 55 | 56 | def main(): 57 | 58 | # get environment variables we need 59 | email = os.getenv('CODEFORCES_HANDLE') 60 | password = os.getenv('CODEFORCES_PASSWORD') 61 | sheet_name = os.getenv('SHEET_NAME') 62 | sheet_duration = os.getenv('SHEET_DURATION') 63 | 64 | #check if there missing environment variables 65 | if not sheet_name or not email or not password: 66 | print(f'{red}\nMissing environment variables') 67 | return 68 | 69 | # get the problems in the valid format 70 | problems = [scrapper(problem) for problem in get_problem_links('Problems_Links.txt')] 71 | 72 | #check if there is invalid problems 73 | if 'Invalid Problem' in problems: 74 | print(f'{red}\nInvalid Problem in Problems...') 75 | return 76 | 77 | #check if there is problem to add or not 78 | if not problems: 79 | print(f'\n{red}Invalid in parsing problems or the Problems_Links.txt is empty\n') 80 | return 81 | 82 | # check if there is more than 26 problems 83 | if len(problems) > 26: 84 | print(f'{red}\nCannot create sheet on codeforces with more than 26 problems') 85 | return 86 | 87 | driver = create_driver() 88 | 89 | # sign in codeforces 90 | driver.get("https://codeforces.com/enter") 91 | print(f'{blue}\nlogging in...\n') 92 | find_element(driver, By.ID, 'handleOrEmail').send_keys(email) 93 | find_element(driver, By.ID, 'password').send_keys(password) 94 | find_element(driver, By.CLASS_NAME, 'submit').click() 95 | time.sleep(3) 96 | 97 | # check if the driver can logging in successfuly 98 | if 'Login' in driver.title: 99 | print(f'{red}\nFailed to logginng in to codeforces') 100 | return 101 | else: 102 | print(f'{green}\nlogging in successfuly ✅\n') 103 | 104 | # go to new mushup page 105 | driver.get('https://codeforces.com/mashup/new') 106 | print(f'{blue}\nCreating the sheet...\n') 107 | time.sleep(3) 108 | find_element(driver, By.CSS_SELECTOR, '#contestName').send_keys(sheet_name) 109 | find_element(driver, By.CSS_SELECTOR, '#contestDuration').send_keys(sheet_duration) 110 | 111 | # add the problems to the gym 112 | for problem in problems: 113 | find_element(driver, By.CSS_SELECTOR, '._MashupContestEditFrame_addProblem > label:nth-child(2) > input:nth-child(1)').send_keys(problem) 114 | find_element(driver, By.CSS_SELECTOR, '._MashupContestEditFrame_addProblemLink > img:nth-child(1)').click() 115 | time.sleep(1) 116 | 117 | find_element(driver, By.CSS_SELECTOR, 'html body div#body div div#pageContent.content-with-sidebar div._MashupContestEditFrame_frame form._MashupContestEditFrame_saveMashup.table-form input.submit').click() 118 | print(f'{green}\nSheet has been added successfully ✅\n') 119 | time.sleep(3) 120 | driver.close() 121 | 122 | 123 | if __name__ == "__main__": 124 | main() -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | selenium 2 | python-dotenv 3 | chromedriver-binary 4 | --------------------------------------------------------------------------------