Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AfO Register #398

Merged
merged 32 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6b090e8
Implement repository & tests (WiP)
khoidt Oct 25, 2023
bc7e1fe
Add AfO service, tests & bibliography routes and tabs
khoidt Oct 30, 2023
fd947c1
Add tests & format
khoidt Oct 31, 2023
2749a66
Impelment AfO Register search forms
khoidt Oct 31, 2023
ced19e9
Implement search & async options (WiP)
khoidt Nov 2, 2023
5d3aacd
Update & fix tests
khoidt Nov 7, 2023
526e661
Implement suggestions (WiP)
khoidt Nov 13, 2023
029b34d
Adjust search, sorting & form (WiP)
khoidt Nov 15, 2023
104f699
Merge remote-tracking branch 'origin/master' into afo-register
khoidt Nov 15, 2023
81b8fce
Correct Markdown string output
khoidt Nov 15, 2023
162e4fd
Update queries & interface, refactor
khoidt Nov 16, 2023
89b0fdf
Refactor search form
khoidt Nov 21, 2023
b556376
Add AfO Register & bibliography intro, style
khoidt Nov 21, 2023
2810b19
Merge remote-tracking branch 'origin/master' into afo-register
khoidt Nov 22, 2023
bf4a4b2
Implement AfO Reg. display in Fragmentarium
khoidt Nov 24, 2023
71a947a
Implement fragment injection, link, style, refactor & update tests
khoidt Nov 26, 2023
669b57a
Merge remote-tracking branch 'origin/master' into afo-register
khoidt Nov 26, 2023
5d30646
Fix CSS style
khoidt Nov 26, 2023
48f9a91
Refactor
khoidt Nov 26, 2023
3a26c4b
Refactor more
khoidt Nov 26, 2023
75d2217
Add missing space
khoidt Nov 26, 2023
b16cc16
Update repository & service tests
khoidt Nov 27, 2023
6b06553
Add factory & display tests
khoidt Nov 27, 2023
c5832c5
Add search results display test
khoidt Nov 27, 2023
8f01369
Add text select tests
khoidt Nov 27, 2023
fa10ed7
Add fragment records test & fix display
khoidt Nov 27, 2023
0d95895
Add & update tests
khoidt Nov 27, 2023
599ad57
Commit after git glitch
khoidt Nov 27, 2023
7a929a3
Extend repository tests
khoidt Nov 27, 2023
93adcf7
Update bibliography tests
khoidt Nov 27, 2023
dcee39c
Add repository test
khoidt Nov 28, 2023
43c19d8
Extend search form tests
khoidt Nov 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/afo-register/application/AfoRegisterService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { testDelegation, TestData } from 'test-support/utils'
import AfoRegisterRepository from 'afo-register/infrastructure/AfoRegisterRepository'
import AfoRegisterRecord from 'afo-register/domain/Record'
import { stringify } from 'query-string'
import ApiClient from 'http/ApiClient'

jest.mock('http/ApiClient')
const apiClient = new (ApiClient as jest.Mock<jest.Mocked<ApiClient>>)()

const afoRegisterRepository = new AfoRegisterRepository(apiClient)

const resultStub = {
afoNumber: 'AfO 1',
page: '2',
text: 'some text',
textNumber: '5',
discussedBy: '',
discussedByNotes: '',
linesDiscussed: '',
}

const query = { afoNumber: resultStub.afoNumber, page: resultStub.page }
const entry = new AfoRegisterRecord(resultStub)

const testData: TestData<AfoRegisterRepository>[] = [
new TestData(
'search',
[
stringify({
afoNumber: 'AfO 1',
page: '2',
}),
],
apiClient.fetchJson,
[entry],
[`/afo-register?${stringify(query)}`, false],
Promise.resolve([resultStub])
),
]
describe('afoRegisterService', () =>
testDelegation(afoRegisterRepository, testData))
40 changes: 40 additions & 0 deletions src/afo-register/application/AfoRegisterService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Promise from 'bluebird'
import AfoRegisterRecord, {
AfoRegisterRecordSuggestion,
} from 'afo-register/domain/Record'
import AfoRegisterRepository from 'afo-register/infrastructure/AfoRegisterRepository'
import FragmentService from 'fragmentarium/application/FragmentService'

export interface afoRegisterSearch {
search(query: string): Promise<readonly AfoRegisterRecord[]>
searchSuggestions(
query: string
): Promise<readonly AfoRegisterRecordSuggestion[]>
}

