|
a/scripts/teamforge-import.py |
|
b/scripts/teamforge-import.py |
|
... |
|
... |
7 |
import os.path
|
7 |
import os.path
|
8 |
from time import mktime
|
8 |
from time import mktime
|
9 |
import json
|
9 |
import json
|
10 |
from urlparse import urlparse
|
10 |
from urlparse import urlparse
|
11 |
from urllib import FancyURLopener
|
11 |
from urllib import FancyURLopener
|
12 |
from pprint import pprint
|
|
|
13 |
from datetime import datetime
|
12 |
from datetime import datetime
|
|
|
13 |
from ConfigParser import ConfigParser
|
14 |
|
14 |
|
15 |
from suds.client import Client
|
15 |
from suds.client import Client
|
16 |
from suds import WebFault
|
16 |
from suds import WebFault
|
17 |
from ming.orm.ormsession import ThreadLocalORMSession
|
17 |
from ming.orm.ormsession import ThreadLocalORMSession
|
18 |
from ming.base import Object
|
18 |
from ming.base import Object
|
|
... |
|
... |
32 |
'''
|
32 |
'''
|
33 |
|
33 |
|
34 |
options = None
|
34 |
options = None
|
35 |
s = None # security token
|
35 |
s = None # security token
|
36 |
users = set()
|
36 |
users = set()
|
|
|
37 |
CONFIG_FILENAME='teamforge-import.cfg'
|
37 |
|
38 |
|
38 |
def make_client(api_url, app):
|
39 |
def make_client(api_url, app):
|
39 |
return Client(api_url + app + '?wsdl', location=api_url + app)
|
40 |
return Client(api_url + app + '?wsdl', location=api_url + app)
|
40 |
|
41 |
|
41 |
def main():
|
42 |
def main():
|
42 |
global options, s
|
43 |
global options, s
|
43 |
optparser = OptionParser(usage='''%prog [--options] [projID projID projID]\nIf no project ids are given, all projects will be migrated''')
|
44 |
config = ConfigParser({
|
|
|
45 |
'api-url':None,
|
|
|
46 |
'attachment-url':'/sf/%s/do/%s/',
|
|
|
47 |
'default-wiki-text':'PRODUCT NAME HERE',
|
|
|
48 |
'username':None,
|
|
|
49 |
'password':None,
|
|
|
50 |
'output-dir':'teamforge-export/',
|
|
|
51 |
'list-project-ids':'false',
|
|
|
52 |
'neighborhood':None,
|
|
|
53 |
'neighborhood-shortname':None,
|
|
|
54 |
'skip-frs-download':'false',
|
|
|
55 |
'skip-unsupported-check':'false'
|
|
|
56 |
})
|
|
|
57 |
if os.path.exists('teamforge-import.cfg'):
|
|
|
58 |
config.read(CONFIG_FILENAME)
|
|
|
59 |
|
|
|
60 |
optparser = OptionParser(
|
|
|
61 |
usage=('%prog [--options] [projID projID projID]\n'
|
|
|
62 |
'If no project ids are given, all projects will be migrated'))
|
|
|
63 |
|
|
|
64 |
# Command-line-only options
|
|
|
65 |
optparser.add_option(
|
|
|
66 |
'--extract-only', action='store_true', dest='extract',
|
|
|
67 |
help='Store data from the TeamForge API on the local filesystem; not load into Allura')
|
|
|
68 |
optparser.add_option(
|
|
|
69 |
'--load-only', action='store_true', dest='load',
|
|
|
70 |
help='Load into Allura previously-extracted data')
|
|
|
71 |
|
|
|
72 |
# Command-line options with defaults in config file
|
|
|
73 |
optparser.add_option(
|
44 |
optparser.add_option('--api-url', dest='api_url', help='e.g. https://hostname/ce-soap50/services/')
|
74 |
'--api-url', dest='api_url', help='e.g. https://hostname/ce-soap50/services/',
|
45 |
optparser.add_option('--attachment-url', dest='attachment_url', default='/sf/%s/do/%s/')
|
75 |
default=config.get('Extract', 'api-url'))
|
46 |
optparser.add_option('--default-wiki-text', dest='default_wiki_text', default='PRODUCT NAME HERE', help='used in determining if a wiki page text is default or changed')
|
76 |
optparser.add_option(
|
47 |
optparser.add_option('-u', '--username', dest='username')
|
77 |
'--attachment-url', dest='attachment_url',
|
48 |
optparser.add_option('-p', '--password', dest='password')
|
78 |
default=config.get('Load', 'attachment-url'))
|
49 |
optparser.add_option('-o', '--output-dir', dest='output_dir', default='teamforge-export/')
|
79 |
optparser.add_option(
|
|
|
80 |
'--default-wiki-text', dest='default_wiki_text',
|
|
|
81 |
help='used in determining if a wiki page text is default or changed',
|
|
|
82 |
default=config.get('Extract', 'default-wiki-text'))
|
|
|
83 |
optparser.add_option(
|
|
|
84 |
'-u', '--username', dest='username',
|
|
|
85 |
default=config.get('Extract', 'username'))
|
|
|
86 |
optparser.add_option(
|
|
|
87 |
'-p', '--password', dest='password',
|
|
|
88 |
default=config.get('Extract', 'password'))
|
|
|
89 |
optparser.add_option(
|
|
|
90 |
'-o', '--output-dir', dest='output_dir',
|
|
|
91 |
default=config.get('Extract', 'output-dir'))
|
|
|
92 |
optparser.add_option(
|
50 |
optparser.add_option('--list-project-ids', action='store_true', dest='list_project_ids')
|
93 |
'--list-project-ids', action='store_true', dest='list_project_ids',
|
51 |
optparser.add_option('--extract-only', action='store_true', dest='extract', help='Store data from the TeamForge API on the local filesystem; not load into Allura')
|
94 |
default=config.getboolean('Extract', 'list-project-ids'))
|
52 |
optparser.add_option('--load-only', action='store_true', dest='load', help='Load into Allura previously-extracted data')
|
95 |
optparser.add_option(
|
53 |
optparser.add_option('-n', '--neighborhood', dest='neighborhood', help='Neighborhood full name, to load in to')
|
96 |
'-n', '--neighborhood', dest='neighborhood',
|
54 |
optparser.add_option('--n-shortname', dest='neighborhood_shortname', help='Neighborhood shortname, for PFS extract SQL')
|
97 |
help='Neighborhood full name, to load in to',
|
|
|
98 |
default=config.get('Load', 'neighborhood'))
|
|
|
99 |
optparser.add_option(
|
|
|
100 |
'--n-shortname', dest='neighborhood_shortname',
|
|
|
101 |
help='Neighborhood shortname, for PFS extract SQL',
|
|
|
102 |
default=config.get('Load', 'neighborhood-shortname'))
|
|
|
103 |
optparser.add_option(
|
55 |
optparser.add_option('--skip-frs-download', action='store_true', dest='skip_frs_download')
|
104 |
'--skip-frs-download', action='store_true', dest='skip_frs_download',
|
|
|
105 |
default=config.getboolean('Extract', 'skip-frs-download'))
|
|
|
106 |
optparser.add_option(
|
56 |
optparser.add_option('--skip-unsupported-check', action='store_true', dest='skip_unsupported_check')
|
107 |
'--skip-unsupported-check', action='store_true', dest='skip_unsupported_check',
|
|
|
108 |
default=config.getboolean('Extract', 'skip-unsupported-check'))
|
57 |
options, project_ids = optparser.parse_args()
|
109 |
options, project_ids = optparser.parse_args()
|
58 |
|
110 |
|
59 |
# neither specified, so do both
|
111 |
# neither specified, so do both
|
60 |
if not options.extract and not options.load:
|
112 |
if not options.extract and not options.load:
|
61 |
options.extract = True
|
113 |
options.extract = True
|
62 |
options.load = True
|
114 |
options.load = True
|
63 |
|
115 |
|
64 |
|
|
|
65 |
if options.extract:
|
116 |
if options.extract:
|
66 |
c = make_client(options.api_url, 'CollabNet')
|
117 |
client = make_client(options.api_url, 'CollabNet')
|
67 |
api_v = c.service.getApiVersion()
|
118 |
api_v = client.service.getApiVersion()
|
68 |
if not api_v.startswith('5.4.'):
|
119 |
if not api_v.startswith('5.4.'):
|
69 |
log.warning('Unexpected API Version %s. May not work correctly.' % api_v)
|
120 |
log.warning('Unexpected API Version %s. May not work correctly.' % api_v)
|
70 |
|
121 |
|
71 |
s = c.service.login(options.username, options.password or getpass('Password: '))
|
122 |
s = client.service.login(options.username, options.password or getpass('Password: '))
|
72 |
teamforge_v = c.service.getVersion(s)
|
123 |
teamforge_v = client.service.getVersion(s)
|
73 |
if not teamforge_v.startswith('5.4.'):
|
124 |
if not teamforge_v.startswith('5.4.'):
|
74 |
log.warning('Unexpected TeamForge Version %s. May not work correctly.' % teamforge_v)
|
125 |
log.warning('Unexpected TeamForge Version %s. May not work correctly.' % teamforge_v)
|
75 |
|
126 |
|
76 |
if options.load:
|
127 |
if options.load:
|
77 |
if not options.neighborhood:
|
128 |
if not options.neighborhood:
|
|
... |
|
... |
87 |
|
138 |
|
88 |
if not project_ids:
|
139 |
if not project_ids:
|
89 |
if not options.extract:
|
140 |
if not options.extract:
|
90 |
log.error('You must specify project ids')
|
141 |
log.error('You must specify project ids')
|
91 |
return
|
142 |
return
|
92 |
projects = c.service.getProjectList(s)
|
143 |
projects = client.service.getProjectList(s)
|
93 |
project_ids = [p.id for p in projects.dataRows]
|
144 |
project_ids = [p.id for p in projects.dataRows]
|
94 |
|
145 |
|
95 |
if options.list_project_ids:
|
146 |
if options.list_project_ids:
|
96 |
print ' '.join(project_ids)
|
147 |
print ' '.join(project_ids)
|
97 |
return
|
148 |
return
|
|
... |
|
... |
99 |
if not os.path.exists(options.output_dir):
|
150 |
if not os.path.exists(options.output_dir):
|
100 |
os.makedirs(options.output_dir)
|
151 |
os.makedirs(options.output_dir)
|
101 |
for pid in project_ids:
|
152 |
for pid in project_ids:
|
102 |
if options.extract:
|
153 |
if options.extract:
|
103 |
try:
|
154 |
try:
|
104 |
project = c.service.getProjectData(s, pid)
|
155 |
project = client.service.getProjectData(s, pid)
|
105 |
log.info('Project: %s %s %s' % (project.id, project.title, project.path))
|
156 |
log.info('Project: %s %s %s' % (project.id, project.title, project.path))
|
106 |
out_dir = os.path.join(options.output_dir, project.id)
|
157 |
out_dir = os.path.join(options.output_dir, project.id)
|
107 |
if not os.path.exists(out_dir):
|
158 |
if not os.path.exists(out_dir):
|
108 |
os.mkdir(out_dir)
|
159 |
os.mkdir(out_dir)
|
109 |
|
160 |
|
110 |
get_project(project, c)
|
161 |
get_project(project, client)
|
111 |
get_files(project)
|
162 |
get_files(project)
|
112 |
get_homepage_wiki(project)
|
163 |
get_homepage_wiki(project)
|
113 |
get_discussion(project)
|
164 |
get_discussion(project)
|
114 |
get_news(project)
|
165 |
get_news(project)
|
115 |
if not options.skip_unsupported_check:
|
166 |
if not options.skip_unsupported_check:
|
|
... |
|
... |
126 |
if options.extract:
|
177 |
if options.extract:
|
127 |
log.info('Users encountered: %s', len(users))
|
178 |
log.info('Users encountered: %s', len(users))
|
128 |
with open(os.path.join(options.output_dir, 'usernames.json'), 'w') as out:
|
179 |
with open(os.path.join(options.output_dir, 'usernames.json'), 'w') as out:
|
129 |
out.write(json.dumps(list(users)))
|
180 |
out.write(json.dumps(list(users)))
|
130 |
|
181 |
|
131 |
def get_project(project, c):
|
182 |
def get_project(project, client):
|
132 |
cats = make_client(options.api_url, 'CategorizationApp')
|
183 |
cats = make_client(options.api_url, 'CategorizationApp')
|
133 |
|
184 |
|
134 |
data = c.service.getProjectData(s, project.id)
|
185 |
data = client.service.getProjectData(s, project.id)
|
135 |
access_level = { 1: 'public', 4: 'private', 3: 'gated community'}[
|
186 |
access_level = { 1: 'public', 4: 'private', 3: 'gated community'}[
|
136 |
c.service.getProjectAccessLevel(s, project.id)
|
187 |
client.service.getProjectAccessLevel(s, project.id)
|
137 |
]
|
188 |
]
|
138 |
admins = c.service.listProjectAdmins(s, project.id).dataRows
|
189 |
admins = client.service.listProjectAdmins(s, project.id).dataRows
|
139 |
members = c.service.getProjectMemberList(s, project.id).dataRows
|
190 |
members = client.service.getProjectMemberList(s, project.id).dataRows
|
140 |
groups = c.service.getProjectGroupList(s, project.id).dataRows
|
191 |
groups = client.service.getProjectGroupList(s, project.id).dataRows
|
141 |
categories = cats.service.getProjectCategories(s, project.id).dataRows
|
192 |
categories = cats.service.getProjectCategories(s, project.id).dataRows
|
142 |
save(json.dumps(dict(
|
193 |
save(json.dumps(dict(
|
143 |
data = dict(data),
|
194 |
data = dict(data),
|
144 |
access_level = access_level,
|
195 |
access_level = access_level,
|
145 |
admins = map(dict, admins),
|
196 |
admins = map(dict, admins),
|