diff --git a/oml/item/api.py b/oml/item/api.py index 04b7889..39ca9a8 100644 --- a/oml/item/api.py +++ b/oml/item/api.py @@ -153,7 +153,6 @@ def remove(data): id } ''' - logger.debug('remove files %s', data) if 'ids' in data and data['ids']: for i in models.Item.query.filter(models.Item.id.in_(data['ids'])): i.remove_file() @@ -229,7 +228,6 @@ def findMetadata(data): response = { 'items': [] } - logger.debug('findMetadata %s', data) key = ','.join(sorted(data)) if key == 'isbn': r = meta.lookup(key, data[key]) @@ -267,7 +265,6 @@ def getMetadata(data): } key can be one of the supported identifiers: isbn10, isbn13, oclc, olid,... ''' - logger.debug('getMetadata %s', data) if 'includeEdits' in data: include_edits = data.pop('includeEdits') else: @@ -340,6 +337,24 @@ def scan(data): return {} actions.register(scan, cache=False) +def export(data): + ''' + takes { + path absolute path to import + list listename (add new items to this list) + mode add|replace + } + ''' + import user.models + l = user.models.List.get(':' + data['list']) + if l: + data['list'] = ':' + data['list'] + state.tasks.queue('export', data) + response = {'status': 'ok'} + else: + response = {'status': 'invalid list'} + return response +actions.register(export, cache=False) def _import(data): ''' @@ -349,7 +364,6 @@ def _import(data): mode copy|move } ''' - logger.debug('api.import %s', data) state.tasks.queue('import', data) return {} actions.register(_import, 'import', cache=False) diff --git a/oml/tasks.py b/oml/tasks.py index ba5bfc1..d08b0ab 100644 --- a/oml/tasks.py +++ b/oml/tasks.py @@ -6,7 +6,6 @@ from queue import Queue from threading import Thread from websocket import trigger_event -import state import logging logger = logging.getLogger(__name__) @@ -23,6 +22,7 @@ class Tasks(Thread): def run(self): import item.scan + from user.models import List while self.connected: m = self.q.get() if m: @@ -32,6 +32,8 @@ class Tasks(Thread): trigger_event('pong', data) elif action == 'import': item.scan.run_import(data) + elif action == 'export': + List.export(data) elif action == 'scan': item.scan.run_scan() else: diff --git a/oml/user/models.py b/oml/user/models.py index c58e96c..acd4604 100644 --- a/oml/user/models.py +++ b/oml/user/models.py @@ -3,7 +3,10 @@ from datetime import datetime import json import hashlib +import os +import shutil +import ox import sqlalchemy as sa from changelog import Changelog @@ -13,6 +16,8 @@ import json_pickler import settings import state import utils +import media +from websocket import trigger_event import logging logger = logging.getLogger(__name__) @@ -323,6 +328,87 @@ class List(db.Model): state.db.session.add(self) state.db.session.commit() + def create_symlinks(self): + pass + + @classmethod + def export(cls, data): + with db.session(): + self = cls.get(data['list']) + if not self: + return + mode = data.get('mode') + prefix = data.get('path') + if mode not in ('add', 'replace'): + logger.debug('invalid mode %s', mode) + return + if not prefix or prefix == '/': + logger.debug('invalid export path %s', prefix) + trigger_event('activity', { + 'activity': 'export', + 'path': prefix, + 'progress': [0, 0], + 'status': {'code': 404, 'text': 'invalid export path'} + }) + return + root = prefix + while not os.path.exists(root) and root != '/': + root = os.path.dirname(root) + if not os.access(root, os.W_OK): + logger.debug('can not write to %s', root) + trigger_event('activity', { + 'activity': 'export', + 'path': prefix, + 'progress': [0, 0], + 'path': prefix, + 'status': {'code': 404, 'text': 'permission denied'} + }) + return + if os.path.exists(prefix): + existing_files = set( + os.path.join(root, f) for root, _, files in os.walk(prefix) for f in files + ) + else: + existing_files = set() + new_files = set() + count = self.get_items().count() + n = 1 + for i in self.get_items(): + if i.files.all(): + f = i.files.all()[0] + source = f.fullpath() + target = os.path.join(prefix, f.path) + if mode == 'add': + p = 1 + parts = target.rsplit('.', 1) + while os.path.exists(target) and media.get_id(target) != f.sha1: + target = '.'.join([parts[0], f.sha1[:p], parts[1]]) + p += 1 + ox.makedirs(os.path.dirname(target)) + if os.path.exists(target): + if mode == 'replace' and media.get_id(target) != f.sha1: + os.unlink(target) + shutil.copy2(source, target) + else: + shutil.copy2(source, target) + new_files.add(target) + trigger_event('activity', { + 'activity': 'export', + 'path': prefix, + 'progress': [n, count] + }) + n += 1 + if mode == 'replace': + for f in list(existing_files - new_files): + os.unlink(f) + utils.remove_empty_folders(prefix) + trigger_event('activity', { + 'activity': 'export', + 'progress': [count, count], + 'path': prefix, + 'status': {'code': 200, 'text': ''}, + }) + class Metadata(db.Model): __tablename__ = 'user_metadata' diff --git a/static/js/importExportDialog.js b/static/js/importExportDialog.js index b654ed9..1480f8b 100644 --- a/static/js/importExportDialog.js +++ b/static/js/importExportDialog.js @@ -331,7 +331,7 @@ oml.ui.importExportDialog = function() { ); $innerPanel.replaceElement(0, renderActivity({ - activity: 'import', + activity: selected, path: data.path, progress: [0, 0] }) @@ -341,7 +341,7 @@ oml.ui.importExportDialog = function() { if (result) { data.list = result.data.id } - oml.api.import({ + oml.api[selected]({ list: data.list, mode: data.mode, path: data.path,