Tomando los enlaces de YouTube de una lista de GitHub Repo Readmes -- python campo con beginner campo con python-3.x campo con url campo con git camp codereview Relacionados El problema

Taking YouTube links out of a list of GitHub repo READMEs


10
vote

problema

Español

Tengo un archivo .csv que contiene presentaciones de asignación de repositorio de estudiantes de GitHub. Hice un script para ir a cada repositorio y extraeré el video de YouTube que deben haber proporcionado en su archivo README.

La estructura del archivo CSV es la siguiente:

  Timestamp,Name,Student Number,Git Repo link   

  #!/usr/bin/python3  import csv import github3 import time import re import argparse from secrets import username, password  # API rate limit for authenticated requests is way higher than anonymous, so login. gh = github3.login(username, password=password) # gh = github3.GitHub() # Anonymous   def parse_args():     parser = argparse.ArgumentParser()     parser.add_argument("filepath", type=str, metavar="filepath", help="Filepath to the input csv file.")      args = parser.parse_args()     args = vars(args)  # Turn into dict-like view.      return args   def get_row_count(filename):     with open(filename, 'r') as file:         return sum(1 for row in csv.reader(file))   def get_repositories(link):     if gh.rate_limit()['resources']['search']['remaining'] == 0:         print("API rate exceeded, sleeping for {0} seconds.".format(gh.rate_limit()['resources']['search']['reset'] - int(time.time()+1)))         time.sleep(gh.rate_limit()['resources']['search']['reset'] - int(time.time()+1))      return gh.search_repositories(link.replace("https://github.com/", "", 1), "", 1)   def main():     filepath = parse_args()['filepath']     if not filepath.endswith('.csv'):         print("Input file must be a .csv file.")         exit()      p = re.compile(r"http(?:s?)://(?:www.)?youtu(?:be.com/watch?v=|.be/)([w-_]*)(&(amp;)?‌​[w?‌​=]*)?") # From http://stackoverflow.com/a/3726073/6549676     row_counter = 0     row_count = get_row_count(filepath)      with open(filepath, 'r') as infile, open(filepath[:3] + "_ytlinks.csv", "w") as outfile:         reader = csv.reader(infile)         next(reader, None)  # Skip header          writer = csv.writer(outfile)         writer.writerow(["Youtube Link", "Name", "GitHub Link"])  # Write header          for row in reader:             for repo in get_repositories(row[3]):                 readme = repo.repository.readme().decoded                 if not readme:                     readme = "No Youtube link found."                  if type(readme) is bytes:                     readme = readme.decode('utf-8')                  ids = p.findall(readme)                 if len(ids) != 0:                     ids = ids[0]                  ids = [x for x in ids if x]                  for _id in ids:                     writer.writerow(['https://www.youtube.com/watch?v={0}'.format(_id), row[1], row[3]])                  if len(ids) == 0:                     writer.writerow(['No Youtube Link Found', row[1], row[3]])              print('Processed row {0} out of {1}'.format(row_counter, row_count))             row_counter += 1      print("Finished.")  if __name__ == "__main__":     main()   
Original en ingles

I have a .csv file that contains student GitHub repository assignment submissions. I made a script to go to each repository and extract the YouTube video that they must have provided in their README file.

The structure of the CSV file is as follows:

Timestamp,Name,Student Number,Git Repo link 

#!/usr/bin/python3  import csv import github3 import time import re import argparse from secrets import username, password  # API rate limit for authenticated requests is way higher than anonymous, so login. gh = github3.login(username, password=password) # gh = github3.GitHub() # Anonymous   def parse_args():     parser = argparse.ArgumentParser()     parser.add_argument("filepath", type=str, metavar="filepath", help="Filepath to the input csv file.")      args = parser.parse_args()     args = vars(args)  # Turn into dict-like view.      return args   def get_row_count(filename):     with open(filename, 'r') as file:         return sum(1 for row in csv.reader(file))   def get_repositories(link):     if gh.rate_limit()['resources']['search']['remaining'] == 0:         print("API rate exceeded, sleeping for {0} seconds.".format(gh.rate_limit()['resources']['search']['reset'] - int(time.time()+1)))         time.sleep(gh.rate_limit()['resources']['search']['reset'] - int(time.time()+1))      return gh.search_repositories(link.replace("https://github.com/", "", 1), "", 1)   def main():     filepath = parse_args()['filepath']     if not filepath.endswith('.csv'):         print("Input file must be a .csv file.")         exit()      p = re.compile(r"http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?xe2x80x8cxe2x80x8b[\w\?xe2x80x8cxe2x80x8b=]*)?") # From http://stackoverflow.com/a/3726073/6549676     row_counter = 0     row_count = get_row_count(filepath)      with open(filepath, 'r') as infile, open(filepath[:3] + "_ytlinks.csv", "w") as outfile:         reader = csv.reader(infile)         next(reader, None)  # Skip header          writer = csv.writer(outfile)         writer.writerow(["Youtube Link", "Name", "GitHub Link"])  # Write header          for row in reader:             for repo in get_repositories(row[3]):                 readme = repo.repository.readme().decoded                 if not readme:                     readme = "No Youtube link found."                  if type(readme) is bytes:                     readme = readme.decode('utf-8')                  ids = p.findall(readme)                 if len(ids) != 0:                     ids = ids[0]                  ids = [x for x in ids if x]                  for _id in ids:                     writer.writerow(['https://www.youtube.com/watch?v={0}'.format(_id), row[1], row[3]])                  if len(ids) == 0:                     writer.writerow(['No Youtube Link Found', row[1], row[3]])              print('Processed row {0} out of {1}'.format(row_counter, row_count))             row_counter += 1      print("Finished.")  if __name__ == "__main__":     main() 
              

Lista de respuestas

14
 
vote
vote
La mejor respuesta
 

Aquí hay algunas preocupaciones / sugerencias:

  • Está leyendo el archivo dos veces, una vez para obtener el recuento de filas y al leer los enlaces. Y, no necesita inicializar el read_vector1 para obtener el recuento de filas, simplemente Uso read_vector2 sobre las líneas en el archivo . Probablemente necesitaría usar read_vector3 Después de obtener el recuento y antes de inicializar el lector de CSV
  • Use read_vector4 Para las variables de lanzamiento (al contar el número de líneas)
  • read_vector5 se puede simplificar como read_vector6
  • parece que no necesita read_vector7 y debe usar 99887776655443318 Método desde que está hasta una sola coincidencia
  • Si hay un solo enlace de repositorio por línea, es probable que tenga 99887776655443319 en lugar de :)(0 y evite el bucle - Recuerde , "piso es mejor que anidado"
  • en lugar de manejar la enumeración con :)(2 manualmente, use :)(3
  • en lugar de acceder a los campos de fila actuales por índice - e.g. :)(4 o :)(5 , puede desempacar la fila en el bucle para el bucle, algo así como (un ejemplo, no conozco su formato de entrada de CSV real):

      :)(6  

    O, puede usar una :)(7 : acceder a los campos por nombres de columnas en lugar de índices mejoraría la legibilidad, por ejemplo, :)(8 en lugar de :)(9

  • No tiene que convertir el def verify(test_input, expected_result): actual_result = check_balance(test_input) assert actual_result == expected_result, "{} expected {} failed".format(test_input, expected_result) print(test_input, actual_result) if __name__ == "__main__": verify(':)(', False) verify(':()', True) verify('abc(b:)cdef(:()', True) verify('a)', False) verify(')(' , False) 0 en un diccionario - devolución def verify(test_input, expected_result): actual_result = check_balance(test_input) assert actual_result == expected_result, "{} expected {} failed".format(test_input, expected_result) print(test_input, actual_result) if __name__ == "__main__": verify(':)(', False) verify(':()', True) verify('abc(b:)cdef(:()', True) verify('a)', False) verify(')(' , False) 1 y luego acceda a los argumentos usando una notación de puntos: por ejemplo. def verify(test_input, expected_result): actual_result = check_balance(test_input) assert actual_result == expected_result, "{} expected {} failed".format(test_input, expected_result) print(test_input, actual_result) if __name__ == "__main__": verify(':)(', False) verify(':()', True) verify('abc(b:)cdef(:()', True) verify('a)', False) verify(')(' , False) 2
 

Here are some concerns/suggestions:

  • you are reading the file twice - once to get the row count and when reading the links. And, you don't need to initialize the csv.reader to get the row count, simply use sum() over the lines in the file. You would probably need to use infile.seek(0) after getting the count and before initializing the csv reader
  • use _ for the throw-away variables (when counting the number of lines)
  • if len(ids) == 0: can be simplified as if not ids:
  • it looks like you don't need .findall() and should use .search() method since you are up to a single match
  • if there is a single repository link per line, you probably should have get_repository() method instead of get_repositories() and avoid the for repo in get_repositories(row[3]): loop - remember, "Flat is better than nested"
  • instead of handling the enumeration with row_counter manually, use enumerate()
  • instead of accessing the current row fields by index - e.g. row[1] or row[3], you can unpack the row in the for loop, something like (an example, I don't know your actual CSV input format):

    for index, username, _, github_link in reader: 

    Or, you can use a csv.DictReader - accessing the fields by column names instead of indexes would improve readability - e.g. row["github_link"] instead of row[3]

  • you don't have to convert the args to a dictionary - return args and then access the arguments using a dot notation - e.g. args.filepath
 
 
4
 
vote

str ya es el tipo predeterminado de cualquier variable analizada con argparse . Además, el predeterminado metavar es solo el nombre en sí.

Lo genial de argparse es que le permite usar las funciones de análisis variables personalizadas como tipo. Por lo tanto, puede poner el tipo de archivo (o en realidad solo finalizar), verifique allí:

  import os   def csv_file(filepath):     if not filepath.endswith('.csv'):         raise ValueError("Input file must be a .csv file.")     if not os.path.isfile(filepath):         raise FileNotFoundError("Could not find file {}".format(filepath)     return filepath   def parse_args():     parser = argparse.ArgumentParser()     parser.add_argument("filepath", type=csv_file, help="Filepath to the input csv file.")     return vars(parser.parse_args())   

Si se plantea una excepción en la función de tipo, Argparse lo atrapará, mostrarlo y salir del programa.

 

str is already the default type of any variable parsed with argparse. Also the default metavar is just the name itself.

The cool thing about argparse is that it allows you to use custom variable parsing functions as type. So you could put the file type (or actually only file ending) check there:

import os   def csv_file(filepath):     if not filepath.endswith('.csv'):         raise ValueError("Input file must be a .csv file.")     if not os.path.isfile(filepath):         raise FileNotFoundError("Could not find file {}".format(filepath)     return filepath   def parse_args():     parser = argparse.ArgumentParser()     parser.add_argument("filepath", type=csv_file, help="Filepath to the input csv file.")     return vars(parser.parse_args()) 

If an exception is raised in the type function, argparse will catch it, display it and exit the program.

 
 
 
 

Relacionados problema

2  Programa de Kotlin para resumir Git Comets by Rogex Match  ( Kotlin program to summarize git commits by regex match ) 
Estoy tratando de aprender Kotlin. Vengo de algunos antecedentes en Java. Como ejercicio de aprendizaje, escribí este programa simple para resumir las ocurren...

3  Restablecer archivos en GIT que se muestran como modificados pero no tienen cambios de acuerdo con "Git Diff"  ( Reset files in git which show as modified but have no changes according to git ) 
Este parece para estar funcionando, pero también es bastante lento y me parece un poco hackeado. IFS=$' ' for currentFile in $(git status | grep "modifi...

8  Bash Script para clonar todos los repositores públicos o gistas para un usuario específico, opcionalmente a un directorio de elección  ( Bash script to clone all public repos or gists for a specified user optionally ) 
Escribí este script como una forma de hacer una copia de seguridad y / o descargar rápidamente un conjunto de repos o gistas de un usuario. No tengo ninguna p...

10  Script de bash para simplificar el flujo de trabajo de implementación del GIT  ( Bash script to simplify git deployment workflow ) 
En mi empresa, sigo el siguiente flujo de trabajo. (Creo que se puede definir un tipo de 'integración continua'.) flujo de trabajo: Tenemos 3 sucursale...

1  Git AutoUpandater  ( Git autoupdater ) 
En mi oficina, tenemos una herramienta de monitoreo interno que utilizamos en los sistemas de clientes para monitorear varios vitales y notificarnos a través ...

4  Script de rubí para crear repos de git  ( Ruby script to create git repos ) 
Escribí mi primer script de rubí, por este tutorial . Crea un repo de git local y remoto. He estado trabajando durante aproximadamente 3 años con PHP, y de...

5  Base para la iteración sobre la historia del git  ( Base for iterating over git history ) 
Quería hacer algunas estadísticas sobre la historia de un repositorio de git. Al principio, intenté usar Gitpython, pero no era tan trivial como lo imaginaba....

10  Nivel faltante de profundidad técnica (ancestro común)  ( Missing level of technical depth common ancestor ) 
Recientemente le hemos dado una prueba de codificación en una compañía de TI de buena reputación. Hubo tres preguntas de codificación. me rechazaron diciend...

4  Descarga un git repo sin .git carpeta  ( Download a git repo without git folder ) 
Viniendo de Python, JavaScript y PHP, me gustaría aprender a escribir Ruby en la forma en que se "supone". El código de Python bien escrito se llama "Pythonic...

5  Herramienta para calcular el tiempo promedio que toma para una solicitud de tracción GitHub para fusionar  ( Tool to calculate the average time that takes for a github pull request to get m ) 
Estoy aprendiendo, y este es mi primer intento de una herramienta de línea de comandos que usa la API de GitHub para calcular el tiempo promedio que toma una ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos