blob: 2f286d004f39e046eeaf0bf2eaefeb0c5931fc05 [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 os.path
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, filename=None, send_email=True):
'''
Export the current project data. Send notification to current user.
:param list tools: list of mount_points to export
:param str filename: optional filename to use
'''
# 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, filename, send_email)
def _bulk_export(project, tools, user, filename=None, send_email=True):
export_filename = filename or project.bulk_export_filename()
export_path = create_export_dir(project, 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:
json_file = os.path.join(export_path, '%s.json' % tool)
with open(json_file, 'w') as f:
app.bulk_export(f)
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(export_path, export_filename)
else:
log.error('Nothing to export')
return None
if not user:
log.info('No user. Skipping notification.')
return
if not send_email:
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 = {
'sender': unicode(tg.config['forgemail.return_path']),
'fromaddr': unicode(tg.config['forgemail.return_path']),
'reply_to': unicode(tg.config['forgemail.return_path']),
'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 = os.path.splitext(export_filename)[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(export_path, export_filename):
"""
Zip exported data for a given path and filename.
Copy it to proper location. Remove temporary files.
"""
zipdir(export_path, os.path.join(os.path.dirname(export_path), export_filename))
# cleanup
shutil.rmtree(export_path)