Skip to content

Commit

Permalink
rr0 datasource
Browse files Browse the repository at this point in the history
  • Loading branch information
Javarome committed Mar 14, 2024
1 parent 1b16b9e commit c7936bb
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 90 deletions.
2 changes: 1 addition & 1 deletion rr0.css
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ a, details summary, .toggle, .note-id, .source-id {
a:hover, details summary:hover, .toggle:hover, .indexed li:hover, form label:hover, .multilang:hover {
border: none;
border-radius: .2em;
background: rgba(184, 197, 212, .49)
background: rgba(184, 197, 212, .19)
}

.canular, .deprecated {
Expand Down
4 changes: 2 additions & 2 deletions source/Source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ export type Publication = {
time: string
}

export abstract class Source {
protected constructor(
export class Source {
constructor(
readonly title: string, readonly authors: string[], readonly publication: Publication,
readonly subTitle: string = undefined, readonly series: string = undefined,
readonly summary: string = undefined, readonly dirName: string = undefined
Expand Down
52 changes: 30 additions & 22 deletions time/RR0CaseRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
import { HtmlRR0SsgContext } from "../RR0SsgContext"
import { RR0CaseSummary } from "./RR0CaseSummary"
import { Source } from "../source/Source"
import { OnlineSource } from "../source/OnlineSource"
import { RR0CaseSummary } from "./datasource/rr0/RR0CaseSummary"
import { TimeTextBuilder } from "./TimeTextBuilder"

export class RR0CaseRenderer {

render(context: HtmlRR0SsgContext, rr0Case: RR0CaseSummary): HTMLLIElement {
const outDoc = context.outputFile.document
const item = outDoc.createElement("li")
const time = rr0Case.time
const timeEl = outDoc.createElement("time") as HTMLTimeElement
timeEl.dateTime = time.toString()
const caseContext = context.clone()
Object.assign(caseContext, {time})
timeEl.textContent = TimeTextBuilder.build(caseContext)
item.append(timeEl)
item.append(" À ")
const placeEl = outDoc.createElement("span")
placeEl.className = "place"
placeEl.textContent = rr0Case.place?.name || ""
item.append(placeEl, ", ", rr0Case.description)
rr0Case.sources.forEach(source => {
const sourceEl = this.thisSourceElement(context, source)
item.append(" ", sourceEl)
})
return item
}

protected thisSourceElement(context: HtmlRR0SsgContext, source: Source) {
const sourceEl = context.outputFile.document.createElement("span")
sourceEl.className = "source"
sourceEl.innerHTML = source.authors.join(" & ") + `: `
sourceEl.innerHTML = source.authors?.join(" & ") + `: `
const doc = context.outputFile.document
if (source instanceof OnlineSource) {
const onlineSource = source as OnlineSource
Expand All @@ -20,26 +43,11 @@ export class RR0CaseRenderer {
sourceEl.textContent = source.title
}
const copyright = doc.createElement("i")
copyright.textContent = source.publication.publisher
sourceEl.append(", ", copyright, ", ", source.publication.time)
const publication = source.publication
if (publication) {
copyright.textContent = publication.publisher
sourceEl.append(", ", copyright, ", ", publication.time)
}
return sourceEl
}

render(context: HtmlRR0SsgContext, rr0Case: RR0CaseSummary): HTMLLIElement {
const outDoc = context.outputFile.document
const item = outDoc.createElement("li")
const timeEl = outDoc.createElement("time")
timeEl.textContent = rr0Case.time.toString()
item.append(timeEl)
item.append(" À ")
const placeEl = outDoc.createElement("span")
placeEl.className = "place"
placeEl.textContent = rr0Case.place.name
item.append(placeEl, ", ", rr0Case.description)
rr0Case.sources.forEach(source => {
const sourceEl = this.thisSourceElement(context, source)
item.append(" ", sourceEl)
})
return item
}
}
9 changes: 8 additions & 1 deletion time/TimeContext.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/**
* Time context for a RR0 page.
*/
Expand Down Expand Up @@ -116,4 +115,12 @@ export class TimeContext {
protected isSet(value: any) {
return value != void 0 && value != null
}

isBefore(other: TimeContext): boolean {
return this.toString().localeCompare(other.toString()) < 0
}

isAfter(other: TimeContext): boolean {
return this.toString().localeCompare(other.toString()) > 0
}
}
57 changes: 32 additions & 25 deletions time/TimeReplacer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,41 +21,19 @@ export class TimeReplacer implements DomReplacement<HtmlRR0SsgContext, HTMLTimeE
constructor(protected timeFiles: string[]) {
}

valueReplacement(context: HtmlRR0SsgContext, timeStr: string,
previousContext: RR0SsgContext | undefined): HTMLElement | undefined {
let replacement = undefined
timeStr = timeStr.trim()
const approximate = timeStr.charAt(0) === "~"
if (approximate) {
timeStr = timeStr.substring(1)
}
static parseDateTime(timeStr: string): [yearStr, monthStr, dayOfMonthStr, hour, minutes, timeZone] {
const dateTimeValues = TimeReplacer.dateTimeRegexp.exec(timeStr)
if (dateTimeValues && dateTimeValues[0]) {
const [yearStr, monthStr, dayOfMonthStr, hour, minutes, timeZone] = dateTimeValues.slice(1)
replacement = this.dateTimeReplacement(context, previousContext, yearStr, monthStr, dayOfMonthStr, hour, minutes,
timeZone, approximate)
} else {
const durationValues = TimeReplacer.durationRegexp.exec(timeStr)
if (durationValues && durationValues[0]) {
const map = durationValues.slice(1)
const [daysStr, hoursStr, minutesStr, secondsStr] = map.reduce((reduced: string[], current: string, i) => {
if (i % 2 !== 0) {
reduced.push(current)
}
return reduced
}, [])
replacement = this.durationReplacement(context, daysStr, hoursStr, minutesStr, secondsStr, approximate)
}
return dateTimeValues.slice(1)
}
return replacement
return undefined
}

static replaceElement(
context: HtmlRR0SsgContext, approximate: boolean, timeFiles: string[], previousContext?: RR0SsgContext
): HTMLElement {
let replacement: HTMLElement | undefined
const absoluteTimeStr = TimeUrlBuilder.fromContext(context)
const url = TimeReplacer.matchExistingTimeFile(absoluteTimeStr, timeFiles)
let title = TimeTextBuilder.build(context)
if (approximate) {
title = context.messages.context.time.approximate(title)
Expand All @@ -70,6 +48,7 @@ export class TimeReplacer implements DomReplacement<HtmlRR0SsgContext, HTMLTimeE
}
const currentFileName = context.inputFile.name
const dirName = currentFileName.substring(0, currentFileName.indexOf("/index"))
const url = TimeReplacer.matchExistingTimeFile(absoluteTimeStr, timeFiles)
if (url && url !== dirName) {
const a = replacement = context.outputFile.document.createElement("a") as HTMLAnchorElement
a.href = UrlUtil.absolute(url)
Expand All @@ -83,6 +62,34 @@ export class TimeReplacer implements DomReplacement<HtmlRR0SsgContext, HTMLTimeE
return replacement
}

valueReplacement(context: HtmlRR0SsgContext, timeStr: string,
previousContext: RR0SsgContext | undefined): HTMLElement | undefined {
let replacement = undefined
timeStr = timeStr.trim()
const approximate = timeStr.charAt(0) === "~"
if (approximate) {
timeStr = timeStr.substring(1)
}
const result = [yearStr, monthStr, dayOfMonthStr, hour, minutes, timeZone] = parseDateTime(parseDateTime)
if (result) {
replacement = this.dateTimeReplacement(context, previousContext, yearStr, monthStr, dayOfMonthStr, hour, minutes,
timeZone, approximate)
} else {
const durationValues = TimeReplacer.durationRegexp.exec(timeStr)
if (durationValues && durationValues[0]) {
const map = durationValues.slice(1)
const [daysStr, hoursStr, minutesStr, secondsStr] = map.reduce((reduced: string[], current: string, i) => {
if (i % 2 !== 0) {
reduced.push(current)
}
return reduced
}, [])
replacement = this.durationReplacement(context, daysStr, hoursStr, minutesStr, secondsStr, approximate)
}
}
return replacement
}

async replacement(context: HtmlRR0SsgContext, original: HTMLTimeElement): Promise<HTMLElement> {
let replacement: HTMLElement | undefined
if (original.dateTime) { // Already done?
Expand Down
25 changes: 15 additions & 10 deletions time/datasource/DatasourceTestCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { RR0CaseRenderer } from "../RR0CaseRenderer"
import { RR0CaseMapping } from "./ChronologyReplacer"
import { HtmlRR0SsgContext, RR0SsgContext } from "../../RR0SsgContext"
import { TimeContext } from "../TimeContext"
import { TimeTextBuilder } from "../TimeTextBuilder"

export class DatasourceTestCase<S> {

Expand All @@ -25,18 +26,22 @@ export class DatasourceTestCase<S> {
checkCaseHTML(context: HtmlRR0SsgContext, nativeCase: S, item: HTMLLIElement, dataDate: Date) {
const datasource = this.mapping.datasource
const expected = this.mapping.mapper.map(context, nativeCase, dataDate)
const nativeTime = this.getTime(nativeCase)
const dayOfMonth = nativeTime.getDayOfMonth()
const dateStr = `${nativeTime.getYear()}-${String(nativeTime.getMonth()).padStart(2, "0")}`
+ (dayOfMonth ? "-" + String(dayOfMonth).padStart(2, "0") : "")
const hour = nativeTime.getHour()
const timeStr = hour ? " " + String(hour).padStart(2, "0") + ":"
+ String(nativeTime.getMinutes()).padStart(2, "0") : ""
const time = this.getTime(nativeCase)
const caseContext = context.clone()
Object.assign(caseContext, {time})
const timeStr = TimeTextBuilder.build(caseContext)
const caseNumber = nativeCase.caseNumber
expect(item.innerHTML).toBe(
`<time>${dateStr}${timeStr}</time> À <span class="place">${expected.place.name}</span>, ${expected.description} <span class="source">${datasource.author}: <a href="${nativeCase.url.href.replaceAll(
const placeStr = expected.place ? ` À <span class="place">${expected.place.name}</span>` : ""
let sources = expected.sources
if (sources?.length > 0) {
const source = sources[0]
const publicationStr = source.publication ? `, ${source.publication.time}` : ""
const sourceStr = ` <span class="source">${datasource.author}: <a href="${nativeCase.url.href.replaceAll(
"&",
"&amp;")}">cas n°&nbsp;${caseNumber}</a>, <i>${datasource.copyright}</i>, ${expected.sources[0].publication.time}</span>`)
"&amp;")}">cas n°&nbsp;${caseNumber}</a>, <i>${datasource.copyright}</i>${publicationStr}</span>`
expect(item.innerHTML).toBe(
`<time datetime="${time.toString()}">${timeStr}</time>${placeStr}, ${expected.description}${sourceStr}`)
}
}

async testRender(context: HtmlRR0SsgContext) {
Expand Down
8 changes: 4 additions & 4 deletions time/datasource/rr0/RR0CaseSummary.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Place } from "../place/Place"
import { TimeContext } from "./TimeContext"
import { Source } from "../source/Source"
import { Place } from "../../../place/Place"
import { TimeContext } from "../../TimeContext"
import { Source } from "../../../source/Source"

export type NamedPlace = {
readonly place: Place
Expand All @@ -9,7 +9,7 @@ export type NamedPlace = {

export type RR0CaseSummary = {
readonly time: TimeContext
readonly place: NamedPlace
readonly place?: NamedPlace
readonly description: string
readonly sources: Source[]
}
88 changes: 68 additions & 20 deletions time/datasource/rr0/RR0HttpDatasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { UrlUtil } from "../../../util/url/UrlUtil"
import { JSDOM } from "jsdom"
import { RR0Datasource } from "./RR0Datasource"
import { TimeContext } from "../../TimeContext"
import { NamedPlace } from "./RR0CaseSummary"
import { NamedPlace, RR0CaseSummary } from "./RR0CaseSummary"
import { Place } from "../../../place/Place"
import { Source } from "../../../source/Source"
import { TimeReplacer } from "../../TimeReplacer"

export class RR0HttpDatasource extends RR0Datasource {
protected readonly http = new HttpSource()
Expand Down Expand Up @@ -48,32 +50,78 @@ export class RR0HttpDatasource extends RR0Datasource {
const caseLink = context.inputFile.name
const url = new URL(caseLink, this.baseUrl)
const timeEl = row.querySelector("time") as HTMLTimeElement
let time: TimeContext
const itemContext = context.clone()
let timeContext = itemContext.time
if (timeEl) {
const itemContext = context.clone()
time = itemContext.time
url.hash = time
const dateTime = new Date(timeEl.dateTime)
time.setYear(dateTime.getFullYear())
time.setMonth(dateTime.getMonth() + 1)
time.setDayOfMonth(dateTime.getDate())
url.hash = timeEl.dateTime
this.getTime(timeContext, timeEl)
timeEl.remove()
}
let namedPlace: NamedPlace
const placeEl = row.querySelector(".place")
const placeEl = row.querySelector(".plac")
if (placeEl) {
const name = placeEl.textContent
const place = new Place()
namedPlace = {name, place}
placeEl.remove()
namedPlace = this.getPlace(placeEl)
const toRemove = ["", " À ", " A ", ", "]
Array.from(row.childNodes).forEach(childNode => {
if (childNode.nodeType === 3 && toRemove.includes(childNode.nodeValue)) {
childNode.remove()
}
})
}
const description = row.textContent.trim().replaceAll("\n", "").replaceAll(/ /g, " ")
return {
url,
place: namedPlace,
time,
description
const sources = this.getSources(row)
const description = this.getDescription(row)
return {url, place: namedPlace, time: timeContext, description, sources}
}

protected getSources(row: HTMLElement): Source[] {
const sources: Source[] = []
const sourceIds = row.querySelectorAll(".source-id")
for (const sourceId of sourceIds) {
const sourceContent = sourceId.querySelector(".source-contents")
const title = this.getDescription(sourceContent)
sourceId.remove()
sources.push(new Source(title))
}
return sources
}

protected getTime(time: TimeContext, timeEl: HTMLTimeElement) {
const result = TimeReplacer.parseDateTime(timeEl.dateTime)
if (result) {
const [yearStr, monthStr, dayOfMonthStr, hour, minutes, timeZone] = result
time.setYear(parseInt(yearStr, 10))
if (monthStr) {
time.setMonth(parseInt(monthStr, 10))
}
if (dayOfMonthStr) {
time.setDayOfMonth(parseInt(dayOfMonthStr, 10))
}
if (hour) {
time.setHour(parseInt(hour, 10))
}
if (minutes) {
time.setMinutes(parseInt(minutes, 10))
}
if (timeZone) {
time.setTimeZone(timeZone)
}
}
}

protected getPlace(placeEl: HTMLElement): NamedPlace {
const name = placeEl.textContent
const place = new Place()
placeEl.remove()
return {name, place}
}

protected getDescription(el: HTMLElement): string {
const notes = el.querySelectorAll(".note-id")
for (const note of notes) {
const noteContents = note.querySelector(".note-contents")
note.replaceWith(` (${noteContents.textContent})`)
}
return el.textContent.trim().replaceAll("\n", "").replace(/\s{2,}/g, " ").replaceAll(" .", ".")
}

protected queryUrl(year: number, month: number, day: number): string {
Expand Down
4 changes: 2 additions & 2 deletions time/datasource/rr0/RR0Mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const rr0Mapper = new RR0CaseSummaryMapper(cityService, rr0Datasource.bas
export const rr0Mapping = {datasource: rr0Datasource, mapper: rr0Mapper}

export const rr0SortComparator
= (c1: RR0CaseSummary,
c2: RR0CaseSummary) => c1.time < c2.time ? -1 : c1.time > c2.time ? 1 : 0
= (c1: RR0CaseSummary, c2: RR0CaseSummary) => !c1.time || c2.time && c1.time.isBefore(
c2.time) ? -1 : !c2.time || c1.time.isAfter(c2.time) ? 1 : 0

export const rr0TimeAccessor = (c: RR0CaseSummary) => c.time
Loading

0 comments on commit c7936bb

Please sign in to comment.