export default class AfoRegisterService implements afoRegisterSearch {
private readonly afoRegisterRepository: AfoRegisterRepository

constructor(afoRegisterRepository: AfoRegisterRepository) {
this.afoRegisterRepository = afoRegisterRepository
}

search(
query: string,
fragmentService?: FragmentService
): Promise<readonly AfoRegisterRecord[]> {
return this.afoRegisterRepository.search(query, fragmentService)
}

searchTextsAndNumbers(
query: readonly string[]
): Promise<readonly AfoRegisterRecord[]> {
return this.afoRegisterRepository.searchTextsAndNumbers(query)
}

searchSuggestions(
query: string
): Promise<readonly AfoRegisterRecordSuggestion[]> {
return this.afoRegisterRepository.searchSuggestions(query)
}
}
48 changes: 48 additions & 0 deletions src/afo-register/domain/Record.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import AfoRegisterRecord from 'afo-register/domain/Record'

describe('AfoRegisterRecord', () => {
const mockRecord = {
afoNumber: '123',
page: '456',
text: 'Sample text',
textNumber: '789',
linesDiscussed: 'Some lines',
discussedBy: 'John Doe',
discussedByNotes: 'Notes by John',
fragmentNumbers: ['BM777'],
}

describe('constructor', () => {
describe('constructor', () => {
it('should initialize properties correctly', () => {
const record = new AfoRegisterRecord(mockRecord)
expect(record.afoNumber).toEqual('123')
expect(record.page).toEqual('456')
expect(record.text).toEqual('Sample text')
expect(record.textNumber).toEqual('789')
expect(record.linesDiscussed).toEqual('Some lines')
expect(record.discussedBy).toEqual('John Doe')
expect(record.discussedByNotes).toEqual('Notes by John')
})
})
})

describe('Converts to Markdown string', () => {
it('Returns the correct markdown string with fragments', () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 2 locations. Consider refactoring.

const record = new AfoRegisterRecord(mockRecord)
const result = record.toMarkdownString()
expect(result).toEqual(
'Sample text 789 ([BM777](/fragmentarium/BM777)), Some lines: John Doe Notes by John<small class="text-black-50 ml-3">[123, 456]</small>'
)
})

it('Handles sup tags correctly', () => {
const recordWithSup = new AfoRegisterRecord({
...mockRecord,
text: 'Sample^text^',
})
const result = recordWithSup.toMarkdownString()
expect(result).toContain('<sup>text</sup>')
})
})
})
94 changes: 94 additions & 0 deletions src/afo-register/domain/Record.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import produce, { Draft, immerable } from 'immer'

interface RecordData {
readonly afoNumber: string
readonly page: string
readonly text: string
readonly textNumber: string
readonly linesDiscussed?: string
readonly discussedBy?: string
readonly discussedByNotes?: string
readonly fragmentNumbers?: string[]
}

export class AfoRegisterRecordSuggestion {
[immerable] = true

readonly text: string
readonly textNumbers: string[]

constructor({
text,
textNumbers,
}: {
readonly text: string
readonly textNumbers: string[]
}) {
this.text = text
this.textNumbers = textNumbers
}
}

export default class AfoRegisterRecord {
[immerable] = true

readonly afoNumber: string
readonly page: string
readonly text: string
readonly textNumber: string
readonly linesDiscussed?: string
readonly discussedBy?: string
readonly discussedByNotes?: string
readonly fragmentNumbers?: string[]

constructor({
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 3 locations. Consider refactoring.

afoNumber,
page,
text,
textNumber,
linesDiscussed,
discussedBy,
discussedByNotes,
fragmentNumbers,
}: RecordData) {
this.afoNumber = afoNumber
this.page = page
this.text = text
this.textNumber = textNumber
this.linesDiscussed = linesDiscussed
this.discussedBy = discussedBy
this.discussedByNotes = discussedByNotes
this.fragmentNumbers = fragmentNumbers
}

toMarkdownString(): string {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function toMarkdownString has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.

let result = this.text + (this.textNumber ? ' ' + this.textNumber : '')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recently made a little helper function padLeft in src/fragmentarium/domain/archaeology.ts because I had to do the x ? ' ' + x : '' thing over and over, which is then just padLeft(this.textNumber). If you like you can move it to common/utils.ts or something and use it here, it might lower the complexity a bit.

if (this.fragmentNumbers && this.fragmentNumbers.length > 0) {
result += ` (${this.fragmentsToMarkdownString()})`
}
if (this.linesDiscussed) result += ', ' + this.linesDiscussed
if (this.discussedBy) result += ': ' + this.discussedBy
if (this.discussedByNotes) result += ' ' + this.discussedByNotes
result += `<small class="text-black-50 ml-3">${`[${this.afoNumber}, ${this.page}]`}</small>`
result = result.replace(/\^([^^]+)\^/g, '<sup>$1</sup>')
return result
}

private fragmentsToMarkdownString(): string {
if (!this.fragmentNumbers) {
return ''
}
return this.fragmentNumbers
.map(
(fragmentNumber) =>
`[${fragmentNumber}](/fragmentarium/${fragmentNumber})`
)
.join(', ')
}

setFragmentNumbers(fragmentNumbers: string[]): AfoRegisterRecord {
return produce(this, (draft: Draft<AfoRegisterRecord>) => {
draft.fragmentNumbers = fragmentNumbers
})
}
}
35 changes: 35 additions & 0 deletions src/afo-register/infrastructure/AfoRegisterRepository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import AfoRegisterRecord from 'afo-register/domain/Record'
import ApiClient from 'http/ApiClient'
import AfoRegisterRepository from './AfoRegisterRepository'
import Promise from 'bluebird'
import { testDelegation, TestData } from 'test-support/utils'

