diff --git a/docs/Caching.md b/docs/Caching.md index 8947faf62..993de85fd 100644 --- a/docs/Caching.md +++ b/docs/Caching.md @@ -31,7 +31,7 @@ if (isNotInEditMode) { - [ ] Move menuCache to seperate file ### Parts to cache - [x] KPI calculator -- [ ] Upcoming releases part ~300ms +- [x] Upcoming releases part ~300ms - [x] OmStatistikken part ~70ms - [ ] RelatedArticles part ~25-100ms - [ ] Default page ~10-60ms diff --git a/docs/PartRendering.md b/docs/PartRendering.md index d1151e50b..dd8f016a1 100644 --- a/docs/PartRendering.md +++ b/docs/PartRendering.md @@ -49,4 +49,4 @@ return { - [ ] statbankLinkList - [ ] statistics - [ ] table -- [ ] upcomingReleases +- [x] upcomingReleases diff --git a/src/main/resources/lib/ssb/cache/partCache.ts b/src/main/resources/lib/ssb/cache/partCache.ts index caba96afe..a7d71b6a9 100644 --- a/src/main/resources/lib/ssb/cache/partCache.ts +++ b/src/main/resources/lib/ssb/cache/partCache.ts @@ -42,6 +42,8 @@ export function clearPartCache(content: Content, branch: string): void { partCache.remove(`${content._id}-publicationArchive`) cacheLog(`try to clear ${content._id}-attachmentTablesFigures from part cache (${branch})`) partCache.remove(`${content._id}-attachmentTablesFigures`) + cacheLog(`try to clear ${content._id}-upcomingReleases from part cache (${branch})`) + partCache.remove(`${content._id}-upcomingReleases`) } } diff --git a/src/main/resources/lib/ssb/cron/cron.ts b/src/main/resources/lib/ssb/cron/cron.ts index befe0bf37..53013d2c8 100644 --- a/src/main/resources/lib/ssb/cron/cron.ts +++ b/src/main/resources/lib/ssb/cron/cron.ts @@ -223,6 +223,7 @@ export function setupCronJobs(): void { clearPartFromPartCache('kpiCalculator') clearPartFromPartCache('husleieCalculator') clearPartFromPartCache('attachmentTablesFigures') + clearPartFromPartCache('upcomingReleases') }, context: cronContext }) diff --git a/src/main/resources/lib/ssb/utils/variantUtils.ts b/src/main/resources/lib/ssb/utils/variantUtils.ts index df156bc5b..dd0ccf889 100644 --- a/src/main/resources/lib/ssb/utils/variantUtils.ts +++ b/src/main/resources/lib/ssb/utils/variantUtils.ts @@ -32,21 +32,8 @@ const { moment } = __non_webpack_require__('/lib/vendor/moment') -export function calculatePeriod(variant: VariantInListing, language: string, nextReleasePassed: boolean = false): string { - let previousFrom: string = variant.previousFrom - let previousTo: string = variant.previousTo - if (nextReleasePassed) { - const upcomingRelease: ReleasesInListing | undefined = variant.upcomingReleases ? forceArray(variant.upcomingReleases).find((r) => { - return r && r.id === variant.nextReleaseId - }) : undefined - - if (upcomingRelease) { - previousFrom = upcomingRelease.periodFrom - previousTo = upcomingRelease.periodTo - } - } - - switch (variant.frekvens) { +function calculatePeriod(frequency: string, previousFrom: string, previousTo: string, language: string): string { + switch (frequency) { case 'År': return calculateYear(previousFrom, previousTo, language) case 'Halvår': @@ -64,6 +51,30 @@ export function calculatePeriod(variant: VariantInListing, language: string, nex } } +function calculatePeriodVariant(variant: VariantInListing, language: string, nextReleasePassed: boolean = false): string { + let previousFrom: string = variant.previousFrom + let previousTo: string = variant.previousTo + if (nextReleasePassed) { + const upcomingRelease: ReleasesInListing | undefined = variant.upcomingReleases ? forceArray(variant.upcomingReleases).find((r) => { + return r && r.id === variant.nextReleaseId + }) : undefined + + if (upcomingRelease) { + previousFrom = upcomingRelease.periodFrom + previousTo = upcomingRelease.periodTo + } + } + + return calculatePeriod(variant.frekvens, previousFrom, previousTo, language) +} + +function calculatePeriodRelease(release: Release, language: string): string { + const periodFrom: string = release.periodFrom + const periodTo: string = release.periodTo + + return calculatePeriod(release.frequency, periodFrom, periodTo, language) +} + function calculateEveryXYear(previousFrom: string, previousTo: string, language: string): string { const dateFrom: Date = new Date(previousFrom) const dateTo: Date = new Date(previousTo) @@ -267,28 +278,70 @@ export function getReleasesForDay( }, []) } -export function filterOnComingReleases(stats: Array, count: number, startDay?: string): Array { - const releases: Array = [] +export function filterOnComingReleases(releases: Array, count: number, startDay?: string): Array { + const releaseArray: Array = [] const day: Date = startDay ? new Date(startDay) : new Date() - for (let i: number = 0; i < count && i < 20; i++) { + for (let i: number = 0; i < count + 1; i++) { day.setDate(day.getDate() + 1) - const releasesOnThisDay: Array = getReleasesForDay(stats, day, 'nextRelease') - if (releasesOnThisDay.length === 0) count++ // if no hits found on this day. add one day - releases.push(...releasesOnThisDay) + const releasesOnThisDay: Array = releases.filter((release: Release) => { + return checkReleaseDateToday(release, day) + }) + releaseArray.push(...releasesOnThisDay) } - return releases + return releaseArray } export function checkVariantReleaseDate(variant: VariantInListing, day: Date, property: keyof VariantInListing): boolean { const dayFromVariant: string = variant[property] as string - if (property === 'previousRelease') { - return sameDay(new Date(dayFromVariant), day) || nextReleasedPassed(variant) + if (property === 'previousRelease' && nextReleasedPassed(variant)) { + return sameDay(new Date(dayFromVariant), day) || sameDay(new Date(variant.nextRelease), day) } else { return sameDay(new Date(dayFromVariant), day) } } +export function checkReleaseDateToday(release: Release, day: Date): boolean { + const releaseDate: string = release.publishTime + return sameDay(new Date(releaseDate), day) +} + export function prepareRelease( + release: Release, + language: string): PreparedStatistics | null { + if (release) { + const preparedVariant: PreparedVariant = formatRelease(release, language) + + const statisticsPagesXP: Content | undefined = query({ + count: 1, + query: `data.statistic LIKE "${release.statisticId}" AND language IN (${language === 'nb' ? '"nb", "nn"' : '"en"'})`, + contentTypes: [`${app.name}:statistics`] + }).hits[0] as unknown as Content + const statisticsPageUrl: string | undefined = statisticsPagesXP ? pageUrl({ + path: statisticsPagesXP._path + }) : undefined + const aboutTheStatisticsContent: Content | null = statisticsPagesXP && statisticsPagesXP.data.aboutTheStatistics ? get({ + key: statisticsPagesXP.data.aboutTheStatistics + }) : null + const seoDescription: string | undefined = statisticsPagesXP ? statisticsPagesXP.x['com-enonic-app-metafields']['meta-data'].seoDescription : '' + + return { + id: release.statisticId, + name: language === 'en' ? release.statisticNameEn : release.statisticName, + shortName: release.shortName, + type: localize({ + key: 'statistic', + locale: language + }), + mainSubject: getMainSubject(release.shortName, language), + variant: preparedVariant, + statisticsPageUrl, + aboutTheStatisticsDescription: aboutTheStatisticsContent ? aboutTheStatisticsContent.data.ingress : seoDescription + } + } + return null +} + +export function prepareStatisticRelease( release: StatisticInListing, language: string, property: keyof VariantInListing = 'previousRelease'): PreparedStatistics | null { @@ -332,9 +385,9 @@ function concatReleaseTimes(variants: Array, language: string, let timePeriodes: Array if (property === 'previousRelease') { - timePeriodes = variants.map((variant: VariantInListing) => calculatePeriod(variant, language, nextReleasedPassed(variant))) + timePeriodes = variants.map((variant: VariantInListing) => calculatePeriodVariant(variant, language, nextReleasedPassed(variant))) } else { - timePeriodes = variants.map((variant: VariantInListing) => calculatePeriod(variant, language)) + timePeriodes = variants.map((variant: VariantInListing) => calculatePeriodVariant(variant, language)) } const formatedTimePeriodes: string = timePeriodes.join(` ${localize({ @@ -371,20 +424,79 @@ function formatVariant(variant: VariantInListing, language: string, property: ke monthNumber: date.getMonth(), year: date.getFullYear(), frequency: variant.frekvens, - period: calculatePeriod(variant, language, nextReleaseDatePassed) + period: calculatePeriodVariant(variant, language, nextReleaseDatePassed) } } +function formatRelease(release: Release, language: string): PreparedVariant { + const date: Date = new Date(release.publishTime) + return { + id: release.variantId, + day: date.getDate(), + monthNumber: date.getMonth(), + year: date.getFullYear(), + frequency: release.frequency, + period: calculatePeriodRelease(release, language) + } +} + +export function getAllReleases(statisticList: Array): Array { + const releases: Array = [] + statisticList.forEach((statistic: StatisticInListing) => { + const variants: Array = statistic.variants ? forceArray(statistic.variants) : [] + variants.forEach((variant: VariantInListing) => { + releases.push({ + publishTime: variant.previousRelease, + periodFrom: variant.previousFrom, + periodTo: variant.previousTo, + frequency: variant.frekvens, + variantId: variant.id, + statisticId: statistic.id, + shortName: statistic.shortName, + statisticName: statistic.name, + statisticNameEn: statistic.nameEN + }) + const upcomingRelease: Array = variant.upcomingReleases ? forceArray(variant.upcomingReleases) : [] + upcomingRelease.forEach((upcomingRelease: ReleasesInListing) => { + releases.push({ + publishTime: upcomingRelease.publishTime, + periodFrom: upcomingRelease.periodFrom, + periodTo: upcomingRelease.periodTo, + frequency: variant.frekvens, + variantId: variant.id, + statisticId: statistic.id, + shortName: statistic.shortName, + statisticName: statistic.name, + statisticNameEn: statistic.nameEN + }) + }) + }) + }) + const publicationsSorted: Array = releases.sort((a, b) => { + return new Date(a.publishTime || '01.01.3000').getTime() - new Date(b.publishTime || '01.01.3000').getTime() + }) + + return publicationsSorted +} + +export function getUpcomingReleases(allReleases: Array): Array { + const serverOffsetInMs: number = app.config && app.config['serverOffsetInMs'] ? parseInt(app.config['serverOffsetInMs']) : 0 + const serverTime: Date = new Date(new Date().getTime() + serverOffsetInMs) + return allReleases.filter((release) => moment(release.publishTime).isAfter(serverTime, 'minute')) +} export interface VariantUtilsLib { - calculatePeriod: (variant: VariantInListing, language: string) => string; addMonthNames: (groupedByYearMonthAndDay: GroupedBy>>, language: string) => Array; groupStatisticsByYear: (statistics: Array) => GroupedBy; groupStatisticsByMonth: (statistics: Array) => GroupedBy; groupStatisticsByDay: (statistics: Array) => GroupedBy; groupStatisticsByYearMonthAndDay: (releasesPrepped: Array) => GroupedBy>>; getReleasesForDay: (statisticList: Array, day: Date, property?: keyof VariantInListing) => Array; - prepareRelease: (release: StatisticInListing, locale: string, property?: keyof VariantInListing, statisticsPageUrl?: string) => PreparedStatistics; - filterOnComingReleases: (stats: Array, daysInTheFuture: number, startDay?: string) => Array; + prepareStatisticRelease: (release: StatisticInListing, locale: string, property?: keyof VariantInListing, statisticsPageUrl?: string) => PreparedStatistics; + prepareRelease: (release: Release, locale: string, statisticsPageUrl?: string) => PreparedStatistics; + filterOnComingReleases: (stats: Array, daysInTheFuture: number, startDay?: string) => Array; + getAllReleases: (statisticList: Array) => Array; + getUpcomingReleases: (allReleases: Array) => Array; + } export interface PreparedStatistics { @@ -408,6 +520,18 @@ export interface PreparedVariant { period: string; } +export interface Release { + publishTime: string; + periodFrom: string; + periodTo: string; + frequency: string; + variantId: string; + statisticId: number; + shortName: string; + statisticName: string; + statisticNameEn: string; +} + export interface DayReleases { day: string; releases: Array; diff --git a/src/main/resources/services/upcomingReleases/upcomingReleases.ts b/src/main/resources/services/upcomingReleases/upcomingReleases.ts index 5b1b387c0..edf0c7b81 100644 --- a/src/main/resources/services/upcomingReleases/upcomingReleases.ts +++ b/src/main/resources/services/upcomingReleases/upcomingReleases.ts @@ -1,12 +1,14 @@ import { Request, Response } from 'enonic-types/controller' import { StatisticInListing } from '../../lib/ssb/dashboard/statreg/types' -import { GroupedBy, PreparedStatistics, YearReleases } from '../../lib/ssb/utils/variantUtils' +import { GroupedBy, PreparedStatistics, YearReleases, Release } from '../../lib/ssb/utils/variantUtils' const { addMonthNames, groupStatisticsByYearMonthAndDay, prepareRelease, - filterOnComingReleases + filterOnComingReleases, + getAllReleases, + getUpcomingReleases } = __non_webpack_require__( '/lib/ssb/utils/variantUtils') const { getAllStatisticsFromRepo @@ -15,16 +17,16 @@ const { exports.get = (req: Request): Response => { // Get statistics const releases: Array = getAllStatisticsFromRepo() + const allReleases: Array = getAllReleases(releases) + const upComingReleases: Array = getUpcomingReleases(allReleases) const count: number = req.params.count ? parseInt(req.params.count) : 2 const language: string = req.params.language ? req.params.language : 'nb' // All statistics published today, and fill up with previous releases. - const releasesFiltered: Array = filterOnComingReleases(releases, count, req.params.start) + const releasesFiltered: Array = filterOnComingReleases(upComingReleases, count, req.params.start) // Choose the right variant and prepare the date in a way it works with the groupBy function - const releasesPrepped: Array = releasesFiltered.map( - (release: StatisticInListing) => prepareRelease(release, language, 'nextRelease') - ) + const releasesPrepped: Array = releasesFiltered.map((release: Release) => prepareRelease(release, language)) // group by year, then month, then day const groupedByYearMonthAndDay: GroupedBy>> = groupStatisticsByYearMonthAndDay(releasesPrepped) diff --git a/src/main/resources/site/parts/publicationArchive/publicationArchive.ts b/src/main/resources/site/parts/publicationArchive/publicationArchive.ts index 22b123436..11590eeb4 100644 --- a/src/main/resources/site/parts/publicationArchive/publicationArchive.ts +++ b/src/main/resources/site/parts/publicationArchive/publicationArchive.ts @@ -4,7 +4,7 @@ import { React4xp, React4xpResponse } from '../../../lib/types/react4xp' import { Content } from 'enonic-types/content' import { PublicationArchivePartConfig } from './publicationArchive-part-config' import { StatisticInListing } from '../../../lib/ssb/dashboard/statreg/types' -import { PreparedStatistics, prepareRelease } from '../../../lib/ssb/utils/variantUtils' +import { PreparedStatistics } from '../../../lib/ssb/utils/variantUtils' import { getAllStatisticsFromRepo } from '../../../lib/ssb/statreg/statistics' import { filterOnPreviousReleases } from '../releasedStatistics/releasedStatistics' import { PublicationItem } from '../../../services/publicationArchive/publicationArchive' @@ -20,6 +20,9 @@ const { getContent, serviceUrl, getComponent } = __non_webpack_require__('/lib/xp/portal') const React4xp: React4xp = __non_webpack_require__('/lib/enonic/react4xp') +const { + prepareStatisticRelease +} = __non_webpack_require__('/lib/ssb/utils/variantUtils') exports.get = (req: Request): React4xpResponse => { return renderPart(req) @@ -40,7 +43,7 @@ function renderPart(req: Request): React4xpResponse { const releasesPrepped: Array = fromPartCache(req, `${content._id}-publicationArchive`, () => { const releases: Array = getAllStatisticsFromRepo() const releasesFiltered: Array = filterOnPreviousReleases(releases, releases.length).filter((r) => r.status === 'A') - return releasesFiltered.map((release: StatisticInListing) => prepareRelease(release, language)) + return releasesFiltered.map((release: StatisticInListing) => prepareStatisticRelease(release, language)) }) const props: PartProperties = { diff --git a/src/main/resources/site/parts/releasedStatistics/releasedStatistics.ts b/src/main/resources/site/parts/releasedStatistics/releasedStatistics.ts index 18226e267..e691f1acd 100644 --- a/src/main/resources/site/parts/releasedStatistics/releasedStatistics.ts +++ b/src/main/resources/site/parts/releasedStatistics/releasedStatistics.ts @@ -35,7 +35,7 @@ const { addMonthNames, getReleasesForDay, groupStatisticsByYearMonthAndDay, - prepareRelease + prepareStatisticRelease } = __non_webpack_require__('/lib/ssb/utils/variantUtils') exports.get = function(req: Request): React4xpResponse | Response { @@ -65,7 +65,7 @@ export function renderPart(req: Request): React4xpResponse { const releasesFiltered: Array = filterOnPreviousReleases(releases, numberOfReleases) // Choose the right variant and prepare the date in a way it works with the groupBy function - const releasesPrepped: Array = releasesFiltered.map((release: StatisticInListing) => prepareRelease(release, currentLanguage)) + const releasesPrepped: Array = releasesFiltered.map((release: StatisticInListing) => prepareStatisticRelease(release, currentLanguage)) // group by year, then month, then day const groupedByYearMonthAndDay: GroupedBy>> = groupStatisticsByYearMonthAndDay(releasesPrepped) diff --git a/src/main/resources/site/parts/upcomingReleases/upcomingReleases.jsx b/src/main/resources/site/parts/upcomingReleases/upcomingReleases.jsx index 9103b1104..526ae4519 100644 --- a/src/main/resources/site/parts/upcomingReleases/upcomingReleases.jsx +++ b/src/main/resources/site/parts/upcomingReleases/upcomingReleases.jsx @@ -157,47 +157,32 @@ function UpcomingReleases(props) { function renderRelease(release, index, date) { const { - type, shortName, name, variant, mainSubject, statisticsPageUrl + type, name, variant, mainSubject, statisticsPageUrl } = release const { day, monthName, year } = date const statisticsPageUrlText = props.statisticsPageUrlText + const showPeriod = (type === 'statistikk' || type === 'statistic') - if (type === 'statistic') { - return ( -
  • -
    - {name} + return ( +
  • +
    +

    {name}

    + {showPeriod && {variant.period} - - {day}. {monthName} {year} / {type} / {mainSubject} - -
    - {statisticsPageUrl && -
    - {statisticsPageUrlText} -
    } -
  • - ) - } else { - return ( -
  • -
    -

    {name}

    - - {day}. {monthName} {year} / {type} / {mainSubject} - -
    - {statisticsPageUrl && + } + + {day}. {monthName} {year} / {type} / {mainSubject} + + + {statisticsPageUrl &&
    {statisticsPageUrlText}
    } -
  • - ) - } + + ) } function renderDay(day, month, year, index) { diff --git a/src/main/resources/site/parts/upcomingReleases/upcomingReleases.ts b/src/main/resources/site/parts/upcomingReleases/upcomingReleases.ts index 1df54ef3d..9f9903837 100644 --- a/src/main/resources/site/parts/upcomingReleases/upcomingReleases.ts +++ b/src/main/resources/site/parts/upcomingReleases/upcomingReleases.ts @@ -3,7 +3,7 @@ import { React4xp, React4xpResponse } from '../../../lib/types/react4xp' import { Content } from 'enonic-types/content' import { Component } from 'enonic-types/portal' import { StatisticInListing } from '../../../lib/ssb/dashboard/statreg/types' -import { GroupedBy, PreparedStatistics, YearReleases } from '../../../lib/ssb/utils/variantUtils' +import { GroupedBy, PreparedStatistics, YearReleases, Release } from '../../../lib/ssb/utils/variantUtils' import { UpcomingReleasesPartConfig } from './upcomingReleases-part-config' import { UpcomingRelease } from '../../content-types/upcomingRelease/upcomingRelease' @@ -24,7 +24,9 @@ const { addMonthNames, groupStatisticsByYearMonthAndDay, prepareRelease, - filterOnComingReleases + filterOnComingReleases, + getAllReleases, + getUpcomingReleases } = __non_webpack_require__( '/lib/ssb/utils/variantUtils') const { getAllStatisticsFromRepo @@ -32,6 +34,9 @@ const { const { localize } = __non_webpack_require__('/lib/xp/i18n') +const { + fromPartCache +} = __non_webpack_require__('/lib/ssb/cache/partCache') exports.get = (req: Request): React4xpResponse => { return renderPart(req) @@ -44,7 +49,6 @@ function renderPart(req: Request): React4xpResponse { const component: Component = getComponent() const currentLanguage: string = content.language ? content.language : 'nb' const count: number = parseInt(component.config.numberOfDays) - const isNotInEditMode: boolean = req.mode !== 'edit' const buttonTitle: string = localize({ key: 'button.showMore', locale: currentLanguage @@ -56,21 +60,28 @@ function renderPart(req: Request): React4xpResponse { const upcomingReleasesServiceUrl: string = serviceUrl({ service: 'upcomingReleases' }) - // Get statistics - const releases: Array = getAllStatisticsFromRepo() - // All statistics published today, and fill up with previous releases. - const releasesFiltered: Array = filterOnComingReleases(releases, count) + const groupedWithMonthNames: Array = fromPartCache(req, `${content._id}-upcomingReleases`, () => { + // Get statistics + const releases: Array = getAllStatisticsFromRepo() + const allReleases: Array = getAllReleases(releases) + const upComingReleases: Array = getUpcomingReleases(allReleases) + + // All statistics published today, and fill up with previous releases. + const releasesFiltered: Array = filterOnComingReleases(upComingReleases, count) - // Choose the right variant and prepare the date in a way it works with the groupBy function - const releasesPrepped: Array = releasesFiltered.map((release: StatisticInListing) => - prepareRelease(release, currentLanguage, 'nextRelease')) + // Choose the right variant and prepare the date in a way it works with the groupBy function + const releasesPrepped: Array = releasesFiltered.map((release: Release) => + prepareRelease(release, currentLanguage)) - // group by year, then month, then day - const groupedByYearMonthAndDay: GroupedBy>> = groupStatisticsByYearMonthAndDay(releasesPrepped) + // group by year, then month, then day + const groupedByYearMonthAndDay: GroupedBy>> = groupStatisticsByYearMonthAndDay(releasesPrepped) - // iterate and format month names - const groupedWithMonthNames: Array = addMonthNames(groupedByYearMonthAndDay, currentLanguage) + // iterate and format month names + // const groupedWithMonthNames: Array = addMonthNames(groupedByYearMonthAndDay, currentLanguage) + + return addMonthNames(groupedByYearMonthAndDay, currentLanguage) + }) const contentReleases: Array = query({ start: 0, @@ -105,9 +116,7 @@ function renderPart(req: Request): React4xpResponse { contentReleases } - return React4xp.render('site/parts/upcomingReleases/upcomingReleases', props, req, { - clientRender: isNotInEditMode - }) + return React4xp.render('site/parts/upcomingReleases/upcomingReleases', props, req) } /*