Add basic import/export handler views, tool to run jobs

This commit is contained in:
Lance Edgar 2021-12-05 17:23:11 -06:00
parent 95da490f9a
commit 282185c5af
7 changed files with 744 additions and 77 deletions

View file

@ -32,6 +32,7 @@ import datetime
import tempfile
import logging
import json
import six
import sqlalchemy as sa
from sqlalchemy import orm
@ -65,6 +66,10 @@ from tailbone.config import global_help_url
log = logging.getLogger(__name__)
class EverythingComplete(Exception):
pass
class MasterView(View):
"""
Base "master" view class. All model master views should derive from this.
@ -1743,6 +1748,78 @@ class MasterView(View):
def get_execute_success_url(self, obj, **kwargs):
return self.get_action_url('view', obj, **kwargs)
def progress_thread(self, sock, success_url, progress):
"""
This method is meant to be used as a thread target. Its job is to read
progress data from ``connection`` and update the session progress
accordingly. When a final "process complete" indication is read, the
socket will be closed and the thread will end.
"""
while True:
try:
self.process_progress(sock, progress)
except EverythingComplete:
break
# close server socket
sock.close()
# finalize session progress
progress.session.load()
progress.session['complete'] = True
if callable(success_url):
success_url = success_url()
progress.session['success_url'] = success_url
progress.session.save()
def process_progress(self, sock, progress):
"""
This method will accept a client connection on the given socket, and
then update the given progress object according to data written by the
client.
"""
connection, client_address = sock.accept()
active_progress = None
# TODO: make this configurable?
suffix = "\n\n.".encode('utf_8')
data = b''
# listen for progress info, update session progress as needed
while True:
# accumulate data bytestring until we see the suffix
byte = connection.recv(1)
data += byte
if data.endswith(suffix):
# strip suffix, interpret data as JSON
data = data[:-len(suffix)]
if six.PY3:
data = data.decode('utf_8')
data = json.loads(data)
if data.get('everything_complete'):
if active_progress:
active_progress.finish()
raise EverythingComplete
elif data.get('process_complete'):
active_progress.finish()
active_progress = None
break
elif 'value' in data:
if not active_progress:
active_progress = progress(data['message'], data['maximum'])
active_progress.update(data['value'])
# reset data buffer
data = b''
# close client connection
connection.close()
def get_merge_fields(self):
if hasattr(self, 'merge_fields'):
return self.merge_fields
@ -2287,7 +2364,10 @@ class MasterView(View):
try:
mapper = orm.object_mapper(row)
except orm.exc.UnmappedInstanceError:
return {self.model_key: row[self.model_key]}
try:
return {self.model_key: row[self.model_key]}
except TypeError:
return {self.model_key: getattr(row, self.model_key)}
else:
pkeys = get_primary_keys(row)
keys = list(pkeys)