summaryrefslogtreecommitdiff
path: root/references/generate-json.py
blob: b8b1f389907ab341953486a7903c3d60f152fc9c (plain) (blame)
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env python

# Generate JSON to import Trac tickets into GitHub issues using the new import API
# described at https://gist.github.com/jonmagic/5282384165e0f86ef105

import os
import time
import json
import yaml
import sqlite3
import hashlib
import argparse
import subprocess

ticket_query = '''
SELECT
    id,
    type,
    owner,
    reporter,
    milestone,
    status,
    resolution,
    summary,
    description,
    component,
    priority,
    time       / 1000000 AS createdtime,
    changetime / 1000000 AS modifiedtime
FROM
    ticket
ORDER BY
    id
'''

comment_query = '''
SELECT
    time / 1000000 AS createdtime,
    author,
    newvalue
FROM
    ticket_change
WHERE
    ticket = ?
AND
    field = 'comment'
AND
    newvalue <> ''
ORDER BY
    time
'''

attachment_query = '''
SELECT
    id,
    filename,
    size,
    author,
    description,
    ipnr,
    time / 1000000 AS createdtime
FROM
    attachment
WHERE
    id = ?
AND
    type = 'ticket'
ORDER BY
    time, filename
'''

def isotime(t):
  return None if t == 0 else time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(t))

def hashname(whatever):
  return hashlib.sha1(unicode(whatever)).hexdigest()

def ticket_text(ticket):
  d = dict(ticket, createdtime = isotime(ticket["createdtime"]), modifiedtime = isotime(ticket["modifiedtime"]))
  return u"{description}\n\n" \
         u"_Trac ticket #{id} component {component} priority {priority}, owner {owner}," \
         u" created by {reporter} on {createdtime}, last modified {modifiedtime}_\n".format(**d)

def comment_text(comment):
  d = dict(comment, createdtime = isotime(comment["createdtime"]))
  return u"{newvalue}\n\n_Trac comment by {author} on {createdtime}_\n".format(**d)

def attachment_text(attachment):
  h1  = hashname(attachment["id"])
  h2  = hashname(attachment["filename"])
  fn2 = os.path.splitext(attachment["filename"])[1]
  fn  = os.path.join(gist_url, h1[:3], h1, h2 + fn2)
  url = "{}/raw/{}/ticket.{}.{}{}".format(gist_url.rstrip("/"), gist_commit, h1, h2, fn2)
  d = dict(attachment, createdtime = isotime(comment["createdtime"]), url = url)
  return u"[{filename}]({url}) {description}\n_Trac attachment by {author} on {createdtime}_\n".format(**d)

def comment_merge(comments, attachments):
  result = []
  while comments and attachments:
    result.append(comments.pop(0) if comments[0]["created_at"] <= attachments[0]["created_at"] else attachments.pop(0))
  return result + comments + attachments

parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("-c", "--config", type = argparse.FileType(),
                    default = "generate-json.yaml", 
                    help = "YAML config mappings")
args = parser.parse_args()

cfg = yaml.safe_load(args.config)
assignee_map   = cfg["assignees"]
type_map       = cfg["type_labels"]
resolution_map = cfg["resolution_labels"]

gist_url = cfg.get("attachment_gist_url")
if gist_url is not None:
  gist_commit = subprocess.check_output(("git", "ls-remote", gist_url, "HEAD")).split()[0]

db = sqlite3.connect(cfg["database"])
db.row_factory    = sqlite3.Row
ticket_cursor     = db.cursor()
comment_cursor    = db.cursor()
attachment_cursor = db.cursor()

if not os.path.isdir(cfg["ticket_directory"]):
  os.makedirs(cfg["ticket_directory"])

for ticket in ticket_cursor.execute(ticket_query):
  comments = comment_merge([dict(created_at = isotime(comment["createdtime"]), body = comment_text(comment))
                            for comment in comment_cursor.execute(comment_query, (ticket["id"],))],
                           [] if gist_url is None else
                           [dict(created_at = isotime(attachment["createdtime"]), body = attachment_text(attachment))
                            for attachment in attachment_cursor.execute(attachment_query, (ticket["id"],))])
  issue = dict(
    title = ticket["summary"],
    body  = ticket_text(ticket),
    created_at = isotime(ticket["createdtime"]),
    updated_at = isotime(ticket["modifiedtime"]))
  if ticket["status"] == "closed":
    issue["closed"] = True
    issue["closed_at"] = isotime(ticket["modifiedtime"])
    comments.append(dict(created_at = isotime(ticket["modifiedtime"]),
                         body = "_Closed with resolution {resolution}_\n".format(**ticket)))
  if ticket["owner"] in assignee_map:
    issue["assignee"] = assignee_map[ticket["owner"]]
  labels = [type_map.get(ticket["type"]), resolution_map.get(ticket["resolution"])]
  while None in labels:
    del labels[labels.index(None)]
  if labels:
    issue["labels"] = labels
  issue = dict(issue = issue)
  if comments:
    issue["comments"] = comments
  with open(os.path.join(cfg["ticket_directory"], "ticket_{:03d}.json".format(ticket["id"])), "wb") as f:
    json.dump(issue, f, indent = 4, sort_keys = True, separators=(",", ": "))