jest.mock('http/ApiClient')

const apiClient = new (ApiClient as jest.Mock<jest.Mocked<ApiClient>>)()
const afoRegisterRepository = new AfoRegisterRepository(apiClient)
const query = '{"afoNumber": "AfO 12", "page": "321"}'
const resultStub = {
afoNumber: 'AfO 12',
page: '321',
text: 'text',
textNumber: 'text number',
linesDiscussed: '',
discussedBy: '',
discussedByNotes: '',
}
const entry = new AfoRegisterRecord(resultStub)

const testData: TestData<AfoRegisterRepository>[] = [
new TestData(
'search',
[query],
apiClient.fetchJson,
[entry],
[`/afo-register?${query}`, false],
Promise.resolve([resultStub])
),
]

describe('AfoRegisterRepository', () =>
testDelegation(afoRegisterRepository, testData))
70 changes: 70 additions & 0 deletions src/afo-register/infrastructure/AfoRegisterRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import AfoRegisterRecord, {
AfoRegisterRecordSuggestion,
} from 'afo-register/domain/Record'
import Promise from 'bluebird'
import FragmentService from 'fragmentarium/application/FragmentService'
import ApiClient from 'http/ApiClient'

function createAfoRegisterRecord(data): AfoRegisterRecord {
return new AfoRegisterRecord(data)
}

function createAfoRegisterRecordSuggestion(data): AfoRegisterRecordSuggestion {
return new AfoRegisterRecordSuggestion(data)
}

function injectFragmentReferecesToRecord(
record: AfoRegisterRecord,
fragmentService: FragmentService
): Promise<AfoRegisterRecord> {
const { text, textNumber } = record
return fragmentService
.query({ traditionalReferences: text + ' ' + textNumber })
.then((queryResult) => {
return record.setFragmentNumbers(
queryResult.items.map((item) => item.museumNumber)
)
})
}

export default class AfoRegisterRepository {
private readonly apiClient: ApiClient

constructor(apiClient: ApiClient) {
this.apiClient = apiClient
}

search(
query: string,
fragmentService?: FragmentService
): Promise<AfoRegisterRecord[]> {
return this.apiClient
.fetchJson(`/afo-register?${query}`, false)
.then((result) => result.map(createAfoRegisterRecord))
.then((records) => {
if (fragmentService) {
return Promise.all(
records.map((record) =>
injectFragmentReferecesToRecord(record, fragmentService)
)
)
} else {
return records
}
})
}

searchTextsAndNumbers(
query: readonly string[]
): Promise<AfoRegisterRecord[]> {
return this.apiClient
.postJson(`/afo-register/texts-numbers`, query, false)
.then((result) => result.map(createAfoRegisterRecord))
}

searchSuggestions(query: string): Promise<AfoRegisterRecordSuggestion[]> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 2 locations. Consider refactoring.

return this.apiClient
.fetchJson(`/afo-register/suggestions?text_query=${query}`, false)
.then((result) => result.map(createAfoRegisterRecordSuggestion))
}
}
38 changes: 38 additions & 0 deletions src/afo-register/ui/AfoRegisterDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react'
import AfoRegisterRecord from 'afo-register/domain/Record'
import MarkdownAndHtmlToHtml from 'common/MarkdownAndHtmlToHtml'

export function AfoRegisterRecordDisplay({
record,
index,
}: {
record: AfoRegisterRecord
index: string | number
}): JSX.Element {
return (
<MarkdownAndHtmlToHtml
key={`md-${index}`}
markdownAndHtml={record.toMarkdownString()}
/>
)
}

export function AfoRegisterRecordsListDisplay({
records,
...props
}: {
records: readonly AfoRegisterRecord[]
} & React.OlHTMLAttributes<HTMLOListElement>): JSX.Element {
if (records.length < 1) {
return <p>No records found</p>
}
return (
<ol {...props}>
{records.map((record, index) => (
<li key={`li-${index}`} className="afoRegisterRecordsList__record">
<AfoRegisterRecordDisplay record={record} index={index} />
</li>
))}
</ol>
)
}
Loading
Loading