Improve search of 'textes' (allow by nature & full text search on title).
This commit is contained in:
parent
6d9efd976f
commit
a64aa82835
21 changed files with 512 additions and 115 deletions
|
@ -1,4 +1,10 @@
|
||||||
import type { Article, SectionTa, Textelr, TexteVersion } from "$lib/legal"
|
import type {
|
||||||
|
Article,
|
||||||
|
SectionTa,
|
||||||
|
Textekali,
|
||||||
|
Textelr,
|
||||||
|
TexteVersion,
|
||||||
|
} from "$lib/legal"
|
||||||
|
|
||||||
export interface Aggregate {
|
export interface Aggregate {
|
||||||
article?: { [id: string]: Article }
|
article?: { [id: string]: Article }
|
||||||
|
@ -6,6 +12,7 @@ export interface Aggregate {
|
||||||
ids?: string[]
|
ids?: string[]
|
||||||
section_ta?: { [id: string]: SectionTa }
|
section_ta?: { [id: string]: SectionTa }
|
||||||
texte_version?: { [id: string]: TexteVersion }
|
texte_version?: { [id: string]: TexteVersion }
|
||||||
|
textekali?: { [id: string]: Textekali }
|
||||||
textelr?: { [id: string]: Textelr }
|
textelr?: { [id: string]: Textelr }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +20,7 @@ export type Follow = typeof allFollows[number]
|
||||||
|
|
||||||
export interface GetRechercheResult extends Aggregate {
|
export interface GetRechercheResult extends Aggregate {
|
||||||
follow: Follow[]
|
follow: Follow[]
|
||||||
id: string
|
id?: string
|
||||||
q?: string
|
q?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +33,7 @@ export interface ListTextesResult extends Aggregate {
|
||||||
follow: Follow[]
|
follow: Follow[]
|
||||||
ids: string[]
|
ids: string[]
|
||||||
limit: number
|
limit: number
|
||||||
|
nature?: string
|
||||||
offset: number
|
offset: number
|
||||||
q?: string
|
q?: string
|
||||||
}
|
}
|
||||||
|
@ -36,6 +44,7 @@ export const allFollows = [
|
||||||
"STRUCT.LIEN_SECTION_TA.@id",
|
"STRUCT.LIEN_SECTION_TA.@id",
|
||||||
"STRUCTURE_TA.LIEN_ART.@id",
|
"STRUCTURE_TA.LIEN_ART.@id",
|
||||||
"STRUCTURE_TA.LIEN_SECTION_TA.@id",
|
"STRUCTURE_TA.LIEN_SECTION_TA.@id",
|
||||||
|
"TEXTEKALI",
|
||||||
"TEXTELR",
|
"TEXTELR",
|
||||||
] as const
|
] as const
|
||||||
export const allFollowsMutable = [...allFollows]
|
export const allFollowsMutable = [...allFollows]
|
||||||
|
|
|
@ -33,6 +33,22 @@ export function auditFollowSearchParams(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function auditFollowWithFalseSearchParams(
|
||||||
|
audit: Audit,
|
||||||
|
data: { [key: string]: unknown },
|
||||||
|
errors: { [key: string]: unknown },
|
||||||
|
remainingKeys: Set<string>,
|
||||||
|
): void {
|
||||||
|
audit.attribute(
|
||||||
|
data,
|
||||||
|
"follow",
|
||||||
|
true,
|
||||||
|
errors,
|
||||||
|
remainingKeys,
|
||||||
|
auditSearchParamsOptionsSet([...allFollowsMutable, "false"]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function auditLimitSearchParam(
|
export function auditLimitSearchParam(
|
||||||
audit: Audit,
|
audit: Audit,
|
||||||
data: { [key: string]: unknown },
|
data: { [key: string]: unknown },
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import { page } from "$app/stores"
|
import { page } from "$app/stores"
|
||||||
import { auditPaginationSearchParams } from "$lib/auditors/search_params"
|
import { auditPaginationSearchParams } from "$lib/auditors/search_params"
|
||||||
import type { PaginationQuery } from "$lib/queries"
|
import type { PaginationQuery } from "$lib/queries"
|
||||||
|
import { urlFromUrlAndQuery } from "$lib/urls"
|
||||||
|
|
||||||
export let count: number | undefined | null = undefined // Count is not always known.
|
export let count: number | undefined | null = undefined // Count is not always known.
|
||||||
export let currentPageCount: number
|
export let currentPageCount: number
|
||||||
|
@ -47,7 +48,7 @@
|
||||||
| string
|
| string
|
||||||
| undefined
|
| undefined
|
||||||
| null
|
| null
|
||||||
| Array<boolean | number | string | undefined | null>
|
| Iterable<boolean | number | string | undefined | null>
|
||||||
},
|
},
|
||||||
{ limit, offset }: { limit?: number; offset?: number } = {},
|
{ limit, offset }: { limit?: number; offset?: number } = {},
|
||||||
) {
|
) {
|
||||||
|
@ -62,23 +63,7 @@
|
||||||
} else {
|
} else {
|
||||||
delete query.offset
|
delete query.offset
|
||||||
}
|
}
|
||||||
const search = new URLSearchParams(
|
return urlFromUrlAndQuery(pathname, query)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pageNumber(limit: number, offset: number): number {
|
function pageNumber(limit: number, offset: number): number {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
$: id = texteVersion.META.META_COMMUN.ID
|
$: id = texteVersion.META.META_COMMUN.ID
|
||||||
|
|
||||||
$: textelr = data.textelr?.[id]
|
$: textelr = data.textekali?.[id] ?? data.textelr?.[id]
|
||||||
|
|
||||||
$: summary = summarizeLegalObject(
|
$: summary = summarizeLegalObject(
|
||||||
{ key: "texte_version" },
|
{ key: "texte_version" },
|
||||||
|
|
|
@ -153,6 +153,10 @@ export interface LienSectionTa {
|
||||||
|
|
||||||
export interface MetaCommun {
|
export interface MetaCommun {
|
||||||
ID: string
|
ID: string
|
||||||
|
URL: string
|
||||||
|
NATURE: string
|
||||||
|
ORIGINE: string
|
||||||
|
ANCIEN_ID: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MetaTexteChronicle {
|
export interface MetaTexteChronicle {
|
||||||
|
@ -180,15 +184,9 @@ export interface SectionTa {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Textekali {
|
export type Textekali = Textelr
|
||||||
META: {
|
|
||||||
META_COMMUN: MetaCommun
|
|
||||||
META_SPEC: {
|
|
||||||
META_TEXTE_CHRONICLE: MetaTexteChronicle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Racine de l'arborescence d'un texte législatif ou règlementaire
|
||||||
export interface Textelr {
|
export interface Textelr {
|
||||||
META: {
|
META: {
|
||||||
META_COMMUN: MetaCommun
|
META_COMMUN: MetaCommun
|
||||||
|
@ -383,7 +381,9 @@ export function menuItemsFromLegalId(
|
||||||
return [
|
return [
|
||||||
{ href: `/textes/${id}`, label: "Présentation" },
|
{ href: `/textes/${id}`, label: "Présentation" },
|
||||||
{ href: `/texte_version/${id}`, label: "TEXTE_VERSION" },
|
{ href: `/texte_version/${id}`, label: "TEXTE_VERSION" },
|
||||||
{ href: `/textelr/${id}`, label: "TEXTELR" },
|
id.startsWith("KALI")
|
||||||
|
? { href: `/textekali/${id}`, label: "TEXTEKALI" }
|
||||||
|
: { href: `/textelr/${id}`, label: "TEXTELR" },
|
||||||
]
|
]
|
||||||
default:
|
default:
|
||||||
return undefined
|
return undefined
|
||||||
|
@ -395,6 +395,9 @@ export function pathnameFromLegalId(id: string): string | undefined {
|
||||||
if (rootType === undefined) {
|
if (rootType === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
if (["texte_version", "textekali", "textelr"].includes(rootType)) {
|
||||||
|
return `/textes/${id}`
|
||||||
|
}
|
||||||
return `/${rootType}/${id}`
|
return `/${rootType}/${id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,11 +421,14 @@ export function pathnameFromLegalObject(
|
||||||
case "section_ta":
|
case "section_ta":
|
||||||
return `/section_ta/${(object as SectionTa).ID}`
|
return `/section_ta/${(object as SectionTa).ID}`
|
||||||
case "texte_version":
|
case "texte_version":
|
||||||
return `/texte_version/${(object as TexteVersion).META.META_COMMUN.ID}`
|
// return `/texte_version/${(object as TexteVersion).META.META_COMMUN.ID}`
|
||||||
|
return `/textes/${(object as TexteVersion).META.META_COMMUN.ID}`
|
||||||
case "textekali":
|
case "textekali":
|
||||||
return `/textekali/${(object as Textekali).META.META_COMMUN.ID}`
|
// return `/textekali/${(object as Textekali).META.META_COMMUN.ID}`
|
||||||
|
return `/textes/${(object as TexteVersion).META.META_COMMUN.ID}`
|
||||||
case "textelr":
|
case "textelr":
|
||||||
return `/textelr/${(object as Textelr).META.META_COMMUN.ID}`
|
// return `/textelr/${(object as Textelr).META.META_COMMUN.ID}`
|
||||||
|
return `/textes/${(object as TexteVersion).META.META_COMMUN.ID}`
|
||||||
case "versions":
|
case "versions":
|
||||||
return `/versions/${(object as VersionsWrapper).eli}`
|
return `/versions/${(object as VersionsWrapper).eli}`
|
||||||
default:
|
default:
|
||||||
|
@ -449,11 +455,14 @@ export function pathnameFromLegalObjectId(
|
||||||
case "section_ta":
|
case "section_ta":
|
||||||
return `/section_ta/${id}`
|
return `/section_ta/${id}`
|
||||||
case "texte_version":
|
case "texte_version":
|
||||||
return `/texte_version/${id}`
|
// return `/texte_version/${id}`
|
||||||
|
return `/textes/${id}`
|
||||||
case "textekali":
|
case "textekali":
|
||||||
return `/textekali/${id}`
|
// return `/textekali/${id}`
|
||||||
|
return `/textes/${id}`
|
||||||
case "textelr":
|
case "textelr":
|
||||||
return `/textelr/${id}`
|
// return `/textelr/${id}`
|
||||||
|
return `/textes/${id}`
|
||||||
case "versions":
|
case "versions":
|
||||||
// Here, id is an ELI.
|
// Here, id is an ELI.
|
||||||
return `/versions/${id}`
|
return `/versions/${id}`
|
||||||
|
|
|
@ -232,20 +232,34 @@ async function importJorf({ resume }: { resume?: string } = {}): Promise<void> {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "TEXTE_VERSION": {
|
case "TEXTE_VERSION": {
|
||||||
const version = element as TexteVersion
|
const texteVersion = element as TexteVersion
|
||||||
|
const textAFragments = [
|
||||||
|
texteVersion.META.META_SPEC.META_TEXTE_VERSION.TITRE,
|
||||||
|
texteVersion.META.META_SPEC.META_TEXTE_VERSION.TITREFULL,
|
||||||
|
]
|
||||||
await db`
|
await db`
|
||||||
INSERT INTO texte_version (
|
INSERT INTO texte_version (
|
||||||
id,
|
id,
|
||||||
data
|
data,
|
||||||
|
nature,
|
||||||
|
text_search
|
||||||
) VALUES (
|
) VALUES (
|
||||||
${version.META.META_COMMUN.ID},
|
${texteVersion.META.META_COMMUN.ID},
|
||||||
${db.json(version as unknown as JSONValue)}
|
${db.json(texteVersion as unknown as JSONValue)},
|
||||||
|
${texteVersion.META.META_COMMUN.NATURE}
|
||||||
|
setweight(to_tsvector('french', ${textAFragments.join(
|
||||||
|
" ",
|
||||||
|
)}), 'A')
|
||||||
)
|
)
|
||||||
ON CONFLICT (id)
|
ON CONFLICT (id)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
data = ${db.json(version as unknown as JSONValue)}
|
data = ${db.json(texteVersion as unknown as JSONValue)},
|
||||||
|
nature = ${texteVersion.META.META_COMMUN.NATURE},
|
||||||
|
text_search = setweight(to_tsvector('french', ${textAFragments.join(
|
||||||
|
" ",
|
||||||
|
)}), 'A')
|
||||||
`
|
`
|
||||||
texteVersionRemainingIds.delete(version.META.META_COMMUN.ID)
|
texteVersionRemainingIds.delete(texteVersion.META.META_COMMUN.ID)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "TEXTELR": {
|
case "TEXTELR": {
|
||||||
|
|
|
@ -190,20 +190,34 @@ async function importKali({ resume }: { resume?: string } = {}): Promise<void> {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "TEXTE_VERSION": {
|
case "TEXTE_VERSION": {
|
||||||
const version = element as TexteVersion
|
const texteVersion = element as TexteVersion
|
||||||
|
const textAFragments = [
|
||||||
|
texteVersion.META.META_SPEC.META_TEXTE_VERSION.TITRE,
|
||||||
|
texteVersion.META.META_SPEC.META_TEXTE_VERSION.TITREFULL,
|
||||||
|
]
|
||||||
await db`
|
await db`
|
||||||
INSERT INTO texte_version (
|
INSERT INTO texte_version (
|
||||||
id,
|
id,
|
||||||
data
|
data,
|
||||||
|
nature,
|
||||||
|
text_search
|
||||||
) VALUES (
|
) VALUES (
|
||||||
${version.META.META_COMMUN.ID},
|
${texteVersion.META.META_COMMUN.ID},
|
||||||
${db.json(version as unknown as JSONValue)}
|
${db.json(texteVersion as unknown as JSONValue)},
|
||||||
|
${texteVersion.META.META_COMMUN.NATURE}
|
||||||
|
setweight(to_tsvector('french', ${textAFragments.join(
|
||||||
|
" ",
|
||||||
|
)}), 'A')
|
||||||
)
|
)
|
||||||
ON CONFLICT (id)
|
ON CONFLICT (id)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
data = ${db.json(version as unknown as JSONValue)}
|
data = ${db.json(texteVersion as unknown as JSONValue)},
|
||||||
|
nature = ${texteVersion.META.META_COMMUN.NATURE},
|
||||||
|
text_search = setweight(to_tsvector('french', ${textAFragments.join(
|
||||||
|
" ",
|
||||||
|
)}), 'A')
|
||||||
`
|
`
|
||||||
texteVersionRemainingIds.delete(version.META.META_COMMUN.ID)
|
texteVersionRemainingIds.delete(texteVersion.META.META_COMMUN.ID)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "TEXTEKALI": {
|
case "TEXTEKALI": {
|
||||||
|
|
|
@ -77,7 +77,7 @@ async function importLegi({ resume }: { resume?: string } = {}): Promise<void> {
|
||||||
`
|
`
|
||||||
).map(({ id }) => id),
|
).map(({ id }) => id),
|
||||||
)
|
)
|
||||||
const texteVersionRemainingElis = new Set(
|
const texteVersionRemainingIds = new Set(
|
||||||
(
|
(
|
||||||
await db<{ id: string }[]>`
|
await db<{ id: string }[]>`
|
||||||
SELECT id
|
SELECT id
|
||||||
|
@ -198,20 +198,34 @@ async function importLegi({ resume }: { resume?: string } = {}): Promise<void> {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "TEXTE_VERSION": {
|
case "TEXTE_VERSION": {
|
||||||
const version = element as TexteVersion
|
const texteVersion = element as TexteVersion
|
||||||
|
const textAFragments = [
|
||||||
|
texteVersion.META.META_SPEC.META_TEXTE_VERSION.TITRE,
|
||||||
|
texteVersion.META.META_SPEC.META_TEXTE_VERSION.TITREFULL,
|
||||||
|
]
|
||||||
await db`
|
await db`
|
||||||
INSERT INTO texte_version (
|
INSERT INTO texte_version (
|
||||||
id,
|
id,
|
||||||
data
|
data,
|
||||||
|
nature,
|
||||||
|
text_search
|
||||||
) VALUES (
|
) VALUES (
|
||||||
${version.META.META_COMMUN.ID},
|
${texteVersion.META.META_COMMUN.ID},
|
||||||
${db.json(version as unknown as JSONValue)}
|
${db.json(texteVersion as unknown as JSONValue)},
|
||||||
|
${texteVersion.META.META_COMMUN.NATURE}
|
||||||
|
setweight(to_tsvector('french', ${textAFragments.join(
|
||||||
|
" ",
|
||||||
|
)}), 'A')
|
||||||
)
|
)
|
||||||
ON CONFLICT (id)
|
ON CONFLICT (id)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
data = ${db.json(version as unknown as JSONValue)}
|
data = ${db.json(texteVersion as unknown as JSONValue)},
|
||||||
|
nature = ${texteVersion.META.META_COMMUN.NATURE},
|
||||||
|
text_search = setweight(to_tsvector('french', ${textAFragments.join(
|
||||||
|
" ",
|
||||||
|
)}), 'A')
|
||||||
`
|
`
|
||||||
texteVersionRemainingElis.delete(version.META.META_COMMUN.ID)
|
texteVersionRemainingIds.delete(texteVersion.META.META_COMMUN.ID)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "TEXTELR": {
|
case "TEXTELR": {
|
||||||
|
@ -310,7 +324,7 @@ async function importLegi({ resume }: { resume?: string } = {}): Promise<void> {
|
||||||
WHERE id = ${id}
|
WHERE id = ${id}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
for (const id of texteVersionRemainingElis) {
|
for (const id of texteVersionRemainingIds) {
|
||||||
console.log(`Deleting TEXTE_VERSION ${id}…`)
|
console.log(`Deleting TEXTE_VERSION ${id}…`)
|
||||||
await db`
|
await db`
|
||||||
DELETE FROM texte_version
|
DELETE FROM texte_version
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
type Article,
|
type Article,
|
||||||
type LegalObjectType,
|
type LegalObjectType,
|
||||||
type SectionTa,
|
type SectionTa,
|
||||||
|
type Textekali,
|
||||||
type Textelr,
|
type Textelr,
|
||||||
type TexteVersion,
|
type TexteVersion,
|
||||||
} from "$lib/legal"
|
} from "$lib/legal"
|
||||||
|
@ -17,6 +18,7 @@ export class Aggregator {
|
||||||
requestedTypeAndIds: Set<TypeAndId> = new Set()
|
requestedTypeAndIds: Set<TypeAndId> = new Set()
|
||||||
section_ta: { [id: string]: SectionTa } = {}
|
section_ta: { [id: string]: SectionTa } = {}
|
||||||
texte_version: { [id: string]: TexteVersion } = {}
|
texte_version: { [id: string]: TexteVersion } = {}
|
||||||
|
textekali: { [id: string]: Textekali } = {}
|
||||||
textelr: { [id: string]: Textelr } = {}
|
textelr: { [id: string]: Textelr } = {}
|
||||||
visitedTypeAndIds: Set<TypeAndId> = new Set()
|
visitedTypeAndIds: Set<TypeAndId> = new Set()
|
||||||
|
|
||||||
|
@ -58,6 +60,25 @@ export class Aggregator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addTextekali(textekali: Textekali): void {
|
||||||
|
const id = textekali.META.META_COMMUN.ID
|
||||||
|
this.textekali[id] = textekali
|
||||||
|
|
||||||
|
if (this.follow.has("STRUCT.LIEN_ART.@id")) {
|
||||||
|
for (const lien of iterArrayOrSingleton(textekali.STRUCT.LIEN_ART)) {
|
||||||
|
this.requestId(lien["@id"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.follow.has("STRUCT.LIEN_SECTION_TA.@id")) {
|
||||||
|
for (const lien of iterArrayOrSingleton(
|
||||||
|
textekali.STRUCT.LIEN_SECTION_TA,
|
||||||
|
)) {
|
||||||
|
this.requestId(lien["@id"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addTextelr(textelr: Textelr): void {
|
addTextelr(textelr: Textelr): void {
|
||||||
const id = textelr.META.META_COMMUN.ID
|
const id = textelr.META.META_COMMUN.ID
|
||||||
this.textelr[id] = textelr
|
this.textelr[id] = textelr
|
||||||
|
@ -79,6 +100,9 @@ export class Aggregator {
|
||||||
const id = texteVersion.META.META_COMMUN.ID
|
const id = texteVersion.META.META_COMMUN.ID
|
||||||
this.texte_version[id] = texteVersion
|
this.texte_version[id] = texteVersion
|
||||||
|
|
||||||
|
if (this.follow.has("TEXTEKALI")) {
|
||||||
|
this.requestTypeAndId(["textekali", id])
|
||||||
|
}
|
||||||
if (this.follow.has("TEXTELR")) {
|
if (this.follow.has("TEXTELR")) {
|
||||||
this.requestTypeAndId(["textelr", id])
|
this.requestTypeAndId(["textelr", id])
|
||||||
}
|
}
|
||||||
|
@ -97,12 +121,9 @@ export class Aggregator {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(): Promise<void> {
|
async getAll(): Promise<void> {
|
||||||
while (true) {
|
while (this.requestedTypeAndIds.size > 0) {
|
||||||
if (this.requestedTypeAndIds.size === 0) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
console.log(
|
console.log(
|
||||||
"this.requestedTypeAndIds.size",
|
"Aggregator.getAll: this.requestedTypeAndIds.size =",
|
||||||
this.requestedTypeAndIds.size,
|
this.requestedTypeAndIds.size,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -138,6 +159,22 @@ export class Aggregator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const textekaliTypeAndIds = [...this.requestedTypeAndIds].filter(
|
||||||
|
([type]) => type === "textekali",
|
||||||
|
)
|
||||||
|
if (textekaliTypeAndIds.length > 0) {
|
||||||
|
this.deleteFromRequestedTypeAndIds(textekaliTypeAndIds)
|
||||||
|
for (const { data } of await db<{ id: string; data: Textekali }[]>`
|
||||||
|
SELECT id, data FROM textekali
|
||||||
|
WHERE id IN ${db(textekaliTypeAndIds.map(([, id]) => id))}
|
||||||
|
`) {
|
||||||
|
this.addTextekali(data)
|
||||||
|
}
|
||||||
|
this.addToVisitedTypeAndIds(textekaliTypeAndIds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const textelrTypeAndIds = [...this.requestedTypeAndIds].filter(
|
const textelrTypeAndIds = [...this.requestedTypeAndIds].filter(
|
||||||
([type]) => type === "textelr",
|
([type]) => type === "textelr",
|
||||||
|
@ -180,6 +217,9 @@ export class Aggregator {
|
||||||
if (Object.keys(this.texte_version).length > 0) {
|
if (Object.keys(this.texte_version).length > 0) {
|
||||||
json.texte_version = this.texte_version
|
json.texte_version = this.texte_version
|
||||||
}
|
}
|
||||||
|
if (Object.keys(this.textekali).length > 0) {
|
||||||
|
json.textekali = this.textekali
|
||||||
|
}
|
||||||
if (Object.keys(this.textelr).length > 0) {
|
if (Object.keys(this.textelr).length > 0) {
|
||||||
json.textelr = this.textelr
|
json.textelr = this.textelr
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import assert from "assert"
|
import assert from "assert"
|
||||||
|
import type { SerializableParameter } from "postgres"
|
||||||
|
|
||||||
|
import type { TexteVersion } from "$lib/legal"
|
||||||
import { db, type Version, versionNumber } from "$lib/server/database"
|
import { db, type Version, versionNumber } from "$lib/server/database"
|
||||||
|
|
||||||
export async function configureDatabase() {
|
export async function configureDatabase() {
|
||||||
|
@ -42,6 +44,17 @@ export async function configureDatabase() {
|
||||||
await db`ALTER TABLE IF EXISTS textes RENAME TO texte_version`
|
await db`ALTER TABLE IF EXISTS textes RENAME TO texte_version`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version.number < 3) {
|
||||||
|
await db`
|
||||||
|
ALTER TABLE IF EXISTS texte_version
|
||||||
|
ADD COLUMN IF NOT EXISTS nature text
|
||||||
|
`
|
||||||
|
await db`
|
||||||
|
ALTER TABLE IF EXISTS texte_version
|
||||||
|
ADD COLUMN IF NOT EXISTS text_search tsvector
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
|
|
||||||
// Tables
|
// Tables
|
||||||
|
@ -108,7 +121,9 @@ export async function configureDatabase() {
|
||||||
await db`
|
await db`
|
||||||
CREATE TABLE IF NOT EXISTS texte_version (
|
CREATE TABLE IF NOT EXISTS texte_version (
|
||||||
id char(20) PRIMARY KEY,
|
id char(20) PRIMARY KEY,
|
||||||
data jsonb NOT NULL
|
data jsonb NOT NULL,
|
||||||
|
nature text NOT NULL,
|
||||||
|
text_search tsvector NOT NULL
|
||||||
)
|
)
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -139,8 +154,62 @@ export async function configureDatabase() {
|
||||||
|
|
||||||
// Apply patches that must be executed after every table is created.
|
// Apply patches that must be executed after every table is created.
|
||||||
|
|
||||||
|
if (version.number < 3) {
|
||||||
|
// Fill "nature" column of table texte_version.
|
||||||
|
for await (const rows of db<
|
||||||
|
Array<{ data: TexteVersion; id: string; nature: string }>
|
||||||
|
>`SELECT data, id, nature FROM texte_version`.cursor(100)) {
|
||||||
|
for (const { data, id, nature } of rows) {
|
||||||
|
if (data.META.META_COMMUN.NATURE !== nature) {
|
||||||
|
await db`
|
||||||
|
UPDATE texte_version
|
||||||
|
SET nature = ${data.META.META_COMMUN.NATURE}
|
||||||
|
WHERE id = ${id}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await db`
|
||||||
|
ALTER TABLE texte_version
|
||||||
|
ALTER COLUMN nature SET NOT NULL
|
||||||
|
`
|
||||||
|
|
||||||
|
// Fill "text_search" column of table texte_version.
|
||||||
|
for await (const rows of db<
|
||||||
|
Array<{ data: TexteVersion; id: string }>
|
||||||
|
>`SELECT data, id FROM texte_version`.cursor(100)) {
|
||||||
|
for (const { data, id } of rows) {
|
||||||
|
const textAFragments = [
|
||||||
|
data.META.META_SPEC.META_TEXTE_VERSION.TITRE,
|
||||||
|
data.META.META_SPEC.META_TEXTE_VERSION.TITREFULL,
|
||||||
|
]
|
||||||
|
// const vector = (await db<{vector: SerializableParameter}[]>`
|
||||||
|
// SELECT
|
||||||
|
// setweight(to_tsvector('french', ${textAFragments.join(" ")}), 'A')
|
||||||
|
// AS vector
|
||||||
|
// `)[0].vector
|
||||||
|
await db`
|
||||||
|
UPDATE texte_version
|
||||||
|
SET text_search = setweight(to_tsvector('french', ${textAFragments.join(
|
||||||
|
" ",
|
||||||
|
)}), 'A')
|
||||||
|
WHERE id = ${id}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await db`
|
||||||
|
ALTER TABLE texte_version
|
||||||
|
ALTER COLUMN text_search SET NOT NULL
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
// Add indexes once every table and column exists.
|
// Add indexes once every table and column exists.
|
||||||
|
|
||||||
|
await db`
|
||||||
|
CREATE INDEX IF NOT EXISTS texte_version_nature_key
|
||||||
|
ON texte_version (nature)
|
||||||
|
`
|
||||||
|
|
||||||
// await db`
|
// await db`
|
||||||
// CREATE INDEX IF NOT EXISTS article_autocompletions_trigrams_idx
|
// CREATE INDEX IF NOT EXISTS article_autocompletions_trigrams_idx
|
||||||
// ON article_autocompletions
|
// ON article_autocompletions
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const db = postgres({
|
||||||
port: config.db.port,
|
port: config.db.port,
|
||||||
user: config.db.user,
|
user: config.db.user,
|
||||||
})
|
})
|
||||||
export const versionNumber = 2
|
export const versionNumber = 3
|
||||||
|
|
||||||
/// Check that database exists and is up to date.
|
/// Check that database exists and is up to date.
|
||||||
export async function checkDb(): Promise<void> {
|
export async function checkDb(): Promise<void> {
|
||||||
|
|
13
src/lib/server/sql.ts
Normal file
13
src/lib/server/sql.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import type { PendingQuery, Row } from "postgres"
|
||||||
|
|
||||||
|
import { db } from "$lib/server/database"
|
||||||
|
|
||||||
|
export function joinSqlClauses(
|
||||||
|
joiner: PendingQuery<Row[]>,
|
||||||
|
clauses: Array<PendingQuery<Row[]>>,
|
||||||
|
): PendingQuery<Row[]> {
|
||||||
|
return clauses.reduce(
|
||||||
|
(acc, clause, index) => db`${acc} ${index > 0 ? joiner : db``} ${clause}`,
|
||||||
|
db``,
|
||||||
|
)
|
||||||
|
}
|
37
src/lib/urls.ts
Normal file
37
src/lib/urls.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
export function urlFromUrlAndQuery(
|
||||||
|
urlOrPathname: string,
|
||||||
|
query: {
|
||||||
|
[key: string]:
|
||||||
|
| boolean
|
||||||
|
| number
|
||||||
|
| string
|
||||||
|
| undefined
|
||||||
|
| null
|
||||||
|
| Iterable<boolean | number | string | undefined | null>
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const search = new URLSearchParams(
|
||||||
|
Object.entries(query).reduce((couples, [key, value]) => {
|
||||||
|
if (value != null) {
|
||||||
|
if (
|
||||||
|
typeof value !== "string" &&
|
||||||
|
typeof (
|
||||||
|
value as Iterable<boolean | number | string | undefined | null>
|
||||||
|
)[Symbol.iterator] === "function"
|
||||||
|
) {
|
||||||
|
for (const item of value as Iterable<
|
||||||
|
boolean | number | string | undefined | null
|
||||||
|
>) {
|
||||||
|
if (item != null) {
|
||||||
|
couples.push([key, item.toString()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
couples.push([key, value.toString()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return couples
|
||||||
|
}, [] as Array<[string, string]>),
|
||||||
|
).toString()
|
||||||
|
return search ? `${urlOrPathname}?${search}` : urlOrPathname
|
||||||
|
}
|
|
@ -1,5 +1,11 @@
|
||||||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
import {
|
||||||
|
type Audit,
|
||||||
|
auditSetNullish,
|
||||||
|
auditTrimString,
|
||||||
|
cleanAudit,
|
||||||
|
} from "@auditors/core"
|
||||||
import { error } from "@sveltejs/kit"
|
import { error } from "@sveltejs/kit"
|
||||||
|
import type { PendingQuery, Row } from "postgres"
|
||||||
|
|
||||||
import type { Follow } from "$lib/aggregates"
|
import type { Follow } from "$lib/aggregates"
|
||||||
import {
|
import {
|
||||||
|
@ -7,10 +13,12 @@ import {
|
||||||
auditLimitSearchParam,
|
auditLimitSearchParam,
|
||||||
auditOffsetSearchParam,
|
auditOffsetSearchParam,
|
||||||
auditQSearchParam,
|
auditQSearchParam,
|
||||||
|
auditSingleton,
|
||||||
} from "$lib/auditors/search_params"
|
} from "$lib/auditors/search_params"
|
||||||
import type { TexteVersion } from "$lib/legal"
|
import type { TexteVersion } from "$lib/legal"
|
||||||
import { Aggregator } from "$lib/server/aggregates"
|
import { Aggregator } from "$lib/server/aggregates"
|
||||||
import { db } from "$lib/server/database"
|
import { db } from "$lib/server/database"
|
||||||
|
import { joinSqlClauses } from "$lib/server/sql"
|
||||||
|
|
||||||
import type { RequestHandler } from "./$types"
|
import type { RequestHandler } from "./$types"
|
||||||
|
|
||||||
|
@ -38,6 +46,14 @@ export function auditSearchParams(
|
||||||
|
|
||||||
auditFollowSearchParams(audit, data, errors, remainingKeys)
|
auditFollowSearchParams(audit, data, errors, remainingKeys)
|
||||||
auditLimitSearchParam(audit, data, errors, remainingKeys)
|
auditLimitSearchParam(audit, data, errors, remainingKeys)
|
||||||
|
audit.attribute(
|
||||||
|
data,
|
||||||
|
"nature",
|
||||||
|
true,
|
||||||
|
errors,
|
||||||
|
remainingKeys,
|
||||||
|
auditSingleton(auditTrimString),
|
||||||
|
)
|
||||||
auditOffsetSearchParam(audit, data, errors, remainingKeys)
|
auditOffsetSearchParam(audit, data, errors, remainingKeys)
|
||||||
auditQSearchParam(audit, data, errors, remainingKeys)
|
auditQSearchParam(audit, data, errors, remainingKeys)
|
||||||
|
|
||||||
|
@ -54,6 +70,7 @@ export const GET: RequestHandler = async ({ url }) => {
|
||||||
limit: number
|
limit: number
|
||||||
offset: number
|
offset: number
|
||||||
q?: string
|
q?: string
|
||||||
|
nature?: string
|
||||||
},
|
},
|
||||||
unknown,
|
unknown,
|
||||||
]
|
]
|
||||||
|
@ -67,13 +84,47 @@ export const GET: RequestHandler = async ({ url }) => {
|
||||||
)
|
)
|
||||||
throw error(400, JSON.stringify(queryError, null, 2))
|
throw error(400, JSON.stringify(queryError, null, 2))
|
||||||
}
|
}
|
||||||
const { follow, limit, offset, q } = query
|
const { follow, limit, nature, offset, q } = query
|
||||||
|
|
||||||
|
const joinClauses: Array<PendingQuery<Row[]>> = []
|
||||||
|
const orderByClauses: Array<PendingQuery<Row[]>> = []
|
||||||
|
const selectClauses: Array<PendingQuery<Row[]>> = [db`data`]
|
||||||
|
const whereClauses: Array<PendingQuery<Row[]>> = []
|
||||||
|
|
||||||
|
if (nature !== undefined) {
|
||||||
|
whereClauses.push(db`nature = ${nature}`)
|
||||||
|
}
|
||||||
|
if (q !== undefined) {
|
||||||
|
joinClauses.push(db`
|
||||||
|
CROSS JOIN (
|
||||||
|
SELECT plainto_tsquery('french', ${q}) AS query
|
||||||
|
) AS constants
|
||||||
|
`)
|
||||||
|
orderByClauses.push(db`rank DESC`)
|
||||||
|
selectClauses.push(db`ts_rank_cd(text_search, query) AS rank`)
|
||||||
|
whereClauses.push(db`query @@ text_search`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const joinClause = joinSqlClauses(db``, joinClauses)
|
||||||
|
const orderByClause =
|
||||||
|
orderByClauses.length > 0
|
||||||
|
? db`ORDER BY ${joinSqlClauses(db`,`, orderByClauses)}`
|
||||||
|
: db``
|
||||||
|
const selectClause = joinSqlClauses(db`,`, selectClauses)
|
||||||
|
const whereClause =
|
||||||
|
whereClauses.length > 0
|
||||||
|
? db`WHERE ${joinSqlClauses(db`AND`, whereClauses)}`
|
||||||
|
: db``
|
||||||
|
|
||||||
const texteVersionArray = (
|
const texteVersionArray = (
|
||||||
await db<{ data: TexteVersion }[]>`
|
await db<{ data: TexteVersion }[]>`
|
||||||
SELECT data FROM texte_version
|
SELECT ${db`${selectClause}`} FROM texte_version
|
||||||
OFFSET ${offset}
|
${db`${joinClause}`}
|
||||||
LIMIT ${limit}
|
${db`${whereClause}`}
|
||||||
`
|
${db`${orderByClause}`}
|
||||||
|
OFFSET ${offset}
|
||||||
|
LIMIT ${limit}
|
||||||
|
`
|
||||||
).map(({ data }) => data)
|
).map(({ data }) => data)
|
||||||
|
|
||||||
const aggregator = new Aggregator(follow)
|
const aggregator = new Aggregator(follow)
|
||||||
|
@ -91,6 +142,7 @@ export const GET: RequestHandler = async ({ url }) => {
|
||||||
(texteVersion) => texteVersion.META.META_COMMUN.ID,
|
(texteVersion) => texteVersion.META.META_COMMUN.ID,
|
||||||
),
|
),
|
||||||
limit,
|
limit,
|
||||||
|
nature,
|
||||||
offset,
|
offset,
|
||||||
q,
|
q,
|
||||||
},
|
},
|
||||||
|
|
25
src/routes/api/textes/natures/+server.ts
Normal file
25
src/routes/api/textes/natures/+server.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { db } from "$lib/server/database"
|
||||||
|
|
||||||
|
import type { RequestHandler } from "./$types"
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async ({ url }) => {
|
||||||
|
const natures = (
|
||||||
|
await db<{ nature: string }[]>`
|
||||||
|
SELECT distinct nature FROM texte_version
|
||||||
|
`
|
||||||
|
).map(({ nature }) => nature)
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
natures,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json; charset=utf-8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
|
@ -4,13 +4,16 @@
|
||||||
import { TreeView } from "augmented-data-viewer"
|
import { TreeView } from "augmented-data-viewer"
|
||||||
|
|
||||||
import { page } from "$app/stores"
|
import { page } from "$app/stores"
|
||||||
import Pagination from "$lib/components/Pagination.svelte"
|
// import Pagination from "$lib/components/Pagination.svelte"
|
||||||
import { summarizeArticleProperties } from "$lib/summaries"
|
import { summarizeArticleProperties } from "$lib/summaries"
|
||||||
|
|
||||||
import type { PageData } from "./$types"
|
import type { PageData } from "./$types"
|
||||||
|
|
||||||
export let data: PageData
|
export let data: PageData
|
||||||
let { articles, q } = data
|
|
||||||
|
$: id = data.id
|
||||||
|
$: q = data.q
|
||||||
|
$: article = id === undefined ? undefined : data.article?.[id]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header class="prose my-6 max-w-full">
|
<header class="prose my-6 max-w-full">
|
||||||
|
@ -34,7 +37,7 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{#if q !== undefined}
|
{#if q !== undefined}
|
||||||
{#if articles === undefined}
|
{#if article === undefined}
|
||||||
<p>Aucun article trouvé</p>
|
<p>Aucun article trouvé</p>
|
||||||
{:else}
|
{:else}
|
||||||
<TreeView
|
<TreeView
|
||||||
|
@ -42,9 +45,9 @@
|
||||||
frame={false}
|
frame={false}
|
||||||
open
|
open
|
||||||
summarize={summarizeArticleProperties}
|
summarize={summarizeArticleProperties}
|
||||||
value={articles}
|
value={article}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Pagination currentPageCount={articles.length ?? 0} />
|
<!-- <Pagination currentPageCount={article.length ?? 0} /> -->
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { TreeView, SummaryView } from "augmented-data-viewer"
|
import { TreeView, SummaryView } from "augmented-data-viewer"
|
||||||
|
|
||||||
|
import IdPagesSwitcher from "$lib/components/IdPagesSwitcher.svelte"
|
||||||
import {
|
import {
|
||||||
summarizeTextekaliProperties,
|
summarizeTextekaliProperties,
|
||||||
summarizeLegalObject,
|
summarizeLegalObject,
|
||||||
|
@ -19,6 +20,8 @@
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<IdPagesSwitcher id={textekali.META.META_COMMUN.ID} />
|
||||||
|
|
||||||
<header class="prose my-6 max-w-full">
|
<header class="prose my-6 max-w-full">
|
||||||
<h2>TEXTEKALI</h2>
|
<h2>TEXTEKALI</h2>
|
||||||
{#if summary !== undefined}
|
{#if summary !== undefined}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// import searchIcon from "@iconify-icons/codicon/search"
|
// import searchIcon from "@iconify-icons/codicon/search"
|
||||||
import { TreeView } from "augmented-data-viewer"
|
import { TreeView } from "augmented-data-viewer"
|
||||||
|
|
||||||
// import { page } from "$app/stores"
|
import { page } from "$app/stores"
|
||||||
import Pagination from "$lib/components/Pagination.svelte"
|
import Pagination from "$lib/components/Pagination.svelte"
|
||||||
import type { Article, SectionTa, Textelr, TexteVersion } from "$lib/legal"
|
import type { Article, SectionTa, Textelr, TexteVersion } from "$lib/legal"
|
||||||
import { summarizeTexteVersionProperties } from "$lib/summaries"
|
import { summarizeTexteVersionProperties } from "$lib/summaries"
|
||||||
|
@ -12,51 +12,80 @@
|
||||||
|
|
||||||
export let data: PageData
|
export let data: PageData
|
||||||
|
|
||||||
const articleById = data.article as { [id: string]: Article }
|
let nature = data.nature ?? ""
|
||||||
const sectionTaById = data.sectionTa as { [id: string]: SectionTa }
|
|
||||||
const texteVersionById = data.texte_version as { [id: string]: TexteVersion }
|
const naturesPromise = (async (): Promise<string[] | undefined> => {
|
||||||
const texteVersionArray = (data.ids as string[]).map(
|
const apiUrl = "/api/textes/natures"
|
||||||
|
const response = await fetch(apiUrl, {
|
||||||
|
headers: { Accept: "application/json" },
|
||||||
|
})
|
||||||
|
if (!response.ok) {
|
||||||
|
const text = await response.text()
|
||||||
|
console.error(
|
||||||
|
`Error in ${$page.url.pathname} while calling ${apiUrl}:\n${response.status} ${response.statusText}\n\n${text}`,
|
||||||
|
)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return (await response.json()).natures
|
||||||
|
})()
|
||||||
|
let q = data.q
|
||||||
|
|
||||||
|
$: texteVersionById = data.texte_version as { [id: string]: TexteVersion }
|
||||||
|
$: texteVersionArray = (data.ids as string[]).map(
|
||||||
(id) => texteVersionById[id],
|
(id) => texteVersionById[id],
|
||||||
)
|
)
|
||||||
const textelrById = data.textelr as { [id: string]: Textelr[] }
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header class="prose my-6 max-w-full">
|
<header class="prose my-6 max-w-full">
|
||||||
<h1>Codes, lois et règlements</h1>
|
<h1>Codes, lois et règlements</h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- <form action={$page.url.pathname} class="mx-auto max-w-sm" method="get">
|
<form action={$page.url.pathname} class="mx-auto max-w-sm" method="get">
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<div class="input-group">
|
<label class="label" for="q">
|
||||||
<input
|
<span class="label-text">Intitulé</span>
|
||||||
class="input input-bordered"
|
|
||||||
name="q"
|
|
||||||
type="search"
|
|
||||||
bind:value={q}
|
|
||||||
/>
|
|
||||||
<button class="btn btn-square" type="submit">
|
|
||||||
<Icon icon={searchIcon} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Législature</span>
|
|
||||||
<select
|
|
||||||
class="select select-bordered"
|
|
||||||
name="legislature"
|
|
||||||
bind:value={legislature}
|
|
||||||
>
|
|
||||||
<option value="">Toutes</option>
|
|
||||||
{#each Object.entries(Legislature) as [key, value]}
|
|
||||||
{#if value !== "*"}
|
|
||||||
<option {value}>{key}</option>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</label>
|
</label>
|
||||||
|
<input
|
||||||
|
class="input input-bordered"
|
||||||
|
id="q"
|
||||||
|
name="q"
|
||||||
|
placeholder="Code civil…"
|
||||||
|
type="search"
|
||||||
|
bind:value={q}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form> -->
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="nature">
|
||||||
|
<span class="label-text">Nature</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
class="select select-bordered"
|
||||||
|
id="nature"
|
||||||
|
name="nature"
|
||||||
|
bind:value={nature}
|
||||||
|
>
|
||||||
|
<option value="">Toutes</option>
|
||||||
|
{#await naturesPromise}
|
||||||
|
{#if nature !== undefined}
|
||||||
|
<option selected value={nature}>{nature}</option>
|
||||||
|
{/if}
|
||||||
|
{:then natures}
|
||||||
|
{#if natures !== undefined}
|
||||||
|
{#each natures as nature1}
|
||||||
|
{#if nature1}
|
||||||
|
<option selected={nature1 === nature} value={nature1}
|
||||||
|
>{nature1}</option
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
{/await}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn my-4" type="submit">Rechercher</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
<TreeView
|
<TreeView
|
||||||
access={{ key: "texte_version" }}
|
access={{ key: "texte_version" }}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import type { ListTextesResult } from "$lib/aggregates"
|
||||||
import type { PageLoad } from "./$types"
|
import type { PageLoad } from "./$types"
|
||||||
|
|
||||||
export const load: PageLoad = async ({ fetch, url }) => {
|
export const load: PageLoad = async ({ fetch, url }) => {
|
||||||
const apiUrl = `/api${url.pathname}`
|
const apiUrl = `/api${url.pathname}${url.search}`
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
headers: { Accept: "application/json" },
|
headers: { Accept: "application/json" },
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
|
|
||||||
export let data: PageData
|
export let data: PageData
|
||||||
|
|
||||||
const id = data.id!
|
|
||||||
const texteVersion = data.texte_version![id]
|
|
||||||
|
|
||||||
let showArticles = false
|
let showArticles = false
|
||||||
let showRawData = false
|
let showRawData = false
|
||||||
|
|
||||||
|
$: id = data.id!
|
||||||
|
$: texteVersion = data.texte_version![id]
|
||||||
|
|
||||||
$: summary = summarizeLegalObject(
|
$: summary = summarizeLegalObject(
|
||||||
{ key: "texte_version" },
|
{ key: "texte_version" },
|
||||||
"texte_version",
|
"texte_version",
|
||||||
|
|
|
@ -1,11 +1,76 @@
|
||||||
import { error } from "@sveltejs/kit"
|
import { error } from "@sveltejs/kit"
|
||||||
|
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||||
|
|
||||||
import type { GetTexteResult } from "$lib/aggregates"
|
import type { Follow, GetTexteResult } from "$lib/aggregates"
|
||||||
|
import { auditFollowWithFalseSearchParams } from "$lib/auditors/search_params"
|
||||||
|
import { urlFromUrlAndQuery } from "$lib/urls"
|
||||||
|
|
||||||
import type { PageLoad } from "./$types"
|
import type { PageLoad } from "./$types"
|
||||||
|
|
||||||
|
export function auditSearchParams(
|
||||||
|
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))
|
||||||
|
|
||||||
|
auditFollowWithFalseSearchParams(audit, data, errors, remainingKeys)
|
||||||
|
|
||||||
|
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||||
|
}
|
||||||
|
|
||||||
export const load: PageLoad = async ({ fetch, url }) => {
|
export const load: PageLoad = async ({ fetch, url }) => {
|
||||||
const apiUrl = `/api${url.pathname}`
|
const [query, queryError] = auditSearchParams(
|
||||||
|
cleanAudit,
|
||||||
|
url.searchParams,
|
||||||
|
) as [
|
||||||
|
{
|
||||||
|
follow: Set<Follow | "false">
|
||||||
|
},
|
||||||
|
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: requestedFollow } = query
|
||||||
|
|
||||||
|
let follow = new Set(requestedFollow)
|
||||||
|
const followFalse = follow.delete("false")
|
||||||
|
if (follow.size === 0 && !followFalse) {
|
||||||
|
// Set default follow
|
||||||
|
follow = new Set([
|
||||||
|
// "STRUCT.LIEN_ART.@id",
|
||||||
|
"STRUCT.LIEN_SECTION_TA.@id",
|
||||||
|
// "STRUCTURE_TA.LIEN_ART.@id",
|
||||||
|
"STRUCTURE_TA.LIEN_SECTION_TA.@id",
|
||||||
|
"TEXTEKALI",
|
||||||
|
"TEXTELR",
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiUrl = urlFromUrlAndQuery(`/api${url.pathname}`, { follow })
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
headers: { Accept: "application/json" },
|
headers: { Accept: "application/json" },
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue