Améliorations du module de recherche de code APE

pull/2524/head
Johan Girod 2023-02-28 11:17:27 +01:00 committed by Jérémy Rialland
parent 0a7d3a04ad
commit 27cfe59840
14 changed files with 10800 additions and 665061 deletions

View File

@ -1,3 +1,3 @@
## Description
Assemble les données des codes APE (en `données-NAF-CPF-APE`) et les données du nombre d'établissements par code APE (dans `nombre-etablissements-par-code-ape-et-departement`) afin d'obtenir les codes APE ainsi que leurs données associées triables par département et par nombre d'établissements en 2021.
Assemble les données des codes APE (dans `données-NAF-CPF-APE`), les descriptions des nomenclatures du Guichet (dans `données-NomenclatureGuichet`) et le nombre détablissements par code APE (dans `nombre-etablissements-par-code-ape`) dans un seul fichier json.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -5,8 +5,8 @@ import { fileURLToPath } from 'url'
import { Data as RawApeData } from '../données-NAF-CPF-APE/convert-pdf.js'
import rawApeData from '../données-NAF-CPF-APE/output.json' assert { type: 'json' }
import rawApeTags from '../données-NomenclatureGuichet/ape_tags.json' assert { type: 'json' }
import { Out as EtablissementsData } from '../nombre-etablissements-par-code-ape-et-departement/convert-json.js'
import rawEtablissementsData from '../nombre-etablissements-par-code-ape-et-departement/output.json' assert { type: 'json' }
import { Out as NbEtablissementsData } from '../nombre-etablissements-par-code-ape/convert-json.js'
import rawEtablissementsData from '../nombre-etablissements-par-code-ape/output.json' assert { type: 'json' }
import { multipleCf } from './custom.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
@ -15,7 +15,7 @@ const OUTPUT_JSON_PATH = join(__dirname, './output.json')
const OUTPUT_MIN_JSON_PATH = join(__dirname, './output.min.json')
const apeData = rawApeData as RawApeData[]
const etablissementsData = rawEtablissementsData as EtablissementsData
const etablissementsData = rawEtablissementsData as NbEtablissementsData
const apeTags = rawApeTags as Record<string, string[]>
interface ApeData {
@ -27,8 +27,6 @@ interface ApeData {
contenuExclu: string[]
}
type NbEtablissement2021Index = number
export interface Output {
/**
* Données textuel pour chaque code APE
@ -36,15 +34,9 @@ export interface Output {
apeData: ApeData[]
/**
* Nombre d'établissement par département et par code APE,
* l'index de ce tableau correspond au index dans indexByCodeApe et indexByCodeDepartement.
* Cela permet de trouver le nombre d'établissement en 2021 avec un couple code APE + code d'un département.
* Nombre d'établissement par code APE,
*/
nbEtablissements2021: number[]
indexByCodeApe: { [codeAPE: string]: NbEtablissement2021Index[] }
indexByCodeDepartement: {
[codeDepartement: string]: NbEtablissement2021Index[]
}
indexByCodeApe: NbEtablissementsData
}
const sousClasses = apeData.filter(({ type }) => type === 'sousClasse')
@ -103,11 +95,8 @@ const output: Output = {
}
}
),
nbEtablissements2021: etablissementsData.data.map(
({ nombre_d_etablissements_2021: nbEtablissements }) => nbEtablissements
),
indexByCodeApe: etablissementsData.indexByCodeApe,
indexByCodeDepartement: etablissementsData.indexByCodeDepartement,
indexByCodeApe: etablissementsData,
}
addContenuExcluToContenuCentral.forEach(([i, j, index]) => {
@ -131,5 +120,26 @@ Object.values(multipleCf).forEach((obj) => {
})
})
output.apeData = output.apeData.map(
({ contenuCentral, contenuAnnexe, contenuExclu, ...rest }) => ({
contenuAnnexe: contenuAnnexe.map(
(text) => text[0].toUpperCase() + text.slice(1)
),
contenuExclu: contenuExclu.map(
(text) => text[0].toUpperCase() + text.slice(1)
),
contenuCentral: contenuCentral.reduce((acc, text) => {
if (text.startsWith('•')) {
acc[acc.length - 1] = acc[acc.length - 1] + '\n' + text.slice(2)
} else {
acc.push(text[0].toUpperCase() + text.slice(1))
}
return acc
}, [] as string[]),
...rest,
})
)
writeFileSync(OUTPUT_JSON_PATH, JSON.stringify(output, null, 2))
writeFileSync(OUTPUT_MIN_JSON_PATH, JSON.stringify(output))

View File

@ -1,4 +0,0 @@
## Description
Convertit les données du nombre d'établissements employeurs du secteur privé, par commune x APE (2006-2021) pour obtenir le nombre d'établissements en 2021 par code APE ou par département.
Les données sont fournies par Open URSSAF et sont disponibles à cette adresse : https://open.urssaf.fr/explore/dataset/etablissements-et-effectifs-salaries-au-niveau-commune-x-ape-last/

View File

