Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
digibib
deichman
Commits
2293e724
Commit
2293e724
authored
Mar 03, 2021
by
Magnus Westergaard
Browse files
DEICH-5630
: Add related content based on work series.
parent
0073c2ba
Changes
10
Hide whitespace changes
Inline
Side-by-side
deichman.no/components/DynamicList/DynamicList.js
View file @
2293e724
...
...
@@ -24,6 +24,7 @@ const DynamicList = ({
favourites
,
onFavourite
,
size
,
cardOptions
,
children
})
=>
{
const
numberOfItems
=
size
||
DEFAULT_SIZES
[
type
];
...
...
@@ -66,6 +67,7 @@ const DynamicList = ({
favourites
=
{
favourites
}
onFavourite
=
{
onFavourite
}
isHorizontal
cardOptions
=
{
cardOptions
}
hideFlagsList
=
{[
"
recommended
"
,
"
liked
"
]}
/
>
)}
...
...
@@ -88,6 +90,7 @@ DynamicList.defaultProps = {
items
:
[],
favourites
:
[],
size
:
undefined
,
cardOptions
:
{},
children
:
[]
};
...
...
@@ -101,6 +104,7 @@ DynamicList.propTypes = {
favourites
:
PropTypes
.
array
,
onFavourite
:
PropTypes
.
func
.
isRequired
,
size
:
PropTypes
.
number
,
cardOptions
:
PropTypes
.
object
,
children
:
PropTypes
.
node
};
...
...
deichman.no/components/PublicationRelatedContent/PublicationRelatedContent.js
View file @
2293e724
...
...
@@ -3,6 +3,7 @@ import PropTypes from "prop-types";
import
DynamicListFromAgent
from
"
../DynamicListFromAgent
"
;
import
DynamicListFromQuery
from
"
../DynamicListFromQuery
"
;
import
WorkSeriesPublicationList
from
"
../WorkSeriesPublicationList
"
;
import
{
translations
}
from
"
../../constants/translations
"
;
import
{
FILM
,
GAME
}
from
"
../../constants/mediaTypes
"
;
import
{
...
...
@@ -35,7 +36,7 @@ const findFallbackContributor = pub => {
const
relatedContentForContributor
=
(
contributor
,
mediaType
)
=>
{
if
(
contributor
)
{
return
(
<
Block
top
=
{
8
}
>
<
Block
top
=
{
8
}
key
=
{
contributor
.
agent
.
id
}
>
<
DynamicListFromAgent
agent
=
{
contributor
.
agent
}
agentRole
=
{
contributor
.
mainEntry
?
"
mainEntry
"
:
contributor
.
role
}
...
...
@@ -66,14 +67,23 @@ export default function PublicationRelatedContent({ publication }) {
)
);
const
{
workSeries
=
[]
}
=
work
;
const
workSeriesContent
=
workSeries
.
map
(
ws
=>
(
<
Block
top
=
{
8
}
key
=
{
ws
.
id
}
>
<
WorkSeriesPublicationList
workSeries
=
{
ws
}
mediaType
=
{
mediaType
}
/
>
<
/Block
>
));
return
(
<>
{
workSeriesContent
}
{
mainEntryOrFallbackContent
}
{
additionalRolesContent
}
<
Block
top
=
{
8
}
>
<
DynamicListFromQuery
query
=
{
`*?mediatype=
${
mediaTypeQuery
}
&sort=issuesTotal:desc&excludeUnavailable=excludeUnavailable`
}
title
=
"
Andre har også lånt
"
inheritRouterQuery
=
{
false
}
randomizeHits
/>
<
/Block
>
...
...
deichman.no/components/WorkCard/NumberInSeries/NumberInSeries.js
0 → 100644
View file @
2293e724
import
React
from
"
react
"
;
import
PropTypes
from
"
prop-types
"
;
// in some cases, the partNumber is a year and we want to style
// it differently, so we define a threshold for what we consider
// to be a year (as opposed to a regular sequential part number)
const
YEAR_THRESHOLD
=
1500
;
const
NumberInSeries
=
({
wsPartNumber
})
=>
{
if
(
!
wsPartNumber
&&
!
Number
.
isInteger
(
wsPartNumber
))
{
return
null
;
}
let
displayText
=
wsPartNumber
;
const
asNumber
=
Number
(
wsPartNumber
);
if
(
Number
.
isInteger
(
asNumber
))
{
displayText
=
asNumber
<
YEAR_THRESHOLD
?
`Nr.
${
asNumber
}
i serien`
:
asNumber
;
}
return
(
<
div
className
=
"
work-card__data-item work-card__data-item--small
"
>
{
displayText
}
<
/div
>
);
};
NumberInSeries
.
propTypes
=
{
wsPartNumber
:
PropTypes
.
oneOfType
([
PropTypes
.
string
,
PropTypes
.
number
])
.
isRequired
};
export
default
NumberInSeries
;
deichman.no/components/WorkCard/NumberInSeries/index.js
0 → 100644
View file @
2293e724
import
NumberInSeries
from
"
./NumberInSeries
"
;
export
default
NumberInSeries
;
deichman.no/components/WorkSeriesPublicationList/WorkSeriesPublicationList.js
0 → 100644
View file @
2293e724
import
React
from
"
react
"
;
import
PropTypes
from
"
prop-types
"
;
import
autoBind
from
"
auto-bind
"
;
import
{
connect
}
from
"
react-redux
"
;
import
{
withRouter
}
from
"
next/router
"
;
import
Link
from
"
next/link
"
;
import
DynamicList
from
"
../DynamicList
"
;
import
NumberInSeries
from
"
../WorkCard/NumberInSeries
"
;
import
{
fullTitle
}
from
"
../../utilities/title
"
;
import
{
pageLinkFromUri
}
from
"
../../utilities/pageLink
"
;
import
{
translations
}
from
"
../../constants/translations
"
;
import
{
toggleFavourites
}
from
"
../../store/application
"
;
import
{
Block
,
Flex
,
Icon
}
from
"
@digibib/deichman-ui
"
;
function
seriesNumber
(
workSeriesUri
)
{
return
({
data
})
=>
{
const
wsInfo
=
data
.
workSeriesInfo
.
find
(
ws
=>
ws
.
uri
===
workSeriesUri
);
return
<
NumberInSeries
wsPartNumber
=
{
wsInfo
?.
number
}
/>
;
};
}
class
WorkSeriesPublicationList
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
isLoading
:
true
};
autoBind
(
this
);
}
componentDidMount
()
{
this
.
search
();
}
handleFavourite
(
recordId
)
{
const
data
=
{
recordId
};
this
.
props
.
toggleFavourites
(
data
);
}
async
search
()
{
const
{
workSeries
,
mediaType
,
size
}
=
this
.
props
;
const
mediaTypeQuery
=
mediaType
?
`?filter=mediaType_
${
translations
[
mediaType
]}
`
:
""
;
this
.
setState
({
isLoading
:
true
});
const
res
=
await
fetch
(
`/api/resources/workseries/
${
workSeries
.
id
}
/publications
${
mediaTypeQuery
}
&size=
${
size
}
&excludeSatellites=excludeSatellites`
);
const
results
=
await
res
.
json
();
this
.
setState
({
isLoading
:
false
,
results
});
}
render
()
{
const
{
favourites
,
workSeries
}
=
this
.
props
;
const
workSeriesLabel
=
fullTitle
(
workSeries
);
const
pLink
=
pageLinkFromUri
(
workSeries
.
uri
,
workSeriesLabel
);
const
{
isLoading
,
results
=
{}
}
=
this
.
state
;
const
{
orderedHits
=
[],
totalHits
=
0
}
=
results
;
const
noHits
=
!
isLoading
&&
orderedHits
.
length
===
0
;
const
displayLink
=
!
isLoading
&&
totalHits
>
orderedHits
.
length
;
const
cardOptions
=
{
additionalInfo
:
[
seriesNumber
(
workSeries
.
uri
)]
};
return
(
<>
<
DynamicList
type
=
"
standard
"
title
=
{
`Mer fra
${
workSeriesLabel
}
`
}
isLoading
=
{
isLoading
}
items
=
{
orderedHits
}
noHits
=
{
noHits
}
totalCount
=
{
totalHits
}
favourites
=
{
favourites
}
onFavourite
=
{
this
.
handleFavourite
}
cardOptions
=
{
cardOptions
}
/
>
{
displayLink
&&
(
<
Flex
align
=
"
center
"
>
<
Icon
type
=
"
arrow-large-right
"
size
=
"
20
"
/>
<
Block
left
=
{
2
}
>
<
Link
href
=
{
pLink
.
href
}
as
=
{
pLink
.
as
}
>
<
a
>
Se
alt
fra
{
workSeriesLabel
}
<
/a
>
<
/Link
>
<
/Block
>
<
/Flex
>
)}
<
/
>
);
}
}
WorkSeriesPublicationList
.
defaultProps
=
{
mediaType
:
""
,
favourites
:
[],
size
:
6
};
WorkSeriesPublicationList
.
propTypes
=
{
mediaType
:
PropTypes
.
string
,
workSeries
:
PropTypes
.
object
.
isRequired
,
favourites
:
PropTypes
.
array
,
toggleFavourites
:
PropTypes
.
func
.
isRequired
,
size
:
PropTypes
.
number
};
function
mapDispatchToProps
(
dispatch
)
{
return
{
toggleFavourites
:
data
=>
dispatch
(
toggleFavourites
(
data
))
};
}
function
mapStateToProps
(
state
)
{
const
{
biblioNumbers
}
=
state
.
favourites
;
return
{
favourites
:
biblioNumbers
};
}
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
)(
withRouter
(
WorkSeriesPublicationList
));
deichman.no/components/WorkSeriesPublicationList/index.js
0 → 100644
View file @
2293e724
import
WorkSeriesPublicationList
from
"
./WorkSeriesPublicationList
"
;
export
default
WorkSeriesPublicationList
;
deichman.no/pages/serie/[seriesSlug].js
View file @
2293e724
...
...
@@ -8,6 +8,7 @@ import { toggleFavourites, unfreezeBody } from "../../store/application";
import
{
hideSearchOverlay
}
from
"
../../store/search
"
;
import
{
extractIdFromSlug
}
from
"
../../utilities/slug
"
;
import
{
workSeriesWorksSort
}
from
"
../../server/utils/sortWorkSeriesWorks
"
;
import
Head
from
"
../../components/Head
"
;
import
FullScreen
from
"
../../components/FullScreen
"
;
...
...
@@ -21,8 +22,7 @@ import "./styles.css";
import
{
uppercaseFirstLetter
,
pluraliseString
}
from
"
../../utilities/string
"
;
import
Pagination
from
"
../../components/Grid/Pagination
"
;
const
MAX_PARTS
=
1500
;
import
NumberInSeries
from
"
../../components/WorkCard/NumberInSeries
"
;
class
SeriesPage
extends
React
.
Component
{
constructor
()
{
...
...
@@ -243,36 +243,6 @@ function mapDispatchToProps(dispatch) {
};
}
function
worksSort
(
a
,
b
)
{
// partNumberAsInteger might be 0 (which is valid)
const
aHasIntegerPartNumber
=
Number
.
isInteger
(
a
.
partNumberAsInteger
);
const
bHasIntegerPartNumber
=
Number
.
isInteger
(
b
.
partNumberAsInteger
);
if
(
aHasIntegerPartNumber
&&
!
bHasIntegerPartNumber
)
{
return
-
1
;
}
if
(
!
aHasIntegerPartNumber
&&
bHasIntegerPartNumber
)
{
return
1
;
}
if
(
aHasIntegerPartNumber
&&
bHasIntegerPartNumber
)
{
const
diff
=
a
.
partNumberAsInteger
-
b
.
partNumberAsInteger
;
if
(
diff
!==
0
)
{
return
diff
;
}
}
if
(
Number
.
isInteger
(
a
.
publicationYear
)
||
Number
.
isInteger
(
b
.
publicationYear
)
)
{
const
diff
=
(
a
.
publicationYear
||
Number
.
MAX_SAFE_INTEGER
)
-
(
b
.
publicationYear
||
Number
.
MAX_SAFE_INTEGER
);
if
(
diff
!==
0
)
{
return
diff
;
}
}
return
`
${
a
.
mainTitle
}
`
.
localeCompare
(
`
${
b
.
mainTitle
}
`
);
}
function
publicationsFromWorks
(
works
,
publications
,
...
...
@@ -280,7 +250,7 @@ function publicationsFromWorks(
otherPublications
)
{
return
works
.
sort
(
worksSort
)
.
sort
(
work
SeriesWork
sSort
)
.
map
((
work
,
index
)
=>
{
let
otherPub
=
{
work
:
{}
};
if
(
!
publications
[
work
.
uri
]
&&
otherPublications
[
work
.
uri
])
{
...
...
@@ -320,22 +290,10 @@ function publicationsFromWorks(
}
function
DisplaySeriesNumber
({
data
})
{
const
numericPart
=
data
.
partNumberAsInteger
;
if
(
numericPart
)
{
return
(
<
div
className
=
"
work-card__data-item work-card__data-item--small
"
>
{
numericPart
<
MAX_PARTS
?
`Nr.
${
numericPart
}
i serien`
:
numericPart
}
<
/div
>
);
}
if
(
data
.
partNumber
)
{
return
(
<
div
className
=
"
work-card__data-item work-card__data-item--small
"
>
{
data
.
partNumber
}
<
/div
>
);
}
return
<><
/>
;
const
number
=
Number
.
isInteger
(
data
.
partNumberAsInteger
)
?
data
.
partNumberAsInteger
:
data
.
partNumber
;
return
<
NumberInSeries
wsPartNumber
=
{
number
}
/>
;
}
function
DisplayNotAvailableInLanguage
(
requestedLanguage
,
{
data
})
{
...
...
deichman.no/server/routes/resources.js
View file @
2293e724
...
...
@@ -7,6 +7,7 @@ const {
}
=
require
(
"
../utils/resourceHelpers
"
);
const
{
translations
}
=
require
(
"
../../constants/translations
"
);
const
{
timeout
}
=
require
(
"
../utils/apiUtils
"
);
const
{
workSeriesWorksSort
}
=
require
(
"
../utils/sortWorkSeriesWorks
"
);
const
logger
=
require
(
"
../../logger
"
)(
__filename
);
const
CALL_ID_HEADER
=
"
Deichman-CallID
"
;
...
...
@@ -388,6 +389,57 @@ routes.get("/workseries/:workSeriesId", async (req, res) => {
}
});
// Get workSeries publications (in order)
routes
.
get
(
"
/workseries/:workSeriesId/publications
"
,
async
(
req
,
res
)
=>
{
const
{
filter
,
excludeSatellites
,
size
}
=
req
.
query
;
let
query
=
filter
?
`?filter=
${
encodeURIComponent
(
filter
)}
`
:
""
;
if
(
excludeSatellites
===
"
excludeSatellites
"
)
{
query
+=
"
&excludeSatellites=excludeSatellites
"
;
}
const
deichmanCallId
=
req
.
headers
[
CALL_ID_HEADER
];
logger
.
info
(
`Fetching publications for workseries
${
req
.
params
.
workSeriesId
}
`
,
{
workSeries_id
:
req
.
params
.
workSeriesId
,
call_id
:
deichmanCallId
}
);
try
{
const
results
=
await
fetch
(
`
${
INTERNAL_URL_EULER
}
/api/augmented/workSeries/
${
req
.
params
.
workSeriesId
}
`
);
const
{
works
}
=
await
results
.
json
();
const
workUris
=
works
.
map
(
work
=>
work
.
uri
);
const
sibylRes
=
await
timeout
(
TIMEOUT_MS
,
fetch
(
`
${
INTERNAL_URL_SIBYL
}
/search/publicationByWorkUris
${
query
}
`
,
{
method
:
"
POST
"
,
body
:
JSON
.
stringify
({
ids
:
workUris
})
})
);
const
{
hits
,
totalHits
}
=
await
sibylRes
.
json
();
works
.
sort
(
workSeriesWorksSort
);
hits
.
sort
(
(
pubA
,
pubB
)
=>
works
.
findIndex
(
w
=>
pubA
.
work
.
uri
===
w
.
uri
)
-
works
.
findIndex
(
w
=>
pubB
.
work
.
uri
===
w
.
uri
)
);
res
.
status
(
200
).
send
({
orderedHits
:
hits
.
slice
(
0
,
size
),
totalHits
});
}
catch
(
error
)
{
logger
.
warn
(
`ERROR calling get workseries publications
${
error
.
message
}
`
,
{
...
error
,
call_id
:
deichmanCallId
});
res
.
sendStatus
(
500
);
}
});
// Get serial by ID
routes
.
get
(
"
/serial/:serialId
"
,
async
(
req
,
res
)
=>
{
const
deichmanCallId
=
req
.
headers
[
CALL_ID_HEADER
];
...
...
deichman.no/server/utils/sortWorkSeriesWorks.js
0 → 100644
View file @
2293e724
/*
Sort works in a workSeries according to
1. partNumber if it's an integer (the one from the WorkSeriesPart relation, not the on which is part of the work's title)
2. publication year
3. main title
*/
function
workSeriesWorksSort
(
a
,
b
)
{
// partNumberAsInteger might be 0 (which is valid)
const
aHasIntegerPartNumber
=
Number
.
isInteger
(
a
.
partNumberAsInteger
);
const
bHasIntegerPartNumber
=
Number
.
isInteger
(
b
.
partNumberAsInteger
);
if
(
aHasIntegerPartNumber
&&
!
bHasIntegerPartNumber
)
{
return
-
1
;
}
if
(
!
aHasIntegerPartNumber
&&
bHasIntegerPartNumber
)
{
return
1
;
}
if
(
aHasIntegerPartNumber
&&
bHasIntegerPartNumber
)
{
const
diff
=
a
.
partNumberAsInteger
-
b
.
partNumberAsInteger
;
if
(
diff
!==
0
)
{
return
diff
;
}
}
if
(
Number
.
isInteger
(
a
.
publicationYear
)
||
Number
.
isInteger
(
b
.
publicationYear
)
)
{
const
diff
=
(
a
.
publicationYear
||
Number
.
MAX_SAFE_INTEGER
)
-
(
b
.
publicationYear
||
Number
.
MAX_SAFE_INTEGER
);
if
(
diff
!==
0
)
{
return
diff
;
}
}
return
`
${
a
.
mainTitle
}
`
.
localeCompare
(
`
${
b
.
mainTitle
}
`
);
}
module
.
exports
=
{
workSeriesWorksSort
};
sibyl/internal/search/publication/publication_search.go
View file @
2293e724
...
...
@@ -134,7 +134,8 @@ func (p *publicationSearchClient) SearchByWorkUris(uris []string, params url.Val
return
SearchResults
{}
}
parsedResults
:=
transformElasticResponse
(
result
,
size
,
excludeAdditionalMediaTypes
,
false
,
DebugFlags
{})
excludeSatellites
:=
params
.
Get
(
"excludeSatellites"
)
==
"excludeSatellites"
parsedResults
:=
transformElasticResponse
(
result
,
size
,
excludeAdditionalMediaTypes
,
excludeSatellites
,
DebugFlags
{})
return
parsedResults
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment