├── requirement.txt ├── Dockerfile ├── README.md └── find_correction.py /requirement.txt: -------------------------------------------------------------------------------- 1 | certifi==2019.3.9 2 | chardet==3.0.4 3 | idna==2.8 4 | progress==1.5 5 | requests==2.22.0 6 | simplejson==3.16.0 7 | tabulate==0.8.3 8 | urllib3==1.25.3 9 | nltk==3.4.5 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY requirement.txt . 6 | 7 | RUN pip install -r requirement.txt 8 | 9 | COPY find_correction.py . 10 | 11 | ENTRYPOINT ["python", "find_correction.py"] 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Update 2 | You can directly go to [42evaluators.com](https://42evaluators.com/) for find peer :D 3 | 4 | # Find A Correction at 42 ! 5 | 6 | This script list all people who are log and validated / register a project. 7 | 8 | ## Example 9 | 10 | ``` 11 | (venv) $> python find_correction.py "Malloc" 12 | Login Project status Validated Final Mark Position 13 | -------- ---------------------- ----------- ------------ ---------- 14 | rfautier in_progress None None e3r5p11 15 | aaklpote in_progress None None e3r10p5 16 | ccaballero in_progress None None e2r4p4 17 | llompal finished True 123 e3r5p19 18 | sgueko waiting_for_correction None None e3r13p9 19 | 20 | (venv) $> 21 | ``` 22 | 23 | ## Prerequisites 24 | 25 | You need to have python3 (``` brew install python3 ```) 26 | 27 | You need to have a token (UID and Secret). 28 | 29 | To do this, go to https://profile.intra.42.fr/oauth/applications/new and create an application (You can enter Fake-Informations) 30 | 31 | Then export to your environment your UID and SECRET 32 | 33 | ``` 34 | export FT42_UID="yourUIDHere" 35 | ``` 36 | 37 | ``` 38 | export FT42_SECRET="yourSecretHere" 39 | ``` 40 | 41 | Or you can enter this environment variable in your RC (~/.zshrc) 42 | 43 | Clone the project 44 | 45 | ``` 46 | git clone https://github.com/rfautier/find_correction.git && cd find_correction 47 | ``` 48 | 49 | Set the python3 environment and requirement 50 | 51 | ``` 52 | virtualenv -p python3 venv && source venv/bin/activate && pip3 install -r requirement.txt 53 | ``` 54 | 55 | ## Quickstart 56 | 57 | ``` 58 | python find_correction.py your_project 59 | ``` 60 | or, with Docker 61 | ``` 62 | docker build -t find_correction . 63 | docker run -e FT42_UID=$FT42_UID -e FT42_SECRET=$FT42_SECRET -v $PWD:/app find_correction your_project 64 | ``` 65 | 66 | 67 | The first time, It's gonna be little longer depending on the projet. 68 | Your gonna download all users who make the project. So next time, it's much,much faster ! 69 | The download is in your current directory, like an hidden files (`ls -a` to see them). 70 | 71 | ## Parameters 72 | 73 | ``` --campus ``` CAMPUS You can specify a campus. Paris is the default. 74 | 75 | ``` --update ``` To re-download all users register to an project 76 | -------------------------------------------------------------------------------- /find_correction.py: -------------------------------------------------------------------------------- 1 | 2 | import os, sys, requests 3 | import time 4 | import simplejson as json 5 | import argparse 6 | import pickle 7 | import nltk 8 | from operator import itemgetter 9 | from progress.bar import ChargingBar 10 | from tabulate import tabulate 11 | 12 | def connection_api(): 13 | args = [ 14 | 'grant_type=client_credentials', 15 | 'client_id=' + os.environ["FT42_UID"], 16 | 'client_secret=' + os.environ["FT42_SECRET"], 17 | ] 18 | status = requests.post("https://api.intra.42.fr/oauth/token?%s" % ("&".join(args))) 19 | token = status.json() 20 | 21 | if not status.status_code == 200: 22 | print("You are not connecting to the 42 API please check README.md") 23 | sys.exit() 24 | return status.json() 25 | 26 | def clear(): 27 | sys.stdout.write("\033[F") 28 | sys.stdout.write("\033[K") 29 | 30 | def get_all_projects(token): 31 | page = 0 32 | lst_projects = [] 33 | while (1): 34 | args = [ 35 | 'access_token=%s' % (token['access_token']), 36 | 'token_type=bearer', 37 | 'page[size]=100', 38 | 'page[number]={}'.format(str(page)), 39 | ] 40 | status = requests.get("https://api.intra.42.fr/v2/cursus/21/projects?%s" % ("&".join(args))) 41 | if not status.status_code == 200: 42 | print("Error during projects search.") 43 | sys.exit() 44 | response = status.json() 45 | if not response: 46 | break 47 | lst_projects += [x['name'] for x in response] 48 | page += 1 49 | return lst_projects 50 | 51 | def get_id_project(name_project, token): 52 | print("Get id of the project") 53 | clear() 54 | args = [ 55 | 'access_token=%s' % (token['access_token']), 56 | 'token_type=bearer', 57 | 'filter[name]=' + str(name_project), 58 | ] 59 | status = requests.get("https://api.intra.42.fr/v2/projects?%s" % ("&".join(args))) 60 | if not status.status_code == 200: 61 | print("Error during project search.") 62 | sys.exit() 63 | response = status.json() 64 | try: 65 | return response[0]['id'] 66 | except: 67 | lst_projects = get_all_projects(token) 68 | print("Project {} not find. Please check your spelling.".format(name_project)) 69 | if lst_projects: 70 | lst_project_sorted = sorted([[nltk.edit_distance(x, name_project), x] for x in lst_projects], key=itemgetter(0)) 71 | print("(Suggestion for 42cursus) The most similar projects are : \n") 72 | #print(lst_project_sorted) 73 | for prj in lst_project_sorted[:5]: 74 | print(prj[1]) 75 | sys.exit() 76 | 77 | def get_user_who_make_the_project(id, token, argument): 78 | page = 0 79 | all_user = get_buffer_file(argument) 80 | if not all_user or argument.update: 81 | print("Get all users who register the project {} (Can take some time the first time...)".format(argument.name_project)) 82 | bar = None 83 | while (1): 84 | args = [ 85 | 'access_token=%s' % (token['access_token']), 86 | 'token_type=bearer', 87 | 'page[size]=100', 88 | 'page[number]={}'.format(str(page)), 89 | ] 90 | status = requests.get("https://api.intra.42.fr/v2/projects/" + str(id) + "/projects_users?%s" % ("&".join(args))) 91 | if not status.status_code == 200: 92 | print("Error during projects users search.") 93 | sys.exit() 94 | if not bar: 95 | bar = ChargingBar('Call API 42', max=(int(status.headers['X-Total']) // 100) + 2) 96 | response = status.json() 97 | if not response: 98 | break 99 | for projet in response: 100 | for team in projet['teams']: 101 | for user in team['users']: 102 | all_user[user['login']] = [projet['status'], projet['validated?'], projet['final_mark']] 103 | page += 1 104 | bar.next() 105 | time.sleep(1) 106 | bar.finish() 107 | clear() 108 | clear() 109 | create_buffer_file(argument, all_user) 110 | return all_user 111 | 112 | def create_buffer_file(args, people_we_want): 113 | try: 114 | with open(".{}.txt".format(args.name_project.replace(" ", "_")), "wb") as fp: #Pickling 115 | pickle.dump(people_we_want, fp) 116 | except: 117 | print("Error") 118 | exit(0) 119 | 120 | def get_buffer_file(args): 121 | b = {} 122 | try : 123 | with open(".{}.txt".format(args.name_project.replace(" ", "_")), "rb") as fp: # Unpickling 124 | b = pickle.load(fp) 125 | except: 126 | pass 127 | return b 128 | 129 | 130 | def get_id_campus(arguments): 131 | print("Get id of the campus") 132 | clear() 133 | args = [ 134 | 'access_token=%s' % (token['access_token']), 135 | 'token_type=bearer', 136 | 'filter[name]=' + str(arguments.campus), 137 | ] 138 | status = requests.get("https://api.intra.42.fr/v2/campus?%s" % ("&".join(args))) 139 | if not status.status_code == 200: 140 | print("Error during campus search.") 141 | sys.exit() 142 | response = status.json() 143 | time.sleep(1) 144 | try: 145 | return response[0]['id'] 146 | except: 147 | print("Campus {} not find. Please check your spelling.".format(arguments.campus)) 148 | sys.exit() 149 | 150 | 151 | def get_all_people_connected(token, args): 152 | all_user = [] 153 | page = 0 154 | if args.campus == "Paris": 155 | id_campus = 1 156 | else: 157 | id_campus = get_id_campus(args) 158 | print("Get all peaple who is actually connected in {}".format(args.campus)) 159 | clear() 160 | bar = None 161 | while (1): 162 | args = [ 163 | 'access_token=%s' % (token['access_token']), 164 | 'token_type=bearer', 165 | 'page[size]=100', 166 | 'page[number]={}'.format(str(page)), 167 | 'filter[active]=true', 168 | ] 169 | status = requests.get("https://api.intra.42.fr/v2/campus/" + str(id_campus) + "/locations?%s" % ("&".join(args))) 170 | if not status.status_code == 200: 171 | print("Error during people connected search.") 172 | sys.exit() 173 | if not bar: 174 | bar = ChargingBar('Call API 42', max=(int(status.headers['X-Total']) // 100) + 2) 175 | response = status.json() 176 | if not response: 177 | break 178 | for poste in response: 179 | all_user.append([poste['user']['login'], poste['host']]) 180 | page += 1 181 | bar.next() 182 | time.sleep(1) 183 | bar.finish() 184 | clear() 185 | return all_user 186 | 187 | if __name__ == "__main__": 188 | parser = argparse.ArgumentParser() 189 | parser.add_argument("name_project", help="Name of the project your search for correction", type=str) 190 | parser.add_argument("--campus", help="Name of your Campus. Default is Paris", type=str, default="Paris") 191 | parser.add_argument("--update", help="Update user who validate the current project.", action="store_true") 192 | args = parser.parse_args() 193 | 194 | if not os.environ["FT42_UID"] or not os.environ["FT42_SECRET"]: 195 | print("You need to export environnement variable FT42_UID and FT42_SECRET. See README.md .") 196 | sys.exit() 197 | 198 | token = connection_api() 199 | id_project = get_id_project(args.name_project, token) 200 | 201 | people_we_want = get_user_who_make_the_project(id_project, token, args) 202 | 203 | people_here = get_all_people_connected(token, args) 204 | 205 | possible_corrector = [] 206 | 207 | for here in people_here: 208 | if here[0] in people_we_want: 209 | possible_corrector.append([here[0], str(people_we_want[here[0]][0]), str(people_we_want[here[0]][1]), str(people_we_want[here[0]][2]), here[1]]) 210 | 211 | possible_corrector_no_duplicate_value = [] # Remove duplicate value 212 | for corrector in possible_corrector: 213 | if not corrector[0] in [x[0] for x in possible_corrector_no_duplicate_value]: 214 | possible_corrector_no_duplicate_value.append(corrector) 215 | possible_corrector = possible_corrector_no_duplicate_value 216 | 217 | if not possible_corrector: 218 | print("No corrector found for this project") 219 | else: 220 | print(tabulate(possible_corrector, headers=['Login', 'Project status', 'Validated', 'Final Mark', 'Position'])) 221 | --------------------------------------------------------------------------------