Escrito por Mark Tse, Desarrollador de Software, Desarrollo de Plataformas D2L
A partir de la versión 10.7.5, los administradores pueden programar extracciones diferenciales además de extracciones completas, lo que permite informes de mayor frecuencia. Estos extractos diferenciales se generan en un intervalo establecido y contienen datos recién disponibles desde que se generaron los extractos diferenciales anteriores.
En este artículo, nos basaremos en el ejemplo de código en Conjuntos de datos de Brightspace: ejemplo de cliente sin encabezado (no interactivo) para mostrar cómo aprovechar las API de la aplicación para mantener una base de datos actualizada con extracciones diferenciales. El código fuente de este artículo está disponible en GitHub (en inglés).
Diferencias de API
Para dar cabida a las extracciones diferenciales, el ejemplo de código se ha modificado para aprovechar /d2l/api/lp/(versión)/dataExport/bds, que se introdujo en la versión 10.7.5. Esta ruta devuelve una lista de todas las extracciones de conjuntos de datos disponibles (completos y diferenciales) y los metadatos correspondientes, incluidos los siguientes: Enlace de descarga
, que apunta a la ubicación de un conjunto de datos determinado.
Resumen de cambios
Para demostrar cómo procesar extracciones completas y diferenciales, el ejemplo de código se aumentó para admitir el conjunto de datos Inscripciones de usuarios. Los archivos de esquema se encuentran en la carpeta de esquema.
El mayor cambio en el ejemplo de código es determinar qué conjunto de datos obtener y cómo obtener el conjunto de datos. El código que realiza una actualización (inserción o actualización) en la base de datos es reutilizable y permanece sin cambios.
Datos estáticos
Agregamos el extracto completo de Inscripciones de usuario a la lista de conjuntos de datos completos existente y una nueva lista para almacenar los extractos diferenciales de interés.
DataSetMetadata = collections.namedtuple('DataSetMetadata', ['plugin', 'table'])... FULL_DATA_SET_METADATA = [ ... DataSetMetadata( plugin = '533f84c8-b2ad-4688-94dc-c839952e9c4f', table = 'user_enrollments' )]DIFF_DATA_SET_METADATA = [ DataSetMetadata( plugin = 'a78735f2-7210-4a57-aac1-e0f6bd714349', table = 'user_enrollments' )]
Métodos auxiliares
Debido a que realizaremos llamadas autenticadas a Brightspace varias veces, se agregó la siguiente función auxiliar:
def get_with_auth(endpoint, access_token): headers = {'Authorization': 'Bearer {}'.format(token_response['access_token'])} response = requests.get(endpoint, headers=headers) if response.status_code != 200: logger.error('Código de estado: %s; content: %s', response.status_code, response.text) response.raise_for_status() return response
El código para extraer el archivo zip y actualizar la base de datos se colocó en su propio método auxiliar para facilitar la lectura:
def unzip_and_update_db(response_content, db_conn_params, Tabla): con IO. BytesIO(response_content) como response_stream: con zipfile. ZipFile(response_stream) como zipped_data_set: files = zipped_data_set.namelist() assert len(files) == 1 csv_name = files[0] con zipped_data_set.open(csv_name) como csv_data: update_db(db_conn_params, table, csv_data)
Descarga de los conjuntos de datos
En primer lugar, añadimos una opción para alternar entre la descarga de extractos completos o diferenciales:
analizador = argparse. ArgumentParser(description='Script para descargar conjuntos de datos.')parser.add_argument( '--differential', action='store_true', help='Usar conjuntos de datos diferenciales en lugar de conjuntos de datos completos')args = parser.parse_args()
A continuación, añadimos un método para obtener un mapeo de todos los conjuntos de datos y sus ubicaciones utilizando la ruta del conjunto de datos de la lista como se mencionó anteriormente:
def get_plugin_link_mapping(config, access_token): data_sets = [] next_page_url = '{bspace_url}/d2l/api/lp/{lp_version}/dataExport/bds'.format( bspace_url=config['bspace_url'], lp_version=API_VERSION ) mientras que next_page_url no es None: list_response = get_with_auth(next_page_url, access_token) list_json = list_response.json() data_sets += list_json['BrightspaceDataSets'] next_page_url = list_json['NextPageUrl'] return { d['PluginId']: d['DownloadLink'] for d in data_sets }... plugin_to_link = get_plugin_link_mapping(config, token_response['access_token'])
Basándonos en el argumento de la línea de comandos, agregamos código para obtener los enlaces de descarga de todos los extractos completos o diferenciales que nos interesan:
# args.differential es verdadero si '--differential' se proporciona como un argumentdata_set_metadata = DIFF_DATA_SET_METADATA if args.differential else FULL_DATA_SET_METADATAplugin_to_link = get_plugin_link_mapping(config, token_response['access_token'])db_conn_params = { 'host': config['dbhost'], 'dbname': config['dbname'], 'user': config['dbuser'], 'password': config['dbpassword']}for plugin, tabla en data_set_metadata: response = get_with_auth( endpoint=plugin_to_link[plugin], access_token=token_response['access_token'] ) unzip_and_update_db(response.content, db_conn_params, table)
El código de actualización de la base de datos sin cambios realizará correctamente un upsert (insertar o actualizar), independientemente de la fuente de datos (extracción completa o diferencial).
Planificación
Este ejemplo de código no incluye la funcionalidad para recuperarse de una extracción diferencial perdida. Dado que las extracciones diferenciales solo contienen datos a partir de la extracción diferencial anterior, una extracción diferencial omitida significaría una brecha de datos en la base de datos de destino, incluso si la siguiente extracción diferencial se procesó correctamente. Por este motivo, este ejemplo de código debe programarse con la misma cadencia que el Diferenciales de conjuntos de datos de Brightspace
programada para evitar que se pierda un extracto diferencial.
La recuperación de extracciones diferenciales perdidas está actualmente fuera del ámbito del ejemplo de código. Sin embargo, una mitigación sencilla sería dirigirse a la misma base de datos con extracciones completas y diferenciales, lo que garantizará que las brechas de datos de las extracciones diferenciales perdidas se llenen cuando se procese la siguiente extracción diaria. Como alternativa, el ejemplo de código podría ampliarse para examinar todos los extractos diferenciales disponibles en el archivo AnteriorDataSets
en cada ejecución para determinar si ha omitido alguna extracción.
Comentarios finales
Al aprovechar las rutas introducidas en la versión 10.7.5, y con algunos ajustes menores en nuestro código de muestra, hemos demostrado cómo mantener una base de datos actualizada mediante programación con extracciones completas y diferenciales. ¡No dudes en publicar cualquier pregunta o comentario a continuación!
Formateo de código por
http://markup.su/highlighter/
.