From 6940477e48b005fac853b581e7990a70354e6ff1 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 7 Sep 2011 17:21:49 +0200 Subject: [PATCH] Profile sorting --- couchpotato/core/helpers/request.py | 4 +- couchpotato/core/helpers/variable.py | 11 +++ couchpotato/core/plugins/profile/main.py | 20 +++++ .../core/plugins/profile/static/profile.css | 50 +++++++++++- couchpotato/core/plugins/quality/static/quality.js | 92 +++++++++++++++++++--- 5 files changed, 162 insertions(+), 15 deletions(-) diff --git a/couchpotato/core/helpers/request.py b/couchpotato/core/helpers/request.py index a26ab81..6ffb7d5 100644 --- a/couchpotato/core/helpers/request.py +++ b/couchpotato/core/helpers/request.py @@ -1,3 +1,4 @@ +from couchpotato.core.helpers.variable import natcmp from flask.globals import current_app from flask.helpers import json from libs.werkzeug.urls import url_decode @@ -42,7 +43,7 @@ def dictToList(params): new = {} for x, value in params.iteritems(): try: - new_value = [dictToList(value[k]) for k in sorted(value.iterkeys())] + new_value = [dictToList(value[k]) for k in sorted(value.iterkeys(), cmp = natcmp)] except: new_value = value @@ -70,4 +71,3 @@ def jsonified(*args, **kwargs): return padded_jsonify(callback, *args, **kwargs) else: return jsonify('text/javascript' if Env.doDebug() else 'application/json', *args, **kwargs) - diff --git a/couchpotato/core/helpers/variable.py b/couchpotato/core/helpers/variable.py index dc92f6a..5dc7319 100644 --- a/couchpotato/core/helpers/variable.py +++ b/couchpotato/core/helpers/variable.py @@ -1,5 +1,6 @@ import hashlib import os.path +import re def isDict(object): return isinstance(object, dict) @@ -41,3 +42,13 @@ def cleanHost(host): host += '/' return host + +def tryInt(s): + try: return int(s) + except: return s + +def natsortKey(s): + return map(tryInt, re.findall(r'(\d+|\D+)', s)) + +def natcmp(a, b): + return cmp(natsortKey(a), natsortKey(b)) diff --git a/couchpotato/core/plugins/profile/main.py b/couchpotato/core/plugins/profile/main.py index 34e5154..739ef76 100644 --- a/couchpotato/core/plugins/profile/main.py +++ b/couchpotato/core/plugins/profile/main.py @@ -16,6 +16,7 @@ class ProfilePlugin(Plugin): addEvent('profile.all', self.all) addApiView('profile.save', self.save) + addApiView('profile.save_order', self.saveOrder) addApiView('profile.delete', self.delete) addEvent('app.initialize', self.fill) @@ -70,6 +71,25 @@ class ProfilePlugin(Plugin): 'profile': profile_dict }) + def saveOrder(self): + + params = getParams() + db = get_session() + + order = 0 + for profile in params.get('ids', []): + p = db.query(Profile).filter_by(id = profile).first() + p.hide = params.get('hidden')[order] + p.order = order + + order += 1 + + db.commit() + + return jsonified({ + 'success': True + }) + def delete(self): id = getParam('id') diff --git a/couchpotato/core/plugins/profile/static/profile.css b/couchpotato/core/plugins/profile/static/profile.css index 1bb3b94..8cb18e3 100644 --- a/couchpotato/core/plugins/profile/static/profile.css +++ b/couchpotato/core/plugins/profile/static/profile.css @@ -45,6 +45,7 @@ .profile .types li { padding: 3px 5px; border-bottom: 1px solid rgba(255,255,255,0.2); + list-style: none; } .profile .types li:last-child { border: 0; } @@ -85,4 +86,51 @@ .profile .types .type:hover:not(.is_empty) .delete { visibility: visible; - } \ No newline at end of file + } + +#profile_ordering { + +} + + #profile_ordering .head { + float: left; + } + + #profile_ordering .head span { + margin: 0 10px 0 -5px; + } + + #profile_ordering ul { + clear: both; + float: left; + margin-left: 20%; + width: 275px; + } + + #profile_ordering li { + clear: both; + overflow: hidden; + cursor: grab; + cursor: -moz-grab; + cursor: -webkit-grab; + border-bottom: 1px solid rgba(255,255,255,0.2); + padding: 0 5px; + } + + #profile_ordering li .check { + margin: 2px 10px 0 0; + vertical-align: top; + } + + #profile_ordering li > span { + display: inline-block; + height: 20px; + vertical-align: top; + line-height: 20px; + } + + #profile_ordering li .handle { + background: url('./handle.png') center; + width: 20px; + float: right; + } \ No newline at end of file diff --git a/couchpotato/core/plugins/quality/static/quality.js b/couchpotato/core/plugins/quality/static/quality.js index 5d3fad2..a20eeef 100644 --- a/couchpotato/core/plugins/quality/static/quality.js +++ b/couchpotato/core/plugins/quality/static/quality.js @@ -39,6 +39,7 @@ var QualityBase = new Class({ self.content = tab.content; self.createProfiles(); + self.createProfileOrdering(); self.createSizes(); }) @@ -67,6 +68,7 @@ var QualityBase = new Class({ self.profile_container = new Element('div.container') ) + // Add profiles, that aren't part of the core (for editing) Object.each(self.profiles, function(profile){ if(!profile.isCore()) $(profile).inject(self.profile_container, 'top') @@ -77,15 +79,80 @@ var QualityBase = new Class({ createProfilesClass: function(data){ var self = this; - if(data){ - return self.profiles[data.id] = new Profile(data); - } - else { - var data = { - 'id': randomString() + var data = data || {'id': randomString()} + + return self.profiles[data.id] = new Profile(data); + }, + + createProfileOrdering: function(){ + var self = this; + + var profile_list; + var group = self.settings.createGroup({ + 'label': 'Profile Order', + 'description': 'Change the order the profiles are in the dropdown list.' + }).adopt( + new Element('.ctrlHolder#profile_ordering').adopt( + new Element('label[text=Order]'), + new Element('.head').adopt( + new Element('span.show[text=Show]'), + new Element('span.profile_label[text=Profile]') + ), + profile_list = new Element('ul') + ) + ).inject(self.content) + + + + + Object.each(self.profiles, function(profile){ + var check; + new Element('li', {'data-id': profile.data.id}).adopt( + check = new Element('input.inlay[type=checkbox]', { + 'checked': !profile.data.hide, + 'events': { + 'change': self.saveProfileOrdering.bind(self) + } + }), + new Element('span.profile_label', { + 'text': profile.data.label + }), + new Element('span.handle') + ).inject(profile_list); + + new Form.Check(check); + + }); + + // Sortable + self.profile_sortable = new Sortables(profile_list, { + 'revert': true, + 'handle': '', + 'opacity': 0.5, + 'onComplete': self.saveProfileOrdering.bind(self) + }); + + }, + + saveProfileOrdering: function(){ + var self = this; + + var ids = []; + var hidden = []; + + self.profile_sortable.list.getElements('li').each(function(el, nr){ + ids.include(el.get('data-id')); + hidden[nr] = +!el.getElement('input[type=checkbox]').get('checked'); + }); + + p(ids, hidden); + Api.request('profile.save_order', { + 'data': { + 'ids': ids, + 'hidden': hidden } - return self.profiles[data.id] = new Profile(data); - } + }); + }, /** @@ -99,21 +166,22 @@ var QualityBase = new Class({ 'description': 'Edit the minimal and maximum sizes (in MB) for each quality.', 'advanced': true }).inject(self.content) - + + new Element('div.item.head').adopt( new Element('span.label', {'text': 'Quality'}), new Element('span.min', {'text': 'Min'}), new Element('span.max', {'text': 'Max'}) ).inject(group) - + Object.each(self.qualities, function(quality){ - new Element('div.item').adopt( + new Element('div.ctrlHolder.item').adopt( new Element('span.label', {'text': quality.label}), new Element('input.min', {'value': quality.size_min}), new Element('input.max', {'value': quality.size_max}) ).inject(group) }); - + } });