blob: 06590021a15f01fb85cc0b0381328b447972921b [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import os
import logging
import shutil
from tempfile import mkstemp
import tg
from pylons import app_globals as g, tmpl_context as c
from allura import model as M
from allura.tasks import mail_tasks
from allura.lib.decorators import task
from allura.lib import helpers as h
from allura.model.repository import zipdir
log = logging.getLogger(__name__)
@task
def bulk_export(tools):
'''
Export the current project data. Send notification to current user
:param list tools: list of mount_points to export
'''
# it's very handy to use c.* within a @task,
# but let's be explicit and keep it separate from the main code
return _bulk_export(c.project, tools, c.user)
def _bulk_export(project, tools, user):
export_filename = project.bulk_export_filename()
not_exported_tools = []
for tool in tools or []:
app = project.app_instance(tool)
if not app:
log.info('Can not load app for %s mount point. Skipping.' % tool)
not_exported_tools.append(tool)
continue
if not app.exportable:
log.info('Tool %s is not exportable. Skipping.' % tool)
not_exported_tools.append(tool)
continue
log.info('Exporting %s...' % tool)
try:
path = create_export_dir(project, export_filename)
temp_name = mkstemp(dir=path)[1]
with open(temp_name, 'w') as f:
with h.push_context(project._id):
app.bulk_export(f)
os.rename(temp_name, os.path.join(path, '%s.json' % tool))
except:
log.error('Something went wrong during export of %s' % tool, exc_info=True)
not_exported_tools.append(tool)
continue
if tools and len(not_exported_tools) < len(tools):
# If that fails, we need to let it fail
# there won't be a valid zip file for the user to get.
zip_and_cleanup(project, export_filename)
else:
log.error('Nothing to export')
return None
if not user:
log.info('No user. Skipping notification.')
return
tmpl = g.jinja2_env.get_template('allura:templates/mail/bulk_export.html')
instructions = tg.config.get('bulk_export_download_instructions', '')
instructions = instructions.format(project=project.shortname, filename=export_filename, c=c)
tmpl_context = {
'instructions': instructions,
'project': project,
'tools': list(set(tools) - set(not_exported_tools)),
'not_exported_tools': not_exported_tools,
}
email = {
'fromaddr': unicode(user.email_address_header()),
'reply_to': unicode(user.email_address_header()),
'message_id': h.gen_message_id(),
'destinations': [unicode(user._id)],
'subject': u'Bulk export for project %s completed' % project.shortname,
'text': tmpl.render(tmpl_context),
}
mail_tasks.sendmail.post(**email)
def create_export_dir(project, export_filename):
"""Create temporary directory for export files"""
# Name temporary directory after project shortname,
# thus zipdir() will use proper prefix inside the archive.
tmp_dir_suffix = export_filename.split('.')[0]
path = os.path.join(project.bulk_export_path(), tmp_dir_suffix)
if not os.path.exists(path):
os.makedirs(path)
return path
def zip_and_cleanup(project, export_filename):
"""
Zip exported data for a given project and filename (no path).
Copy it to proper location. Remove temporary files.
"""
path = project.bulk_export_path()
temp = os.path.join(path, export_filename.split('.')[0])
zip_path_temp = os.path.join(temp, export_filename)
zip_path = os.path.join(path, export_filename)
zipdir(temp, zip_path_temp)
# cleanup
shutil.move(zip_path_temp, zip_path)
shutil.rmtree(temp)