Add KALI.

This commit is contained in:
Emmanuel 2022-08-13 18:44:43 +02:00
parent 00d3281a68
commit 9a1d93ca65
19 changed files with 1135 additions and 1 deletions

View file

@ -12,6 +12,7 @@ Legal data sources:
- https://www.dila.premier-ministre.gouv.fr/repertoire-des-informations-publiques/les-donnees-juridiques
- DOLE: https://echanges.dila.gouv.fr/OPENDATA/DOLE/
- JORF: https://echanges.dila.gouv.fr/OPENDATA/JORF/
- KALI: https://echanges.dila.gouv.fr/OPENDATA/KALI/
- LEGI: https://echanges.dila.gouv.fr/OPENDATA/LEGI/
## Installation

View file

@ -38,7 +38,21 @@ export interface DossierLegislatif {
}
}
export type Etat = "MODIFIE" | "VIGUEUR"
export type Etat = "MODIFIE" | "PERIME" | "VIGUEUR"
export interface Idcc {
META: {
META_COMMUN: MetaCommun
META_SPEC: {
META_CONTENEUR: {
TITRE: string
ETAT: Etat
NUM: string
DATE_PUBLI: string
}
}
}
}
export interface IdWrapper {
eli: string
@ -61,9 +75,11 @@ export interface Jo {
export type LegalObject =
| Article
| DossierLegislatif
| Idcc
| IdWrapper
| Jo
| SectionTa
| Textekali
| Textelr
| TexteVersion
| VersionsWrapper
@ -72,9 +88,11 @@ export type LegalObjectType =
| "article"
| "dossier_legislatif"
| "id"
| "idcc"
| "jo"
| "section_ta"
| "texte_version"
| "textekali"
| "textelr"
| "versions"
@ -98,6 +116,27 @@ export interface SectionTa {
STRUCTURE_TA: { LIEN_ART?: LienArt | LienArt[] }
}
export interface Textekali {
META: {
META_COMMUN: MetaCommun
META_SPEC: {
META_TEXTE_CHRONICLE: {
CID: string
NUM: string
NUM_PARUTION: string
NUM_SEQUENCE: string
NOR: string
DATE_PUBLI: string
DATE_TEXTE: string
DERNIERE_MODIFICATION: string
ORIGINE_PUBLI: string
PAGE_DEB_PUBLI: string
PAGE_FIN_PUBLI: string
}
}
}
}
export interface Textelr {
META: {
META_COMMUN: MetaCommun
@ -176,12 +215,16 @@ export function pathnameFromLegalObject(
}`
case "id":
return `/id/${(object as IdWrapper).eli}`
case "idcc":
return `/idcc/${(object as Idcc).META.META_COMMUN.ID}`
case "jo":
return `/jo/${(object as Jo).META.META_COMMUN.ID}`
case "section_ta":
return `/section_ta/${(object as SectionTa).ID}`
case "texte_version":
return `/texte_version/${(object as TexteVersion).META.META_COMMUN.ID}`
case "textekali":
return `/textekali/${(object as Textekali).META.META_COMMUN.ID}`
case "textelr":
return `/textelr/${(object as Textelr).META.META_COMMUN.ID}`
case "versions":
@ -203,12 +246,16 @@ export function pathnameFromLegalObjectId(
case "id":
// Here, id is an ELI.
return `/id/{id}`
case "idcc":
return `/idcc/${id}`
case "jo":
return `/jo/${id}`
case "section_ta":
return `/section_ta/${id}`
case "texte_version":
return `/texte_version/${id}`
case "textekali":
return `/textekali/${id}`
case "textelr":
return `/textelr/${id}`
case "versions":

View file

@ -0,0 +1,127 @@
import { auditChain, auditRequire, strictAudit } from "@auditors/core"
import assert from "assert"
import { XMLParser } from "fast-xml-parser"
import fs from "fs-extra"
import he from "he"
import path from "path"
import type { JSONValue } from "postgres"
import sade from "sade"
import type { DossierLegislatif, XmlHeader } from "$lib/data"
import { db } from "$lib/server/database"
import { walkDir } from "$lib/server/file_systems"
const xmlParser = new XMLParser({
attributeNamePrefix: "@",
ignoreAttributes: false,
stopNodes: [
"DOSSIER_LEGISLATIF.CONTENU.CONTENU_DOSSIER_1",
"DOSSIER_LEGISLATIF.CONTENU.CONTENU_DOSSIER_2",
"DOSSIER_LEGISLATIF.CONTENU.EXPOSE_MOTIF",
],
tagValueProcessor: (_tagName, tagValue) => he.decode(tagValue),
})
async function importDole({ resume }: { resume?: string } = {}): Promise<void> {
let skip = resume !== undefined
const deleteRemainingIds = !skip
const dossierLegislatifRemainingIds = new Set(
(
await db<{ id: string }[]>`
SELECT id
FROM dossier_legislatif
`
).map(({ id }) => id),
)
const dataDir = path.join("..", "dila-data", "dole")
assert(await fs.pathExists(dataDir))
iterXmlFiles: for (const relativeSplitPath of walkDir(dataDir)) {
const relativePath = path.join(...relativeSplitPath)
if (skip) {
if (relativePath.startsWith(resume!)) {
skip = false
console.log(`Resuming at file ${relativePath}...`)
} else {
continue
}
}
const filePath = path.join(dataDir, relativePath)
if (!filePath.endsWith(".xml")) {
console.info(`Skipping non XML file at ${filePath}`)
continue
}
try {
const xmlString: string = await fs.readFile(filePath, {
encoding: "utf8",
})
const xmlData = xmlParser.parse(xmlString)
for (const [key, element] of Object.entries(xmlData) as [
string,
DossierLegislatif | XmlHeader,
][]) {
switch (key) {
case "?xml": {
const xmlHeader = element as XmlHeader
assert.strictEqual(xmlHeader["@encoding"], "UTF-8", filePath)
assert.strictEqual(xmlHeader["@version"], "1.0", filePath)
break
}
case "DOSSIER_LEGISLATIF": {
const dossierLegislatif = element as DossierLegislatif
await db`
INSERT INTO dossier_legislatif (
id,
data
) VALUES (
${dossierLegislatif.META.META_COMMUN.ID},
${db.json(dossierLegislatif as unknown as JSONValue)}
)
ON CONFLICT (id)
DO UPDATE SET
data = ${db.json(dossierLegislatif as unknown as JSONValue)}
`
dossierLegislatifRemainingIds.delete(
dossierLegislatif.META.META_COMMUN.ID,
)
break
}
default: {
console.warn(
`Unexpected root element "${key}" in XML file: ${filePath}`,
)
break iterXmlFiles
}
}
}
} catch (e) {
console.error("An error occurred while parsing XML file", filePath)
throw e
}
}
if (deleteRemainingIds) {
for (const id of dossierLegislatifRemainingIds) {
console.log(`Deleting DOSSIER_LEGISLATIF ${id}`)
await db`
DELETE FROM dossier_legislatif
WHERE id = ${id}
`
}
}
}
sade("import_dole", true)
.describe("Import Dila's DOLE database")
.option("-r", "--resume", "Resume import at given relative file path")
.example(
"--resume dole/global/JORF/DOLE/00/00/36/07/36/JORFDOLE000036073697.xml",
)
.action(async (options) => {
await importDole(options)
process.exit(0)
})
.parse(process.argv)

View file

@ -0,0 +1,301 @@
import { auditChain, auditRequire, strictAudit } from "@auditors/core"
import assert from "assert"
import { XMLParser } from "fast-xml-parser"
import fs from "fs-extra"
import he from "he"
import path from "path"
import type { JSONValue } from "postgres"
import sade from "sade"
import type {
Article,
Idcc,
SectionTa,
Textekali,
Textelr,
TexteVersion,
XmlHeader,
} from "$lib/data"
import { db } from "$lib/server/database"
import { walkDir } from "$lib/server/file_systems"
const xmlParser = new XMLParser({
attributeNamePrefix: "@",
ignoreAttributes: false,
stopNodes: ["ARTICLE.BLOC_TEXTUEL.CONTENU", "ARTICLE.SM.CONTENU"],
tagValueProcessor: (_tagName, tagValue) => he.decode(tagValue),
})
async function importKali({ resume }: { resume?: string } = {}): Promise<void> {
let skip = resume !== undefined
const deleteRemainingIds = !skip
const articleRemainingIds = new Set(
(
await db<{ id: string }[]>`
SELECT id
FROM article
WHERE id LIKE 'KALI%'
`
).map(({ id }) => id),
)
const idccRemainingIds = new Set(
(
await db<{ id: string }[]>`
SELECT id
FROM idcc
`
).map(({ id }) => id),
)
const sectionTaRemainingIds = new Set(
(
await db<{ id: string }[]>`
SELECT id
FROM section_ta
WHERE id LIKE 'KALI%'
`
).map(({ id }) => id),
)
const textekaliRemainingIds = new Set(
(
await db<{ id: string }[]>`
SELECT id
FROM textekali
`
).map(({ id }) => id),
)
const textelrRemainingIds = new Set(
(
await db<{ id: string }[]>`
SELECT id
FROM textelr
WHERE id LIKE 'KALI%'
`
).map(({ id }) => id),
)
const texteVersionRemainingIds = new Set(
(
await db<{ id: string }[]>`
SELECT id
FROM texte_version
WHERE id LIKE 'KALI%'
`
).map(({ id }) => id),
)
const dataDir = path.join("..", "dila-data", "kali")
assert(await fs.pathExists(dataDir))
iterXmlFiles: for (const relativeSplitPath of walkDir(dataDir)) {
const relativePath = path.join(...relativeSplitPath)
if (skip) {
if (relativePath.startsWith(resume!)) {
skip = false
console.log(`Resuming at file ${relativePath}...`)
} else {
continue
}
}
const filePath = path.join(dataDir, relativePath)
if (!filePath.endsWith(".xml")) {
console.info(`Skipping non XML file at ${filePath}`)
continue
}
try {
const xmlString: string = await fs.readFile(filePath, {
encoding: "utf8",
})
const xmlData = xmlParser.parse(xmlString)
for (const [key, element] of Object.entries(xmlData) as [
string,
(
| Article
| Idcc
| SectionTa
| Textekali
| Textelr
| TexteVersion
| XmlHeader
),
][]) {
switch (key) {
case "?xml": {
const xmlHeader = element as XmlHeader
assert.strictEqual(xmlHeader["@encoding"], "UTF-8", filePath)
assert.strictEqual(xmlHeader["@version"], "1.0", filePath)
break
}
case "ARTICLE": {
const article = element as Article
await db`
INSERT INTO article (
id,
data
) VALUES (
${article.META.META_COMMUN.ID},
${db.json(article as unknown as JSONValue)}
)
ON CONFLICT (id)
DO UPDATE SET
data = ${db.json(article as unknown as JSONValue)}
`
articleRemainingIds.delete(article.META.META_COMMUN.ID)
break
}
case "IDCC": {
const idcc = element as Idcc
await db`
INSERT INTO idcc (
id,
data
) VALUES (
${idcc.META.META_COMMUN.ID},
${db.json(idcc as unknown as JSONValue)}
)
ON CONFLICT (id)
DO UPDATE SET
data = ${db.json(idcc as unknown as JSONValue)}
`
idccRemainingIds.delete(idcc.META.META_COMMUN.ID)
break
}
case "SECTION_TA": {
const section = element as SectionTa
await db`
INSERT INTO section_ta (
id,
data
) VALUES (
${section.ID},
${db.json(section as unknown as JSONValue)}
)
ON CONFLICT (id)
DO UPDATE SET
data = ${db.json(section as unknown as JSONValue)}
`
sectionTaRemainingIds.delete(section.ID)
break
}
case "TEXTE_VERSION": {
const version = element as TexteVersion
await db`
INSERT INTO texte_version (
id,
data
) VALUES (
${version.META.META_COMMUN.ID},
${db.json(version as unknown as JSONValue)}
)
ON CONFLICT (id)
DO UPDATE SET
data = ${db.json(version as unknown as JSONValue)}
`
texteVersionRemainingIds.delete(version.META.META_COMMUN.ID)
break
}
case "TEXTEKALI": {
const textekali = element as Textekali
await db`
INSERT INTO textekali (
id,
data
) VALUES (
${textekali.META.META_COMMUN.ID},
${db.json(textekali as unknown as JSONValue)}
)
ON CONFLICT (id)
DO UPDATE SET
data = ${db.json(textekali as unknown as JSONValue)}
`
textekaliRemainingIds.delete(textekali.META.META_COMMUN.ID)
break
}
case "TEXTELR": {
const textelr = element as Textelr
await db`
INSERT INTO textelr (
id,
data
) VALUES (
${textelr.META.META_COMMUN.ID},
${db.json(textelr as unknown as JSONValue)}
)
ON CONFLICT (id)
DO UPDATE SET
data = ${db.json(textelr as unknown as JSONValue)}
`
textelrRemainingIds.delete(textelr.META.META_COMMUN.ID)
break
}
default: {
console.warn(
`Unexpected root element "${key}" in XML file: ${filePath}`,
)
break iterXmlFiles
}
}
}
} catch (e) {
console.error("An error occurred while parsing XML file", filePath)
throw e
}
}
if (deleteRemainingIds) {
for (const id of articleRemainingIds) {
console.log(`Deleting ARTICLE ${id}`)
await db`
DELETE FROM article
WHERE id = ${id}
`
}
for (const id of idccRemainingIds) {
console.log(`Deleting IDCC ${id}`)
await db`
DELETE FROM idcc
WHERE id = ${id}
`
}
for (const id of sectionTaRemainingIds) {
console.log(`Deleting SECTION_TA ${id}`)
await db`
DELETE FROM section_ta
WHERE id = ${id}
`
}
for (const id of textekaliRemainingIds) {
console.log(`Deleting TEXTEKALI ${id}`)
await db`
DELETE FROM textekali
WHERE id = ${id}
`
}
for (const id of textelrRemainingIds) {
console.log(`Deleting TEXTELR ${id}`)
await db`
DELETE FROM textelr
WHERE id = ${id}
`
}
for (const id of texteVersionRemainingIds) {
console.log(`Deleting TEXTE_VERSION ${id}`)
await db`
DELETE FROM texte_version
WHERE id = ${id}
`
}
}
}
sade("import_kali", true)
.describe("Import Dila's KALI database")
.option("-r", "--resume", "Resume import at given relative file path")
.example(
"--resume global/conteneur/KALI/CONT/00/00/05/63/50/KALICONT000005635082.xml",
)
.action(async (options) => {
await importKali(options)
process.exit(0)
})
.parse(process.argv)

View file

@ -80,6 +80,14 @@ export async function configureDatabase() {
)
`
// Table: idcc
await db`
CREATE TABLE IF NOT EXISTS idcc (
id char(20) PRIMARY KEY,
data jsonb NOT NULL
)
`
// Table: jo
await db`
CREATE TABLE IF NOT EXISTS jo (
@ -104,6 +112,14 @@ export async function configureDatabase() {
)
`
// Table: textekali
await db`
CREATE TABLE IF NOT EXISTS textekali (
id char(20) PRIMARY KEY,
data jsonb NOT NULL
)
`
// Table: textelr
await db`
CREATE TABLE IF NOT EXISTS textelr (

View file

@ -5,6 +5,7 @@ import {
type Article,
assertNeverLegalObjectType,
bestItemForDate,
type Idcc,
type IdWrapper,
type Jo,
type LegalObject,
@ -12,6 +13,7 @@ import {
pathnameFromLegalObject,
pathnameFromLegalObjectId,
type SectionTa,
type Textekali,
type Textelr,
type TexteVersion,
type LienArt,
@ -107,6 +109,17 @@ export const summarizeDossierLegislatifProperties: Summarizer = (
return undefined
}
export const summarizeIdccProperties: Summarizer = (access, value) => {
if (access?.key === "idcc" && typeof value !== "number") {
return summarizeLegalObject(access, "idcc", value)
}
if (typeof access?.key === "number" && access?.access?.key === "idcc") {
return summarizeLegalObjectToLink(access, "idcc", value)
}
return undefined
}
export const summarizeIdWrapperProperties: Summarizer = (access, value) => {
if (access?.key === "id" && typeof value !== "number") {
return summarizeLegalObject(access, "id", value)
@ -182,6 +195,10 @@ export function summarizeLegalObject(
case "id":
const idWrapper = value as IdWrapper | undefined
return idWrapper?.eli
case "idcc": {
const idcc = value as Idcc | undefined
return idcc?.META.META_SPEC.META_CONTENEUR.TITRE
}
case "jo": {
const jo = value as Jo | undefined
return jo?.META.META_SPEC.META_CONTENEUR.TITRE
@ -200,6 +217,10 @@ export function summarizeLegalObject(
const texteVersion = value as TexteVersion | undefined
return texteVersion?.META.META_SPEC.META_TEXTE_VERSION.TITREFULL
}
case "textekali": {
const textekali = value as Textekali | undefined
return textekali?.META.META_COMMUN.ID
}
case "textelr": {
const textelr = value as Textelr | undefined
return textelr?.META.META_COMMUN.ID
@ -282,6 +303,17 @@ export const summarizeSectionTaProperties: Summarizer = (access, value) => {
return undefined
}
export const summarizeTextekaliProperties: Summarizer = (access, value) => {
if (access?.key === "textekali" && typeof value !== "number") {
return summarizeLegalObject(access, "textekali", value)
}
if (typeof access?.key === "number" && access?.access?.key === "textekali") {
return summarizeLegalObjectToLink(access, "textekali", value)
}
return undefined
}
export const summarizeTextelrProperties: Summarizer = (access, value) => {
if (access?.key === "textelr" && typeof value !== "number") {
return summarizeLegalObject(access, "textelr", value)

View file

@ -32,9 +32,11 @@
{ href: "/article", label: "ARTICLE" },
{ href: "/dossier_legislatif", label: "DOSSIER_LEGISLATIF" },
{ href: "/id", label: "ID" },
{ href: "/idcc", label: "IDCC" },
{ href: "/jo", label: "JO" },
{ href: "/section_ta", label: "SECTION_TA" },
{ href: "/texte_version", label: "TEXTE_VERSION" },
{ href: "/textekali", label: "TEXTEKALI" },
{ href: "/textelr", label: "TEXTELR" },
{ href: "/versions", label: "VERSIONS" },
],

View file

@ -0,0 +1,43 @@
<script lang="ts">
import { TreeView, SummaryView } from "augmented-data-viewer"
import ErrorAlert from "$lib/components/errors/ErrorAlert.svelte"
import type { DossierLegislatif } from "$lib/data"
import {
summarizeDossierLegislatifProperties,
summarizeLegalObject,
} from "$lib/summaries"
export let error: unknown
let dossierLegislatif: DossierLegislatif
export { dossierLegislatif as dossier_legislatif }
const summary = summarizeLegalObject(
{ key: "dossier_legislatif" },
"dossier_legislatif",
dossierLegislatif,
)
</script>
<header class="prose my-6 max-w-full">
<h2>DOSSIER_LEGISLATIF</h2>
{#if summary !== undefined}
<h1>
<SummaryView {summary} />
</h1>
{/if}
</header>
{#if error != null}
<ErrorAlert {error} />
{/if}
{#if error == null}
<TreeView
access={{ key: "dossier_legislatif" }}
frame={false}
open
summarize={summarizeDossierLegislatifProperties}
value={dossierLegislatif}
/>
{/if}

View file

@ -0,0 +1,22 @@
import type { RequestHandler } from "@sveltejs/kit"
import type { JSONObject } from "@sveltejs/kit/types/private"
import type { DossierLegislatif } from "$lib/data"
import { db } from "$lib/server/database"
export const GET: RequestHandler = async ({ params }) => {
const dossierLegislatif = (
await db<{ data: DossierLegislatif }[]>`
SELECT data FROM dossier_legislatif
WHERE id = ${params.id}
`
).map(({ data }) => data)[0]
if (dossierLegislatif === undefined) {
return { headers: { "Access-Control-Allow-Origin": "*" }, status: 404 }
}
return {
headers: { "Access-Control-Allow-Origin": "*" },
body: { dossier_legislatif: dossierLegislatif as unknown as JSONObject },
}
}

View file

@ -0,0 +1,68 @@
<script lang="ts">
// import Icon from "@iconify/svelte"
// import searchIcon from "@iconify-icons/codicon/search"
import { TreeView } from "augmented-data-viewer"
// import { page } from "$app/stores"
import ErrorAlert from "$lib/components/errors/ErrorAlert.svelte"
import Pagination from "$lib/components/Pagination.svelte"
import type { DossierLegislatif } from "$lib/data"
import { summarizeDossierLegislatifProperties } from "$lib/summaries"
let dossiersLegislatifs: DossierLegislatif[]
export { dossiersLegislatifs as dossier_legislatif }
export let error: unknown
</script>
<header class="prose my-6 max-w-full">
<h1>DOSSIER_LEGISLATIF</h1>
</header>
<!-- <form action={$page.url.pathname} class="mx-auto max-w-sm" method="get">
<div class="form-control">
<div class="input-group">
<input
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>
</div>
</form> -->
{#if error != null}
<ErrorAlert {error} />
{/if}
{#if error == null}
<TreeView
access={{ key: "dossier_legislatif" }}
frame={false}
open
summarize={summarizeDossierLegislatifProperties}
value={dossiersLegislatifs}
/>
<Pagination currentPageCount={dossiersLegislatifs.length ?? 0} />
{/if}

View file

@ -0,0 +1,74 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import type { RequestHandler } from "@sveltejs/kit"
import type { JSONObject } from "@sveltejs/kit/types/private"
import { auditSearchQueryContent } from "$lib/auditors/queries"
import type { DossierLegislatif } from "$lib/data"
import { db } from "$lib/server/database"
export function auditQuery(
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))
auditSearchQueryContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
}
export const GET: RequestHandler = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
{ 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)}`,
)
return {
// status: 400,
headers: { "Access-Control-Allow-Origin": "*" },
body: {
error: {
query: queryError as unknown as JSONObject,
},
},
}
}
const { limit, offset } = query
const dossiersLegislatifs = (
await db<{ data: DossierLegislatif }[]>`
SELECT data FROM dossier_legislatif
OFFSET ${offset}
LIMIT ${limit}
`
).map(({ data }) => data)
return {
headers: { "Access-Control-Allow-Origin": "*" },
body: {
dossier_legislatif: dossiersLegislatifs as unknown as JSONObject[],
},
}
}

View file

@ -0,0 +1,35 @@
<script lang="ts">
import { TreeView, SummaryView } from "augmented-data-viewer"
import ErrorAlert from "$lib/components/errors/ErrorAlert.svelte"
import type { Idcc } from "$lib/data"
import { summarizeIdccProperties, summarizeLegalObject } from "$lib/summaries"
export let error: unknown
export let idcc: Idcc
const summary = summarizeLegalObject({ key: "idcc" }, "idcc", idcc)
</script>
<header class="prose my-6 max-w-full">
<h2>IDCC</h2>
{#if summary !== undefined}
<h1>
<SummaryView {summary} />
</h1>
{/if}
</header>
{#if error != null}
<ErrorAlert {error} />
{/if}
{#if error == null}
<TreeView
access={{ key: "idcc" }}
frame={false}
open
summarize={summarizeIdccProperties}
value={idcc}
/>
{/if}

22
src/routes/idcc/[id].ts Normal file
View file

@ -0,0 +1,22 @@
import type { RequestHandler } from "@sveltejs/kit"
import type { JSONObject } from "@sveltejs/kit/types/private"
import type { Idcc } from "$lib/data"
import { db } from "$lib/server/database"
export const GET: RequestHandler = async ({ params }) => {
const idcc = (
await db<{ data: Idcc }[]>`
SELECT data FROM idcc
WHERE id = ${params.id}
`
).map(({ data }) => data)[0]
if (idcc === undefined) {
return { headers: { "Access-Control-Allow-Origin": "*" }, status: 404 }
}
return {
headers: { "Access-Control-Allow-Origin": "*" },
body: { idcc: idcc as unknown as JSONObject },
}
}

View file

@ -0,0 +1,68 @@
<script lang="ts">
// import Icon from "@iconify/svelte"
// import searchIcon from "@iconify-icons/codicon/search"
import { TreeView } from "augmented-data-viewer"
// import { page } from "$app/stores"
import ErrorAlert from "$lib/components/errors/ErrorAlert.svelte"
import Pagination from "$lib/components/Pagination.svelte"
import type { Idcc } from "$lib/data"
import { summarizeIdccProperties } from "$lib/summaries"
export let error: unknown
let idccArray: Idcc[]
export { idccArray as idcc }
</script>
<header class="prose my-6 max-w-full">
<h1>IDCC</h1>
</header>
<!-- <form action={$page.url.pathname} class="mx-auto max-w-sm" method="get">
<div class="form-control">
<div class="input-group">
<input
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>
</div>
</form> -->
{#if error != null}
<ErrorAlert {error} />
{/if}
{#if error == null}
<TreeView
access={{ key: "idcc" }}
frame={false}
open
summarize={summarizeIdccProperties}
value={idccArray}
/>
<Pagination currentPageCount={idccArray.length ?? 0} />
{/if}

72
src/routes/idcc/index.ts Normal file
View file

@ -0,0 +1,72 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import type { RequestHandler } from "@sveltejs/kit"
import type { JSONObject } from "@sveltejs/kit/types/private"
import { auditSearchQueryContent } from "$lib/auditors/queries"
import type { Idcc } from "$lib/data"
import { db } from "$lib/server/database"
export function auditQuery(
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))
auditSearchQueryContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
}
export const GET: RequestHandler = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
{ 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)}`,
)
return {
// status: 400,
headers: { "Access-Control-Allow-Origin": "*" },
body: {
error: {
query: queryError as unknown as JSONObject,
},
},
}
}
const { limit, offset } = query
const idccArray = (
await db<{ data: Idcc }[]>`
SELECT data FROM idcc
OFFSET ${offset}
LIMIT ${limit}
`
).map(({ data }) => data)
return {
headers: { "Access-Control-Allow-Origin": "*" },
body: { idcc: idccArray as unknown as JSONObject[] },
}
}

View file

@ -0,0 +1,42 @@
<script lang="ts">
import { TreeView, SummaryView } from "augmented-data-viewer"
import ErrorAlert from "$lib/components/errors/ErrorAlert.svelte"
import type { Textekali } from "$lib/data"
import {
summarizeTextekaliProperties,
summarizeLegalObject,
} from "$lib/summaries"
export let error: unknown
export let textekali: Textekali
const summary = summarizeLegalObject(
{ key: "textekali" },
"textekali",
textekali,
)
</script>
<header class="prose my-6 max-w-full">
<h2>TEXTEKALI</h2>
{#if summary !== undefined}
<h1>
<SummaryView {summary} />
</h1>
{/if}
</header>
{#if error != null}
<ErrorAlert {error} />
{/if}
{#if error == null}
<TreeView
access={{ key: "textekali" }}
frame={false}
open
summarize={summarizeTextekaliProperties}
value={textekali}
/>
{/if}

View file

@ -0,0 +1,22 @@
import type { RequestHandler } from "@sveltejs/kit"
import type { JSONObject } from "@sveltejs/kit/types/private"
import type { Textekali } from "$lib/data"
import { db } from "$lib/server/database"
export const GET: RequestHandler = async ({ params }) => {
const textekali = (
await db<{ data: Textekali }[]>`
SELECT data FROM textekali
WHERE id = ${params.id}
`
).map(({ data }) => data)[0]
if (textekali === undefined) {
return { headers: { "Access-Control-Allow-Origin": "*" }, status: 404 }
}
return {
headers: { "Access-Control-Allow-Origin": "*" },
body: { textekali: textekali as unknown as JSONObject },
}
}

View file

@ -0,0 +1,68 @@
<script lang="ts">
// import Icon from "@iconify/svelte"
// import searchIcon from "@iconify-icons/codicon/search"
import { TreeView } from "augmented-data-viewer"
// import { page } from "$app/stores"
import ErrorAlert from "$lib/components/errors/ErrorAlert.svelte"
import Pagination from "$lib/components/Pagination.svelte"
import type { Textekali } from "$lib/data"
import { summarizeTextekaliProperties } from "$lib/summaries"
export let error: unknown
let textekaliArray: Textekali[]
export { textekaliArray as textekali }
</script>
<header class="prose my-6 max-w-full">
<h1>TEXTEKALI</h1>
</header>
<!-- <form action={$page.url.pathname} class="mx-auto max-w-sm" method="get">
<div class="form-control">
<div class="input-group">
<input
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>
</div>
</form> -->
{#if error != null}
<ErrorAlert {error} />
{/if}
{#if error == null}
<TreeView
access={{ key: "textekali" }}
frame={false}
open
summarize={summarizeTextekaliProperties}
value={textekaliArray}
/>
<Pagination currentPageCount={textekaliArray.length ?? 0} />
{/if}

View file

@ -0,0 +1,72 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import type { RequestHandler } from "@sveltejs/kit"
import type { JSONObject } from "@sveltejs/kit/types/private"
import { auditSearchQueryContent } from "$lib/auditors/queries"
import type { Textekali } from "$lib/data"
import { db } from "$lib/server/database"
export function auditQuery(
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))
auditSearchQueryContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
}
export const GET: RequestHandler = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
{ 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)}`,
)
return {
// status: 400,
headers: { "Access-Control-Allow-Origin": "*" },
body: {
error: {
query: queryError as unknown as JSONObject,
},
},
}
}
const { limit, offset } = query
const textekaliArray = (
await db<{ data: Textekali }[]>`
SELECT data FROM textekali
OFFSET ${offset}
LIMIT ${limit}
`
).map(({ data }) => data)
return {
headers: { "Access-Control-Allow-Origin": "*" },
body: { textekali: textekaliArray as unknown as JSONObject[] },
}
}