diff --git a/src/lib/aggregates.ts b/src/lib/aggregates.ts index 00e8a4b..e984c29 100644 --- a/src/lib/aggregates.ts +++ b/src/lib/aggregates.ts @@ -1,6 +1,22 @@ +import type { Article, SectionTa, Textelr, TexteVersion } from "$lib/legal" + +export interface Aggregate { + article?: { [id: string]: Article } + id?: string + ids?: string[] + section_ta?: { [id: string]: SectionTa } + texte_version?: { [id: string]: TexteVersion } + textelr?: { [id: string]: Textelr } +} + export type Follow = typeof allFollows[number] export const allFollows = [ "LIENS.LIEN[@sens=cible,@typelien=CREATION].@id", + "STRUCT.LIEN_ART.@id", + "STRUCT.LIEN_SECTION_TA.@id", + "STRUCTURE_TA.LIEN_ART.@id", + "STRUCTURE_TA.LIEN_SECTION_TA.@id", + "TEXTELR", ] as const export const allFollowsMutable = [...allFollows] diff --git a/src/lib/auditors/search_params.ts b/src/lib/auditors/search_params.ts index 1bf9570..1c17df7 100644 --- a/src/lib/auditors/search_params.ts +++ b/src/lib/auditors/search_params.ts @@ -57,7 +57,7 @@ export function auditLimitSearchParam( (value: number) => value <= 100, "Value must be less than or equal to 100", ), - auditSetNullish(10), + auditSetNullish(20), ), ) } diff --git a/src/lib/components/ArticleView.svelte b/src/lib/components/ArticleView.svelte new file mode 100644 index 0000000..0ef1206 --- /dev/null +++ b/src/lib/components/ArticleView.svelte @@ -0,0 +1,21 @@ + + +{#if summary !== undefined} + + + +{/if} + +{@html article.BLOC_TEXTUEL.CONTENU} diff --git a/src/lib/components/IdPagesSwitcher.svelte b/src/lib/components/IdPagesSwitcher.svelte new file mode 100644 index 0000000..db9c5fa --- /dev/null +++ b/src/lib/components/IdPagesSwitcher.svelte @@ -0,0 +1,20 @@ + + +{#if items !== undefined} +
+ {#each items as { href, label }} + {label} + {/each} +
+{/if} diff --git a/src/lib/components/Pagination.svelte b/src/lib/components/Pagination.svelte index 8aaac44..f823f9e 100644 --- a/src/lib/components/Pagination.svelte +++ b/src/lib/components/Pagination.svelte @@ -38,7 +38,17 @@ function newPaginationUrl( pathname: string, - query: Partial | { [key: string]: string }, + query: + | Partial + | { + [key: string]: + | boolean + | number + | string + | undefined + | null + | Array + }, { limit, offset }: { limit?: number; offset?: number } = {}, ) { query = { ...query } @@ -53,10 +63,20 @@ delete query.offset } const search = new URLSearchParams( - Object.entries(query).map(([key, value]) => [ - key, - typeof value === "number" ? value.toString() : value, - ]), + Object.entries(query).reduce((couples, [key, value]) => { + if (value != null) { + if (Array.isArray(value)) { + for (const item of value) { + if (item != null) { + couples.push([key, item.toString()]) + } + } + } else { + couples.push([key, value.toString()]) + } + } + return couples + }, [] as Array<[string, string]>), ).toString() return search ? `${pathname}?${search}` : pathname } diff --git a/src/lib/components/SectionTaView.svelte b/src/lib/components/SectionTaView.svelte new file mode 100644 index 0000000..a342907 --- /dev/null +++ b/src/lib/components/SectionTaView.svelte @@ -0,0 +1,50 @@ + + + + {sectionTa.TITRE_TA} + + +
+ {#if showArticles} + {#each [...iterArrayOrSingleton(sectionTa.STRUCTURE_TA.LIEN_ART)] as lienArt} + {@const article = data.article?.[lienArt["@id"]]} + {#if article !== undefined} + + {/if} + {/each} + {:else if sectionTa.STRUCTURE_TA.LIEN_ART !== undefined} +
    + {#each [...iterArrayOrSingleton(sectionTa.STRUCTURE_TA.LIEN_ART)] as lienArt} +
  • + Article {lienArt["@num"]} +
  • + {/each} +
+ {/if} + + {#each [...iterArrayOrSingleton(sectionTa.STRUCTURE_TA.LIEN_SECTION_TA)] as lienSectionTa} + {@const sectionTa = data.section_ta?.[lienSectionTa["@id"]]} + {#if sectionTa !== undefined} + + {/if} + {/each} +
diff --git a/src/lib/components/TexteVersionView.svelte b/src/lib/components/TexteVersionView.svelte new file mode 100644 index 0000000..4c411d3 --- /dev/null +++ b/src/lib/components/TexteVersionView.svelte @@ -0,0 +1,36 @@ + + +{#if summary !== undefined} + + + +{/if} + +{#if textelr !== undefined} + +{/if} diff --git a/src/lib/components/TextelrView.svelte b/src/lib/components/TextelrView.svelte new file mode 100644 index 0000000..d6fe0f2 --- /dev/null +++ b/src/lib/components/TextelrView.svelte @@ -0,0 +1,37 @@ + + +{#if showArticles} + {#each [...iterArrayOrSingleton(textelr.STRUCT.LIEN_ART)] as lienArt} + {@const article = data.article?.[lienArt["@id"]]} + {#if article !== undefined} + + {/if} + {/each} +{:else if textelr.STRUCT.LIEN_ART !== undefined} +
    + {#each [...iterArrayOrSingleton(textelr.STRUCT.LIEN_ART)] as lienArt} +
  • + Article {lienArt["@num"]} +
  • + {/each} +
+{/if} + +{#each [...iterArrayOrSingleton(textelr.STRUCT.LIEN_SECTION_TA)] as lienSectionTa} + {@const sectionTa = data.section_ta?.[lienSectionTa["@id"]]} + {#if sectionTa !== undefined} + + {/if} +{/each} diff --git a/src/lib/legal.ts b/src/lib/legal.ts index 1b4a565..671df52 100644 --- a/src/lib/legal.ts +++ b/src/lib/legal.ts @@ -140,6 +140,17 @@ export interface LienArt { "@origine": "LEGI" } +export interface LienSectionTa { + "@id": string + "@cid": string + "@fin": string + "@niv": string + "@url": string + "@etat": Etat + "@debut": string + "#text": string +} + export interface MetaCommun { ID: string } @@ -165,6 +176,7 @@ export interface SectionTa { TITRE_TA: string STRUCTURE_TA: { LIEN_ART?: LienArt | LienArt[] + LIEN_SECTION_TA?: LienSectionTa | LienSectionTa[] } } @@ -186,6 +198,7 @@ export interface Textelr { } STRUCT: { LIEN_ART?: LienArt | LienArt[] + LIEN_SECTION_TA?: LienSectionTa | LienSectionTa[] } VERSIONS: { VERSION: TextelrVersion | TextelrVersion[] @@ -293,7 +306,7 @@ export const appMenu: MenuItem[] = [ { items: [ { href: "/idcc", label: "Accords de branche et conventions collectives" }, - { href: "/texte_version", label: "Codes, lois et règlements" }, + { href: "/textes", label: "Codes, lois et règlements" }, { href: "/dossier_legislatif", label: "Dossiers législatifs" }, { href: "/jo", label: "Journal officiel" }, ], @@ -355,36 +368,34 @@ export function bestItemForDate( return items[0] } +export function menuItemsFromLegalId( + id: string | undefined | null, +): MenuItem[] | undefined { + if (id == null) { + return undefined + } + const rootType = rootTypeFromLegalId(id) + if (rootType === undefined) { + return undefined + } + switch (rootType) { + case "texte_version": + return [ + { href: `/textes/${id}`, label: "Présentation" }, + { href: `/texte_version/${id}`, label: "TEXTE_VERSION" }, + { href: `/textelr/${id}`, label: "TEXTELR" }, + ] + default: + return undefined + } +} + export function pathnameFromLegalId(id: string): string | undefined { - if (id.match(/^[A-Z]{4}ARTI/) !== null) { - return `/article/${id}` + const rootType = rootTypeFromLegalId(id) + if (rootType === undefined) { + return undefined } - if (id.match(/^[A-Z]{4}DOLE/) !== null) { - return `/dossier_legislatif/${id}` - } - if (id.match(/^KALICONT/) !== null) { - return `/idcc/${id}` - } - if (id.match(/^JORFCONT/) !== null) { - return `/jo/${id}` - } - if (id.match(/^[A-Z]{4}SCTA/) !== null) { - return `/section_ta/${id}` - } - if (id.match(/^[A-Z]{4}TEXT/) !== null) { - return `/texte_version/${id}` - } - // TODO: Ce test est redondant avec le précédent (pour KALITEXT). - // => Trouver quel lien mettre par défaut. - // if (id.match(/^KALITEXT/) !== null) { - // return `/textekali/${id}` - // } - // TODO: Ce test est redondant avec le précédent (pour JORFTEXT & LEGITEXT). - // => Trouver quel lien mettre par défaut. - // if (id.match(/^[A-Z]{4}TEXT/) !== null) { - // return `/textelr/${id}` - // } - return undefined + return `/${rootType}/${id}` } export function pathnameFromLegalObject( @@ -450,3 +461,28 @@ export function pathnameFromLegalObjectId( assertNeverLegalObjectType(type) } } + +export function rootTypeFromLegalId(id: string): LegalObjectType | undefined { + if (!id) { + return undefined + } + if (id.match(/^[A-Z]{4}ARTI/) !== null) { + return "article" + } + if (id.match(/^[A-Z]{4}DOLE/) !== null) { + return "dossier_legislatif" + } + if (id.match(/^KALICONT/) !== null) { + return "idcc" + } + if (id.match(/^JORFCONT/) !== null) { + return "jo" + } + if (id.match(/^[A-Z]{4}SCTA/) !== null) { + return "section_ta" + } + if (id.match(/^[A-Z]{4}TEXT/) !== null) { + return "texte_version" + } + throw new Error(`Unexpected legal ID: "${id}"`) +} diff --git a/src/lib/server/aggregates.ts b/src/lib/server/aggregates.ts index bb7bf63..83c700c 100644 --- a/src/lib/server/aggregates.ts +++ b/src/lib/server/aggregates.ts @@ -1,20 +1,24 @@ import { iterArrayOrSingleton } from "@tricoteuses/explorer-tools" -import type { Follow } from "$lib/aggregates" -import type { Article, Lien } from "$lib/legal" +import type { Aggregate, Follow } from "$lib/aggregates" +import { + rootTypeFromLegalId, + type Article, + type LegalObjectType, + type SectionTa, + type Textelr, + type TexteVersion, +} from "$lib/legal" import { db } from "$lib/server/database" -export interface Aggregate { - article?: { [id: string]: Article } - id?: string - ids?: string[] -} - export class Aggregator { article: { [id: string]: Article } = {} follow: Set - requestedIds: Set = new Set() - visitedIds: Set = new Set() + requestedTypeAndIds: Set = new Set() + section_ta: { [id: string]: SectionTa } = {} + texte_version: { [id: string]: TexteVersion } = {} + textelr: { [id: string]: Textelr } = {} + visitedTypeAndIds: Set = new Set() constructor(follow: Iterable) { this.follow = new Set(follow) @@ -33,46 +37,135 @@ export class Aggregator { } } - addToVisitedIds(ids: string[]) { - for (const id of ids) { - this.visitedIds.add(id) + addSectionTa(sectionTa: SectionTa): void { + const id = sectionTa.ID + this.section_ta[id] = sectionTa + + if (this.follow.has("STRUCTURE_TA.LIEN_ART.@id")) { + for (const lien of iterArrayOrSingleton( + sectionTa.STRUCTURE_TA.LIEN_ART, + )) { + this.requestId(lien["@id"]) + } + } + + if (this.follow.has("STRUCTURE_TA.LIEN_SECTION_TA.@id")) { + for (const lien of iterArrayOrSingleton( + sectionTa.STRUCTURE_TA.LIEN_SECTION_TA, + )) { + this.requestId(lien["@id"]) + } } } - deleteFromRequestedIds(ids: string[]) { - for (const id of ids) { - this.requestedIds.delete(id) + addTextelr(textelr: Textelr): void { + const id = textelr.META.META_COMMUN.ID + this.textelr[id] = textelr + + if (this.follow.has("STRUCT.LIEN_ART.@id")) { + for (const lien of iterArrayOrSingleton(textelr.STRUCT.LIEN_ART)) { + this.requestId(lien["@id"]) + } + } + + if (this.follow.has("STRUCT.LIEN_SECTION_TA.@id")) { + for (const lien of iterArrayOrSingleton(textelr.STRUCT.LIEN_SECTION_TA)) { + this.requestId(lien["@id"]) + } + } + } + + addTexteVersion(texteVersion: TexteVersion): void { + const id = texteVersion.META.META_COMMUN.ID + this.texte_version[id] = texteVersion + + if (this.follow.has("TEXTELR")) { + this.requestTypeAndId(["textelr", id]) + } + } + + addToVisitedTypeAndIds(typeAndIds: TypeAndId[]) { + for (const typeAndId of typeAndIds) { + this.visitedTypeAndIds.add(typeAndId) + } + } + + deleteFromRequestedTypeAndIds(typeAndIds: TypeAndId[]) { + for (const typeAndId of typeAndIds) { + this.requestedTypeAndIds.delete(typeAndId) } } async getAll(): Promise { while (true) { - if (this.requestedIds.size === 0) { + if (this.requestedTypeAndIds.size === 0) { break } - console.log("this.requestedIds.size", this.requestedIds.size) + console.log( + "this.requestedTypeAndIds.size", + this.requestedTypeAndIds.size, + ) { - const articleIds = [...this.requestedIds].filter( - (id) => id.match(/^[A-Z]{4}ARTI/) !== null, + const articleTypeAndIds = [...this.requestedTypeAndIds].filter( + ([type]) => type === "article", ) - if (articleIds.length > 0) { - this.deleteFromRequestedIds(articleIds) + if (articleTypeAndIds.length > 0) { + this.deleteFromRequestedTypeAndIds(articleTypeAndIds) for (const { data } of await db<{ id: string; data: Article }[]>` SELECT id, data FROM article - WHERE id IN ${db(articleIds)} + WHERE id IN ${db(articleTypeAndIds.map(([, id]) => id))} `) { this.addArticle(data) } - this.addToVisitedIds(articleIds) + this.addToVisitedTypeAndIds(articleTypeAndIds) + } + } + + { + const sectionTaTypeAndIds = [...this.requestedTypeAndIds].filter( + ([type]) => type === "section_ta", + ) + if (sectionTaTypeAndIds.length > 0) { + this.deleteFromRequestedTypeAndIds(sectionTaTypeAndIds) + for (const { data } of await db<{ id: string; data: SectionTa }[]>` + SELECT id, data FROM section_ta + WHERE id IN ${db(sectionTaTypeAndIds.map(([, id]) => id))} + `) { + this.addSectionTa(data) + } + this.addToVisitedTypeAndIds(sectionTaTypeAndIds) + } + } + + { + const textelrTypeAndIds = [...this.requestedTypeAndIds].filter( + ([type]) => type === "textelr", + ) + if (textelrTypeAndIds.length > 0) { + this.deleteFromRequestedTypeAndIds(textelrTypeAndIds) + for (const { data } of await db<{ id: string; data: Textelr }[]>` + SELECT id, data FROM textelr + WHERE id IN ${db(textelrTypeAndIds.map(([, id]) => id))} + `) { + this.addTextelr(data) + } + this.addToVisitedTypeAndIds(textelrTypeAndIds) } } } } requestId(id: string): void { - if (!this.visitedIds.has(id)) { - this.requestedIds.add(id) + const rootType = rootTypeFromLegalId(id) + if (rootType !== undefined) { + return this.requestTypeAndId([rootType, id]) + } + } + + requestTypeAndId(typeAndId: TypeAndId): void { + if (!this.visitedTypeAndIds.has(typeAndId)) { + this.requestedTypeAndIds.add(typeAndId) } } @@ -81,6 +174,17 @@ export class Aggregator { if (Object.keys(this.article).length > 0) { json.article = this.article } + if (Object.keys(this.section_ta).length > 0) { + json.section_ta = this.section_ta + } + if (Object.keys(this.texte_version).length > 0) { + json.texte_version = this.texte_version + } + if (Object.keys(this.textelr).length > 0) { + json.textelr = this.textelr + } return json } } + +export type TypeAndId = [LegalObjectType, string] diff --git a/src/lib/server/doers/recherche.ts b/src/lib/server/doers/recherche.ts index 3711fa0..48b1342 100644 --- a/src/lib/server/doers/recherche.ts +++ b/src/lib/server/doers/recherche.ts @@ -1,13 +1,13 @@ import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { error } from "@sveltejs/kit" -import type { Follow } from "$lib/aggregates" +import type { Aggregate, Follow } from "$lib/aggregates" import { auditFollowSearchParams, auditQSearchParam, } from "$lib/auditors/search_params" import type { Article } from "$lib/legal" -import { type Aggregate, Aggregator } from "$lib/server/aggregates" +import { Aggregator } from "$lib/server/aggregates" import { db } from "$lib/server/database" export function auditSearchParams( @@ -41,15 +41,15 @@ export function auditSearchParams( export const doGetRecherche = async ( url: URL, ): Promise< - | (Aggregate & { - q: string - }) - | null + Aggregate & { + follow: Follow[] + q?: string + } > => { const [query, queryError] = auditSearchParams( cleanAudit, url.searchParams, - ) as [{ follow: Follow[]; q?: string }, unknown] + ) as [{ follow: Set; q?: string }, unknown] if (queryError !== null) { console.error( `Error in ${url.pathname} query:\n${JSON.stringify( @@ -62,6 +62,8 @@ export const doGetRecherche = async ( } const { follow, q } = query + const aggregator = new Aggregator(follow) + let id: string | undefined = undefined if (q !== undefined) { // https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006308296/ // https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006308296/1983-12-30/ @@ -69,23 +71,25 @@ export const doGetRecherche = async ( // https://www.legifrance.gouv.fr/loda/article_lc/LEGIARTI000036456533 // https://www.legifrance.gouv.fr/loda/article_lc/LEGIARTI000036456533/2018-01-01 // https://www.legifrance.gouv.fr/loda/id/LEGIARTI000006317314/1983-12-30 - const articleId = q.match(/LEGIARTI\d+/)?.[0] - if (articleId != null) { + id = q.match(/LEGIARTI\d+/)?.[0] + if (id != null) { const article = ( await db<{ data: Article }[]>` SELECT data FROM article - WHERE id = ${articleId} - LIMIT 1 + WHERE id = ${id} ` ).map(({ data }) => data)[0] if (article !== undefined) { - const aggregator = new Aggregator(follow) aggregator.addArticle(article) - await aggregator.getAll() - - return { ...aggregator.toJson(), id: article.META.META_COMMUN.ID, q } } } } - return null + await aggregator.getAll() + + return { + ...aggregator.toJson(), + follow: [...follow], + id, + q, + } } diff --git a/src/lib/server/doers/textes.ts b/src/lib/server/doers/textes.ts new file mode 100644 index 0000000..7a12cd8 --- /dev/null +++ b/src/lib/server/doers/textes.ts @@ -0,0 +1,178 @@ +import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" +import { error } from "@sveltejs/kit" + +import type { Aggregate, Follow } from "$lib/aggregates" +import { + auditFollowSearchParams, + auditLimitSearchParam, + auditOffsetSearchParam, + auditQSearchParam, +} from "$lib/auditors/search_params" +import type { TexteVersion } from "$lib/legal" +import { Aggregator } from "$lib/server/aggregates" +import { db } from "$lib/server/database" + +export function auditGetTexteSearchParams( + audit: Audit, + query: URLSearchParams, +): [unknown, unknown] { + if (query == null) { + return [query, null] + } + if (!(query instanceof URLSearchParams)) { + return audit.unexpectedType(query, "URLSearchParams") + } + + const data: { [key: string]: unknown } = {} + for (const [key, value] of query.entries()) { + let values = data[key] as string[] | undefined + if (values === undefined) { + values = data[key] = [] + } + values.push(value) + } + const errors: { [key: string]: unknown } = {} + const remainingKeys = new Set(Object.keys(data)) + + auditFollowSearchParams(audit, data, errors, remainingKeys) + + return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) +} + +export function auditListTextesSearchParams( + audit: Audit, + query: URLSearchParams, +): [unknown, unknown] { + if (query == null) { + return [query, null] + } + if (!(query instanceof URLSearchParams)) { + return audit.unexpectedType(query, "URLSearchParams") + } + + const data: { [key: string]: unknown } = {} + for (const [key, value] of query.entries()) { + let values = data[key] as string[] | undefined + if (values === undefined) { + values = data[key] = [] + } + values.push(value) + } + const errors: { [key: string]: unknown } = {} + const remainingKeys = new Set(Object.keys(data)) + + auditFollowSearchParams(audit, data, errors, remainingKeys) + auditLimitSearchParam(audit, data, errors, remainingKeys) + auditOffsetSearchParam(audit, data, errors, remainingKeys) + auditQSearchParam(audit, data, errors, remainingKeys) + + return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) +} + +export const doGetTexte = async ( + id: string, + url: URL, +): Promise< + Aggregate & { + follow: Follow[] + } +> => { + const [query, queryError] = auditGetTexteSearchParams( + cleanAudit, + url.searchParams, + ) as [ + { + follow: Set + }, + unknown, + ] + if (queryError !== null) { + console.error( + `Error in ${url.pathname} query:\n${JSON.stringify( + query, + null, + 2, + )}\n\nError:\n${JSON.stringify(queryError, null, 2)}`, + ) + throw error(400, JSON.stringify(queryError, null, 2)) + } + const { follow } = query + const texteVersion = ( + await db<{ data: TexteVersion }[]>` + SELECT data FROM texte_version + WHERE ID = ${id} + ` + ).map(({ data }) => data)[0] + if (texteVersion === undefined) { + throw error(404, `TEXTE_VERSION ${id} non trouvé`) + } + + const aggregator = new Aggregator(follow) + aggregator.addTexteVersion(texteVersion) + await aggregator.getAll() + + return { + ...aggregator.toJson(), + follow: [...follow], + id, + } +} + +export const doListTextes = async ( + url: URL, +): Promise< + Aggregate & { + follow: Follow[] + limit: number + offset: number + q?: string + } +> => { + const [query, queryError] = auditListTextesSearchParams( + cleanAudit, + url.searchParams, + ) as [ + { + follow: Set + limit: number + offset: number + q?: string + }, + unknown, + ] + if (queryError !== null) { + console.error( + `Error in ${url.pathname} query:\n${JSON.stringify( + query, + null, + 2, + )}\n\nError:\n${JSON.stringify(queryError, null, 2)}`, + ) + throw error(400, JSON.stringify(queryError, null, 2)) + } + const { follow, limit, offset, q } = query + const texteVersionArray = ( + await db<{ data: TexteVersion }[]>` + SELECT data FROM texte_version + OFFSET ${offset} + LIMIT ${limit} + ` + ).map(({ data }) => data) + + const aggregator = new Aggregator(follow) + for (const texteVersion of texteVersionArray) { + aggregator.addTexteVersion(texteVersion) + } + await aggregator.getAll() + + return { + ...aggregator.toJson(), + follow: [...follow], + ids: texteVersionArray.map( + (texteVersion) => texteVersion.META.META_COMMUN.ID, + ), + limit, + offset, + q, + } +} diff --git a/src/lib/summaries.ts b/src/lib/summaries.ts index 5972e8a..9433b49 100644 --- a/src/lib/summaries.ts +++ b/src/lib/summaries.ts @@ -14,6 +14,7 @@ import { type LegalObjectType, type Lien, type LienArt, + type LienSectionTa, pathnameFromLegalId, pathnameFromLegalObject, pathnameFromLegalObjectId, @@ -261,7 +262,7 @@ export function summarizeLegalObject( ] return { items: [ - `Article ${metaArticle.TYPE} ${metaArticle.ETAT} n° ${metaArticle.NUM}, en vigueur`, + `Article ${metaArticle.NUM} ${metaArticle.TYPE} ${metaArticle.ETAT}, en vigueur`, ...(metaArticle.DATE_FIN === "2999-01-01" ? ([ " depuis le ", @@ -373,7 +374,7 @@ export const summarizeLienArt: Summarizer = (access, value) => { return undefined } return { - content: `Article n° ${lienArt["@num"]}`, + content: `Article ${lienArt["@num"]}`, href: pathnameFromLegalObjectId("article", lienArt["@id"]), type: "link", } @@ -423,6 +424,36 @@ export const summarizeLienId: Summarizer = (access, value) => { } } +export const summarizeLienSectionTa: Summarizer = (access, value) => { + const lienSectionTa = value as LienSectionTa | undefined + if (lienSectionTa === undefined) { + return undefined + } + return { + content: lienSectionTa["#text"], + href: pathnameFromLegalObjectId("section_ta", lienSectionTa["@id"]), + type: "link", + } +} + +export const summarizeLienSectionTaId: Summarizer = (access, value) => { + const lienSectionTa = value as LienSectionTa | undefined + if (lienSectionTa === undefined) { + return undefined + } + return { + items: [ + { + content: JSON.stringify(lienSectionTa["@id"]), + type: "raw_data", + }, + { class: "mt-1 mx-1", icon: arrowRight, inline: true, type: "icon" }, + summarizeLienSectionTa(access, lienSectionTa)!, + ], + type: "concatenation", + } +} + export const summarizeSectionTaProperties: Summarizer = (access, value) => { if (access?.key === "section_ta" && typeof value !== "number") { return summarizeLegalObject(access, "section_ta", value) @@ -434,6 +465,14 @@ export const summarizeSectionTaProperties: Summarizer = (access, value) => { if (access?.key === "@cid" && access?.access?.key === "TEXTE") { return summarizeLegalIdToLink(access, value) } + if ( + access?.key === "@cid" && + (access?.access?.key === "LIEN_SECTION_TA" || + (typeof access?.access?.key === "number" && + access?.access?.access?.key === "LIEN_SECTION_TA")) + ) { + return summarizeLegalIdToLink(access, value) + } if ( access?.key === "@id" && (access?.access?.key === "LIEN_ART" || @@ -442,12 +481,27 @@ export const summarizeSectionTaProperties: Summarizer = (access, value) => { ) { return summarizeLienArtId(access.access, access.parent) } + if ( + access?.key === "@id" && + (access?.access?.key === "LIEN_SECTION_TA" || + (typeof access?.access?.key === "number" && + access?.access?.access?.key === "LIEN_SECTION_TA")) + ) { + return summarizeLienSectionTaId(access.access, access.parent) + } if ( (access?.key === "LIEN_ART" && !Array.isArray(value)) || (typeof access?.key === "number" && access?.access?.key === "LIEN_ART") ) { return summarizeLienArt(access, value) } + if ( + (access?.key === "LIEN_SECTION_TA" && !Array.isArray(value)) || + (typeof access?.key === "number" && + access?.access?.key === "LIEN_SECTION_TA") + ) { + return summarizeLienSectionTa(access, value) + } return undefined } @@ -490,6 +544,14 @@ export const summarizeTextelrProperties: Summarizer = (access, value) => { return summarizeLegalObjectToLink(access, "textelr", value) } + if ( + access?.key === "@cid" && + (access?.access?.key === "LIEN_SECTION_TA" || + (typeof access?.access?.key === "number" && + access?.access?.access?.key === "LIEN_SECTION_TA")) + ) { + return summarizeLegalIdToLink(access, value) + } if ( access?.key === "@id" && (access?.access?.key === "LIEN_ART" || @@ -498,6 +560,14 @@ export const summarizeTextelrProperties: Summarizer = (access, value) => { ) { return summarizeLienArtId(access.access, access.parent) } + if ( + access?.key === "@id" && + (access?.access?.key === "LIEN_SECTION_TA" || + (typeof access?.access?.key === "number" && + access?.access?.access?.key === "LIEN_SECTION_TA")) + ) { + return summarizeLienSectionTaId(access.access, access.parent) + } if (access?.key === "CID") { return summarizeLegalIdToLink(access, value) } @@ -507,6 +577,13 @@ export const summarizeTextelrProperties: Summarizer = (access, value) => { ) { return summarizeLienArt(access, value) } + if ( + (access?.key === "LIEN_SECTION_TA" && !Array.isArray(value)) || + (typeof access?.key === "number" && + access?.access?.key === "LIEN_SECTION_TA") + ) { + return summarizeLienSectionTa(access, value) + } if (access?.key === "LIEN_TXT") { return summarizeTextelrVersionLienTxt(access, value) } diff --git a/src/routes/api/recherche/+server.ts b/src/routes/api/recherche/+server.ts index a0ed47e..5b43846 100644 --- a/src/routes/api/recherche/+server.ts +++ b/src/routes/api/recherche/+server.ts @@ -4,11 +4,9 @@ import type { RequestHandler } from "./$types" export const GET: RequestHandler = async ({ url }) => { const result = await doGetRecherche(url) - return result === null - ? new Response(null, { status: 204 }) - : new Response(JSON.stringify(result, null, 2), { - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - }) + return new Response(JSON.stringify(result, null, 2), { + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) } diff --git a/src/routes/api/textes/+server.ts b/src/routes/api/textes/+server.ts new file mode 100644 index 0000000..e374aa1 --- /dev/null +++ b/src/routes/api/textes/+server.ts @@ -0,0 +1,12 @@ +import { doListTextes } from "$lib/server/doers/textes" + +import type { RequestHandler } from "./$types" + +export const GET: RequestHandler = async ({ url }) => { + const result = await doListTextes(url) + return new Response(JSON.stringify(result, null, 2), { + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) +} diff --git a/src/routes/api/textes/[id]/+server.ts b/src/routes/api/textes/[id]/+server.ts new file mode 100644 index 0000000..553e8c1 --- /dev/null +++ b/src/routes/api/textes/[id]/+server.ts @@ -0,0 +1,12 @@ +import { doGetTexte } from "$lib/server/doers/textes" + +import type { RequestHandler } from "./$types" + +export const GET: RequestHandler = async ({ params, url }) => { + const result = await doGetTexte(params.id, url) + return new Response(JSON.stringify(result, null, 2), { + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) +} diff --git a/src/routes/section_ta/[id]/+page.server.ts b/src/routes/section_ta/[id]/+page.server.ts index c1d60d9..4068773 100644 --- a/src/routes/section_ta/[id]/+page.server.ts +++ b/src/routes/section_ta/[id]/+page.server.ts @@ -14,7 +14,7 @@ export const load: PageServerLoad = async ({ params }) => { ).map(({ data }) => data)[0] if (sectionTa === undefined) { - throw error(404, `Section TA ${params.id} non trouvée`) + throw error(404, `SECTION_TA ${params.id} non trouvé`) } return { section_ta: sectionTa } } diff --git a/src/routes/texte_version/[id]/+page.server.ts b/src/routes/texte_version/[id]/+page.server.ts index 23aaf6b..47213e2 100644 --- a/src/routes/texte_version/[id]/+page.server.ts +++ b/src/routes/texte_version/[id]/+page.server.ts @@ -14,7 +14,7 @@ export const load: PageServerLoad = async ({ params }) => { ).map(({ data }) => data)[0] if (texteVersion === undefined) { - throw error(404, `Texte version ${params.id} non trouvé`) + throw error(404, `TEXTE_VERSION ${params.id} non trouvé`) } return { texte_version: texteVersion } } diff --git a/src/routes/texte_version/[id]/+page.svelte b/src/routes/texte_version/[id]/+page.svelte index 6b5be11..93583b3 100644 --- a/src/routes/texte_version/[id]/+page.svelte +++ b/src/routes/texte_version/[id]/+page.svelte @@ -1,6 +1,7 @@ + +

TEXTE_VERSION

{#if summary !== undefined} diff --git a/src/routes/textekali/[id]/+page.server.ts b/src/routes/textekali/[id]/+page.server.ts index 67336c3..f2d3555 100644 --- a/src/routes/textekali/[id]/+page.server.ts +++ b/src/routes/textekali/[id]/+page.server.ts @@ -14,7 +14,7 @@ export const load: PageServerLoad = async ({ params }) => { ).map(({ data }) => data)[0] if (textekali === undefined) { - throw error(404, `Texte KALI ${params.id} non trouvé`) + throw error(404, `TEXTEKALI ${params.id} non trouvé`) } return { textekali } } diff --git a/src/routes/textelr/[id]/+page.server.ts b/src/routes/textelr/[id]/+page.server.ts index 125ef4a..40b8cd3 100644 --- a/src/routes/textelr/[id]/+page.server.ts +++ b/src/routes/textelr/[id]/+page.server.ts @@ -14,7 +14,7 @@ export const load: PageServerLoad = async ({ params }) => { ).map(({ data }) => data)[0] if (textelr === undefined) { - throw error(404, `Texte LR ${params.id} non trouvé`) + throw error(404, `TEXTELR ${params.id} non trouvé`) } return { textelr: textelr } } diff --git a/src/routes/textelr/[id]/+page.svelte b/src/routes/textelr/[id]/+page.svelte index e941d59..7e1ecda 100644 --- a/src/routes/textelr/[id]/+page.svelte +++ b/src/routes/textelr/[id]/+page.svelte @@ -1,6 +1,7 @@ + +

TEXTELR

{#if summary !== undefined} diff --git a/src/routes/textes/+page.server.ts b/src/routes/textes/+page.server.ts new file mode 100644 index 0000000..5bb1c7f --- /dev/null +++ b/src/routes/textes/+page.server.ts @@ -0,0 +1,7 @@ +import { doListTextes } from "$lib/server/doers/textes" + +import type { PageServerLoad } from "./$types" + +export const load: PageServerLoad = async ({ url }) => { + return (await doListTextes(url)) ?? {} +} diff --git a/src/routes/textes/+page.svelte b/src/routes/textes/+page.svelte new file mode 100644 index 0000000..2914dca --- /dev/null +++ b/src/routes/textes/+page.svelte @@ -0,0 +1,69 @@ + + +
+

Codes, lois et règlements

+
+ + + + + + diff --git a/src/routes/textes/[id]/+page.server.ts b/src/routes/textes/[id]/+page.server.ts new file mode 100644 index 0000000..9e7a293 --- /dev/null +++ b/src/routes/textes/[id]/+page.server.ts @@ -0,0 +1,7 @@ +import { doGetTexte } from "$lib/server/doers/textes" + +import type { PageServerLoad } from "./$types" + +export const load: PageServerLoad = async ({ params, url }) => { + return (await doGetTexte(params.id, url)) ?? {} +} diff --git a/src/routes/textes/[id]/+page.svelte b/src/routes/textes/[id]/+page.svelte new file mode 100644 index 0000000..c0464ac --- /dev/null +++ b/src/routes/textes/[id]/+page.svelte @@ -0,0 +1,55 @@ + + + + +
+

Codes, lois et règlements

+ {#if summary !== undefined} +

+ +

+ {/if} +
+ +
+
+ +
+
+ +
+
+ +{#if showRawData} + +{:else} + +{/if}