@ -1,158 +0,0 @@
/* eslint-disable no-console */
/* eslint-disable camelcase */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { writeFileSync } from 'fs'
import got from 'got'
import { Writable } from 'node:stream'
import { join } from 'path'
import streamJson from 'stream-json'
import streamPick from 'stream-json/filters/Pick.js'
import streamValues from 'stream-json/streamers/StreamValues.js'
import { fileURLToPath } from 'url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const OUTPUT_JSON_PATH = './output.json'
const url =
'https://open.urssaf.fr/explore/dataset/etablissements-et-effectifs-salaries-au-niveau-commune-x-ape-last/download/?format=json&timezone=Europe/Berlin&lang=fr'
export interface Data {
nombre_d_etablissements_2021: number
code_ape: string
ape: string
code_departement: string
departement: string
region: string
}
export interface Out {
data: Data[]
indexByCodeApe: { [codeAPE: string]: number[] }
indexByCodeDepartement: { [codeDepartement: string]: number[] }
}
const out: Out = {
data: [],
indexByCodeApe: {},
indexByCodeDepartement: {},
}
const count = { code_ape: 0, code_departement: 0, total: 0 }
const stream = got
.stream(url)
.pipe(streamJson.parser())
.pipe(streamPick.pick({ filter: /^\d+\.fields/ }))
.pipe(streamValues.streamValues())
.pipe(
new Writable({
objectMode: true,
write(data: { value: Data }, _, cb) {
const {
nombre_d_etablissements_2021,
code_ape,
ape,
code_departement,
departement,
region,
} = data.value
const elem: Data | null =
nombre_d_etablissements_2021 > 0
? {
nombre_d_etablissements_2021,
code_ape,
ape,
code_departement,
departement,
region,
}
: null
if (!elem) {
return cb(null)
}
++count.total
const log = [
'[elements parsed]:',
count.total,
'[element added]:',
out.data.length,
'[estimated percentage done]:',
// 730 codes ape * 100 départements
Math.round((out.data.length * 100) / (730 * 100)).toString() + '%',
]
if (!(elem.code_ape in out.indexByCodeApe)) {
console.log(
'[new ape code]:',
elem.code_ape,
'[count]:',
++count.code_ape,
...log
)
}
if (!(elem.code_departement in out.indexByCodeDepartement)) {
console.log(
'[new departement code]:',
elem.code_departement,
'[count]:',
++count.code_departement,
...log
)
}
const actualAPE = out.indexByCodeApe[elem.code_ape] ?? []
const actualDep =
out.indexByCodeDepartement[elem.code_departement] ?? []
const small =
actualAPE.length < actualDep.length ? actualAPE : actualDep
const large =
actualAPE.length > actualDep.length ? actualAPE : actualDep
let index = small.find(
(a) =>
large.includes(a) &&
elem.code_ape === out.data[a].code_ape &&
elem.code_departement === out.data[a].code_departement
)
if (typeof index === 'undefined') {
index = out.data.length
out.data.push(elem)
out.indexByCodeApe[elem.code_ape] ??= []
out.indexByCodeDepartement[elem.code_departement] ??= []
} else {
out.data[index].nombre_d_etablissements_2021 +=
elem.nombre_d_etablissements_2021
}
if (!out.indexByCodeApe[elem.code_ape].includes(index)) {
out.indexByCodeApe[elem.code_ape].push(index)
}
if (
!out.indexByCodeDepartement[elem.code_departement].includes(index)
) {
out.indexByCodeDepartement[elem.code_departement].push(index)
}
cb(null)
},
})
)
stream.on('error', (err: Error) => {
console.error(err)
throw err
})
stream.on('close', () => {
console.log('close')
writeFileSync(join(__dirname, OUTPUT_JSON_PATH), JSON.stringify(out, null, 1))
})
console.log('conversion in progress...')

View File

@ -0,0 +1,37 @@
/* eslint-disable no-console */
/* eslint-disable camelcase */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { writeFileSync } from 'fs'
import { join } from 'path'
import { fileURLToPath } from 'url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const OUTPUT_JSON_PATH = './output.json'
const url =
'https://open.urssaf.fr/api/explore/v2.1/catalog/datasets/nombre-detablissements-employeurs-et-effectifs-salaries-du-secteur-prive-france-/exports/json?lang=fr&timezone=Europe%2FBerlin'
export interface Data {
nombre_d_etablissements_2021: number
code_ape: string
}
export interface Out {
[codeAPE: string]: number
}
const response = await fetch(url)
if (!response.ok) {
throw new Error(`Error while fetching data : ${response.status}`)
}
const json: Array<Data> = await response.json()
const out = json.reduce((acc, { code_ape, nombre_d_etablissements_2021 }) => {
acc[code_ape] = nombre_d_etablissements_2021 ?? 0
return acc
}, {} as Out)
writeFileSync(join(__dirname, OUTPUT_JSON_PATH), JSON.stringify(out, null, 1))

View File

