From 996a754db58513c9f8195efe23d4775c8f615236 Mon Sep 17 00:00:00 2001 From: j Date: Mon, 19 May 2014 22:58:00 +0200 Subject: [PATCH] allow custom metadata --- oml/changelog.py | 18 +++--- oml/item/api.py | 11 ++-- oml/item/models.py | 24 ++++++-- oml/item/views.py | 4 +- oml/meta/__init__.py | 6 +- oml/meta/loc.py | 5 +- oml/oxflask/api.py | 3 +- static/js/browser.js | 2 +- static/js/gridView.js | 2 +- static/js/identifyDialog.js | 77 ++++++++++++------------ static/js/infoView.js | 116 ++++++++++++------------------------ static/txt.js/txt.js | 2 +- 12 files changed, 126 insertions(+), 144 deletions(-) diff --git a/oml/changelog.py b/oml/changelog.py index 85c0411..a482847 100644 --- a/oml/changelog.py +++ b/oml/changelog.py @@ -161,13 +161,17 @@ class Changelog(db.Model): i = Item.get(itemid) if i.timestamp > timestamp: return True - key = meta.keys()[0] - if not meta[key] and i.meta.get('mainid') == key: - logger.debug('remove id mapping %s currenrlt %s', key, meta[key], i.meta[key]) - i.update_mainid(key, meta[key]) - elif meta[key] and (i.meta.get('mainid') != key or meta[key] != i.meta.get(key)): - logger.debug('new mapping %s %s currently %s %s', key, meta[key], i.meta.get('mainid'), i.meta.get(i.meta.get('mainid'))) - i.update_mainid(key, meta[key]) + keys = filter(lambda k: k in Item.id_keys, meta.keys()) + if keys: + key = keys[0] + if not meta[key] and i.meta.get('mainid') == key: + logger.debug('remove id mapping %s currently %s', key, meta[key], i.meta[key]) + i.update_mainid(key, meta[key]) + elif meta[key] and (i.meta.get('mainid') != key or meta[key] != i.meta.get(key)): + logger.debug('new mapping %s %s currently %s %s', key, meta[key], i.meta.get('mainid'), i.meta.get(i.meta.get('mainid'))) + i.update_mainid(key, meta[key]) + else: + i.update_meta(meta) i.modified = datetime.fromtimestamp(float(timestamp)) i.save() return True diff --git a/oml/item/api.py b/oml/item/api.py index f293efa..77a99bc 100644 --- a/oml/item/api.py +++ b/oml/item/api.py @@ -125,12 +125,11 @@ def edit(data): item.update_mainid(key, data[key]) response = item.json() elif not item.meta.get('mainid'): - logger.debug('chustom data %s', data) - for key in ('title', 'author', 'date', 'publisher', 'edition'): - if key in data: - item.meta[key] = data[key] - item.update() - logger.debug('FIXME: custom metadata not published to changelog!!!') + logger.debug('setting chustom metadata %s', data) + item.update_meta(data) + response = item.json() + else: + logger.debug('invalid metadata %s', data) else: logger.info('can only edit available items') return response diff --git a/oml/item/models.py b/oml/item/models.py index a0f7419..8276be9 100644 --- a/oml/item/models.py +++ b/oml/item/models.py @@ -18,8 +18,6 @@ import ox import settings from settings import db, config -from user.models import User - from person import get_sort_name import media @@ -250,12 +248,17 @@ class Item(db.Model): db.session.add(f) def update(self): + for key in ('mediastate', 'coverRatio'): + if key in self.meta: + if key not in self.info: + self.info[key] = self.meta[key] + del self.meta[key] users = map(str, list(self.users)) - self.meta['mediastate'] = 'available' # available, unavailable, transferring + self.info['mediastate'] = 'available' # available, unavailable, transferring if self.transferadded and self.transferprogress < 1: - self.meta['mediastate'] = 'transferring' + self.info['mediastate'] = 'transferring' else: - self.meta['mediastate'] = 'available' if settings.USER_ID in users else 'unavailable' + self.info['mediastate'] = 'available' if settings.USER_ID in users else 'unavailable' self.update_sort() self.update_find() self.update_lists() @@ -266,6 +269,15 @@ class Item(db.Model): db.session.add(self) db.session.commit() + def update_meta(self, data): + self.meta = data + self.update() + self.modified = datetime.now() + self.save() + user = state.user() + if user in self.users: + Changelog.record(user, 'edititem', self.id, data) + def update_mainid(self, key, id): record = {} if id: @@ -317,7 +329,7 @@ class Item(db.Model): covers[self.id] = cover if cover: img = Image.open(StringIO(cover)) - self.meta['coverRatio'] = img.size[0]/img.size[1] + self.info['coverRatio'] = img.size[0]/img.size[1] for p in (':128', ':256', ':512'): del covers['%s%s' % (self.id, p)] return cover diff --git a/oml/item/views.py b/oml/item/views.py index f000f84..5473d05 100644 --- a/oml/item/views.py +++ b/oml/item/views.py @@ -73,10 +73,10 @@ def cover(id, size=None): if size: data = covers['%s:%s' % (id, size)] = resize_image(data, size=size) data = str(data) - if not 'coverRatio' in item.meta: + if not 'coverRatio' in item.info: #img = Image.open(StringIO(str(covers[id]))) img = Image.open(StringIO(data)) - item.meta['coverRatio'] = img.size[0]/img.size[1] + item.info['coverRatio'] = img.size[0]/img.size[1] db.session.add(item) db.session.commit() resp = make_response(data) diff --git a/oml/meta/__init__.py b/oml/meta/__init__.py index 329a832..5c2f001 100644 --- a/oml/meta/__init__.py +++ b/oml/meta/__init__.py @@ -24,7 +24,11 @@ providers = [ ('abebooks', 'isbn10') ] -def find(title, author=None, publisher=None, date=None): +def find(**kargs): + title = kargs.get('title') + author = kargs.get('author') + publisher = kargs.get('publisher') + date = kargs.get('date') #results = google.find(title=title, author=author, publisher=publisher, date=date) results = duckduckgo.find(title=title, author=author, publisher=publisher, date=date) ''' diff --git a/oml/meta/loc.py b/oml/meta/loc.py index b1eb4d7..3fcfde4 100644 --- a/oml/meta/loc.py +++ b/oml/meta/loc.py @@ -38,7 +38,7 @@ def lookup(id): title = mods.findall(ns + 'titleInfo') if not title: return {} - info['title'] = ''.join([e.text for e in title[0]]) + info['title'] = ''.join([': ' + e.text.strip() if e.tag == ns + 'subTitle' else ' ' + e.text.strip() for e in title[0]]).strip() origin = mods.findall(ns + 'originInfo') if origin: info['place'] = [] @@ -70,6 +70,9 @@ def lookup(id): if a.attrib.get('usage') == 'primary': info['author'].append(' '.join([e.text for e in a.findall(ns + 'namePart') if not e.attrib.get('type') in ('date', )])) info['author'] = [ox.normalize_name(a) for a in info['author']] + toc = mods.findall(ns + 'tableOfContents') + if toc: + info['description'] = toc[0].text.strip() for key in info.keys(): if not info[key]: del info[key] diff --git a/oml/oxflask/api.py b/oml/oxflask/api.py index 4a7aab8..c74570f 100644 --- a/oml/oxflask/api.py +++ b/oml/oxflask/api.py @@ -5,12 +5,13 @@ from __future__ import division, with_statement import inspect import sys import json -import logging from flask import request, Blueprint from .shortcuts import render_to_json_response, json_response +import logging logger = logging.getLogger('oxflask.api') + app = Blueprint('oxflask', __name__) @app.route('/api/', methods=['POST', 'OPTIONS']) diff --git a/static/js/browser.js b/static/js/browser.js index 90629d2..3c72960 100644 --- a/static/js/browser.js +++ b/static/js/browser.js @@ -29,7 +29,7 @@ oml.ui.browser = function() { borderWidth: Math.round(size / 64) + 'px 0', borderStyle: 'solid', borderColor: 'rgba(' + color[2].join(', ') + ')', - margin: Math.round(size / 18) + 'px ' + Math.round(width / 3) + 'px', + margin: Math.round(size / 18) + 'px ' + Math.round(width / 2 - 14) + 'px', fontSize: Math.round(size / 16) + 'px', textAlign: 'center', color: 'rgba(' + color[2].join(', ') + ')', diff --git a/static/js/gridView.js b/static/js/gridView.js index ccb5da7..276c905 100644 --- a/static/js/gridView.js +++ b/static/js/gridView.js @@ -28,7 +28,7 @@ oml.ui.gridView = function() { borderWidth: Math.round(size / 64) + 'px 0', borderStyle: 'solid', borderColor: 'rgba(' + color[2].join(', ') + ')', - margin: Math.round(size / 18) + 'px ' + Math.round(width / 3) + 'px', + margin: Math.round(size / 18) + 'px ' + Math.round(width / 2 - 14) + 'px', fontSize: Math.round(size / 16) + 'px', textAlign: 'center', color: 'rgba(' + color[2].join(', ') + ')', diff --git a/static/js/identifyDialog.js b/static/js/identifyDialog.js index 4196f72..d517dfc 100644 --- a/static/js/identifyDialog.js +++ b/static/js/identifyDialog.js @@ -14,19 +14,12 @@ oml.ui.identifyDialog = function(data) { }), keys = [ - 'title', 'author', 'publisher', 'date' + 'title', 'author', 'publisher', 'date', 'edition', 'language' ].map(function(id) { - var key = Ox.getObjectById(oml.config.sortKeys, id); + var key = Ox.getObjectById(oml.config.itemKeys, id); return { format: key.format, id: id, - operator: key.operator, - width: { - title: 288, - author: 224, - publisher: 160, - date: 96 - Ox.UI.SCROLLBAR_SIZE - }[id], title: key.title, visible: true }; @@ -61,7 +54,7 @@ oml.ui.identifyDialog = function(data) { $titlePanel = Ox.SplitPanel({ elements: [ {element: $titleForm, size: 96}, - {element: Ox.Element()} + {element: renderResults()} ], orientation: 'vertical' }), @@ -134,12 +127,13 @@ oml.ui.identifyDialog = function(data) { ); that.options({content: Ox.LoadingScreen().start()}); that.disableButtons(); + Ox.print('VALUE SENT:', edit) oml.api.edit(edit, function(result) { that.close(); Ox.Request.clearCache('find'); oml.$ui.browser.reloadList(true); Ox.Request.clearCache(data.id); - oml.$ui.infoView.updateElement(result.data); + oml.$ui.infoView.updateElement(data.id); }); } }) @@ -163,7 +157,6 @@ oml.ui.identifyDialog = function(data) { disableButtons(); $titlePanel.replaceElement(1, Ox.LoadingScreen().start()); oml.api.findMetadata(data, function(result) { - // FIXME: CONCAT HERE var items = result.data.items.map(function(item, index) { return Ox.extend({index: (index + 1).toString()}, item); }); @@ -352,42 +345,48 @@ oml.ui.identifyDialog = function(data) { var $list = Ox.TableList({ columns: [ { - format: function(value) { - return Ox.getObjectById(ids, value).title; + format: function(value, data) { + return value + ? '' + Ox.getObjectById(ids, value).title + + ': ' + data[data.mainid] + : 'No ID' }, id: 'mainid', visible: true, - width: 64 - }, - { - format: function(value, data) { - return data[data.mainid]; - }, - id: 'index', - visible: true, - width: 128 - Ox.UI.SCROLLBAR_SIZE + width: 192 - Ox.UI.SCROLLBAR_SIZE } ], - items: items, + items: [{ + 'index': '0', + 'mainid': '' + }].concat(items || []), keys: ['mainid', 'isbn10', 'isbn13'], min: 1, max: 1, scrollbarVisible: true, - sort: [{key: 'mainid', operator: '+'}], + sort: [{key: 'index', operator: '+'}], unique: 'index' }) .bindEvent({ select: function(data) { - var index = data.ids[0], mainid; - mainid = $list.value(index, 'mainid'); - titleValue = Ox.extend({}, mainid, $list.value(index, mainid)); - $results.replaceElement(1, Ox.LoadingScreen().start()); - oml.api.getMetadata(titleValue, function(result) { - if (index == $list.options('selected')[0]) { - $results.replaceElement(1, oml.ui.infoView(result.data)); - that.options('buttons')[1].options({disabled: false}); - } - }); + var index = data.ids[0], + mainid = $list.value(index, 'mainid'); + if (!mainid) { + titleValue = {}; + keys.forEach(function(key) { + titleValue[key.id] = titleInputValue(key.id); + }); + $results.replaceElement(1, oml.ui.infoView(titleValue)); + } else { + titleValue = Ox.extend({}, mainid, $list.value(index, mainid)); + $results.replaceElement(1, Ox.LoadingScreen().start()); + oml.api.getMetadata(titleValue, function(result) { + if (index == $list.options('selected')[0]) { + $results.replaceElement(1, oml.ui.infoView(result.data)); + that.options('buttons')[1].options({disabled: false}); + } + }); + } } }), $results = Ox.SplitPanel({ @@ -405,14 +404,14 @@ oml.ui.identifyDialog = function(data) { $titleInputs = keys.map(function(key, index) { return Ox.Input({ label: Ox._(key.title), - labelWidth: 64, + labelWidth: 80, value: data[key.id], - width: 360 + width: 240 }) .css({ position: 'absolute', - left: index < 2 ? '16px' : '392px', - top: index % 2 == 0 ? '16px' : '40px' + left: 16 + Math.floor(index / 2) * 248 + 'px', + top: 16 + (index % 2) * 24 + 'px' }) .bindEvent({ submit: function(data) { diff --git a/static/js/infoView.js b/static/js/infoView.js index 2832347..a459c33 100644 --- a/static/js/infoView.js +++ b/static/js/infoView.js @@ -4,7 +4,9 @@ oml.ui.infoView = function(identifyData) { var ui = oml.user.ui, - css = getCSS(ui.coverSize, oml.config.coverRatio), + coverSize = identifyData ? 256 : ui.coverSize, + + css = getCSS(coverSize, oml.config.coverRatio), that = Ox.Element() .addClass('OxTextPage') @@ -39,7 +41,7 @@ oml.ui.infoView = function(identifyData) { right: !identifyData ? '176px' : 16 + Ox.UI.SCROLLBAR_SIZE + 'px', top: '16px' }) - [ui.coverSize == 512 ? 'hide' : 'show']() + [coverSize == 512 ? 'hide' : 'show']() .appendTo(that), $data, @@ -226,8 +228,9 @@ oml.ui.infoView = function(identifyData) { } function toggleCoverSize(ratio) { - var coverSize = ui.coverSize == 256 ? 512 : 256, - css = getCSS(coverSize, ratio); + var css; + coverSize = coverSize == 256 ? 512 : 256, + css = getCSS(coverSize, ratio); //$cover.animate(css.cover, 250); $info.animate(css.info, 250); $image.animate(css.image, 250); @@ -237,7 +240,7 @@ oml.ui.infoView = function(identifyData) { } function updateCover(ratio) { - var css = getCSS(ui.coverSize, ratio); + var css = getCSS(coverSize, ratio); $image.css(css.image).show(); $reflectionImage.css(css.image); $reflection.css(css.reflection).show(); @@ -271,7 +274,7 @@ oml.ui.infoView = function(identifyData) { ? '/' + data.id + '/cover512.jpg?' + data.modified : data.cover, ratio = data.coverRatio || oml.config.coverRatio, - size = ui.coverSize, + size = coverSize, reflectionSize = Math.round(size / 2); $elements.forEach(function($element) { @@ -307,7 +310,9 @@ oml.ui.infoView = function(identifyData) { .hide() .bindEvent({ singleclick: function() { - toggleCoverSize(ratio); + if (!identifyData) { + toggleCoverSize(ratio); + } } }) .appendTo($cover); @@ -341,101 +346,56 @@ oml.ui.infoView = function(identifyData) { } else if ($element == $info) { $('
') - .css({marginTop: '-2px'}) - .append( - Ox.EditableContent({ - clickLink: oml.clickLink, - editable: isEditable, - placeholder: formatLight(Ox._('Unknown Title')), - tooltip: isEditable ? oml.getEditTooltip() : '', - value: data.title || '' - }) - .bindEvent({ - submit: function(data) { - editMetadata('title', data.value); - } - }) - .css({ - fontSize: '13px', - fontWeight: 'bold' - }) + .css({ + marginTop: '-2px', + fontSize: '13px', + fontWeight: 'bold' + }) + .html( + data.title + || '' + + Ox._('No Title') + + '' ) .appendTo($info); - if (data.author || isEditable) { + if (data.author) { $('
') .css({ marginTop: '4px', fontSize: '13px', fontWeight: 'bold' }) - .append( - Ox.EditableContent({ - clickLink: oml.clickLink, - editable: isEditable, - format: function(value) { - return !identifyData - ? formatValue(value.split(', '), 'author') - : value; - }, - placeholder: formatLight(Ox._('Unknown Author')), - tooltip: isEditable ? oml.getEditTooltip() : '', - value: data.author ? data.author.join(', ') : '' - }) - .css({ - fontSize: '13px', - fontWeight: 'bold' - }) - .bindEvent({ - submit: function(data) { - editMetadata('author', value.split(', ')); - } - }) - ) + .html(formatValue(data.author, 'author')) .appendTo($info); } - if (!isEditable) { + if (data.place || data.publisher || data.date) { $('
') .css({ marginTop: '8px' }) .text( - (data.place || '') + (data.place || []).join(' ; ') + (data.place && (data.publisher || data.date) ? ' : ' : '') + (data.publisher || '') + (data.publisher && data.date ? ', ' : '') + (data.date || '') ) .appendTo($info); - } else { - var $div = $('
') - .addClass('OxSelectable') - .css({marginTop: '8px'}) + } + + if (data.edition || data.language) { + $('
') + .css({ + marginTop: '8px' + }) + .text( + (data.edition || '') + + (data.edition && data.language ? '; ' : '') + + (data.language || '') + ) .appendTo($info); - ['edition', 'publisher', 'date'].forEach(function(key, index) { - index && $('
').css({float: 'left'}).html('; ').appendTo($div); - $('
') - .css({float: 'left'}) - .html(formatKey(key)) - .appendTo($div); - Ox.EditableContent({ - clickLink: oml.clickLink, - format: function(value) { - return formatValue(value.split(', '), key) - }, - placeholder: formatLight('unknown'), - tooltip: oml.getEditTooltip(), - value: data[key] || '' - }) - .css({float: 'left'}) - .bindEvent({ - submit: function(event) { - editMetadata(key, event.value); - } - }) - .appendTo($div); - }); } if (data.classification) { diff --git a/static/txt.js/txt.js b/static/txt.js/txt.js index 4b12048..8687c21 100644 --- a/static/txt.js/txt.js +++ b/static/txt.js/txt.js @@ -42,7 +42,7 @@ txtjs.open = function(url) { scale; text = Ox.encodeHTMLEntities(text) .replace(/\r\n/g, '\n') - .replace(/\n/g, '
'); + .replace(/[\r\n]/g, '
'); $text.html(text); $scrollText.html(text); var textHeight = $text[0].clientHeight,