155 lines
6.0 KiB
Python
155 lines
6.0 KiB
Python
# w.c.s. - web application for online forms
|
|
# Copyright (C) 2005-2018 Entr'ouvert
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import io
|
|
import os
|
|
import sys
|
|
import traceback
|
|
|
|
import psycopg2
|
|
from django.core.management.base import BaseCommand, CommandError
|
|
from django.utils.encoding import force_bytes
|
|
|
|
from wcs import sql
|
|
from wcs.formdef import FormDef
|
|
from wcs.qommon.misc import localstrftime
|
|
from wcs.qommon.publisher import get_publisher_class
|
|
from wcs.qommon.storage import atomic_write
|
|
from wcs.users import User
|
|
|
|
|
|
class Command(BaseCommand):
|
|
|
|
help = 'Setup postgresql connection parameters and migrate existing objects.'
|
|
|
|
def add_arguments(self, parser):
|
|
parser.add_argument('-d', '--domain', required=True)
|
|
parser.add_argument('--database', required=True)
|
|
parser.add_argument('--host')
|
|
parser.add_argument('--port', type=int)
|
|
parser.add_argument('--user')
|
|
parser.add_argument('--password')
|
|
|
|
def handle(self, **options):
|
|
self.publisher = self.get_publisher(options['domain'])
|
|
if self.publisher.is_using_postgresql():
|
|
raise CommandError('tenant already using postgresql')
|
|
|
|
self.setup_connection(**options)
|
|
sql.get_connection(new=True)
|
|
self.store_users()
|
|
self.store_forms()
|
|
self.publisher.write_cfg()
|
|
self.enable_connection()
|
|
self.publisher.initialize_sql()
|
|
self.publisher.cleanup()
|
|
|
|
def get_publisher(self, domain):
|
|
publisher_class = get_publisher_class()
|
|
if domain not in publisher_class.get_tenants():
|
|
raise CommandError('unknown tenant')
|
|
publisher = publisher_class.create_publisher()
|
|
|
|
publisher.app_dir = os.path.join(publisher.app_dir, domain)
|
|
publisher.set_config()
|
|
return publisher
|
|
|
|
def setup_connection(self, **kwargs):
|
|
options = {}
|
|
for k in ['host', 'port', 'database', 'user', 'password']:
|
|
if k in kwargs:
|
|
options[k] = kwargs.get(k)
|
|
self.publisher.cfg['postgresql'] = options
|
|
|
|
def enable_connection(self):
|
|
if not self.publisher.site_options.has_option('options', 'postgresql'):
|
|
if not self.publisher.site_options.has_section('options'):
|
|
self.publisher.site_options.add_section('options')
|
|
self.publisher.site_options.set('options', 'postgresql', 'true')
|
|
options_file = os.path.join(self.publisher.app_dir, 'site-options.cfg')
|
|
stringio = io.StringIO()
|
|
self.publisher.site_options.write(stringio)
|
|
atomic_write(options_file, force_bytes(stringio.getvalue()))
|
|
|
|
def store_users(self):
|
|
errors = []
|
|
print('converting users')
|
|
sql.do_user_table()
|
|
count = User.count()
|
|
for i, user_id in enumerate(User.keys()):
|
|
user = User.get(user_id)
|
|
user.__class__ = sql.SqlUser
|
|
try:
|
|
user.store()
|
|
except AssertionError:
|
|
errors.append((user, traceback.format_exc()))
|
|
self.update_progress(100 * i / count)
|
|
sql.SqlUser.fix_sequences()
|
|
|
|
if errors:
|
|
with open('error_user.log', 'w') as error_log:
|
|
for user, trace in errors:
|
|
error_log.write('user_id %s\n' % user.id)
|
|
error_log.write(trace)
|
|
error_log.write('-' * 80)
|
|
error_log.write('\n\n')
|
|
print('There were some errors, see error_user.log for details.')
|
|
|
|
def store_forms(self):
|
|
errors = []
|
|
for formdef in FormDef.select():
|
|
print('converting %s' % formdef.name)
|
|
sql.do_formdef_tables(formdef, rebuild_views=True, rebuild_global_views=True)
|
|
data_class = formdef.data_class(mode='files')
|
|
count = data_class.count()
|
|
|
|
# load all objects a first time, to allow the migrate() code to be
|
|
# run and the eventual changes properly saved.
|
|
for id in data_class.keys():
|
|
formdata = data_class.get(id)
|
|
delattr(sys.modules['formdef'], formdef.data_class_name)
|
|
|
|
# once this is done, reload and store everything in postgresql
|
|
sql_data_class = formdef.data_class(mode='sql')
|
|
for i, id in enumerate(data_class.keys()):
|
|
formdata = data_class.get(id)
|
|
formdata._formdef = formdef
|
|
formdata._evolution = formdata.evolution
|
|
formdata.__class__ = sql_data_class
|
|
try:
|
|
formdata.store()
|
|
except (AssertionError, psycopg2.DataError):
|
|
errors.append((formdata, traceback.format_exc()))
|
|
self.update_progress(100 * i / count)
|
|
sql_data_class.fix_sequences()
|
|
|
|
if errors:
|
|
with open('error_formdata.log', 'w') as error_log:
|
|
for formdata, trace in errors:
|
|
error_log.write(
|
|
'%s %s - %s\n' % (formdata.formdef, formdata.id, localstrftime(formdata.receipt_time))
|
|
)
|
|
error_log.write(trace)
|
|
error_log.write('-' * 80)
|
|
error_log.write('\n\n')
|
|
print('There were some errors, see error_formdata.log.')
|
|
|
|
def update_progress(self, progress, num_columns=120):
|
|
sys.stdout.write(
|
|
'[%s] %s%%\r'
|
|
% (('#' * int((num_columns - 10) * progress / 100)).ljust(num_columns - 15), progress)
|
|
)
|