Escrito por Mark Tse, desenvolvedor de software, desenvolvimento de plataforma D2L
A partir da versão 10.7.5, os administradores podem agendar extrações diferenciais além de extrações completas, permitindo relatórios de maior frequência. Esses extratos diferenciais são gerados em um intervalo definido e contêm dados recém-disponíveis desde que os extratos diferenciais anteriores foram gerados.
Neste artigo, vamos nos basear no exemplo de código em Conjuntos de dados do Brightspace - Exemplo de cliente headless (não interativo) para mostrar como aproveitar as APIs do aplicativo para manter um banco de dados atualizado com extrações diferenciais. O código-fonte deste artigo está disponível em GitHub.
Diferenças de API
Para acomodar extrações diferenciais, o exemplo de código foi modificado para alavancar /d2l/api/lp/(versão)/dataExport/bds, que foi introduzido na versão 10.7.5. Essa rota retorna uma lista de todas as extrações de conjunto de dados disponíveis (completas e diferenciais) e quaisquer metadados correspondentes, incluindo Link de download, que aponta para a localização de um determinado conjunto de dados.
Visão geral das mudanças
Para demonstrar como processar extrações completas e diferenciais, o exemplo de código foi aumentado para dar suporte ao conjunto de dados Registros de Usuário. Os arquivos de esquema são encontrados na pasta de esquema.
A maior alteração no exemplo de código é determinar qual conjunto de dados obter e como obter o conjunto de dados. O código que faz um upsert (inserir ou atualizar) no banco de dados é reutilizável e permanece inalterado.
Dados estáticos
Adicionamos a extração completa de Registros de Usuário à lista completa do conjunto de dados existente e uma nova lista para armazenar as extrações diferenciais de interesse.
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
Como faremos chamadas autenticadas para o Brightspace várias vezes, a seguinte função auxiliar foi adicionada:
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 status: %s; content: %s', response.status_code, response.text) response.raise_for_status() return response
O código para extrair o arquivo zip e atualizar o banco de dados foi colocado em seu próprio método auxiliar para facilitar a leitura:
def unzip_and_update_db(response_content, db_conn_params, tabela): com io. BytesIO(response_content) como response_stream: com zipfile. ZipFile(response_stream) as zipped_data_set: files = zipped_data_set.namelist() assert len(files) == 1 csv_name = files[0] with zipped_data_set.open(csv_name) as csv_data: update_db(db_conn_params, table, csv_data)
Fazendo download dos conjuntos de dados
Primeiro, adicionamos uma opção para alternar entre o download de extratos completos ou diferenciais:
parser = argparse. ArgumentParser(description='Script para baixar conjuntos de dados.')parser.add_argument( '--differential', action='store_true', help='Usar conjuntos de dados diferenciais em vez de conjuntos de dados completos')args = parser.parse_args()
Em seguida, adicionamos um método para obter um mapeamento de todos os conjuntos de dados e suas localizações usando a rota do conjunto de dados da lista, conforme mencionado acima:
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 ) enquanto next_page_url não é Nenhum: 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'])
Com base no argumento da linha de comando, adicionamos código para obter os links de download para todas as extrações completas ou diferenciais nas quais estamos interessados:
# args.differential é verdadeiro se '--differential' for fornecido como um argumentdata_set_metadata = DIFF_DATA_SET_METADATA se 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']}para plugin, tabela em data_set_metadata: resposta = get_with_auth( endpoint=plugin_to_link[plugin], access_token=token_response['access_token'] ) unzip_and_update_db(response.content, db_conn_params, table)
O código de atualização do banco de dados inalterado fará corretamente um upsert (inserir ou atualizar), independentemente da fonte de dados (extração completa ou diferencial).
Agendamento
Este exemplo de código não inclui a funcionalidade para recuperação de uma extração diferencial perdida. Como os extratos diferenciais contêm apenas dados a partir do extrato diferencial anterior, um extrato diferencial perdido significaria uma lacuna de dados no banco de dados de destino, mesmo que o próximo extrato diferencial fosse processado com êxito. Por esse motivo, este exemplo de código deve ser agendado na mesma cadência que o Diferenciais dos conjuntos de dados do Brightspace tarefa agendada para evitar a perda de uma extração diferencial.
A recuperação de extrações diferenciais perdidas está atualmente fora do escopo do exemplo de código. No entanto, uma mitigação simples seria direcionar o mesmo banco de dados com extrações completas e diferenciais, o que garantirá que quaisquer lacunas de dados de extrações diferenciais perdidas sejam preenchidas quando a próxima extração diária for processada. Como alternativa, o exemplo de código pode ser estendido para examinar todos os extratos diferenciais disponíveis no AnteriorConjuntos de Dados em cada execução para determinar se ele perdeu alguma extração.
Comentários finais
Aproveitando as rotas introduzidas na versão 10.7.5 e com alguns pequenos ajustes em nosso código de exemplo, demonstramos como manter programaticamente um banco de dados atualizado com extrações completas e diferenciais. Sinta-se à vontade para postar quaisquer perguntas ou comentários abaixo!
Formatação de código por
http://markup.su/highlighter/
.