@ -1,17 +0,0 @@ | |||
FROM neo4j:latest | |||
ENV NEO4J_AUTH=none | |||
RUN cd /var/lib/neo4j/plugins \ | |||
&& wget http://products.graphaware.com/download/framework-server-community/graphaware-server-community-all-3.0.6.43.jar \ | |||
&& wget http://products.graphaware.com/download/noderank/graphaware-noderank-3.0.6.43.3.jar | |||
RUN echo "dbms.unmanaged_extension_classes=com.graphaware.server=/graphaware" >> /var/lib/neo4j/conf/neo4j.conf | |||
RUN echo 'com.graphaware.runtime.enabled=true\n\ | |||
com.graphaware.module.NR.1=com.graphaware.module.noderank.NodeRankModuleBootstrapper\n\ | |||
com.graphaware.module.NR.maxTopRankNodes=10\n\ | |||
com.graphaware.module.NR.dampingFactor=0.85\n\ | |||
com.graphaware.module.NR.propertyKey=nodeRank\n'\ | |||
>> /var/lib/neo4j/conf/neo4j.conf | |||
RUN echo 'com.graphaware.runtime.stats.disabled=true\n\ | |||
com.graphaware.server.stats.disabled=true\n'\ | |||
>> /var/lib/neo4j/conf/neo4j.conf |
@ -1,37 +0,0 @@ | |||
import api from '../api'; | |||
export const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST'; | |||
export const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS'; | |||
export const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL'; | |||
export function fetchSuggestions() { | |||
return (dispatch, getState) => { | |||
dispatch(fetchSuggestionsRequest()); | |||
api(getState).get('/api/v1/accounts/suggestions').then(response => { | |||
dispatch(fetchSuggestionsSuccess(response.data)); | |||
}).catch(error => { | |||
dispatch(fetchSuggestionsFail(error)); | |||
}); | |||
}; | |||
}; | |||
export function fetchSuggestionsRequest() { | |||
return { | |||
type: SUGGESTIONS_FETCH_REQUEST | |||
}; | |||
}; | |||
export function fetchSuggestionsSuccess(accounts) { | |||
return { | |||
type: SUGGESTIONS_FETCH_SUCCESS, | |||
accounts: accounts | |||
}; | |||
}; | |||
export function fetchSuggestionsFail(error) { | |||
return { | |||
type: SUGGESTIONS_FETCH_FAIL, | |||
error: error | |||
}; | |||
}; |
@ -1,86 +0,0 @@ | |||
import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
import AccountContainer from '../../../containers/account_container'; | |||
import { FormattedMessage } from 'react-intl'; | |||
const outerStyle = { | |||
position: 'relative' | |||
}; | |||
const headerStyle = { | |||
fontSize: '14px', | |||
fontWeight: '500', | |||
display: 'block', | |||
padding: '10px', | |||
color: '#9baec8', | |||
background: '#454b5e', | |||
overflow: 'hidden' | |||
}; | |||
const nextStyle = { | |||
display: 'inline-block', | |||
float: 'right', | |||
fontWeight: '400', | |||
color: '#2b90d9' | |||
}; | |||
const SuggestionsBox = React.createClass({ | |||
propTypes: { | |||
accountIds: ImmutablePropTypes.list, | |||
perWindow: React.PropTypes.number | |||
}, | |||
getInitialState () { | |||
return { | |||
index: 0 | |||
}; | |||
}, | |||
getDefaultProps () { | |||
return { | |||
perWindow: 2 | |||
}; | |||
}, | |||
mixins: [PureRenderMixin], | |||
handleNextClick (e) { | |||
e.preventDefault(); | |||
let newIndex = this.state.index + 1; | |||
if (this.props.accountIds.skip(this.props.perWindow * newIndex).size === 0) { | |||
newIndex = 0; | |||
} | |||
this.setState({ index: newIndex }); | |||
}, | |||
render () { | |||
const { accountIds, perWindow } = this.props; | |||
if (!accountIds || accountIds.size === 0) { | |||
return <div />; | |||
} | |||
let nextLink = ''; | |||
if (accountIds.size > perWindow) { | |||
nextLink = <a href='#' style={nextStyle} onClick={this.handleNextClick}><FormattedMessage id='suggestions_box.refresh' defaultMessage='Refresh' /></a>; | |||
} | |||
return ( | |||
<div style={outerStyle}> | |||
<strong style={headerStyle}> | |||
<FormattedMessage id='suggestions_box.who_to_follow' defaultMessage='Who to follow' /> {nextLink} | |||
</strong> | |||
{accountIds.skip(perWindow * this.state.index).take(perWindow).map(accountId => <AccountContainer key={accountId} id={accountId} withNote={false} />)} | |||
</div> | |||
); | |||
} | |||
}); | |||
export default SuggestionsBox; |
@ -1,8 +0,0 @@ | |||
import { connect } from 'react-redux'; | |||
import SuggestionsBox from '../components/suggestions_box'; | |||
const mapStateToProps = (state) => ({ | |||
accountIds: state.getIn(['user_lists', 'suggestions']) | |||
}); | |||
export default connect(mapStateToProps)(SuggestionsBox); |
@ -1,50 +0,0 @@ | |||
# frozen_string_literal: true | |||
class FollowSuggestion | |||
class << self | |||
def get(for_account_id, limit = 10) | |||
neo = Neography::Rest.new | |||
query = <<END | |||
MATCH (a {account_id: {id}})-[:follows]->(b)-[:follows]->(c) | |||
WHERE a <> c | |||
AND NOT (a)-[:follows]->(c) | |||
RETURN DISTINCT c.account_id, count(b), c.nodeRank | |||
ORDER BY count(b) DESC, c.nodeRank DESC | |||
LIMIT {limit} | |||
END | |||
results = neo.execute_query(query, id: for_account_id, limit: limit) | |||
if results.empty? || results['data'].empty? | |||
results = fallback(for_account_id, limit) | |||
elsif results['data'].size < limit | |||
results['data'] = (results['data'] + fallback(for_account_id, limit - results['data'].size)['data']).uniq | |||
end | |||
account_ids = results['data'].map(&:first) | |||
blocked_ids = Block.where(account_id: for_account_id).pluck(:target_account_id) | |||
accounts_map = Account.where(id: account_ids - blocked_ids).with_counters.map { |a| [a.id, a] }.to_h | |||
account_ids.map { |id| accounts_map[id] }.compact | |||
rescue Neography::NeographyError, Excon::Error::Socket => e | |||
Rails.logger.error e | |||
return [] | |||
end | |||
private | |||
def fallback(for_account_id, limit) | |||
neo = Neography::Rest.new | |||
query = <<END | |||
MATCH (b) | |||
RETURN b.account_id | |||
ORDER BY b.nodeRank DESC | |||
LIMIT {limit} | |||
END | |||
neo.execute_query(query, id: for_account_id, limit: limit) | |||
end | |||
end | |||
end |
@ -1,5 +0,0 @@ | |||
Neography.configure do |config| | |||
config.protocol = "http" | |||
config.server = ENV.fetch('NEO4J_HOST') { 'localhost' } | |||
config.port = ENV.fetch('NEO4J_PORT') { 7474 } | |||
end |