@ -0,0 +1,689 @@
{
"0610Z": 12,
"0811Z": 453,
"0891Z": 13,
"0990Z": 23,
"1013B": 1949,
"1031Z": 59,
"1042Z": 4,
"1051B": 11,
"1061A": 318,
"1071B": 1440,
"1071C": 27062,
"1072Z": 589,
"1081Z": 22,
"1083Z": 513,
"1092Z": 81,
"1101Z": 400,
"1102A": 138,
"1104Z": 28,
"1107A": 97,
"1107B": 120,
"1310Z": 125,
"1411Z": 39,
"1419Z": 417,
"1420Z": 16,
"1623Z": 1438,
"1629Z": 492,
"1721C": 63,
"1812Z": 2904,
"1813Z": 1707,
"1820Z": 43,
"1910Z": 0,
"2013A": 2,
"2020Z": 78,
"2030Z": 340,
"2041Z": 282,
"2042Z": 821,
"2059Z": 281,
"2110Z": 90,
"2311Z": 19,
"2314Z": 12,
"2319Z": 175,
"2320Z": 50,
"2342Z": 16,
"2343Z": 9,
"2363Z": 1617,
"2410Z": 91,
"2420Z": 131,
"2432Z": 7,
"2441Z": 11,
"2445Z": 55,
"2452Z": 32,
"2454Z": 108,
"2512Z": 1130,
"2521Z": 46,
"2529Z": 108,
"2550B": 1017,
"2561Z": 1615,
"2562A": 413,
"2573B": 468,
"2591Z": 10,
"2594Z": 112,
"2612Z": 325,
"2712Z": 457,
"2720Z": 58,
"2751Z": 82,
"2790Z": 374,
"2813Z": 166,
"2814Z": 141,
"2829A": 184,
"2893Z": 321,
"2899B": 486,
"2910Z": 194,
"3011Z": 152,
"3030Z": 264,
"3040Z": 5,
"3109A": 131,
"3109B": 1846,
"3212Z": 951,
"3213Z": 422,
"3220Z": 197,
"3250B": 257,
"3312Z": 4649,
"3320A": 2357,
"3320C": 837,
"3513Z": 1277,
"3521Z": 57,
"3522Z": 323,
"3530Z": 354,
"3700Z": 1127,
"3821Z": 807,
"3822Z": 189,
"4110D": 379,
"4212Z": 167,
"4213B": 14,
"4222Z": 1403,
"4299Z": 910,
"4311Z": 608,
"4312B": 922,
"4321A": 26565,
"4322B": 13318,
"4329B": 2237,
"4391B": 9189,
"4399B": 1532,
"4399E": 551,
"4519Z": 1171,
"4520B": 1635,
"4531Z": 3389,
"4532Z": 4693,
"4540Z": 3288,
"4616Z": 381,
"4617A": 220,
"4617B": 747,
"4622Z": 484,
"4624Z": 86,
"4631Z": 2207,
"4632B": 178,
"4642Z": 3375,
"4644Z": 672,
"4646Z": 2423,
"4647Z": 860,
"4651Z": 2457,
"4661Z": 3625,
"4669A": 3139,
"4672Z": 1163,
"4675Z": 1420,
"4711C": 3981,
"4719A": 164,
"4721Z": 3677,
"4722Z": 11091,
"4724Z": 3247,
"4725Z": 4096,
"4759B": 5135,
"4761Z": 2421,
"4762Z": 4712,
"4772B": 1461,
"4773Z": 20468,
"4778A": 12498,
"4778B": 975,
"4791A": 2072,
"4920Z": 259,
"4931Z": 896,
"4932Z": 10016,
"4939B": 1525,
"4939C": 158,
"4941C": 721,
"4950Z": 200,
"5010Z": 460,
"5020Z": 124,
"5040Z": 412,
"5110Z": 446,
"5210A": 411,
"5222Z": 435,
"5224A": 153,
"5520Z": 5445,
"5610A": 72133,
"5610B": 540,
"5630Z": 22336,
"5812Z": 22,
"5819Z": 428,
"5821Z": 227,
"5911B": 2427,
"6202A": 16764,
"6391Z": 342,
"6399Z": 115,
"6491Z": 99,
"6520Z": 30,
"6611Z": 29,
"6612Z": 280,
"6622Z": 15304,
"6831Z": 25649,
"6832B": 321,
"6910Z": 21884,
"6920Z": 18376,
"7010Z": 15285,
"7111Z": 10202,
"7732Z": 2728,
"7740Z": 397,
"8020Z": 1513,
"8121Z": 12323,
"8211Z": 4596,
"8423Z": 129,
"8551Z": 3073,
"8559B": 5079,
"8623Z": 19948,
"8690B": 3837,
"8720B": 127,
"8730B": 266,
"8810C": 1933,
"9101Z": 197,
"9313Z": 3048,
"9321Z": 1086,
"9491Z": 3809,
"9601A": 381,
"9601B": 3213,
"9602A": 39046,
"9609Z": 4611,
"5920Z": 1508,
"6020B": 107,
"6110Z": 1853,
"6411Z": 125,
"6430Z": 2703,
"7420Z": 1657,
"7500Z": 5294,
"7712Z": 530,
"7722Z": 12,
"7729Z": 2000,
"7733Z": 166,
"7734Z": 60,
"8010Z": 4456,
"8110Z": 46556,
"8122Z": 3741,
"8129A": 912,
"8292Z": 895,
"8422Z": 0,
"8424Z": 1,
"8430C": 284,
"8553Z": 7459,
"8610Z": 2644,
"8622C": 9645,
"8690A": 5163,
"8690E": 6682,
"8690F": 1248,
"8710B": 1345,
"8730A": 1348,
"8810B": 1057,
"9104Z": 213,
"9200Z": 400,
"9311Z": 2561,
"9319Z": 1937,
"9523Z": 924,
"9604Z": 2292,
"0721Z": 0,
"0729Z": 58,
"0812Z": 1493,
"0892Z": 16,
"0893Z": 11,
"0899Z": 27,
"1012Z": 248,
"1013A": 615,
"1032Z": 73,
"1039A": 222,
"1039B": 318,
"1041A": 93,
"1051C": 488,
"1062Z": 12,
"1071A": 639,
"1085Z": 618,
"1102B": 114,
"1103Z": 40,
"1106Z": 17,
"1200Z": 9,
"1320Z": 183,
"1391Z": 36,
"1393Z": 29,
"1395Z": 35,
"1414Z": 129,
"1511Z": 68,
"1512Z": 675,
"1520Z": 191,
"1610A": 553,
"1621Z": 83,
"1624Z": 736,
"1712Z": 126,
"1722Z": 50,
"1723Z": 92,
"1729Z": 290,
"1811Z": 43,
"2013B": 89,
"2016Z": 170,
"2017Z": 13,
"2051Z": 44,
"2120Z": 416,
"2221Z": 330,
"2312Z": 328,
"2332Z": 105,
"2341Z": 222,
"2344Z": 25,
"2351Z": 76,
"2362Z": 33,
"2364Z": 78,
"2365Z": 9,
"2391Z": 25,
"2399Z": 438,
"2434Z": 22,
"2443Z": 17,
"2444Z": 27,
"2451Z": 59,
"2453Z": 105,
"2511Z": 3035,
"2540Z": 56,
"2562B": 4754,
"2571Z": 131,
"2593Z": 391,
"2630Z": 318,
"2640Z": 121,
"2651A": 93,
"2651B": 671,
"2680Z": 3,
"2731Z": 22,
"2733Z": 182,
"2811Z": 81,
"2812Z": 353,
"2815Z": 121,
"2821Z": 75,
"2822Z": 579,
"2823Z": 19,
"2824Z": 7,
"2894Z": 48,
"2895Z": 28,
"2896Z": 40,
"2931Z": 91,
"2932Z": 489,
"3091Z": 41,
"3099Z": 16,
"3101Z": 597,
"3102Z": 429,
"3211Z": 9,
"3250A": 3995,
"3299Z": 613,
"3314Z": 871,
"3317Z": 75,
"3320B": 1915,
"3514Z": 159,
"3831Z": 302,
"4110A": 3042,
"4110B": 90,
"4120A": 10811,
"4120B": 5451,
"4211Z": 1652,
"4213A": 129,
"4312A": 9358,
"4331Z": 10243,
"4332A": 19800,
"4332B": 8199,
"4334Z": 19091,
"4399D": 3410,
"4511Z": 16799,
"4612B": 180,
"4615Z": 250,
"4623Z": 791,
"4633Z": 443,
"4637Z": 276,
"4639B": 1992,
"4641Z": 1000,
"4643Z": 711,
"4648Z": 355,
"4649Z": 4750,
"4662Z": 504,
"4663Z": 1128,
"4664Z": 56,
"4665Z": 426,
"4666Z": 1058,
"4669C": 2997,
"4673A": 9169,
"4673B": 2537,
"4674A": 1631,
"4674B": 2077,
"4690Z": 5822,
"4711D": 10917,
"4711E": 179,
"4730Z": 3219,
"4752B": 2719,
"4753Z": 675,
"4759A": 7848,
"4764Z": 6902,
"4771Z": 32956,
"4772A": 5799,
"4775Z": 5395,
"4776Z": 8653,
"4778C": 13276,
"4779Z": 2294,
"4791B": 3497,
"4799B": 1201,
"4910Z": 1605,
"4941B": 14212,
"4942Z": 1394,
"5030Z": 131,
"5122Z": 3,
"5210B": 3437,
"5223Z": 389,
"5229A": 1063,
"5510Z": 17258,
"5530Z": 3069,
"5590Z": 924,
"5629A": 4189,
"5811Z": 1064,
"5813Z": 971,
"5814Z": 1565,
"5829C": 3223,
"5911A": 2981,
"5911C": 1839,
"5912Z": 682,
"5913A": 156,
"5913B": 69,
"6020A": 138,
"6130Z": 40,
"6190Z": 1530,
"6201Z": 11176,
"6312Z": 1252,
"6419Z": 18075,
"6492Z": 946,
"6499Z": 307,
"6511Z": 405,
"6512Z": 5486,
"6619A": 11053,
"6810Z": 3800,
"6820A": 5512,
"6832A": 5997,
"7021Z": 4513,
"7022Z": 37967,
"7112A": 1788,
"7112B": 25181,
"7312Z": 1436,
"7410Z": 3995,
"7490A": 1894,
"7490B": 4982,
"7711A": 2756,
"7721Z": 1292,
"7735Z": 46,
"7739Z": 1560,
"7820Z": 20983,
"7830Z": 2372,
"7912Z": 650,
"7990Z": 1757,
"8030Z": 104,
"8129B": 713,
"8130Z": 710,
"8219Z": 9332,
"8220Z": 751,
"8230Z": 2938,
"8291Z": 268,
"8299Z": 8692,
"8411Z": 44,
"8412Z": 346,
"8430A": 1304,
"8430B": 184,
"8531Z": 1632,
"8532Z": 944,
"8541Z": 42,
"8552Z": 3125,
"8622B": 1880,
"8720A": 2132,
"8790A": 1832,
"8899A": 751,
"9003B": 568,
"9103Z": 582,
"9312Z": 26234,
"9411Z": 2483,
"9412Z": 1668,
"9499Z": 33416,
"9521Z": 410,
"9522Z": 855,
"9525Z": 257,
"9529Z": 1699,
"9603Z": 5165,
"0510Z": 0,
"0620Z": 0,
"0910Z": 10,
"1011Z": 861,
"1020Z": 324,
"1041B": 42,
"1051A": 126,
"1051D": 60,
"1052Z": 321,
"1061B": 61,
"1071D": 2944,
"1073Z": 173,
"1082Z": 1133,
"1084Z": 114,
"1086Z": 122,
"1089Z": 658,
"1091Z": 321,
"1105Z": 875,
"1330Z": 197,
"1392Z": 904,
"1394Z": 52,
"1396Z": 188,
"1399Z": 278,
"1412Z": 70,
"1413Z": 1350,
"1431Z": 29,
"1439Z": 59,
"1610B": 377,
"1622Z": 19,
"1711Z": 8,
"1721A": 239,
"1721B": 292,
"1724Z": 9,
"1814Z": 137,
"1920Z": 61,
"2011Z": 124,
"2012Z": 37,
"2014Z": 216,
"2015Z": 114,
"2052Z": 44,
"2053Z": 145,
"2060Z": 9,
"2211Z": 39,
"2219Z": 381,
"2222Z": 555,
"2223Z": 655,
"2229A": 1011,
"2229B": 507,
"2313Z": 148,
"2331Z": 51,
"2349Z": 20,
"2352Z": 41,
"2361Z": 769,
"2369Z": 121,
"2370Z": 1349,
"2431Z": 12,
"2433Z": 171,
"2442Z": 67,
"2446Z": 10,
"2530Z": 23,
"2550A": 347,
"2572Z": 231,
"2573A": 378,
"2592Z": 90,
"2599A": 78,
"2599B": 787,
"2611Z": 302,
"2620Z": 154,
"2652Z": 82,
"2660Z": 112,
"2670Z": 119,
"2711Z": 204,
"2732Z": 113,
"2740Z": 443,
"2752Z": 54,
"2825Z": 656,
"2829B": 414,
"2830Z": 476,
"2841Z": 157,
"2849Z": 86,
"2891Z": 17,
"2892Z": 122,
"2899A": 38,
"2920Z": 1059,
"3012Z": 232,
"3020Z": 47,
"3092Z": 96,
"3103Z": 125,
"3230Z": 260,
"3240Z": 148,
"3291Z": 57,
"3311Z": 631,
"3313Z": 380,
"3315Z": 1012,
"3316Z": 234,
"3319Z": 272,
"3320D": 946,
"3511Z": 1396,
"3512Z": 184,
"3523Z": 96,
"3600Z": 894,
"3811Z": 1225,
"3812Z": 223,
"3832Z": 2349,
"3900Z": 527,
"4110C": 452,
"4221Z": 921,
"4291Z": 153,
"4313Z": 325,
"4321B": 217,
"4322A": 16449,
"4329A": 3687,
"4332C": 1145,
"4333Z": 8561,
"4339Z": 2688,
"4391A": 5756,
"4399A": 3039,
"4399C": 39891,
"4520A": 32910,
"4611Z": 217,
"4612A": 5,
"4613Z": 547,
"4614Z": 494,
"4618Z": 1968,
"4619A": 393,
"4619B": 3277,
"4621Z": 1638,
"4632A": 492,
"4632C": 146,
"4634Z": 3796,
"4635Z": 33,
"4636Z": 270,
"4638A": 672,
"4638B": 1506,
"4639A": 469,
"4645Z": 1819,
"4652Z": 2134,
"4669B": 8308,
"4671Z": 1007,
"4676Z": 1228,
"4677Z": 439,
"4711A": 1644,
"4711B": 13548,
"4711F": 1877,
"4719B": 3817,
"4723Z": 1482,
"4726Z": 4328,
"4729Z": 7495,
"4741Z": 2627,
"4742Z": 2766,
"4743Z": 645,
"4751Z": 2290,
"4752A": 3796,
"4754Z": 2225,
"4763Z": 132,
"4765Z": 1496,
"4774Z": 5815,
"4777Z": 4851,
"4781Z": 6120,
"4782Z": 412,
"4789Z": 1470,
"4799A": 1111,
"4939A": 1532,
"4941A": 11550,
"5121Z": 28,
"5221Z": 3827,
"5224B": 353,
"5229B": 4004,
"5310Z": 3648,
"5320Z": 616,
"5610C": 46970,
"5621Z": 4279,
"5629B": 5630,
"5829A": 563,
"5829B": 140,
"5914Z": 1125,
"6120Z": 389,
"6202B": 712,
"6203Z": 671,
"6209Z": 890,
"6420Z": 24740,
"6530Z": 5,
"6619B": 3792,
"6621Z": 1273,
"7311Z": 7301,
"7320Z": 824,
"7430Z": 545,
"7731Z": 87,
"7810Z": 821,
"8425Z": 24,
"8520Z": 4009,
"8559A": 11331,
"8621Z": 15537,
"8690C": 12,
"8690D": 4441,
"8790B": 2256,
"8810A": 8069,
"8891A": 10445,
"8891B": 1117,
"9002Z": 2513,
"9003A": 575,
"9102Z": 341,
"9329Z": 6593,
"9492Z": 194,
"9511Z": 1398,
"9524Z": 373,
"9602B": 11604,
"6010Z": 1027,
"6311Z": 2469,
"6629Z": 286,
"6630Z": 5215,
"6820B": 11485,
"7120A": 5771,
"7120B": 5258,
"7211Z": 826,
"7219Z": 2018,
"7220Z": 305,
"7711B": 188,
"7911Z": 4222,
"8413Z": 837,
"8421Z": 2,
"8510Z": 173,
"8542Z": 1216,
"8560Z": 364,
"8622A": 869,
"8710A": 3585,
"8710C": 825,
"8899B": 9438,
"9001Z": 16964,
"9004Z": 878,
"9420Z": 2237,
"9512Z": 456
}

View File

@ -12,19 +12,16 @@
"scripts": {
"start:données-NAF-CPF-APE": "ts-node-esm ./codeAPESearch/données-NAF-CPF-APE/convert-pdf.ts",
"start:données-NomenclatureGuichet": "ts-node-esm ./codeAPESearch/données-NomenclatureGuichet/convert.ts",
"start:nombre-etablissements-par-code-ape-et-departement": "ts-node-esm ./codeAPESearch/nombre-etablissements-par-code-ape-et-departement/convert-json.ts",
"start:nombre-etablissements-par-code-ape": "ts-node-esm ./codeAPESearch/nombre-etablissements-par-code-ape/convert-json.ts",
"start:données-code-APE": "ts-node-esm ./codeAPESearch/données-code-APE/reduce-json.ts",
"start:watch:données-NAF-CPF-APE": "nodemon -x 'yarn start:données-NAF-CPF-APE' -e 'ts'",
"start:watch:nombre-etablissements-par-code-ape-et-departement": "nodemon -x 'yarn start:nombre-etablissements-par-code-ape-et-departement' -e 'ts'",
"start:watch:nombre-etablissements-par-code-ape": "nodemon -x 'yarn start:nombre-etablissements-par-code-ape' -e 'ts'",
"start:watch:données-code-APE": "nodemon -x 'yarn start:données-code-APE' -e 'ts'"
},
"dependencies": {
"@types/stream-json": "^1.7.3",
"csv": "^6.2.7",
"csv-parser": "^3.0.0",
"got": "^12.5.3",
"pdfdataextract": "^3.2.0",
"stream-json": "^1.7.5",
"ts-node": "^10.9.1"
},
"devDependencies": {

View File

@ -1,7 +1,9 @@
import { AriaRadioProps } from '@react-types/radio'
import styled from 'styled-components'
import { ComponentProps } from 'react'
import styled, { css } from 'styled-components'
import { Markdown } from '@/components/utils/markdown'
import { CardContainer } from '@/design-system/card/Card'
import { Emoji } from '@/design-system/emoji'
import { LabelBody, RadioPoint, RadioSkeleton, VisibleRadio } from './Radio'
@ -17,18 +19,29 @@ const StyledRadioPoint = styled(RadioPoint)`
margin-top: 0.2rem;
`
export const StyledRadioSkeleton = styled(RadioSkeleton)`
flex: 1 100%;
margin-bottom: ${({ theme }) => theme.spacings.xs};
export const StyledRadioSkeleton = ({
children,
...rest
}: ComponentProps<typeof RadioSkeleton>) => (
<StyledCardContainer $inert={rest.isDisabled} as={RadioSkeleton} {...rest}>
{children}
</StyledCardContainer>
)
const StyledCardContainer = styled(CardContainer)`
${VisibleRadio} {
border-radius: var(--radius) !important;
display: flex;
flex: 1 100%;
border-width: 0px;
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25);
padding: ${({ theme }) => theme.spacings.sm};
padding: 0;
margin: 0;
width: 100%;
background: transparent;
border: none;
}
${({ theme, $inert }) =>
$inert &&
css`
background-color: ${theme.colors.extended.grey[200]};
`}
margin: 0.5rem 0;
`
type RadioCardProps = AriaRadioProps & {

View File

@ -0,0 +1,112 @@
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled, { css } from 'styled-components'
import { Appear } from '@/components/ui/animate'
import { Chip } from '@/design-system'
import InfoBulle from '@/design-system/InfoBulle'
import { Button } from '@/design-system/buttons'
import { ChevronIcon } from '@/design-system/icons'
import { Grid } from '@/design-system/layout'
import { H3, H4 } from '@/design-system/typography/heading'
import { Li, Ul } from '@/design-system/typography/list'
interface ResultProps {
debug: string | null
item: {
title: string
codeApe: string
contenuCentral: string[]
contenuAnnexe: string[]
contenuExclu: string[]
}
}
export const Result = ({ item, debug }: ResultProps) => {
const { title, codeApe, contenuCentral, contenuAnnexe, contenuExclu } = item
const [open, setOpen] = useState(false)
const { t } = useTranslation()
return (
<Grid container style={{ alignItems: 'center' }}>
<Grid item xs={12} sm={8} md={9} xl={10}>
<H3 style={{ marginTop: 0, marginBottom: '.5rem' }}>
{title} <Chip type="secondary">{codeApe}</Chip>
{debug && (
<InfoBulle>
<pre>{debug}</pre>
</InfoBulle>
)}
</H3>
</Grid>
<StyledGrid item xs={12} sm={4} md={3} xl={2}>
<Button
light
size="XXS"
onPress={() => setOpen((x) => !x)}
aria-expanded={open}
aria-controls={`info-${codeApe}`}
aria-label={!open ? t('En savoir plus') : t('Replier')}
>
{!open ? t('En savoir plus') : t('Replier')}{' '}
<StyledChevron aria-hidden $isOpen={open} />
</Button>
</StyledGrid>
{open && (
<Appear id={`info-${codeApe}`}>
{contenuCentral.length ? (
<>
<H4>Contenu central de cette activité :</H4>
<Ul>
{contenuCentral.map((contenu, i) => (
<Li key={i}>{contenu}</Li>
))}
</Ul>
</>
) : null}
{contenuAnnexe.length ? (
<>
<H4>Contenu annexe de cette activité :</H4>
<Ul>
{contenuAnnexe.map((contenu, i) => (
<Li key={i}>{contenu}</Li>
))}
</Ul>
</>
) : null}
{contenuExclu.length ? (
<>
<H4>Contenu exclu de cette activité :</H4>
<Ul>
{contenuExclu.map((contenu, i) => (
<Li key={i}>{contenu}</Li>
))}
</Ul>
</>
) : null}
</Appear>
)}
</Grid>
)
}
const StyledGrid = styled(Grid)`
display: flex;
justify-content: end;
align-items: center;
`
const StyledChevron = styled(ChevronIcon)<{ $isOpen: boolean }>`
vertical-align: middle;
transform: rotate(-90deg);
transition: transform 0.3s;
${({ $isOpen }) =>
!$isOpen &&
css`
transform: rotate(90deg);
`}
`

View File

@ -1,26 +1,23 @@
import UFuzzy from '@leeoniya/ufuzzy'
import { Fragment, useEffect, useMemo, useRef, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import styled, { css } from 'styled-components'
import styled from 'styled-components'
import FeedbackForm from '@/components/Feedback/FeedbackForm'
import { FromTop } from '@/components/ui/animate'
import {
Chip,
PopoverWithTrigger,
RadioCardGroup,
TextField,
SearchField,
} from '@/design-system'
import { Button } from '@/design-system/buttons'
import { Emoji } from '@/design-system/emoji'
import { StyledRadioSkeleton } from '@/design-system/field/Radio/RadioCard'
import { ChevronIcon } from '@/design-system/icons'
import { Grid, Spacing } from '@/design-system/layout'
import { H3 } from '@/design-system/typography/heading'
import { Body } from '@/design-system/typography/paragraphs'
import { useAsyncData } from '@/hooks/useAsyncData'
import { Output as Data } from '../../../../../scripts/codeAPESearch/données-code-APE/reduce-json'
import { Result } from './Result'
interface SearchableData {
original: string[]
@ -67,142 +64,6 @@ const buildResearch = (lazyData: Data | null) => {
}
}
const nbEtablissementParDepartement = (
data: Data,
department: string,
code: string
) => {
const { indexByCodeApe, indexByCodeDepartement, nbEtablissements2021 } = data
const index =
indexByCodeApe[code] &&
indexByCodeDepartement[department]?.find((i) =>
indexByCodeApe[code].includes(i)
)
return typeof index === 'number'
? nbEtablissements2021[index]
: indexByCodeApe[code]?.reduce(
(acc, index) => acc + nbEtablissements2021[index],
0
) ?? 0
}
interface ResultProps {
debug: string | null
item: {
title: string
codeApe: string
contenuCentral: string[]
contenuAnnexe: string[]
contenuExclu: string[]
}
}
const Result = ({ item, debug }: ResultProps) => {
const { title, codeApe, contenuCentral, contenuAnnexe, contenuExclu } = item
const [open, setOpen] = useState(false)
const { t } = useTranslation()
return (
<Grid container style={{ alignItems: 'center' }}>
<Grid item xs={12} sm={8} md={9} xl={10}>
<H3 style={{ marginTop: 0, marginBottom: '.5rem' }}>
{title}
<Chip type="secondary">APE: {codeApe}</Chip>
</H3>
{debug && <pre>{debug}</pre>}
</Grid>
<StyledGrid item xs={12} sm={4} md={3} xl={2}>
<Button
size="XXS"
light
onPress={() => setOpen((x) => !x)}
aria-label={!open ? t('En savoir plus') : t('Replier')}
aria-expanded={open}
aria-controls={`info-${codeApe}`}
>
{!open ? t('En savoir plus') : t('Replier')}{' '}
<StyledChevron aria-hidden $isOpen={open} />
</Button>
</StyledGrid>
{open && (
<FromTop id={`info-${codeApe}`}>
<Grid item xs={12}>
<p style={{ marginTop: 0 }}>
{contenuCentral.length ? (
<>
<span style={{ fontWeight: 'bold' }}>
Contenu central de cette activité :
</span>
<br />
</>
) : null}
{contenuCentral.map((contenu, i) => (
<Fragment key={i}>
{contenu}
<br />
</Fragment>
))}
{contenuCentral.length ? <br /> : null}
{contenuAnnexe.length ? (
<>
<span style={{ fontWeight: 'bold' }}>
Contenu annexe de cette activité :
</span>
<br />
</>
) : null}
{contenuAnnexe.map((contenu, i) => (
<Fragment key={i}>
{contenu}
<br />
</Fragment>
))}
{contenuAnnexe.length ? <br /> : null}
{contenuExclu.length ? (
<>
<span style={{ fontWeight: 'bold' }}>
Contenu exclu de cette activité :
</span>
<br />
</>
) : null}
{contenuExclu.map((contenu, i) => (
<Fragment key={i}>
{contenu}
<br />
</Fragment>
))}
</p>
</Grid>
</FromTop>
)}
</Grid>
)
}
const StyledGrid = styled(Grid)`
display: flex;
justify-content: end;
`
const StyledChevron = styled(ChevronIcon)<{ $isOpen: boolean }>`
vertical-align: middle;
transform: rotate(-90deg);
transition: transform 0.3s;
${({ $isOpen }) =>
!$isOpen &&
css`
transform: rotate(90deg);
`}
`
interface ListResult {
item: Data['apeData'][0]
score: number
@ -216,11 +77,10 @@ interface SearchCodeApeProps {
export default function SearchCodeAPE({ disabled }: SearchCodeApeProps) {
const { t } = useTranslation()
const [job, setJob] = useState('')
const [department, setDepartment] = useState('')
const [selected, setSelected] = useState('')
const [list, setList] = useState<ListResult[]>([])
const lazyData = useAsyncData(
const lazyData: Data | null = useAsyncData(
() =>
import(
'../../../../../scripts/codeAPESearch/données-code-APE/output.min.json'
@ -267,11 +127,11 @@ export default function SearchCodeAPE({ disabled }: SearchCodeApeProps) {
const genericLatin = search(genericList.latinized, latinizedValue)
/**
* Calcul le score final avec 3/4 * le score de la recherche + 1/4 * le score en fonction
* du nombre de création d'entreprise par code APE et departement en 2021
* Calcul le score final avec 2/3 * le score de la recherche + 1/3 * le score en fonction
* du nombre de création d'entreprise par code APE en 2021
*/
const computeScore = (scoreFuzzy: number, scoreNbEtablissement: number) =>
(3 / 4) * scoreFuzzy + (1 / 4) * scoreNbEtablissement
(2 / 3) * scoreFuzzy + (1 / 3) * scoreNbEtablissement
const results = [
...new Set([
@ -284,11 +144,7 @@ export default function SearchCodeAPE({ disabled }: SearchCodeApeProps) {
.map((item, index, arr) => ({
item,
scoreFuzzy: index / arr.length,
nbEtablissement: nbEtablissementParDepartement(
lazyData,
department,
item.codeApe.replace('.', '')
),
nbEtablissement: lazyData.indexByCodeApe[item.codeApe.replace('.', '')],
}))
.sort(({ nbEtablissement: a }, { nbEtablissement: b }) => b - a)
.map(({ item, scoreFuzzy, nbEtablissement }, index, arr) => {
@ -310,63 +166,51 @@ export default function SearchCodeAPE({ disabled }: SearchCodeApeProps) {
setList(results)
prevValue.current = job
}, [buildedResearch, department, job, lazyData])
}, [buildedResearch, job, lazyData])
const ret = (
<Body as="div">
<Grid container>
<Grid item xs={4}>
<TextField
value={department}
onChange={setDepartment}
label={t('Département')}
/>
</Grid>
<Grid item xs={8}>
<TextField
value={job}
onChange={setJob}
label={t('Votre activité')}
/>
</Grid>
<Grid container>
<Grid item lg={12} xl={10}>
<SearchField
value={job}
onChange={setJob}
label={t("Mots-clés définissants l'activité")}
placeholder={t('Par exemple : coiffure, boulangerie ou restauration')}
/>
<Spacing xs />
{list.length > 0 && (
<FromTop>
<StyledRadioCardGroup
value={selected}
onChange={setSelected}
isDisabled={disabled}
>
{list.slice(0, 25).map(({ item, debug }) => {
return (
<StyledRadioSkeleton
isDisabled={disabled}
value={item.codeApe}
key={item.codeApe}
visibleRadioAs="div"
>
<Result item={item} debug={debug} />
</StyledRadioSkeleton>
)
})}
</StyledRadioCardGroup>
</FromTop>
)}
<Spacing md />
<ActivityNotFound />
</Grid>
{IS_DEVELOPMENT && (
<>
[selected: <span>{disabled ? 'disabled' : selected || 'none'}</span>]
</>
)}
<StyledRadioCardGroup
value={selected}
onChange={setSelected}
isDisabled={disabled}
>
<Grid container>
{list.slice(0, 25).map(({ item, debug }) => {
return (
<StyledRadioSkeleton
value={item.codeApe}
key={item.codeApe}
visibleRadioAs="div"
>
<Result item={item} debug={debug} />
</StyledRadioSkeleton>
)
})}
</Grid>
</StyledRadioCardGroup>
<Spacing sm />
<ActivityNotFound />
</Body>
</Grid>
)
return ret
}
const StyledRadioCardGroup = styled(RadioCardGroup)`
margin-top: 1rem;
flex-direction: row;
flex-wrap: wrap;
`

View File

@ -9149,25 +9149,6 @@ __metadata:
languageName: node
linkType: hard
"@types/stream-chain@npm:*":
version: 2.0.1
resolution: "@types/stream-chain@npm:2.0.1"
dependencies:
"@types/node": "*"
checksum: 6a59da0374a74ad6aff48c669c72d3060d428d1015af94b00d290ede68706781a8419daf490af7935f0f9f2796da6965aead74ad1795f72c488630451241db55
languageName: node
linkType: hard
"@types/stream-json@npm:^1.7.3":
version: 1.7.3
resolution: "@types/stream-json@npm:1.7.3"
dependencies:
"@types/node": "*"
"@types/stream-chain": "*"
checksum: cafa47c27bbd0cf446d1c90442cf53240a8a8a3828e73ba456720d6612b03d32d04067c9c0e54105b94584f37c7ed2f1a941b0600fbc6c095c25c1b401d184bb
languageName: node
linkType: hard
"@types/styled-components@npm:^5.1.26":
version: 5.1.26
resolution: "@types/styled-components@npm:5.1.26"
@ -26255,13 +26236,10 @@ __metadata:
version: 0.0.0-use.local
resolution: "scripts@workspace:scripts"
dependencies:
"@types/stream-json": ^1.7.3
csv: ^6.2.7
csv-parser: ^3.0.0
got: ^12.5.3
nodemon: ^2.0.20
pdfdataextract: ^3.2.0
stream-json: ^1.7.5
ts-node: ^10.9.1
languageName: unknown
linkType: soft
@ -27228,13 +27206,6 @@ __metadata:
languageName: node
linkType: hard
"stream-chain@npm:^2.2.5":
version: 2.2.5
resolution: "stream-chain@npm:2.2.5"
checksum: c83cbf504bd11e2bcbe761a92801295b3decac7ffa4092ceffca2eb1b5d0763bcc511fa22cd8044e8a18c21ca66794fd10c8d9cd1292a3e6c0d83a4194c6b8ed
languageName: node
linkType: hard
"stream-each@npm:^1.1.0":
version: 1.2.3
resolution: "stream-each@npm:1.2.3"
@ -27258,15 +27229,6 @@ __metadata:
languageName: node
linkType: hard
"stream-json@npm:^1.7.5":
version: 1.7.5
resolution: "stream-json@npm:1.7.5"
dependencies:
stream-chain: ^2.2.5
checksum: d6229cd0342cbfb2f0d4be2b289cd2fe283aaa808329ec5c925b9840fc694fa91400f10bbf3275ac39c039f314fe6ab844d380341e738654ce224482f266ad2b
languageName: node
linkType: hard
"stream-shift@npm:^1.0.0":
version: 1.0.1
resolution: "stream-shift@npm:1.0.1"