Created
September 9, 2016 00:29
-
-
Save shikajiro/54b971d35b244f43288e7177a6871d45 to your computer and use it in GitHub Desktop.
backlogが出力したcsvファイルをgitlabに登録するスクリプト
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
""" | |
backlogが出力したcsvファイルをgitlabに登録するスクリプト | |
user_id は予め登録しておく必要があります。 | |
users に追記してください。 | |
gitlabのtokenは暫定で特定ユーザーのものを利用しています。 | |
使い方 | |
./backlog2gitlab.py project_id gitlab_private_token filename.csv | |
csvのデータを全て登録する | |
./backlog2gitlab.py 1234567890 hogehoge tasks.csv | |
gitlabに登録されているissueをほぼ全て削除する | |
タスク量が多い場合は複数回実行すること | |
./backlog2gitlab.py 1234567890 hogehoge clear | |
""" | |
import csv | |
import requests | |
import sys | |
from datetime import datetime | |
BASE_URL = "https://gitlab.com/api/v3" | |
HEADERS = {} | |
USERS = { | |
# backlog_user_id : gitlab_user_id, | |
1073892321: 694413, # shikajiro | |
} | |
def convert_status(bl_status_id): | |
if bl_status_id == 4: | |
return "close" | |
return None | |
def create_issue(project_id, | |
title, | |
description, | |
assignee_id, | |
milestone_id, | |
labels, | |
created_at, | |
due_date, | |
confidential=False, | |
): | |
""" | |
curl --request POST --header "PRIVATE-TOKEN: ybK4qZF4sshtm_hQKzN-" https://gitlab.com/api/v3/projects/1638259/issues?title=Issues%20with%20auth&labels=bug | |
id integer yes The ID of a project | |
title string yes The title of an issue | |
description string no The description of an issue | |
confidential boolean no Set an issue to be confidential. Default is false. | |
assignee_id integer no The ID of a user to assign issue | |
milestone_id integer no The ID of a milestone to assign issue | |
labels string no Comma-separated label names for an issue | |
created_at string no Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project owner rights) | |
due_date string no Date time string in the format YEAR-MONTH-DAY, e.g. 2016-03-11 | |
""" | |
url = "{0}/projects/{1}/issues".format(BASE_URL, project_id) | |
payload = { | |
"title": title, | |
"description": description, | |
"confidential": confidential, | |
"assignee_id": assignee_id, | |
"milestone_id": milestone_id, | |
"labels": labels, | |
"created_at": datetime.strptime(created_at, "%Y-%m-%d %H:%M").strftime("%Y-%m-%dT%H:%M:00Z"), | |
"due_date": due_date, | |
} | |
res = requests.post(url, headers=HEADERS, data=payload) | |
print "create issue" | |
print res.json() | |
return res.json() | |
def close_issue(project_id, issue_id): | |
print "try close issue {0}".format(issue_id) | |
url = "{0}/projects/{1}/issues/{2}".format(BASE_URL, project_id, issue_id) | |
payload = { | |
"state_event": "close", | |
} | |
res = requests.put(url, headers=HEADERS, data=payload) | |
print res.json() | |
return res.json() | |
def delete_issue(project_id, issue_id): | |
print "try delete issue {0}".format(issue_id) | |
url = "{0}/projects/{1}/issues/{2}".format(BASE_URL, project_id, issue_id) | |
res = requests.delete(url, headers=HEADERS) | |
print res.json() | |
return res.json() | |
def get_issues(project_id): | |
print "try get issues" | |
result = [] | |
for page in range(1, 30): | |
url = "{0}/projects/{1}/issues?page={2}".format(BASE_URL, project_id, page) | |
res = requests.get(url, headers=HEADERS) | |
print res.json() | |
result.extend(res.json()) | |
return result | |
def create_note(project_id, | |
issue_id, | |
body, | |
created_at=None): | |
""" | |
:param project_id (required) - The ID of a project | |
:param issue_id (required) - The ID of an issue | |
:param body (required) - The content of a note | |
:param created_at (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z | |
:return: | |
""" | |
url = "{0}/projects/{1}/issues/{2}/notes".format(BASE_URL, project_id, issue_id) | |
payload = { | |
"body": body, | |
# "created_at": created_at, | |
} | |
return requests.post(url, headers=HEADERS, data=payload) | |
def create_milestone(project_id, title, description=None, due_date=None): | |
url = "{0}/projects/{1}/milestones".format(BASE_URL, project_id) | |
payload = { | |
"title": title, | |
# "description": description, | |
# "due_date": due_date, | |
} | |
print payload | |
res = requests.post(url, headers=HEADERS, data=payload) | |
return res.json() | |
def get_milestones(project_id): | |
maps = {} | |
for i in range(1, 4): | |
url = "{0}/projects/{1}/milestones?page={2}".format(BASE_URL, project_id, i) | |
res = requests.get(url, headers=HEADERS) | |
res_json = res.json() | |
maps.update({j["title"]: j["id"] for j in res_json}) | |
print "get_milestones" | |
print maps | |
return maps | |
if __name__ == "__main__": | |
p_id = None | |
csv_file = None | |
param = sys.argv | |
if len(param) < 4: | |
print "Arguments shortage." | |
quit() | |
# プロジェクトIDを取得 | |
if len(param) >= 2: | |
p_id = param[1] | |
if not isinstance(p_id, int): | |
print "project_id is not currect." | |
quit() | |
if len(param) >= 3: | |
token = param[2] | |
if not isinstance(token, str): | |
print "token is not currect." | |
quit() | |
HEADERS = {'PRIVATE-TOKEN': token} | |
# clear を指定した場合は | |
if len(param) >= 4: | |
three = param[3] | |
if not isinstance(three, str): | |
quit() | |
if three == "clear": | |
print "clear issues" | |
issues = get_issues(p_id) | |
for issue in issues: | |
delete_issue(p_id, issue["id"]) | |
quit() | |
if ".csv" in three: | |
csv_file = three | |
print "start backlog2gitlab. project_id={0} csv_file0{1}".format(p_id, csv_file) | |
milestones = get_milestones(p_id) | |
reader = csv.reader(open(csv_file, 'r')) | |
reader.next() | |
for row in reader: | |
kind = row[6].decode('utf-8') | |
category = row[8].decode('utf-8') | |
version = row[10].decode('utf-8') | |
title = row[11].decode('utf-8') | |
description = row[12].decode('utf-8') | |
status_id = row[13] | |
status = row[14].decode('utf-8') | |
milestone_id = row[17] | |
milestone_name = row[18].decode('utf-8').strip() | |
assignee_id = row[21] | |
assignee_name = row[22].decode('utf-8') | |
created_at = row[25] | |
due_date = row[27] | |
updated_at = row[30] | |
comment = row[33].decode('utf-8') # 33 after | |
print( | |
"kind=" + kind + ", \n" + | |
"category=" + category + ", \n" + | |
"version=" + version + ", \n" + | |
"title=" + title + ", \n" + | |
"description=" + description + ", \n" + | |
"status_id=" + status_id + ", \n" + | |
"status=" + status + ", \n" + | |
# "priority_id=" + priority_id + ", \n" + | |
# "priority=" + priority + ", \n" + | |
"milestone_id=" + milestone_id + ", \n" + | |
"milestone_name=" + milestone_name + ", \n" + | |
"assignee_id=" + assignee_id + ", \n" + | |
"assignee_name=" + assignee_name + ", \n" + | |
"created_at=" + created_at + ", \n" + | |
"updated_at=" + updated_at + ", \n" + | |
"due_date=" + due_date + ", \n" + | |
"comment=" + comment + "\n\n" | |
"") | |
# gitlabに存在しないマイルストーンを追加 | |
if milestone_name and milestone_name not in milestones.keys(): | |
m = create_milestone(p_id, milestone_name) | |
print m | |
milestones[milestone_name] = m["id"] | |
# 種別、カテゴリ、発生バージョンはラベルとして登録 | |
labels = [kind, category, version] | |
user_id = None | |
if assignee_id: | |
user_id = USERS.get(int(assignee_id)) | |
milestone = None | |
if milestone_name: | |
milestone = milestones.get(milestone_name) | |
issue = create_issue(p_id, | |
title, | |
description, | |
user_id, | |
milestone, | |
labels, | |
created_at, | |
due_date) | |
# closeタスクはpost時に設定できないので、編集処理にてクローズを行う | |
if status_id == "4": | |
close_issue(p_id, issue["id"]) | |
# コメントはcsvの33列目以降に存在するので、列が無くなるまで取得し、登録する | |
note_index = 33 | |
while True: | |
print "start create comment index={0}".format(note_index) | |
if len(row) < note_index: | |
break | |
try: | |
body = row[note_index] | |
if body: | |
print "write comment {0} => {1}".format(note_index, body) | |
create_note(p_id, issue["id"], body) | |
except IndexError: | |
print "end comment" | |
note_index += 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment