diff --git a/beat/web/experiments/static/experiments/js/utils.js b/beat/web/experiments/static/experiments/js/utils.js index 49efd3000c80d1078f5836ee609f2a66b3cd654b..762e200945fedc9885054c18c5490e7c7981908f 100644 --- a/beat/web/experiments/static/experiments/js/utils.js +++ b/beat/web/experiments/static/experiments/js/utils.js @@ -18,7 +18,7 @@ if (beat.experiments.utils === undefined) //---------------------------------------------------------------------------------------- // Display an experiment result in a DOM element //---------------------------------------------------------------------------------------- -beat.experiments.utils.displayResult = function(element, value, type, colors, additional_params, callback) +beat.experiments.utils.displayResult = function(element, value, type, colors, additional_params, selected_plot_template, accessible_plot_templates, callback) { //Function to read plot settings function _read_plot_settings() @@ -359,6 +359,27 @@ beat.experiments.utils.displayResult = function(element, value, type, colors, ad settings_button.className = 'settings'; settings_button.id = 'settings_'+element.id; + var settings_select = document.createElement('select'); + settings_select.className = 'settings_select'; + settings_select.id = 'settings_select_'+element.id; + + for(var j = 0; j<accessible_plot_templates.length; j++) + { + var option_select = document.createElement('option'); + option_select.className = 'option_select'; + option_select.id = 'option_select_'+element.id; + option_select.value = accessible_plot_templates[j].name; + + if(accessible_plot_templates[j].name == selected_plot_template) + { + option_select.selected = true; + } + + option_select.innerHTML = accessible_plot_templates[j].name; + + settings_select.appendChild(option_select); + } + var png_button = document.createElement('span'); png_button.className = 'pngbtn'; png_button.id = 'pngbtn_'+element.id; @@ -379,27 +400,9 @@ beat.experiments.utils.displayResult = function(element, value, type, colors, ad settings_plot.appendChild(settings_popup); + $(element).prepend(settings_select); $(element).prepend(settings_button); $(element).prepend(png_button); - //var settings_overlay = document.createElement('div'); - //settings_overlay.className = 'ui-widget-overlay ui-front'; - - - //// Behavior - //$(settings_overlay).click(function() { - // $(settings_overlay).hide(); - // document.getElementById('settings_plot_'+element.id).style.display = "none"; - // //$(settings_overlay).remove(); - // //settings_overlay = null; - // //panel.background = null; - - // //if (panel.onCancel) - // // panel.onCancel(); - - // //$(window).off('keydown.list_selector'); - - // //return false; - // }); $(element).append(settings_plot); document.getElementById('settings_plot_'+element.id).style.display = "none"; @@ -414,6 +417,22 @@ beat.experiments.utils.displayResult = function(element, value, type, colors, ad }); }); + $(settings_select).on('change', function() { + console.log($(settings_select).val()); + for(var j = 0; j<accessible_plot_templates.length; j++) + { + + if(accessible_plot_templates[j].name == $(settings_select).val()) + { + var new_params = JSON.parse(accessible_plot_templates[j].data); + _regenerate_graph(element, width, height, new_params); + } + + + settings_select.appendChild(option_select); + } + + }); diff --git a/beat/web/navigation/admin.py b/beat/web/navigation/admin.py index 52258e147dd23dcedd17c5fb07eb5441b71123c5..66b57871187205af3217f0b266f2688db266b214 100644 --- a/beat/web/navigation/admin.py +++ b/beat/web/navigation/admin.py @@ -12,11 +12,23 @@ from django.contrib.auth.models import User from .models import Agreement from .models import Search +from .models import PlotTemplate +from .models import PlotRelationship class AgreementInline(admin.StackedInline): model = Agreement +class PlotRelationshipInline(admin.StackedInline): + model = PlotRelationship + +def rehash_search(modeladmin, request, queryset): + """Recalculates the hash of a search""" + + for q in queryset: q.save() + +rehash_search.short_description = 'Rehash selected search' + class SearchAdmin(admin.ModelAdmin): list_display = ('id', 'author', 'name', 'version', 'options', 'filters', 'settings', 'analyzer', 'plot_parameters') @@ -27,6 +39,15 @@ class SearchAdmin(admin.ModelAdmin): list_display_links = ('id', 'options', 'filters', 'settings', 'analyzer', 'plot_parameters') list_filter = ('options', 'filters', 'settings', 'analyzer', 'plot_parameters') + readonly_fields = ('hash',) + + actions = [ + rehash_search, + ] + + inlines = ( + PlotRelationshipInline, + ) admin.site.register(Search, SearchAdmin) @@ -51,3 +72,28 @@ class UserAdmin(UserAdmin): admin.site.unregister(User) admin.site.register(User, UserAdmin) + +def rehash_plotTemplate(modeladmin, request, queryset): + """Recalculates the hash of a plot template""" + + for q in queryset: q.save() + +rehash_plotTemplate.short_description = 'Rehash selected plot templates' + +class PlotTemplateAdmin(admin.ModelAdmin): + + list_display = ('id', 'author', 'name', 'version', 'dataformat', 'data', 'default') + search_fields = ['dataformat__name', + 'authors__username', + 'name', + ] + list_display_links = ('id', 'name') + + list_filter = ('default',) + readonly_fields = ('hash',) + + actions = [ + rehash_plotTemplate, + ] + +admin.site.register(PlotTemplate, PlotTemplateAdmin) diff --git a/beat/web/navigation/api.py b/beat/web/navigation/api.py index 9ba0e9af04034add3d3de3668e94409abe0889dc..1e5e79b5803c5fad9a2b3cbad829d97a54aed522 100644 --- a/beat/web/navigation/api.py +++ b/beat/web/navigation/api.py @@ -13,7 +13,9 @@ from ..experiments.models import Experiment from ..experiments.utils import experiment_results_to_json from ..toolchains.models import Toolchain from ..common.models import Shareable +from ..team.models import Team from .models import Search +from .models import PlotTemplate from ..utils.api import check_api_user from ..utils.api import jsonify @@ -22,6 +24,7 @@ from ..utils.api import report_server_error from .templatetags.gravatar import gravatar_hash from .utils import share_search +from .utils import plot_template_to_json import simplejson as json import re @@ -47,11 +50,14 @@ def search(request): try: data = json.loads(request.body) - + print "++++" + print data + print "++++" query = None filters = None options = None plot_parameters = None + plots_relationship = None table_settings = None if data.has_key('query'): @@ -94,6 +100,9 @@ def search(request): if data.has_key('settings'): table_settings = data['settings'] + + if data.has_key('plots_relationship'): + plots_relationship = data['plots_relationship'] except: return HttpResponse(status=400) @@ -208,6 +217,7 @@ def search(request): 'filters': filters, 'options': options, 'plot_parameters': plot_parameters, + 'plots_relationship': plots_relationship, 'table_settings': table_settings, 'query': { 'type': scope_type, @@ -376,6 +386,153 @@ def search_save(request): #------------------------------------------------ +def accessible_plot_templates(request): + + def _addToResult(result, plot_templates, accessibility, fields_to_return, + limit_to_latest_versions): + + for plot_template in plot_templates: + try: + entry = filter(lambda x: x['name'] == plot_template.fullname(), result)[0] + continue + except: + pass + + if limit_to_latest_versions: + last_version = True + + try: + entry = filter(lambda x: x['name'].startswith('%s/%s/' % (plot_template.author.username, plot_template.name)), result)[0] + + if entry['version'] >= plot_template.version: + continue + else: + result.remove(entry) + except: + pass + + elif 'last_version' in fields_to_return: + entries = filter(lambda x: x['name'].startswith('%s/%s/' % (plot_template.author.username, plot_template.name)), result) + entries.sort(lambda a, b: cmp(a['version'], b['version'])) + + if len(entries) > 0: + if entries[-1]['version'] >= plot_template.version: + last_version = False + else: + last_version = True + entries[-1]['last_version'] = False + else: + last_version = True + + entry = {} + + if 'name' in fields_to_return: + entry['name'] = plot_template.fullname() + + if 'version' in fields_to_return: + entry['version'] = plot_template.version + + if 'last_version' in fields_to_return: + entry['last_version'] = last_version + + if 'short_description' in fields_to_return: + entry['short_description'] = plot_template.short_description + + if 'description' in fields_to_return: + entry['description'] = plot_template.short_description + + if 'is_owner' in fields_to_return: + entry['is_owner'] = False + + if 'fork_of' in fields_to_return: + entry['fork_of'] = (plot_template.fork_of.fullname() if plot_template.fork_of is not None else None) + + if 'previous_version' in fields_to_return: + entry['previous_version'] = (plot_template.previous_version.fullname() if plot_template.previous_version is not None else None) + + if 'accessibility' in fields_to_return: + entry['accessibility'] = accessibility + + if 'hash' in fields_to_return: + entry['hash'] = plot_template.hash + + if 'creation_date' in fields_to_return: + entry['creation_date'] = plot_template.creation_date.isoformat(' ') + if 'dataformat' in fields_to_return: + entry['dataformat'] = plot_template.dataformat.fullname() + + if 'data' in fields_to_return: + entry['data'] = plot_template.data + + if 'default' in fields_to_return: + entry['default'] = plot_template.default + + result.append(entry) + + if not(check_api_user(request)): + return HttpResponse(status=403) + + # Check the validity of the request + if request.method != 'GET': + return HttpResponse(status=400) + + fields_to_return = [ 'name', 'version', 'last_version', 'short_description', + 'description', 'fork_of', 'previous_version', 'is_owner', + 'accessibility', 'hash', 'creation_date', + 'referenced_formats', 'dataformat', 'data', 'default' ] + + try: + result = [] + limit_to_latest_versions = (request.GET.has_key('latest_versions') and (request.GET['latest_versions'] != 0)) + + query_set = PlotTemplate.objects.select_related() + # Retrieve all the data formats of the user + if not(request.user.is_anonymous()): + plot_templates = list(query_set.filter(author=request.user) \ + .order_by('name', '-version')) + + previous_name = None + for plot_template in plot_templates: + last_version = (previous_name != plot_template.name) + + if not(limit_to_latest_versions) or last_version: + entry = plot_template_to_json(plot_template, request.user, fields_to_return, last_version=last_version) + #entry = plot_template.data + result.append(entry) + + previous_name = plot_template.name + + # Retrieve all the globally public data formats + if not(request.user.is_anonymous()): + plot_templates = query_set.exclude(author=request.user) + else: + plot_templates = query_set + + plot_templates = list(plot_templates.filter(sharing=Shareable.PUBLIC) \ + .order_by('name', '-version')) + + _addToResult(result, plot_templates, 'public', fields_to_return, limit_to_latest_versions) + + # Retrieve all the data formats shared only with this user or user's team + if not(request.user.is_anonymous()): + plot_templates = list(query_set.exclude(author=request.user) \ + .filter(Q(sharing=Shareable.SHARED), + Q(shared_with=request.user) | + Q(shared_with_team__in=Team.objects.filter(members=request.user))) \ + .order_by('name', '-version')) + + _addToResult(result, plot_templates, 'confidential', fields_to_return, + limit_to_latest_versions) + + # Sort the data formats and sends the response + result.sort(cmp=lambda x, y: cmp(x['name'], y['name'])) + return jsonify(result) + except: + return report_server_error() + +#------------------------------------------------ + + def search_list(request, author_name): try: # get the request from author_name database diff --git a/beat/web/navigation/api_urls.py b/beat/web/navigation/api_urls.py index 67931f61ed9355f5c0a7cbdefc1435997cd9050f..df5fc209387fcf76ad6146523204b5ca72cfdaff 100644 --- a/beat/web/navigation/api_urls.py +++ b/beat/web/navigation/api_urls.py @@ -4,6 +4,7 @@ from django.conf.urls import * urlpatterns = patterns('beat.web.navigation.api', (r'^search/$', 'search'), (r'^search/save/$', 'search_save'), + (r'^search/plot_template/$', 'accessible_plot_templates'), (r'^search/list/(?P<author_name>\w+)/$', 'search_list'), (r'^search/(?P<author_name>\w+)/(?P<label>[\w\-]+)/$', 'search_dispatch'), (r'^search/share/(?P<author_name>\w+)/(?P<label>[\w\-]+)/$', 'search_share') diff --git a/beat/web/navigation/models.py b/beat/web/navigation/models.py index 22a2ea0bcda14894f4814547c089999d35b279ac..7cd56644a4297d34deb1b1aedc98f8df41aba355 100644 --- a/beat/web/navigation/models.py +++ b/beat/web/navigation/models.py @@ -10,6 +10,7 @@ from django.contrib.auth.models import User from ..common.models import Contribution from ..algorithms.models import Algorithm from ..dataformats.models import DataFormat +import beat.core class Agreement(models.Model): @@ -37,6 +38,17 @@ class Search(Contribution): analyzer = models.ForeignKey(Algorithm, null=True) plot_parameters = models.TextField(default='', blank=True) + def save(self, *args, **kwargs): + # if the validation works, update the hash + full_template_name = "" + for template in self.templates.all(): + full_template_name += template.name+template.template.fullname() + + self.hash = beat.core.hash.hashJSONStr(self.options+self.filters+self.settings+self.analyzer.fullname()+self.plot_parameters+full_template_name) + + # Invoke the base implementation + super(Search, self).save(*args, **kwargs) + def as_json(self): retval = { 'options': self.options, @@ -65,6 +77,19 @@ class PlotTemplate(Contribution): dataformat = models.ForeignKey(DataFormat) data = models.TextField(default='', blank=True) default = models.BooleanField(default=False) + + def save(self, *args, **kwargs): + if self.default == True: + plot_templates_default = PlotTemplate.objects.filter(default=True, dataformat=self.dataformat) + for plot_template in plot_templates_default: + plot_template.default = False + plot_template.save() + print plot_templates_default + # if the validation works, update the hash + self.hash = beat.core.hash.hashJSONStr(self.data) + + # Invoke the base implementation + super(PlotTemplate, self).save(*args, **kwargs) class PlotRelationship(models.Model): name = models.CharField(max_length=64) diff --git a/beat/web/navigation/templates/navigation/search.html b/beat/web/navigation/templates/navigation/search.html index cccd6093256fab6c9e85a79b0474528133d296df..a1fccf6dd4779ff2f34438da637e7f48856df6b8 100644 --- a/beat/web/navigation/templates/navigation/search.html +++ b/beat/web/navigation/templates/navigation/search.html @@ -97,28 +97,49 @@ list_selector, multiple_selector ); - - $.ajax({ - type: "POST", - url: '{{ URL_PREFIX }}/api/search/', + $.ajax({ + type: "GET", + url: '{{ URL_PREFIX }}/api/search/plot_template/', + //data: JSON.stringify({ + // //filters: filter_list.toJSON(), + // //options: options, + // //label: label, + // //analyzer: analyzer, + // //settings:view.settings, + // //plot_parameters: plot_params, + //}), + contentType: "application/json; charset=utf-8", + + success: function(plots) { + //alert("query \"" + plots + "\" saved"); + $.ajax({ + type: "POST", + url: '{{ URL_PREFIX }}/api/search/', {% if data %} - data: '{{ data|safe }}', + data: '{{ data|safe }}', {% else %} - data: JSON.stringify({ - query: '{{ query }}', - }), + data: JSON.stringify({ + query: '{{ query }}', + }), {% endif %} - contentType: "application/json; charset=utf-8", - dataType: "json", - - success: function(data) { - search_results_panel.display(data); - }, + contentType: "application/json; charset=utf-8", + dataType: "json", + + success: function(data) { + search_results_panel.display(data, plots); + }, + + error: function(jqXHR, textStatus, errorThrown) { + alert('Error: ' + errorThrown); + } + }); + }, + + error: function(jqXHR, textStatus, errorThrown) { + alert('Error: ' + jqXHR.responseText); + } + }); - error: function(jqXHR, textStatus, errorThrown) { - alert('Error: ' + errorThrown); - } - }); {% endif %} }); diff --git a/beat/web/navigation/utils.py b/beat/web/navigation/utils.py index b846aae1716ca0e42ecc3c0ac6cb6eee5d8dfd10..edaf68030c679b139b25eda22e838a1af6641610 100644 --- a/beat/web/navigation/utils.py +++ b/beat/web/navigation/utils.py @@ -8,6 +8,81 @@ from ..common.models import Shareable import simplejson as json +#---------------------------------------------------------- + +def plot_template_to_json(plot_template, request_user, fields_to_return, last_version=None): + + # Check that the user has access to the plot_template + (has_access, accessibility) = plot_template.accessibility_for(request_user) + if not(has_access): + return None + + + # Prepare the response + result = {} + + if 'name' in fields_to_return: + result['name'] = plot_template.fullname() + + if 'version' in fields_to_return: + result['version'] = plot_template.version + + if 'last_version' in fields_to_return: + result['last_version'] = last_version + + if 'short_description' in fields_to_return: + result['short_description'] = plot_template.short_description + + if 'description' in fields_to_return: + result['description'] = plot_template.description + + if ('is_owner' in fields_to_return) and not(request_user.is_anonymous()): + result['is_owner'] = (plot_template.author == request_user) + + if 'previous_version' in fields_to_return: + result['previous_version'] = (plot_template.previous_version.fullname() if plot_template.previous_version is not None else None) + + if 'fork_of' in fields_to_return: + result['fork_of'] = (plot_template.fork_of.fullname() if plot_template.fork_of is not None else None) + + if 'creation_date' in fields_to_return: + result['creation_date'] = plot_template.creation_date.isoformat(' ') + + if 'hash' in fields_to_return: + result['hash'] = plot_template.hash + + if 'accessibility' in fields_to_return: + result['accessibility'] = accessibility + + if 'can_be_deleted' in fields_to_return: + result['can_be_deleted'] = plot_template.can_be_deleted() + + if 'dataformat' in fields_to_return: + result['dataformat'] = plot_template.dataformat.fullname() + + if 'data' in fields_to_return: + result['data'] = plot_template.data + + if 'default' in fields_to_return: + result['default'] = plot_template.default + + # Retrieve the sharing preferences of the data format (if applicable) + if not(request_user.is_anonymous()) and (request_user == plot_template.author): + if 'sharing' in fields_to_return: + result['sharing'] = { + 'status': plot_template.sharing_string().lower() + } + + if plot_template.shared_with.count() > 0: + result['sharing']['shared_with'] = map(lambda x: x.username, plot_template.shared_with.iterator()) + + if plot_template.shared_with_team.count() > 0: + result['sharing']['shared_with_team'] = map(lambda x: x.teamname, plot_template.shared_with_team.iterator()) + + return result + + + #---------------------------------------------------------- def share_search(search, users=None): diff --git a/beat/web/navigation/views.py b/beat/web/navigation/views.py index cad3c5349d43a06de5ddb7f924347f59ece9bc95..4425a864bbb4c05b6c8e6b7a7e7bfae3f12357a0 100644 --- a/beat/web/navigation/views.py +++ b/beat/web/navigation/views.py @@ -158,11 +158,18 @@ def search(request): def search_query(request, username, queryname): # get the query from the DB query = get_object_or_404(Search, author__username = username, name = queryname) + + #adding plot relationship + template_relationship = {} + for template in query.templates.all(): + template_relationship[template.name] = template.template.fullname() + data = {} data['settings'] = json.loads(query.settings) data['options'] = json.loads(query.options) data['filters'] = json.loads(query.filters) data['plot_parameters'] = json.loads(query.plot_parameters) + data['plots_relationship'] = template_relationship return render_to_response('navigation/search.html', { 'query' : '', diff --git a/beat/web/settings/settings.py b/beat/web/settings/settings.py index fcfd66324932ff2b947840c19e3a95a1e8e0b5d1..3688ee7c5dc5be2faccf065036aa7a70071ef0fe 100644 --- a/beat/web/settings/settings.py +++ b/beat/web/settings/settings.py @@ -46,8 +46,14 @@ ALLOWED_HOSTS = [ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': 'django.sql3', + #'ENGINE': 'django.db.backends.sqlite3', + #'NAME': 'django.sql3', + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'beat', + 'USER': 'ftarsett', + 'PASSWORD': 'pwd4beat', + 'HOST':'127.0.0.1', + 'PORT': '3307', }, } diff --git a/beat/web/ui/static/ui/js/beat.search.panels.js b/beat/web/ui/static/ui/js/beat.search.panels.js index e8cf09ed1a441d53aabfa36d4dc81f4d85e3cede..73012e3a537957d909acb3a43bb210943c1ac7e5 100644 --- a/beat/web/ui/static/ui/js/beat.search.panels.js +++ b/beat/web/ui/static/ui/js/beat.search.panels.js @@ -36,6 +36,7 @@ beat.search.panels.SearchResults = function(panel_id, query, base_url, use_https this.view = null; this.list_selector = list_selector; this.multiple_selector = multiple_selector; + this.plots = null; $(this.content_element).addClass('progress'); } @@ -44,7 +45,7 @@ beat.search.panels.SearchResults = function(panel_id, query, base_url, use_https //---------------------------------------------------------------------------------------- // Load a list of experiments from the given URL //---------------------------------------------------------------------------------------- -beat.search.panels.SearchResults.prototype.display = function(data) +beat.search.panels.SearchResults.prototype.display = function(data, plots) { var panel = this; @@ -71,6 +72,7 @@ beat.search.panels.SearchResults.prototype.display = function(data) $(this.content_element).removeClass('progress'); this.data = data; + this.data.plots = plots; var menu_element = $(this.element).find('.aside'); @@ -411,9 +413,9 @@ beat.search.panels.ResultsView.prototype.displayData = function(data) var tmp_read_settings = {} - console.log(data); if(data.plot_parameters != null) this.plot_params = data.plot_parameters; + this.table = new beat.search.panels.ResultsTable(data.results.experiments, data.results.dataformats, this.panel.base_url, @@ -1715,12 +1717,64 @@ beat.search.panels.ScatterChart = function(parent, field_name, experiments, colo if (this.infos !== null) { - //check additional parameters for saved search - if(this.view.plot_params != null) + + var additional_params = null; + var selected_plot_template = null; + var accessible_plot_templates = myview.panel.data.plots; + if(accessible_plot_templates.length>0) { - var additional_params = this.view.plot_params[this.container.id]; - this.redraw(additional_params); + var plots_relationship = myview.panel.data.plots_relationship; + var found_set_template = false; + var plots_relationship_key = Object.keys(plots_relationship); + if(plots_relationship_key.length>0) + { + for(var i=0; i<plots_relationship_key.length; i++ ) + { + if(plots_relationship_key == this.container.id) + { + found_set_template = true; + for(var j=0; j<accessible_plot_templates.length;j++) + { + if(plots_relationship[plots_relationship_key[i]] == accessible_plot_templates[j].name) + { + found_set_template = true; + additional_params = JSON.parse(accessible_plot_templates[j].data); + selected_plot_template = accessible_plot_templates[j].name; + } + } + } + } + + if(!found_set_template) + { + for(var j=0; j<accessible_plot_templates.length;j++) + { + if(this.infos.__type__ == accessible_plot_templates[j].dataformat && accessible_plot_templates[j].default == true) + { + found_set_template = true; + additional_params = JSON.parse(accessible_plot_templates[j].data); + selected_plot_template = accessible_plot_templates[j].name; + } + } + + } + + } + else + { + } } + + if(additional_params != null) + { + this.redraw(additional_params, selected_plot_template, accessible_plot_templates); + } + //check additional parameters for saved search + //if(this.view.plot_params != null) + //{ + // var additional_params = this.view.plot_params[this.container.id]; + // this.redraw(additional_params); + //} else { this.redraw(); @@ -1774,7 +1828,7 @@ beat.search.panels.ScatterChart.prototype.display = function(display) //---------------------------------------------------------------------------------------- // Redraw the chart //---------------------------------------------------------------------------------------- -beat.search.panels.ScatterChart.prototype.redraw = function(additional_params) +beat.search.panels.ScatterChart.prototype.redraw = function(additional_params, selected_plot_template, accessible_plot_templates) { colors_to_plot = null; this.infos.data = []; @@ -1830,7 +1884,7 @@ beat.search.panels.ScatterChart.prototype.redraw = function(additional_params) if (colors_to_plot == null) colors_to_plot = this.colors; - beat.experiments.utils.displayResult(this.container, this.infos, 'chart', colors_to_plot, additional_params,function (chart_id, chart_parameters){ + beat.experiments.utils.displayResult(this.container, this.infos, 'chart', colors_to_plot, additional_params, selected_plot_template, accessible_plot_templates, function (chart_id, chart_parameters){ view.setPlotParams(chart_id, chart_parameters); }); }