En este artículo presento el método que utilicé para hacer una copia de seguridad de los datos de los proyectos gestionados en nulab Backlog.
nulab Backlog es simple y fácil de usar, pero cuando un proyecto se vuelve grande, la jerarquía de tareas solo permite dos niveles, lo que a veces hace que quieras migrar a otra herramienta de gestión de proyectos. En esos casos, incluso si no planeas importar los datos de Backlog a otra herramienta, es tranquilizador poder copiar todo el proyecto por si más adelante necesitas consultar descripciones pasadas. Sin embargo, simplemente buscar y guardar las tareas no guarda los archivos adjuntos. Por ello, utilizo Python para hacer una copia de seguridad de los adjuntos también.
Environment
- Python
Code
Primero, guarda la información del proyecto y la API Key en backlog_credential.py.
|
1 2 3 |
api_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' project_key = 'PROJECT' url_base = 'https://xxxxxxxxx.backlog.com' |
Luego ejecuta el siguiente programa. Esto guardará las tareas, comentarios y archivos adjuntos en formato JSON.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
from backlog_credential import * from pathlib import Path import requests import time import os SLEEP_TIME = 1 DEBUG = False def build_get_url(path, params = {}): url = url_base + path + '?apiKey=' + api_key for key in params: url += '&' + key + '=' + str(params[key]) return url def get_all_projects(): url = build_get_url('/api/v2/projects') response = requests.get(url) response.raise_for_status() return response.json() def get_project_id(project_key): for project in get_all_projects(): if project['projectKey'] == project_key: return project['id'] raise Exception('Project not found') def get_issue_list(project_id, offset = 0): params = { 'projectId[]': project_id, 'offset': offset, 'count': 100, 'sort': 'created', } url = build_get_url('/api/v2/issues', params) result = requests.get(url) # raise exception if not success result.raise_for_status() time.sleep(SLEEP_TIME) if DEBUG: print("headers:", result.headers) return result.json() def get_issue(issue_id): url = build_get_url('/api/v2/issues/' + str(issue_id)) result = requests.get(url) time.sleep(SLEEP_TIME) return result.json() def get_all_issues(project_id, offset=0): offset = 0 issues = [] while True: result = get_issue_list(project_id, offset) issues += result if len(result) < 100: break offset += 100 return issues def get_issue_comments(issue_key): params = { 'count': 100, 'order': 'asc', } url = build_get_url( '/api/v2/issues/' + issue_key + '/comments', params) result = requests.get(url) # raise exception if not success result.raise_for_status() return result.json() def get_attached_file_list(issue_key): url = build_get_url( '/api/v2/issues/' + issue_key + '/attachments') result = requests.get(url) time.sleep(SLEEP_TIME) return result.json() def get_and_save_attached_file(issue_key, attachment_id, filepath): url = build_get_url( '/api/v2/issues/' + issue_key + '/attachments/' + str(attachment_id)) result = requests.get(url) time.sleep(SLEEP_TIME) with open(filepath, 'wb') as f: f.write(result.content) return result # create issue directory, and save the content as content.json def backup_issue(issue, backup_dir): issue_id = issue['issueKey'] issue_path = os.path.join(backup_dir, 'issues', str(issue_id)) if not os.path.exists(issue_path): os.makedirs(issue_path) with open(os.path.join(issue_path, 'content.json'), 'w') as f: f.write(str(issue)) # get all comments and save it as comments.json comments = get_issue_comments(issue_id) with open(os.path.join(issue_path, 'comments.json'), 'w') as f: f.write(str(comments)) # get attached file list and save it as attached_files.json attached_files = get_attached_file_list(issue_id) with open(os.path.join(issue_path, 'attached_files.json'), 'w') as f: f.write(str(attached_files)) # download attached files for file in attached_files: get_and_save_attached_file( issue_id, file['id'], os.path.join(issue_path, file['name'])) def backup_project(project_key, backup_dir='backup'): backup_dir = Path(backup_dir) if not backup_dir.exists(): backup_dir.mkdir() project_id = get_project_id(project_key) issues = get_all_issues(project_id) for issue in issues: backup_issue(issue, backup_dir) if __name__ == '__main__': backup_project(project_key) |
Fue creado alrededor de 2024 y se basa en la API de nulab Backlog de esa época.
La función backup_project permite pasar el directorio de destino de la copia de seguridad como segundo argumento.


