Skip to content
Snippets Groups Projects
Commit f1ecf008 authored by ahamelers's avatar ahamelers
Browse files

unmatched citation working (#190, #159, #194) WIP: citation note

parent 246d7302
No related branches found
No related tags found
2 merge requests!60Dev,!29Shared data model
......@@ -54,9 +54,9 @@ class PubMedSearch extends React.Component {
pmcid: null,
inEPMC: false,
unmatched:
this.props.metadata &&
(!this.props.metadata.articleIds ||
this.props.metadata.articleIds.length === 0),
this.props.manuscript &&
(!this.props.manuscript.meta.articleIds ||
this.props.manuscript.meta.articleIds.length === 0),
pubProvided: false,
}
this.onSearch = this.onSearch.bind(this)
......@@ -65,8 +65,15 @@ class PubMedSearch extends React.Component {
this.onSelected = this.onSelected.bind(this)
this.handleUnmatchedInfo = this.handleUnmatchedInfo.bind(this)
}
handleUnmatchedInfo(metadata) {
this.props.citationData(metadata)
handleUnmatchedInfo(unm) {
if (unm.journal && unm.journal.meta.pmcStatus) {
this.setState({ pubProvided: unm.journal.journalTitle })
} else {
const citationData = unm
citationData.journalId = citationData.journal.id
delete citationData.journal
this.props.citationData(citationData)
}
}
onQueryChange(event) {
this.setState({
......@@ -190,7 +197,7 @@ class PubMedSearch extends React.Component {
<UnmatchedCitation
journal={journalInfo}
note={note ? note.content : ''}
title={meta.title}
title={meta && meta.title}
unmatchedInfo={this.handleUnmatchedInfo}
/>
</Page>
......
import React from 'react'
import { ApolloConsumer } from 'react-apollo'
import * as Joi from 'joi'
import { debounce, omit } from 'lodash'
import { debounce } from 'lodash'
import { Button, ErrorText, H2, TextField } from '@pubsweet/ui'
import { SEARCH_JOURNALS } from './operations'
import {
Buttons,
HTMLString,
......@@ -12,35 +14,29 @@ import {
} from '../ui'
export default class UnmatchedCitation extends React.Component {
constructor(props) {
super(props)
this.state = {
loading: false,
title: this.props.title,
query: this.props.journal ? this.props.journal.journalTitle : '',
journal: this.props.journal,
note: this.props.note,
enabled: this.props.title && this.props.journal,
journals: [],
titleMessage: '',
journalMessage: '',
}
this.onQueryChange = this.onQueryChange.bind(this)
this.onSearch = debounce(this.onSearch.bind(this), 100)
this.enable = this.enable.bind(this)
this.submitData = this.submitData.bind(this)
this.onTitleChange = this.onTitleChange.bind(this)
this.onJournalChange = this.onJournalChange.bind(this)
state = {
loading: false,
title: this.props.title,
query: this.props.journal ? this.props.journal.journalTitle : '',
journal: this.props.journal,
note: this.props.note,
enabled: this.props.title && this.props.journal,
journals: [],
titleMessage: '',
journalMessage: '',
}
enable() {
enable = () => {
const validTitle = Joi.validate(this.state.title, Joi.string().required())
const validJournal = Joi.validate(
this.state.journal,
Joi.object({
title: Joi.string().required(),
longTitle: Joi.string(),
nlmuniqueid: Joi.string(),
id: Joi.string(),
journalTitle: Joi.string().required(),
meta: Joi.object({
pmcStatus: Joi.boolean(),
}),
}),
{ allowUnknown: true },
)
if (!validTitle.error && !validJournal.error) {
this.setState({ enabled: true })
......@@ -51,75 +47,28 @@ export default class UnmatchedCitation extends React.Component {
})
}
}
submitData() {
submitData = () => {
if (this.state.enabled) {
const metadata = {
title: this.state.title,
const citationData = {
meta: {
title: this.state.title,
},
}
if (this.state.journal.nlmuniqueid) {
metadata.journalMeta = omit(this.state.journal, 'longTitle')
if (this.state.journal.id) {
citationData.journal = this.state.journal
} else {
metadata.customMeta = {
unmatchedJournal: this.state.journal.title,
}
citationData.meta.unmatchedJournal = this.state.journal.journalTitle
}
if (this.state.note) {
metadata.citationNote = {
/* if (this.state.note) {
citationD.citationNote = {
notesType: 'user citation',
content: this.state.note,
}
}
this.props.unmatchedInfo(metadata)
}
}
onQueryChange(e) {
const query = e.target.value
this.setState({
query: e ? query : '',
journals: [],
loading: true,
})
if (query.trim().length > 0) {
this.onSearch(query)
}
}
async onSearch(query) {
const eSearch = `/eutils/esearch?db=nlmcatalog&term=${query}*[ta]%20AND%20ncbijournals[filter]&retstart=0`
const response = await fetch(eSearch, {
headers: new Headers({
Authorization: `Bearer ${window.localStorage.getItem('token')}`,
}),
})
const json = await response.json()
const ids = json.esearchresult.idlist
const hitcount = json.esearchresult.count
const newState = { loading: false }
if (hitcount > 0) {
const eFetch = `/eutils/efetch?db=nlmcatalog&id=${ids.join()}`
const summary = await fetch(eFetch, {
headers: new Headers({
Authorization: `Bearer ${window.localStorage.getItem('token')}`,
}),
})
const xml = await summary.text()
const parser = new DOMParser()
const xmlDoc = parser.parseFromString(xml, 'text/xml')
const records = Array.from(xmlDoc.documentElement.children)
const journals = records.reduce((list, record) => {
if (record.querySelector('MedlineTA')) {
list.push({
longTitle: record.querySelector('TitleMain Title').innerHTML,
title: record.querySelector('MedlineTA').innerHTML,
nlmuniqueid: record.querySelector('NlmUniqueID').innerHTML,
})
}
return list
}, [])
newState.journals = journals
} */
this.props.unmatchedInfo(citationData)
}
this.setState(newState)
}
onTitleChange(event) {
onTitleChange = event => {
this.setState(
{
titleMessage: '',
......@@ -130,12 +79,13 @@ export default class UnmatchedCitation extends React.Component {
},
)
}
onJournalChange(journal) {
onJournalSelect = journal => {
const { id, journalTitle, meta } = journal
this.setState(
{
journalMessage: '',
journal,
query: journal.title,
journal: { id, journalTitle, meta },
query: journal.journalTitle,
},
() => {
this.enable()
......@@ -157,49 +107,73 @@ export default class UnmatchedCitation extends React.Component {
{this.state.titleMessage && (
<ErrorText>{this.state.titleMessage}</ErrorText>
)}
<SearchSelect
invalidTest={this.state.journalMessage}
label="Search for Journal"
onInput={this.onQueryChange}
optionsOnChange={this.onJournalChange}
placeholder="Journal title abbreviation"
query={this.state.query}
singleSelect
>
{this.state.loading && (
<Loading>
<LoadingIcon />
</Loading>
)}
{this.state.journals.map(journal => (
<SearchSelect.Option
data-option={journal}
key={journal.title + journal.id}
propKey={journal.title + journal.id}
>
<HTMLString string={journal.longTitle} /> [
<em>
<HTMLString string={journal.title} />
</em>
]
</SearchSelect.Option>
))}
{this.state.query.trim().length > 0 && (
<SearchSelect.Option
data-option={{ title: this.state.query }}
key="submit"
propKey="submit"
>
<em>
<HTMLString
string={`Submit &apos;${
this.state.query
}&apos; as your journal name`}
/>
</em>
</SearchSelect.Option>
)}
</SearchSelect>
<ApolloConsumer>
{client => {
const searchJournals = debounce(async () => {
const { query } = this.state
const { data } = await client.query({
query: SEARCH_JOURNALS,
variables: { query },
})
this.setState({
journals: data.alphaJournals,
loading: false,
})
}, 100)
const onQueryChange = e => {
const query = e.target.value
this.setState({
query: e ? query : '',
journals: [],
loading: true,
})
if (query.trim().length > 0) {
searchJournals()
}
}
return (
<SearchSelect
invalidTest={this.state.journalMessage}
label="Search for journal title"
onInput={onQueryChange}
optionsOnChange={this.onJournalSelect}
placeholder="The journal title"
query={this.state.query}
singleSelect
>
{this.state.loading && (
<Loading>
<LoadingIcon />
</Loading>
)}
{this.state.journals.map(journal => (
<SearchSelect.Option
data-option={journal}
key={journal.id}
propKey={journal.id}
>
<HTMLString string={journal.journalTitle} />
</SearchSelect.Option>
))}
{this.state.query.trim().length > 0 && (
<SearchSelect.Option
data-option={{ journalTitle: this.state.query }}
key="submit"
propKey="submit"
>
<em>
<HTMLString
string={`Submit &apos;${
this.state.query
}&apos; as your journal name`}
/>
</em>
</SearchSelect.Option>
)}
</SearchSelect>
)
}}
</ApolloConsumer>
{this.state.journalMessage && (
<ErrorText>{this.state.journalMessage}</ErrorText>
)}
......
......@@ -6,39 +6,19 @@ export const JOURNAL_INFO = gql`
id
journalTitle
meta {
nlmta
pmcStatus
pubmedStatus
}
}
}
`
export const CREATE_NOTE = gql`
mutation CreateNote($data: NewNoteInput!) {
createNote(data: $data) {
id
meta {
notes {
id
notesType
content
}
}
}
}
`
export const UPDATE_NOTE = gql`
mutation UpdateNote($data: NoteInput!) {
updateNote(data: $data) {
export const SEARCH_JOURNALS = gql`
query($query: String!) {
alphaJournals(query: $query) {
id
journalTitle
meta {
notes {
id
notesType
content
}
pmcStatus
}
}
}
......
......@@ -99,6 +99,7 @@ const SearchInput = styled.input`
border: none;
background-color: transparent;
padding: 0 ${th('gridUnit')};
box-sizing: border-box;
font-family: inherit;
font-size: inherit;
&:focus {
......@@ -108,12 +109,13 @@ const SearchInput = styled.input`
const DropdownIcon = styled(Icon)`
border-left: ${th('borderWidth')} ${th('borderStyle')} ${borderColor};
background-color: ${th('colorBackgroundHue')};
height: calc((${th('gridUnit')} * 6) - (${th('borderWidth')} * 2));
height: calc(100% - (${th('borderWidth')} * 4));
box-sizing: border-box;
display: inline-flex;
align-items: center;
position: absolute;
right: ${th('borderWidth')};
top: ${th('borderWidth')};
right: calc(${th('borderWidth')} * 2);
top: calc(${th('borderWidth')} * 2);
${override('ui.Select.DropdownIcon')};
${override('ui.SearchSelect.DropdownIcon')};
`
......
......@@ -61,16 +61,31 @@ class Journal extends EpmcBaseModel {
}
static async selectByNlmId(nlmId) {
const row = await runQuery(
buildQuery
.select('journal.*')
.from('journal')
.where('meta,nlmuniqueid', nlmId)
.first(),
)
if (!row) {
throw new Error('journal not found')
}
return rowToEntity(row)
}
static async searchByTitle(query) {
const rows = await runQuery(
buildQuery
.select('journal.*')
.from('journal')
.where('meta,nlmuniqueid', nlmId),
.where('journalTitle', 'ILIKE', `${query}%`)
.orderBy('journalTitle'),
)
if (!rows) {
throw new Error('journal not found')
}
return rowToEntity(rows[0])
return rowToEntity(rows)
}
static async selectAll() {
......
......@@ -24,6 +24,7 @@ const JournalManager = {
return { ...journal, id }
},
selectWithNLM: async nlmId => Journal.selectByNlmId(nlmId),
searchByTitle: async query => Journal.searchByTitle(query),
}
module.exports = JournalManager
......@@ -11,6 +11,13 @@ const resolvers = {
return JournalManager.selectWithNLM(nlmId)
},
async alphaJournals(_, { query }, { user }) {
if (!user) {
throw new Error('You are not authenticated!')
}
return JournalManager.searchByTitle(query)
},
},
}
......
extend type Query {
selectWithNLM(nlmId: String!): Journal!
}
\ No newline at end of file
alphaJournals(query: String!): [Journal]!
}
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