Commit 0a064d63 authored by Florian Hübsch's avatar Florian Hübsch
Browse files

Fix pagination and add pagination to SearchAPI.

parent c797986e
......@@ -2,29 +2,54 @@ module Chemotion
class SearchAPI < Grape::API
include Grape::Kaminari
# TODO implement search cache?
helpers do
def serialization_by_elements(elements)
serialized_samples = elements.fetch(:samples, []).map{|s| SampleSerializer.new(s).serializable_hash.deep_symbolize_keys}
serialized_reactions = elements.fetch(:reactions, []).map{|s| ReactionSerializer.new(s).serializable_hash.deep_symbolize_keys}
serialized_wellplates = elements.fetch(:wellplates, []).map{|s| WellplateSerializer.new(s).serializable_hash.deep_symbolize_keys}
serialized_screens = elements.fetch(:screens, []).map{|s| ScreenSerializer.new(s).serializable_hash.deep_symbolize_keys}
def page_size
7
end
def pages(total_elements)
total_elements.fdiv(page_size).ceil
end
def serialization_by_elements_and_page(elements, page=1)
samples = elements.fetch(:samples, [])
reactions = elements.fetch(:reactions, [])
wellplates = elements.fetch(:wellplates, [])
screens = elements.fetch(:screens, [])
serialized_samples = Kaminari.paginate_array(samples).page(page).per(page_size).map{|s| SampleSerializer.new(s).serializable_hash.deep_symbolize_keys}
serialized_reactions = Kaminari.paginate_array(reactions).page(page).per(page_size).map{|s| ReactionSerializer.new(s).serializable_hash.deep_symbolize_keys}
serialized_wellplates = Kaminari.paginate_array(wellplates).page(page).per(page_size).map{|s| WellplateSerializer.new(s).serializable_hash.deep_symbolize_keys}
serialized_screens = Kaminari.paginate_array(screens).page(page).per(page_size).map{|s| ScreenSerializer.new(s).serializable_hash.deep_symbolize_keys}
{
samples: {
elements: serialized_samples,
totalElements: elements.fetch(:samples, []).size
totalElements: samples.size,
page: page,
pages: pages(samples.size),
per_page: page_size
},
reactions: {
elements: serialized_reactions,
totalElements: elements.fetch(:reactions, []).size
totalElements: reactions.size,
page: page,
pages: pages(reactions.size),
per_page: page_size
},
wellplates: {
elements: serialized_wellplates,
totalElements: elements.fetch(:wellplates, []).size
totalElements: wellplates.size,
page: page,
pages: pages(wellplates.size),
per_page: page_size
},
screens: {
elements: serialized_screens,
totalElements: elements.fetch(:screens, []).size
totalElements: screens.size,
page: page,
pages: pages(screens.size),
per_page: page_size
}
}
end
......@@ -43,10 +68,11 @@ module Chemotion
AllElementSearch.new(arg).search_by_substring
end
# TODO only elements of current user
unless params[:collection_id] == "all"
scope = scope.by_collection_id(params[:collection_id].to_i)
end
scope
scope # joins(:collections).where('collections.user_id = ?', current_user.id).references(:collections)
end
def elements_by_scope(scope)
......@@ -68,7 +94,7 @@ module Chemotion
when Wellplate
elements[:wellplates] = scope
elements[:screens] = scope.flat_map(&:screen).compact.uniq
elements[:samples] = scope.flat_map(&:wells).compact.flat_map(&:sample).uniq
elements[:samples] = scope.flat_map(&:samples).uniq
elements[:reactions] = elements[:samples].flat_map(&:reactions).uniq
when Screen
elements[:screens] = scope
......@@ -88,8 +114,9 @@ module Chemotion
resource :search do
namespace :all do
desc "Return all matched elements and associations"
desc "Return all matched elements and associations for substring query"
params do
optional :page, type: Integer
requires :selection, type: Hash
requires :collection_id, type: String
end
......@@ -100,13 +127,14 @@ module Chemotion
scope = scope_by_search_by_method_arg_and_collection_id(search_by_method, arg, params[:collection_id])
serialization_by_elements(elements_by_scope(scope))
serialization_by_elements_and_page(elements_by_scope(scope), params[:page])
end
end
namespace :samples do
desc "Return samples and associated elements by search selection"
params do
optional :page, type: Integer
requires :selection, type: Hash
requires :collection_id, type: String
end
......@@ -122,13 +150,14 @@ module Chemotion
samples = scope.by_collection_id(params[:collection_id].to_i)
end
serialization_by_elements(elements_by_scope(samples))
serialization_by_elements(elements_by_scope(samples), params[:page])
end
end
namespace :reactions do
desc "Return reactions and associated elements by search selection"
params do
optional :page, type: Integer
requires :selection, type: Hash
requires :collection_id, type: String
end
......@@ -144,13 +173,14 @@ module Chemotion
reactions = scope.by_collection_id(params[:collection_id].to_i)
end
serialization_by_elements(elements_by_scope(reactions))
serialization_by_elements(elements_by_scope(reactions), params[:page])
end
end
namespace :wellplates do
desc "Return wellplates and associated elements by search selection"
params do
optional :page, type: Integer
requires :selection, type: Hash
requires :collection_id, type: String
end
......@@ -166,13 +196,14 @@ module Chemotion
wellplates = scope.by_collection_id(params[:collection_id].to_i)
end
serialization_by_elements(elements_by_scope(wellplates))
serialization_by_elements(elements_by_scope(wellplates), params[:page])
end
end
namespace :screens do
desc "Return wellplates and associated elements by search selection"
params do
optional :page, type: Integer
requires :selection, type: Hash
requires :collection_id, type: String
end
......@@ -188,7 +219,7 @@ module Chemotion
screens = scope.by_collection_id(params[:collection_id].to_i)
end
serialization_by_elements(elements_by_scope(screens))
serialization_by_elements(elements_by_scope(screens), params[:page])
end
end
......
......@@ -111,7 +111,7 @@ Aviator.setRoutes({
} else {
console.log("generateEmptyReaction")
ElementActions.generateEmptyReaction()
}
}
}
},
'/:reactionID': 'show',
......@@ -125,6 +125,7 @@ Aviator.setRoutes({
} else {
ElementActions.fetchWellplateById(wellplateId);
}
//UIActions.selectTab(3)
}
},
'/:wellplateId': 'showOrNew'
......
......@@ -78,7 +78,6 @@ export default class ElementsTable extends React.Component {
let elementsDidChange = elements && ! deepEqual(elements, this.state.elements);
let currentElementDidChange = ! deepEqual(currentElement, this.state.currentElement);
if (elementsDidChange) {
this.setState({
elements, page, pages, perPage, totalElements, currentElement
......
......@@ -54,6 +54,28 @@ export default class List extends React.Component {
handleTabSelect(tab) {
UIActions.selectTab(tab);
// TODO sollte in tab action handler
let type;
switch(tab) {
case 1:
type = 'sample';
break;
case 2:
type = 'reaction';
break;
case 3:
type = 'wellplate';
break;
case 4:
type = 'screen';
}
let uiState = UIStore.getState();
let page = uiState[type].page;
UIActions.setPagination({type: type, page: page})
}
render() {
......
......@@ -24,8 +24,8 @@ class ElementActions {
// -- Search --
fetchBasedOnSearchSelectionAndCollection(selection, collectionId) {
SearchFetcher.fetchBasedOnSearchSelectionAndCollection(selection, collectionId)
fetchBasedOnSearchSelectionAndCollection(selection, collectionId, currentPage) {
SearchFetcher.fetchBasedOnSearchSelectionAndCollection(selection, collectionId, currentPage)
.then((result) => {
this.dispatch(result);
}).catch((errorMessage) => {
......
......@@ -5,7 +5,7 @@ import Wellplate from '../models/Wellplate';
import Screen from '../models/Screen';
export default class SearchFetcher {
static fetchBasedOnSearchSelectionAndCollection(selection, collectionId) {
static fetchBasedOnSearchSelectionAndCollection(selection, collectionId, currentPage) {
let promise = fetch('/api/v1/search/' + selection.elementType, {
credentials: 'same-origin',
method: 'POST',
......@@ -15,7 +15,8 @@ export default class SearchFetcher {
},
body: JSON.stringify({
selection: selection,
collection_id: collectionId
collection_id: collectionId,
page: currentPage
})
})
.then((response) => {
......@@ -23,19 +24,31 @@ export default class SearchFetcher {
}).then((json) => {
let samples = {
elements: json.samples.elements.map((s) => new Sample(s)),
totalElements: json.samples.totalElements
totalElements: json.samples.totalElements,
page: json.samples.page,
pages: json.samples.pages,
perPage: json.samples.per_page
}
let reactions = {
reactions: json.reactions.elements.map((r) => new Reaction(r)),
totalElements: json.reactions.totalElements
elements: json.reactions.elements.map((r) => new Reaction(r)),
totalElements: json.reactions.totalElements,
page: json.reactions.page,
pages: json.reactions.pages,
perPage: json.reactions.per_page
}
let wellplates = {
wellplates: json.wellplates.elements.map((w) => new Wellplate(w)),
totalElements: json.wellplates.totalElements
elements: json.wellplates.elements.map((w) => new Wellplate(w)),
totalElements: json.wellplates.totalElements,
page: json.wellplates.page,
pages: json.wellplates.pages,
perPage: json.wellplates.per_page
}
let screens = {
screens: json.screens.elements.map((s) => new Screen(s)),
totalElements: json.screens.totalElements
elements: json.screens.elements.map((s) => new Screen(s)),
totalElements: json.screens.totalElements,
page: json.screens.page,
pages: json.screens.pages,
perPage: json.screens.per_page
}
return {
......
......@@ -24,7 +24,7 @@ export default class Search extends React.Component {
UIActions.setSearchSelection(selection);
let uiState = UIStore.getState();
ElementActions.fetchBasedOnSearchSelectionAndCollection(selection, uiState.currentCollection.id);
ElementActions.fetchBasedOnSearchSelectionAndCollection(selection, uiState.currentCollection.id, 1);
}
search(query) {
......
......@@ -14,28 +14,28 @@ class ElementStore {
totalElements: 0,
page: null,
pages: null,
per_page: null
perPage: null
},
reactions: {
elements: [],
totalElements: 0,
page: null,
pages: null,
per_page: null
perPage: null
},
wellplates: {
elements: [],
totalElements: 0,
page: null,
pages: null,
per_page: null
perPage: null
},
screens: {
elements: [],
totalElements: 0,
page: null,
pages: null,
per_page: null
perPage: null
}
},
currentElement: null
......@@ -71,6 +71,7 @@ class ElementStore {
handleCreateScreen: ElementActions.createScreen,
handleUnselectCurrentElement: UIActions.deselectAllElements,
// FIXME ElementStore listens to UIActions?
handleSetPagination: UIActions.setPagination,
handleRefreshElements: ElementActions.refreshElements,
handleGenerateEmptyElement: [ElementActions.generateEmptyWellplate, ElementActions.generateEmptyScreen, ElementActions.generateEmptySample, ElementActions.generateEmptyReaction],
......@@ -265,19 +266,29 @@ class ElementStore {
this.waitFor(UIStore.dispatchToken);
let uiState = UIStore.getState();
let page = uiState[type].page;
switch (type) {
case 'sample':
ElementActions.fetchSamplesByCollectionId(uiState.currentCollection.id, {page: page});
break;
case 'reaction':
ElementActions.fetchReactionsByCollectionId(uiState.currentCollection.id, {page: page});
break;
case 'wellplate':
ElementActions.fetchWellplatesByCollectionId(uiState.currentCollection.id, {page: page});
break;
case 'screen':
ElementActions.fetchScreensByCollectionId(uiState.currentCollection.id, {page: page});
break;
this.state.elements[type+'s'].page = page;
let currentSearchSelection = uiState.currentSearchSelection;
// TODO if page changed -> fetch
// if there is a currentSearchSelection we have to execute the respective action
if(currentSearchSelection != null) {
ElementActions.fetchBasedOnSearchSelectionAndCollection(currentSearchSelection, uiState.currentCollection.id, page);
} else {
switch (type) {
case 'sample':
ElementActions.fetchSamplesByCollectionId(uiState.currentCollection.id, {page: page});
break;
case 'reaction':
ElementActions.fetchReactionsByCollectionId(uiState.currentCollection.id, {page: page});
break;
case 'wellplate':
ElementActions.fetchWellplatesByCollectionId(uiState.currentCollection.id, {page: page});
break;
case 'screen':
ElementActions.fetchScreensByCollectionId(uiState.currentCollection.id, {page: page});
break;
}
}
}
}
......
......@@ -63,7 +63,6 @@ class UIStore {
}
handleSelectTab(tab) {
console.log('handleSelectTab: ' + tab)
this.state.currentTab = tab;
}
......@@ -127,6 +126,7 @@ class UIStore {
let state = this.state;
let hasChanged = (!state.currentCollection || state.currentCollection.id != collection.id) || (state.currentSearchSelection != null);
// TODO state.pagination does not make sense
if(hasChanged) {
this.state.currentCollection = collection;
this.state.currentCollectionId = collection.id;
......@@ -138,6 +138,7 @@ class UIStore {
}
}
// FIXME this method is also defined in ElementStore
handleSetPagination(pagination) {
let {type, page} = pagination;
this.state[type].page = page;
......
......@@ -81,7 +81,7 @@ class Reaction < ActiveRecord::Base
composer = SVG::ReactionComposer.new(inchikeys, label: label)
self.reaction_svg_file = composer.compose_reaction_svg_and_save
rescue Exception => e
p "**** SVG::ReactionComposer failed ***"
Rails.logger.info("**** SVG::ReactionComposer failed ***")
end
end
......
......@@ -41,8 +41,4 @@ class Wellplate < ActiveRecord::Base
Well.where(wellplate_id: id).delete_all
CollectionsWellplate.where(wellplate_id: id).delete_all
end
def samples
wells.flat_map(&:sample)
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment