|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "id": "9efed105", |
| 6 | + "metadata": {}, |
| 7 | + "source": [ |
| 8 | + "## `contest.standings`\n", |
| 9 | + "\n", |
| 10 | + "Devuelve la descripción del concurso y la parte solicitada del ranking (clasificación).\n", |
| 11 | + "\n", |
| 12 | + "### Parámetros\n", |
| 13 | + "\n", |
| 14 | + "| Parámetro | Descripción |\n", |
| 15 | + "|-------------------|-------------|\n", |
| 16 | + "| `contestId` *(Requerido)* | ID del concurso. No es el número de ronda. Se puede ver en la URL del concurso. Por ejemplo: `/contest/566/status`. |\n", |
| 17 | + "| `asManager` | Booleano. Si se establece como `true`, la respuesta incluirá información disponible para los organizadores del concurso. De lo contrario, solo mostrará la información accesible para los participantes. Debes ser organizador del concurso para poder usar esta opción. |\n", |
| 18 | + "| `from` | Índice (basado en 1) de la fila del ranking desde donde comenzar la lista. |\n", |
| 19 | + "| `count` | Número de filas del ranking que se desea obtener. |\n", |
| 20 | + "| `handles` | Lista de nombres de usuario (handles) separados por punto y coma. No se aceptan más de 10,000 handles. |\n", |
| 21 | + "| `room` | Si se especifica, solo se mostrarán los participantes de ese room. Si no se indica, se mostrarán todos los participantes. |\n", |
| 22 | + "| `showUnofficial` | Si se establece como `true`, se mostrarán todos los participantes (incluyendo virtuales y fuera de competencia). De lo contrario, solo se mostrarán los concursantes oficiales. |\n", |
| 23 | + "| `participantTypes` | Lista separada por comas de los tipos de participantes (sin espacios). Valores posibles: `CONTESTANT`, `PRACTICE`, `VIRTUAL`, `MANAGER`, `OUT_OF_COMPETITION`. Solo se mostrarán los participantes con los tipos especificados. |\n" |
| 24 | + ] |
| 25 | + }, |
| 26 | + { |
| 27 | + "cell_type": "markdown", |
| 28 | + "id": "b578b0b8", |
| 29 | + "metadata": {}, |
| 30 | + "source": [ |
| 31 | + "# Extracción de datos apartir de un concurso en específico\n", |
| 32 | + "\n", |
| 33 | + "Usaremos la referencia de from para iniciar desde el top 1 (from 1), count se puede excluir para extraer la totalida de la lista de participantes. Sin embargo para este proyecto solo se usará un límite de 5000 participantes por concurso. Además solo los participiantes oficales del concurso. \n", |
| 34 | + "\n", |
| 35 | + "\n", |
| 36 | + "\n" |
| 37 | + ] |
| 38 | + }, |
| 39 | + { |
| 40 | + "cell_type": "code", |
| 41 | + "execution_count": null, |
| 42 | + "id": "4cf091a5", |
| 43 | + "metadata": {}, |
| 44 | + "outputs": [], |
| 45 | + "source": [ |
| 46 | + "import requests\n", |
| 47 | + "import time\n", |
| 48 | + "import pandas as pd\n", |
| 49 | + "from datetime import datetime\n", |
| 50 | + "\n", |
| 51 | + "def get_contest_data_to_dataframe(contest_id,participants_limit):\n", |
| 52 | + " # Obtener información del concurso\n", |
| 53 | + " contest_url = f\"https://codeforces.com/api/contest.standings?contestId={contest_id}&from=1&count={participants_limit}&showUnofficial=false\"\n", |
| 54 | + " contest_response = requests.get(contest_url)\n", |
| 55 | + " contest_data = contest_response.json()\n", |
| 56 | + " \n", |
| 57 | + " if contest_data['status'] != 'OK':\n", |
| 58 | + " print(\"Error al obtener datos del concurso\")\n", |
| 59 | + " return None\n", |
| 60 | + " \n", |
| 61 | + " contest_info = contest_data['result']['contest'] # Información del concurso\n", |
| 62 | + " contest_name = contest_info['name'] #Se obtiene el nombre del concurso \n", |
| 63 | + " # Obtener la fecha de inicio del concurso\n", |
| 64 | + " contest_start_time = datetime.fromtimestamp(contest_info['startTimeSeconds']).strftime('%Y-%m-%d') # Formato de fecha\n", |
| 65 | + " \n", |
| 66 | + "\n", |
| 67 | + "\n", |
| 68 | + " # Obtener participantes y problemas\n", |
| 69 | + " problems = contest_data['result']['problems']\n", |
| 70 | + " rows = contest_data['result']['rows'] # Cada fila representa un participante\n", |
| 71 | + " num_problems = len(problems)\n", |
| 72 | + " \n", |
| 73 | + " # Obtener todos los handles (son nombres de usuario) de los participantes\n", |
| 74 | + " handles = [row['party']['members'][0]['handle'] for row in rows] # Se obtiene el nombre de usuario por una lista de comprensión qu itera en cada fia\n", |
| 75 | + " \n", |
| 76 | + " # Obtener metadatos de usuarios\n", |
| 77 | + " users_info_url = f\"https://codeforces.com/api/user.info?handles={';'.join(handles)}\"\n", |
| 78 | + " users_response = requests.get(users_info_url)\n", |
| 79 | + " users_data = users_response.json().get('result', []) if users_response.json()['status'] == 'OK' else []\n", |
| 80 | + " \n", |
| 81 | + " # Mapear metadatos\n", |
| 82 | + " user_metadata = {user['handle']: {\n", |
| 83 | + " 'country': user.get('country'),\n", |
| 84 | + " 'city': user.get('city'),\n", |
| 85 | + " 'rating': user.get('rating'),\n", |
| 86 | + " 'maxRating': user.get('maxRating')\n", |
| 87 | + " } for user in users_data}\n", |
| 88 | + "\n", |
| 89 | + " participants_data = []\n", |
| 90 | + " \n", |
| 91 | + " for row in rows:\n", |
| 92 | + " handle = row['party']['members'][0]['handle']\n", |
| 93 | + " participant_data = {\n", |
| 94 | + " \"author_handle\": handle,\n", |
| 95 | + " \"contest_name\": contest_name,\n", |
| 96 | + " \"contest_start_time\": contest_start_time,\n", |
| 97 | + " **user_metadata.get(handle, {\n", |
| 98 | + " 'country': None,\n", |
| 99 | + " 'city': None,\n", |
| 100 | + " 'rating': None,\n", |
| 101 | + " 'maxRating': None\n", |
| 102 | + " })\n", |
| 103 | + " }\n", |
| 104 | + " \n", |
| 105 | + " # Procesar problemas\n", |
| 106 | + " for i in range(num_problems):\n", |
| 107 | + " problem_index = chr(65 + i)\n", |
| 108 | + " problem_result = row['problemResults'][i]\n", |
| 109 | + " \n", |
| 110 | + " # Datos básicos\n", |
| 111 | + " participant_data.update({\n", |
| 112 | + " f\"finished_{problem_index}\": problem_result['points'] > 0,\n", |
| 113 | + " f\"rating_{problem_index}\": problems[i].get('rating')\n", |
| 114 | + " })\n", |
| 115 | + " \n", |
| 116 | + " # Inicializar campos de envío\n", |
| 117 | + " participant_data[f\"language_{problem_index}\"] = None\n", |
| 118 | + " participant_data[f\"relative_time_{problem_index}\"] = None\n", |
| 119 | + " participant_data[f\"time_to_answer_{problem_index}\"] = None\n", |
| 120 | + " \n", |
| 121 | + " # Obtener envíos\n", |
| 122 | + " status_url = f\"https://codeforces.com/api/contest.status?contestId={contest_id}&handle={handle}\"\n", |
| 123 | + " status_response = requests.get(status_url)\n", |
| 124 | + " time.sleep(1)\n", |
| 125 | + " \n", |
| 126 | + " if status_response.json()['status'] == 'OK':\n", |
| 127 | + " submissions = status_response.json()['result']\n", |
| 128 | + " prev_time = 0\n", |
| 129 | + " \n", |
| 130 | + " for submission in submissions:\n", |
| 131 | + " if submission['verdict'] == 'OK':\n", |
| 132 | + " problem_index = submission['problem']['index']\n", |
| 133 | + " participant_data[f\"language_{problem_index}\"] = submission['programmingLanguage']\n", |
| 134 | + " rt = submission['relativeTimeSeconds']\n", |
| 135 | + " participant_data[f\"relative_time_{problem_index}\"] = rt\n", |
| 136 | + " participant_data[f\"time_to_answer_{problem_index}\"] = rt - prev_time\n", |
| 137 | + " prev_time = rt\n", |
| 138 | + " \n", |
| 139 | + " participants_data.append(participant_data)\n", |
| 140 | + " \n", |
| 141 | + " # Crear DataFrame\n", |
| 142 | + " df = pd.DataFrame(participants_data)\n", |
| 143 | + " \n", |
| 144 | + " # Ordenar columnas\n", |
| 145 | + " ordenadito = ['author_handle']\n", |
| 146 | + " \n", |
| 147 | + " \n", |
| 148 | + " for prefix in ['finished', 'language', 'relative_time', 'time_to_answer', 'rating']:\n", |
| 149 | + " ordenadito.extend([f\"{prefix}_{chr(65 + i)}\" for i in range(num_problems)]) # A, B, C, ... el char 65 es A\n", |
| 150 | + " \n", |
| 151 | + " ordenadito = ordenadito +[\n", |
| 152 | + " 'contest_name', 'contest_start_time', \n", |
| 153 | + " 'country', 'city', 'rating', 'maxRating'\n", |
| 154 | + " ]\n", |
| 155 | + " return df[ordenadito] # Retornar el DataFrame con las columnas ordenadas\n", |
| 156 | + "\n", |
| 157 | + "# Uso\n", |
| 158 | + "contest_id = 566\n", |
| 159 | + "participants_limit = 10 # Limitar a los primeros 10 participantes\n", |
| 160 | + "df = get_contest_data_to_dataframe(contest_id,participants_limit)\n" |
| 161 | + ] |
| 162 | + }, |
| 163 | + { |
| 164 | + "cell_type": "code", |
| 165 | + "execution_count": null, |
| 166 | + "id": "205830ec", |
| 167 | + "metadata": {}, |
| 168 | + "outputs": [], |
| 169 | + "source": [ |
| 170 | + "#Para obtener el CSV iterar en todos los ID de concurso que quieras" |
| 171 | + ] |
| 172 | + }, |
| 173 | + { |
| 174 | + "cell_type": "code", |
| 175 | + "execution_count": null, |
| 176 | + "id": "3d7574f1", |
| 177 | + "metadata": {}, |
| 178 | + "outputs": [], |
| 179 | + "source": [ |
| 180 | + "# Paso 1 filtrar concursos por fecha y nombre de concurso\n", |
| 181 | + "\n", |
| 182 | + "url = \"https://codeforces.com/api/contest.list\"\n", |
| 183 | + "response = requests.get(url)\n", |
| 184 | + "data= response.json()\n", |
| 185 | + "data\n", |
| 186 | + "\n", |
| 187 | + "\n", |
| 188 | + "# Filtrar concursos por fecha y nombre\n", |
| 189 | + "filtrado = []\n", |
| 190 | + "if response.status_code == 200: \n", |
| 191 | + " data = response.json() \n", |
| 192 | + " if data['status'] == 'OK': \n", |
| 193 | + " for contest in data['result']['name']: \n", |
| 194 | + " # Verifica si existe 'startTimeSeconds' en el concurso\n", |
| 195 | + " if 'startTimeSeconds' in contest:\n", |
| 196 | + " start_time = datetime.fromtimestamp(contest['startTimeSeconds']) \n", |
| 197 | + " if datetime(2024, 7, 1) <= start_time <= datetime(2024, 12, 31): # Límite de fechas\n", |
| 198 | + " filtrado.append({ \n", |
| 199 | + " 'id_concurso': contest['id'], \n", |
| 200 | + " 'nombre_concurso': contest['name'], \n", |
| 201 | + " 'hora_inicio_concurso': start_time, \n", |
| 202 | + " 'duracion_(segundos)': contest['durationSeconds'], \n", |
| 203 | + " 'tipo': contest['type'], \n", |
| 204 | + " })\n", |
| 205 | + "\n", |
| 206 | + "# Convertir a DataFrame para visualizar mejor \n", |
| 207 | + "df = pd.DataFrame(filtrado)\n", |
| 208 | + "df_filtrado = df[df[\"nombre_concurso\"].str.contains(\"round|hello|good bye\", case=False, na=False)]\n", |
| 209 | + "\n", |
| 210 | + "lista_de_concursos = df_filtrado['id_concurso'].tolist() # Obtener lista de IDs de concursos filtrados\n", |
| 211 | + "#lista_de_concursos.to_csv(\"lista_de_concursos.csv\", index=False)\n", |
| 212 | + "\n", |
| 213 | + "for lista in lista_de_concursos:\n", |
| 214 | + " df = get_contest_data_to_dataframe(lista,participants_limit=10)\n", |
| 215 | + " if df is not None:\n", |
| 216 | + " df.to_csv(f\"codeforces_contest_{lista}_num_participantes_{participants_limit}.csv\", index=False)\n", |
| 217 | + "\n", |
| 218 | + "#Así obtendrás un CSV por cada concurso que cumpla con los criterios de búsqueda, con los primeros 10 participantes de cada concurso., cuyo límite se puede extender." |
| 219 | + ] |
| 220 | + } |
| 221 | + ], |
| 222 | + "metadata": { |
| 223 | + "kernelspec": { |
| 224 | + "display_name": "sns", |
| 225 | + "language": "python", |
| 226 | + "name": "python3" |
| 227 | + }, |
| 228 | + "language_info": { |
| 229 | + "codemirror_mode": { |
| 230 | + "name": "ipython", |
| 231 | + "version": 3 |
| 232 | + }, |
| 233 | + "file_extension": ".py", |
| 234 | + "mimetype": "text/x-python", |
| 235 | + "name": "python", |
| 236 | + "nbconvert_exporter": "python", |
| 237 | + "pygments_lexer": "ipython3", |
| 238 | + "version": "3.11.4" |
| 239 | + } |
| 240 | + }, |
| 241 | + "nbformat": 4, |
| 242 | + "nbformat_minor": 5 |
| 243 | +} |
0 commit comments