├── .gitignore ├── Chef.py ├── Config.py ├── Git.py ├── GitterClasses.py ├── Problem.py ├── README.md ├── Submission.py └── start.py /.gitignore: -------------------------------------------------------------------------------- 1 | */ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /Chef.py: -------------------------------------------------------------------------------- 1 | from GitterClasses import * 2 | 3 | problemlist = [] 4 | 5 | 6 | def buildProblemListAndFileStructure(user): 7 | url = 'https://www.codechef.com/users/' + user 8 | print '\nAttempting First Fetch' 9 | 10 | OK = False 11 | while OK is False: 12 | try: 13 | response = urllib2.urlopen(url) # open webpage 14 | print 'Success' 15 | OK = True 16 | except urllib2.HTTPError as e: 17 | print 'Failure.\nAn HTTP error occured : ' + str(e.code) 18 | print 'Refetching..' 19 | 20 | html = response.read() # read the response 21 | soup = BeautifulSoup(html, "html.parser") 22 | tdps = soup.findAll('p') 23 | 24 | finaltdp = [] 25 | for tdp in tdps: 26 | if len(tdp.findAll('b')) != 0 and len(tdp.findAll('span')) != 0: 27 | finaltdp.append(tdp) 28 | 29 | if not os.path.exists(Config.githubRepo): # if directory doesn't exist 30 | print 'Creating Directory: ' + Config.githubRepo 31 | os.makedirs(Config.githubRepo) 32 | 33 | for tdp in finaltdp: 34 | contestcode = tdp.find('b') 35 | contestcode = contestcode.get_text() 36 | print contestcode 37 | 38 | if not os.path.exists(Config.githubRepo + '/' + contestcode + '/'): # if directory doesn't exist 39 | # print 'Creating Directory: CodechefGitterSolutions/' + 40 | # contestcode + '/' 41 | os.makedirs( 42 | Config.githubRepo + 43 | '/' + 44 | contestcode + 45 | '/') # create directory 46 | 47 | problems = tdp.findAll('a') 48 | 49 | for problem in problems: 50 | print '--> ' + problem['href'] 51 | problemlist.append( 52 | Problem(contestcode, 53 | problem['href'], 54 | [])) # instantiate object and add to list 55 | 56 | print str(len(problemlist)) + ' problems found.\n' 57 | 58 | totalFetchCount = 0 59 | 60 | 61 | def updateSubmissionList(): # fill submission details in submissionlist of each problem 62 | global totalFetchCount 63 | print 'Updating submission details.' 64 | i = 0 65 | total = len(problemlist) 66 | for problem in problemlist: 67 | problem.updateSubmissionList() 68 | i = i + 1 69 | print str(float(i * 100) / total)[0:5] + '% done.\n' 70 | totalFetchCount = totalFetchCount + len(problem.submissionList) 71 | 72 | 73 | def fetchSubmissions(): 74 | print 'Fetching all required solutions (' + str(totalFetchCount) + ' total)' 75 | fetchedCount = 0 76 | for problem in problemlist: 77 | problem.fetchAllSubmissions() 78 | fetchedCount = fetchedCount + len(problem.submissionList) 79 | if fetchedCount is not 0: 80 | print str(float(fetchedCount * 100) / totalFetchCount)[0:5] + '% done.\n' 81 | -------------------------------------------------------------------------------- /Config.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | codechefUser = '' 3 | githubUser = '' 4 | githubPasswd = '' 5 | githubRepo = '' 6 | 7 | pass 8 | -------------------------------------------------------------------------------- /Git.py: -------------------------------------------------------------------------------- 1 | from GitterClasses import * 2 | 3 | 4 | class Git(object): 5 | 6 | remoteUrl = '' 7 | 8 | def __init__(self, username, password, repo): 9 | self.username = username 10 | self.password = password 11 | self.repo = repo 12 | self.remoteUrl = 'https://www.github.com/' + username + '/' + repo 13 | 14 | def clone(self): 15 | if os.path.exists(self.repo): 16 | sys.exit( 17 | 'fatal: destination path \'' + 18 | self.repo + 19 | '\' already exists and is not an empty directory.') 20 | os.system('git clone ' + self.remoteUrl) 21 | 22 | def add(self): 23 | os.chdir(self.repo) 24 | os.system('git add .') 25 | 26 | def commit(self, msg): 27 | os.system('git commit -m \'' + msg + '\'') 28 | 29 | def push(self): 30 | pass 31 | os.system( 32 | 'git push https://' + 33 | self.username + 34 | ':' + 35 | self.password + 36 | '@github.com/' + 37 | self.username + 38 | '/' + 39 | self.repo) 40 | 41 | 42 | # REPO_NAME = 'test' 43 | # REMOTE_URL = 'https://github.com/aedorado/' + REPO_NAME + '.git' 44 | # FILE_NAME = 'new_file.txt' 45 | # MSG = 'A new commit' 46 | 47 | # if os.path.exists(REPO_NAME): 48 | # shutil.rmtree(REPO_NAME) 49 | 50 | # os.system('git clone ' + REMOTE_URL) 51 | # os.chdir(REPO_NAME) 52 | # os.system('pwd') 53 | 54 | # open(FILE_NAME, 'wb').close() 55 | 56 | # os.system('git add .') 57 | # os.system('git commit -m \'' + MSG + '\'') 58 | # os.system('git push https://aedorado:****@github.com/aedorado/test.git') 59 | # git push 60 | # https://:@github.com// 61 | 62 | # print "---- DONE ----" 63 | -------------------------------------------------------------------------------- /GitterClasses.py: -------------------------------------------------------------------------------- 1 | import urllib2 # to interact with urls 2 | import os # to interact with os 3 | import re # to use regular expressions 4 | import sys 5 | import shutil 6 | import HTMLParser 7 | import glob # to read directories 8 | import getpass # to safely get password 9 | from bs4 import BeautifulSoup # to parse scraped webpage 10 | 11 | from Config import * 12 | from Submission import * 13 | from Problem import * 14 | from Git import * 15 | import Chef 16 | -------------------------------------------------------------------------------- /Problem.py: -------------------------------------------------------------------------------- 1 | from GitterClasses import * 2 | 3 | # problem class 4 | 5 | 6 | class Problem: 7 | 8 | def __init__(self, contestcode, statuslink, submissionList): 9 | self.contestcode = contestcode 10 | self.statuslink = statuslink 11 | self.submissionList = [] 12 | self.pcode = self.getPcodeFromStatusLink(statuslink) 13 | 14 | def getPcodeFromStatusLink(self, statuslink): 15 | pattern = '[A-Z0-9]*,' 16 | pcode = re.search(pattern, statuslink) 17 | pcode = pcode.group(0) 18 | pcode = pcode[:-1] # remove , form 'KSPHERES,' 19 | return pcode 20 | 21 | def updateSubmissionList(self): 22 | 23 | print 'Updating submittion list for ' + self.pcode 24 | url = 'https://www.codechef.com' + self.statuslink 25 | print url 26 | 27 | OK = False 28 | while OK is False: 29 | try: 30 | response = urllib2.urlopen(url) # open webpage 31 | print 'Success' 32 | OK = True 33 | except urllib2.HTTPError as e: 34 | print 'Failure.\nAn HTTP error occured : ' + str(e.code) 35 | print 'Refetching..' 36 | 37 | html = response.read() 38 | soup = BeautifulSoup(html, 'html.parser') 39 | rows = soup.findAll('tr', {'class': 'kol'}) 40 | print str(len(rows)) + ' submittions found.' 41 | accepted_sols = 0 42 | 43 | for row in rows: 44 | tds = row.findAll('td') 45 | 46 | verdict = tds[3].find('span')['title'] 47 | if verdict != 'accepted' and verdict != '': 48 | # print '\n~ Continued ~\n' 49 | continue 50 | elif verdict == '': 51 | points = tds[3].find('span').get_text() 52 | if points.find('100') == -1: 53 | continue 54 | verdict = 'accepted' 55 | 56 | accepted_sols = accepted_sols + 1 57 | 58 | sid = tds[0].get_text() 59 | 60 | path_list = glob.glob( 61 | Config.githubRepo + 62 | '/' + 63 | self.contestcode + 64 | '/*' + 65 | self.pcode + 66 | '*') 67 | 68 | cont = False 69 | for path in path_list: 70 | if path.find(sid) != -1: 71 | print str(sid) + ' already available.' 72 | cont = True 73 | break 74 | 75 | if cont: 76 | continue 77 | 78 | lang = tds[6].get_text() 79 | link = 'https://www.codechef.com' + \ 80 | tds[7].find('a')['href'].replace( 81 | 'viewsolution', 'viewplaintext') 82 | 83 | self.submissionList.append( 84 | Submission(sid, 85 | verdict, 86 | link, 87 | lang, 88 | self.contestcode, 89 | self.pcode)) 90 | 91 | print str(accepted_sols) + ' accepted submissions.' 92 | print str(len(self.submissionList)) + ' need to be updated.' 93 | 94 | def fetchAllSubmissions(self): # fetch all submissions of a given problem 95 | i = 0 96 | for submission in self.submissionList: 97 | submission.fetchAndSave( 98 | i) # fetch each submissions of a given problem 99 | i = i + 1 100 | pass 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #CodeChef-Gitter 2 | 3 | A tool to save and update all solutions submitted on CodeChef to GitHub. 4 | 5 | ####To save all submissions locally : 6 | ```bash 7 | $ python test.py fetch codechef-username 8 | ``` 9 | ####To save all submissions locally and push to GitHub : 10 | ```bash 11 | $ python test.py fetch codechef-username git-push github-username/repo-name 12 | ``` -------------------------------------------------------------------------------- /Submission.py: -------------------------------------------------------------------------------- 1 | from GitterClasses import * 2 | 3 | # submission class 4 | 5 | 6 | class Submission: 7 | 8 | def __init__(self, sid, verdict, link, lang, contestcode, pcode): 9 | self.sid = sid 10 | self.verdict = verdict 11 | self.link = link 12 | self.lang = lang 13 | self.contestcode = contestcode 14 | self.pcode = pcode 15 | 16 | def fetchAndSave(self, i): 17 | print 'Fetching ' + self.contestcode + '/' + self.pcode + '_' + str(i) + ' in ' + self.lang 18 | OK = False 19 | while OK is False: 20 | try: 21 | response = urllib2.urlopen(self.link) # open webpage 22 | print 'Success' 23 | OK = True 24 | except urllib2.HTTPError as e: 25 | print 'Failure.\nAn HTTP error occured : ' + str(e.code) 26 | print 'Refetching..' 27 | 28 | html = response.read() 29 | if i != 0: 30 | opfile = open( 31 | Config.githubRepo + '/' + self.contestcode + '/' + self.pcode + '_' + str( 32 | i) + '_' + str( 33 | self.sid) + self.getExtension( 34 | self.lang), 35 | 'w') 36 | else: 37 | opfile = open( 38 | Config.githubRepo + '/' + self.contestcode + '/' + self.pcode + '_' + str( 39 | i) + '_' + str( 40 | self.sid) + self.getExtension( 41 | self.lang), 42 | 'w') 43 | opfile.write(HTMLParser.HTMLParser().unescape(html)[5:-6]) 44 | pass 45 | 46 | 47 | def getExtension(self, lang): 48 | if lang.find('JAVA') >= 0: 49 | return '.java' 50 | if lang.find('PYTH') >= 0: 51 | return '.py' 52 | if lang.find('C++') >= 0: 53 | return '.cpp' 54 | if lang.find('C') >= 0: 55 | return '.c' 56 | -------------------------------------------------------------------------------- /start.py: -------------------------------------------------------------------------------- 1 | from GitterClasses import * 2 | 3 | if len(sys.argv) == 5 and sys.argv[1] == 'fetch' and sys.argv[3] == 'git-push': 4 | print 'Fetch from Codechef and push to github.' 5 | Config.githubUser = (sys.argv[4])[:(sys.argv[4]).find('/')] 6 | Config.githubRepo = (sys.argv[4])[(sys.argv[4]).find('/') + 1:] 7 | pass1 = getpass.getpass('Github Password : ') 8 | pass2 = getpass.getpass('Retype Password : ') 9 | if (pass1 != pass2): 10 | sys.exit('Passwords dont match!') 11 | else: 12 | Config.githubPasswd = pass1 13 | git = Git(Config.githubUser, Config.githubPasswd, Config.githubRepo) 14 | git.clone() 15 | elif len(sys.argv) == 3 and sys.argv[1] == 'fetch': 16 | print 'Fetch and save from Codechef' 17 | Config.codechefUser = sys.argv[2] 18 | Config.githubRepo = sys.argv[2] # to set name of local directory as codechefUsername 19 | else: 20 | sys.exit( 21 | 'Kindly follow correct format for input.\n\tpython start.py fetch \nOR\n\tpython start.py fetch git-push /') 22 | 23 | Chef.buildProblemListAndFileStructure(Config.codechefUser) 24 | Chef.updateSubmissionList() 25 | Chef.fetchSubmissions() 26 | 27 | if len(sys.argv) == 3: 28 | sys.exit('Done.') 29 | 30 | git.add() 31 | git.commit('Another Auto Commit!') 32 | git.push() 33 | 34 | sys.exit( 35 | '\nTask Completed\n\nFORK from : https://github.com/aedorado/CodeChef-Gitter\n') 36 | --------------------------------------------------------------------------------