Commit 2edafb02 authored by Emmanuel Raviart's avatar Emmanuel Raviart
Browse files

Rename stock to security.

parent 1d42f894
...@@ -15,7 +15,7 @@ Edit `src/server-config.js` to change database informations. Then ...@@ -15,7 +15,7 @@ Edit `src/server-config.js` to change database informations. Then
npm install npm install
``` ```
## Retrieve or generate JSON files of issuers, stocks, etc ## Retrieve or generate JSON files of issuers, securities, etc
### Either retrieve JSON files from their git repository ### Either retrieve JSON files from their git repository
...@@ -28,7 +28,7 @@ git clone https://gitlab.huma-num.fr/eurhisfirm/dfih-open-data.git data ...@@ -28,7 +28,7 @@ git clone https://gitlab.huma-num.fr/eurhisfirm/dfih-open-data.git data
```bash ```bash
node_modules/.bin/babel-node scripts/generate-issuers.js node_modules/.bin/babel-node scripts/generate-issuers.js
node_modules/.bin/babel-node scripts/generate-stock-exchanges.js node_modules/.bin/babel-node scripts/generate-stock-exchanges.js
node_modules/.bin/babel-node scripts/generate-stocks.js node_modules/.bin/babel-node scripts/generate-securities.js
``` ```
## Launch server ## Launch server
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<ul> <ul>
{#each rows as row (row[0])} {#each rows as row (row[0])}
<li> <li>
<a id="{row[2]}" href="/stocks/{row[0]}/prices" data-container="body" data-trigger="manual" data-toggle="popover" <a id="{row[2]}" href="/securities/{row[0]}/prices" data-container="body" data-trigger="manual" data-toggle="popover"
data-html="true" data-placement="top" data-content="Loading price data..." on:mouseenter="popoverHover(event)" data-html="true" data-placement="top" data-content="Loading price data..." on:mouseenter="popoverHover(event)"
on:mouseleave="popoverLeave(event)">{row[1]}</a> on:mouseleave="popoverLeave(event)">{row[1]}</a>
</li> </li>
......
...@@ -3,7 +3,7 @@ import oracledb from "oracledb" ...@@ -3,7 +3,7 @@ import oracledb from "oracledb"
import { pool } from "../../../../database" import { pool } from "../../../../database"
export async function get(req, res) { export async function get(req, res) {
const { stock: stockId } = req.params const { security: securityId } = req.params
const connection = await pool.getConnection() const connection = await pool.getConnection()
let result = null let result = null
...@@ -68,11 +68,11 @@ export async function get(req, res) { ...@@ -68,11 +68,11 @@ export async function get(req, res) {
(case when extract(day from np.day) < 10 or extract(day from np.day) > 20 (case when extract(day from np.day) < 10 or extract(day from np.day) > 20
then decode(np.setdate, 13, np.max) END) as Max_LiqFin1 then decode(np.setdate, 13, np.max) END) as Max_LiqFin1
from notation_price np from notation_price np
where np.notation in (select id from notation where stock = :stockId) where np.notation in (select id from notation where stock = :securityId)
and np.market = 1 and np.market = 1
) c ON (cd.exact_date = c.day) ) c ON (cd.exact_date = c.day)
where nvl(cd.exact_date, c.day) >= (select min(startdate) from notation where stock = :stockId) where nvl(cd.exact_date, c.day) >= (select min(startdate) from notation where stock = :securityId)
and nvl(cd.exact_date, c.day) <= (select max(enddate) from notation where stock = :stockId) and nvl(cd.exact_date, c.day) <= (select max(enddate) from notation where stock = :securityId)
) )
group by notation, day group by notation, day
) terme ) terme
...@@ -98,10 +98,10 @@ export async function get(req, res) { ...@@ -98,10 +98,10 @@ export async function get(req, res) {
select nep.day, nep.notation, select nep.day, nep.notation,
first_value(nep.price) first_value(nep.price)
over (partition by nep.day, nep.notation order by nep.id desc) as dernier_prix_comptant over (partition by nep.day, nep.notation order by nep.id desc) as dernier_prix_comptant
from notation_extra_price nep where nep.notation in (select id from notation where stock = :stockId) from notation_extra_price nep where nep.notation in (select id from notation where stock = :securityId)
) a ON (cd.exact_date = a.day) ) a ON (cd.exact_date = a.day)
where nvl(cd.exact_date, a.day) >= (select min(startdate) from notation where stock = :stockId) where nvl(cd.exact_date, a.day) >= (select min(startdate) from notation where stock = :securityId)
and nvl(cd.exact_date, a.day) <= (select max(enddate) from notation where stock = :stockId) and nvl(cd.exact_date, a.day) <= (select max(enddate) from notation where stock = :securityId)
) )
) pnep ) pnep
full outer join full outer join
...@@ -115,11 +115,11 @@ export async function get(req, res) { ...@@ -115,11 +115,11 @@ export async function get(req, res) {
( (
select np.day, np.notation, nvl(np.price_yesterday, np.previous) as dernier_prix_comptant select np.day, np.notation, nvl(np.price_yesterday, np.previous) as dernier_prix_comptant
from notation_price np from notation_price np
where np.notation in (select id from notation where stock = :stockId) where np.notation in (select id from notation where stock = :securityId)
and np.market = 2 and np.market = 2
) b ON (cd.exact_date = b.day) ) b ON (cd.exact_date = b.day)
where nvl(cd.exact_date, b.day) >= (select min(startdate) from notation where stock = :stockId) where nvl(cd.exact_date, b.day) >= (select min(startdate) from notation where stock = :securityId)
and nvl(cd.exact_date, b.day) <= (select max(enddate) from notation where stock = :stockId) and nvl(cd.exact_date, b.day) <= (select max(enddate) from notation where stock = :securityId)
) )
) pnp ) pnp
ON (pnep.notation = pnp.notation and pnep.day = pnp.day) ON (pnep.notation = pnp.notation and pnep.day = pnp.day)
...@@ -128,7 +128,7 @@ export async function get(req, res) { ...@@ -128,7 +128,7 @@ export async function get(req, res) {
order by comptant.day order by comptant.day
`, `,
{ {
stockId: { dir: oracledb.BIND_IN, val: stockId, type: oracledb.STRING }, securityId: { dir: oracledb.BIND_IN, val: securityId, type: oracledb.STRING },
} }
) )
} catch (error) { } catch (error) {
......
...@@ -4,9 +4,9 @@ import path from "path" ...@@ -4,9 +4,9 @@ import path from "path"
import serverConfig from "../../../../server-config" import serverConfig from "../../../../server-config"
export function get(req, res) { export function get(req, res) {
const { stock: stockId } = req.params const { security: securityId } = req.params
const stockFilePath = path.join(serverConfig.dataDir, "stocks", `stock_${stockId}.json`) const securityFilePath = path.join(serverConfig.dataDir, "securities", `security_${securityId}.json`)
if (!fs.existsSync(stockFilePath)) { if (!fs.existsSync(securityFilePath)) {
res.writeHead(404, { res.writeHead(404, {
"Content-Type": "application/json", "Content-Type": "application/json",
}) })
...@@ -15,7 +15,7 @@ export function get(req, res) { ...@@ -15,7 +15,7 @@ export function get(req, res) {
{ {
error: { error: {
code: 404, code: 404,
message: `Stock ${stockId} doesn't exist.`, message: `Security ${securityId} doesn't exist.`,
}, },
}, },
null, null,
...@@ -23,7 +23,7 @@ export function get(req, res) { ...@@ -23,7 +23,7 @@ export function get(req, res) {
) )
) )
} }
if (!fs.lstatSync(stockFilePath).isFile()) { if (!fs.lstatSync(securityFilePath).isFile()) {
res.writeHead(404, { res.writeHead(404, {
"Content-Type": "application/json", "Content-Type": "application/json",
}) })
...@@ -32,7 +32,7 @@ export function get(req, res) { ...@@ -32,7 +32,7 @@ export function get(req, res) {
{ {
error: { error: {
code: 404, code: 404,
message: `Stock ${stockId} doesn't exist (not a file).`, message: `Security ${securityId} doesn't exist (not a file).`,
}, },
}, },
null, null,
...@@ -43,5 +43,5 @@ export function get(req, res) { ...@@ -43,5 +43,5 @@ export function get(req, res) {
res.writeHead(200, { res.writeHead(200, {
"Content-Type": "application/json", "Content-Type": "application/json",
}) })
res.end(fs.readFileSync(stockFilePath)) res.end(fs.readFileSync(securityFilePath))
} }
...@@ -28,10 +28,10 @@ ...@@ -28,10 +28,10 @@
{/if} {/if}
</li> </li>
<li class="nav-item"> <li class="nav-item">
{#if child.segment === "stocks"} {#if child.segment === "securities"}
<a class="nav-link active" href="/issuers/{issuer.id}/stocks" rel="prefetch">Stocks <span class="sr-only">(current)</span></a> <a class="nav-link active" href="/issuers/{issuer.id}/securities" rel="prefetch">Securities <span class="sr-only">(current)</span></a>
{:else} {:else}
<a class="nav-link" href="/issuers/{issuer.id}/stocks" rel="prefetch">Stocks</a> <a class="nav-link" href="/issuers/{issuer.id}/securities" rel="prefetch">Securities</a>
{/if} {/if}
</li> </li>
<li class="nav-item"> <li class="nav-item">
......
...@@ -50,15 +50,15 @@ ...@@ -50,15 +50,15 @@
</div> </div>
{/if} {/if}
<!-- {#if issuer.stocks} <!-- {#if issuer.securities}
<div class="card my-3"> <div class="card my-3">
<div class="card-header"> <div class="card-header">
<h3 class="mb-0"> <h3 class="mb-0">
<button class="btn btn-light" type="button"> <button class="btn btn-light" type="button">
{#if Object.keys(issuer.stocks).length > 1} {#if Object.keys(issuer.securities).length > 1}
{Object.keys(issuer.stocks).length} stocks {Object.keys(issuer.securities).length} securities
{:else} {:else}
{Object.keys(issuer.stocks).length} stock {Object.keys(issuer.securities).length} stock
{/if} {/if}
</button> </button>
</h3> </h3>
......
<p class="my-4 text-muted">
{#if Object.keys(issuer.securities).length > 1}
{Object.keys(issuer.securities).length} results
{:else}
{Object.keys(issuer.securities).length} result
{/if}
</p>
<div class="list-group">
{#each Object.values(issuer.securities) as security (security.id)}
<a class="list-group-item list-group-item-action" href="/securities/{security.id}">
<div>{(security.name || "Titre sans nom").toUpperCase()}</div>
<div class="text-muted">{security.share_type.name}</div>
<div class="text-muted">{[...new Set(security.types.map(securityType => securityType.name))].sort().join(", ")}</div>
</a>
{/each}
</div>
<p class="my-4 text-muted">
{#if Object.keys(issuer.stocks).length > 1}
{Object.keys(issuer.stocks).length} results
{:else}
{Object.keys(issuer.stocks).length} result
{/if}
</p>
<div class="list-group">
{#each Object.values(issuer.stocks) as stock (stock.id)}
<a class="list-group-item list-group-item-action" href="/stocks/{stock.id}">
<div>{(stock.name || "Titre sans nom").toUpperCase()}</div>
<div class="text-muted">{stock.share_type.name}</div>
<div class="text-muted">{[...new Set(stock.types.map(stockType => stockType.name))].sort().join(", ")}</div>
</a>
{/each}
</div>
<svelte:head> <svelte:head>
<title>{stock.name} | Stocks | Eurhisfirm</title> <title>{security.name} | Securities | Eurhisfirm</title>
</svelte:head> </svelte:head>
<main class="container-fluid"> <main class="container-fluid">
<h1> <h1>
<span class="text-muted">Stock</span> {stock.name} <span class="text-muted">Security</span> {security.name}
<small class="text-muted"> <small class="text-muted">
{stock.share_type.name} {security.share_type.name}
/ /
{[...new Set(stock.types.map(stockType => stockType.name))].sort().join(", ")} {[...new Set(security.types.map(securityType => securityType.name))].sort().join(", ")}
</small> </small>
</h1> </h1>
...@@ -32,35 +32,35 @@ ...@@ -32,35 +32,35 @@
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="nav-item"> <li class="nav-item">
{#if child.segment === undefined} {#if child.segment === undefined}
<a class="nav-link active" href="/stocks/{stock.id}" rel="prefetch">Summary <span class="sr-only">(current)</span></a> <a class="nav-link active" href="/securities/{security.id}" rel="prefetch">Summary <span class="sr-only">(current)</span></a>
{:else} {:else}
<a class="nav-link" href="/stocks/{stock.id}" rel="prefetch">Summary</a> <a class="nav-link" href="/securities/{security.id}" rel="prefetch">Summary</a>
{/if} {/if}
</li> </li>
<li class="nav-item"> <li class="nav-item">
{#if child.segment === "dividends"} {#if child.segment === "dividends"}
<a class="nav-link active" href="/stocks/{stock.id}/dividends" rel="prefetch">Dividends <span class="sr-only">(current)</span></a> <a class="nav-link active" href="/securities/{security.id}/dividends" rel="prefetch">Dividends <span class="sr-only">(current)</span></a>
{:else} {:else}
<a class="nav-link" href="/stocks/{stock.id}/dividends" rel="prefetch">Dividends</a> <a class="nav-link" href="/securities/{security.id}/dividends" rel="prefetch">Dividends</a>
{/if} {/if}
</li> </li>
<li class="nav-item"> <li class="nav-item">
{#if child.segment === "prices"} {#if child.segment === "prices"}
<a class="nav-link active" href="/stocks/{stock.id}/prices" rel="prefetch">Prices <span class="sr-only">(current)</span></a> <a class="nav-link active" href="/securities/{security.id}/prices" rel="prefetch">Prices <span class="sr-only">(current)</span></a>
{:else} {:else}
<a class="nav-link" href="/stocks/{stock.id}/prices" rel="prefetch">Prices</a> <a class="nav-link" href="/securities/{security.id}/prices" rel="prefetch">Prices</a>
{/if} {/if}
</li> </li>
</ul> </ul>
<svelte:component this={child.component} {...child.props} {stock} /> <svelte:component this={child.component} {...child.props} {security} />
</main> </main>
<script> <script>
export default { export default {
computed: { computed: {
simplifiedIssuers({ stock }) { simplifiedIssuers({ security }) {
const simplifiedIssuerByIdAndName = stock.issuers.reduce((accumulator, issuer) => { const simplifiedIssuerByIdAndName = security.issuers.reduce((accumulator, issuer) => {
accumulator[`${issuer.name} ${issuer.id}`] = { accumulator[`${issuer.name} ${issuer.id}`] = {
id: issuer.id, id: issuer.id,
name: issuer.name, name: issuer.name,
...@@ -71,8 +71,8 @@ ...@@ -71,8 +71,8 @@
}, },
}, },
async preload({ params /* , query */ }) { async preload({ params /* , query */ }) {
const { stock: stockId } = params const { security: securityId } = params
const response = await this.fetch(`/api/static/stocks/${stockId}`) const response = await this.fetch(`/api/static/securities/${securityId}`)
const result = const result =
response.ok || response.status === 400 response.ok || response.status === 400
? await response.json() ? await response.json()
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
if (result.error) { if (result.error) {
return this.error(result.error.code, result.error.message) return this.error(result.error.code, result.error.message)
} }
return { stock: result } return { security: result }
}, },
} }
</script> </script>
\ No newline at end of file
<svelte:head> <svelte:head>
<title>Dividends | {stock.name} | Stocks | Eurhisfirm</title> <title>Dividends | {security.name} | Securities | Eurhisfirm</title>
</svelte:head> </svelte:head>
TODO TODO
{#each Object.values(stock.stock_exchanges || {}).sort(stockExchange => stockExchange.name) as stockExchange (`stock_exchange_${stockExchange.id}`)} {#each Object.values(security.stock_exchanges || {}).sort(stockExchange => stockExchange.name) as stockExchange (`stock_exchange_${stockExchange.id}`)}
<div class="card my-3"> <div class="card my-3">
<div class="card-header"> <div class="card-header">
Listed on <i>{stockExchange.name}</i> Listed on <i>{stockExchange.name}</i>
......
<svelte:head> <svelte:head>
<title>Prices | {stock.name} | Stocks | Eurhisfirm</title> <title>Prices | {security.name} | Securities | Eurhisfirm</title>
</svelte:head> </svelte:head>
<svelte:window on:resize="resize()"/> <svelte:window on:resize="resize()"/>
...@@ -112,8 +112,8 @@ ...@@ -112,8 +112,8 @@
) )
}, },
async preload({ params /* query */ }) { async preload({ params /* query */ }) {
const { stock: stockId } = params const { security: securityId } = params
const response = await this.fetch(`/api/stocks/${stockId}/prices`) const response = await this.fetch(`/api/securities/${securityId}/prices`)
const result = const result =
response.ok || response.status === 400 response.ok || response.status === 400
? await response.json() ? await response.json()
......
<svelte:head> <svelte:head>
<title>Prices | {stock.name} | Stocks | Eurhisfirm</title> <title>Prices | {security.name} | Securities | Eurhisfirm</title>
</svelte:head> </svelte:head>
<div id="container" style="height:800px; width:100%;"></div> <div id="container" style="height:800px; width:100%;"></div>
...@@ -112,8 +112,8 @@ ...@@ -112,8 +112,8 @@
}) })
}, },
async preload({ params /* query */ }) { async preload({ params /* query */ }) {
const { stock: stockId } = params const { security: securityId } = params
const response = await this.fetch(`/api/stocks/${stockId}/prices`) const response = await this.fetch(`/api/securities/${securityId}/prices`)
const result = const result =
response.ok || response.status === 400 response.ok || response.status === 400
? await response.json() ? await response.json()
......
...@@ -10,7 +10,7 @@ import { cleanUpLine, objectsFromSqlResult } from "../model" ...@@ -10,7 +10,7 @@ import { cleanUpLine, objectsFromSqlResult } from "../model"
const dataDir = "data" const dataDir = "data"
let pool = null let pool = null
async function generateStocks() { async function generateSecurities() {
const connection = await pool.getConnection() const connection = await pool.getConnection()
try { try {
console.log("Reading stock exchanges...") console.log("Reading stock exchanges...")
...@@ -44,8 +44,8 @@ async function generateStocks() { ...@@ -44,8 +44,8 @@ async function generateStocks() {
} }
} }
console.log("Loading stock types...") console.log("Loading security types...")
const stockTypeById = {} const securityTypeById = {}
{ {
const entries = objectsFromSqlResult( const entries = objectsFromSqlResult(
await connection.execute( await connection.execute(
...@@ -59,22 +59,22 @@ async function generateStocks() { ...@@ -59,22 +59,22 @@ async function generateStocks() {
) )
) )
for (let entry of entries) { for (let entry of entries) {
stockTypeById[entry.id] = entry securityTypeById[entry.id] = entry
} }
} }
const stocksDir = path.join(dataDir, "stocks") const securitiesDir = path.join(dataDir, "securities")
if (!fs.existsSync(stocksDir)) { if (!fs.existsSync(securitiesDir)) {
fs.mkdirSync(stocksDir) fs.mkdirSync(securitiesDir)
} }
const existingStocksFilenames = new Set( const existingSecuritiesFilenames = new Set(
fs.readdirSync(stocksDir).filter(filename => /^stock_[0-9]+\.json$/.test(filename)) fs.readdirSync(securitiesDir).filter(filename => /^security_[0-9]+\.json$/.test(filename))
) )
console.log("Loading stocks...") console.log("Loading securities...")
const issuersDir = path.join(dataDir, "issuers") const issuersDir = path.join(dataDir, "issuers")
assert(fs.existsSync(issuersDir), `Issuers directory not found: ${issuersDir}`) assert(fs.existsSync(issuersDir), `Issuers directory not found: ${issuersDir}`)
const stocks = objectsFromSqlResult( const securities = objectsFromSqlResult(
await connection.execute( await connection.execute(
` `
select select
...@@ -85,18 +85,19 @@ async function generateStocks() { ...@@ -85,18 +85,19 @@ async function generateStocks() {
` `
) )
) )
const stocksIds = new Set() const securitiesIds = new Set()
const summaryByIssuerId = {} const summaryByIssuerId = {}
for (let stock of stocks) { for (let security of securities) {
// console.log(`Loading stock ${stock.id}...`) // console.log(`Loading security ${security.id}...`)
stocksIds.add(stock.id) securitiesIds.add(security.id)
stock.share_type = shareTypeById[stock.sharetype] security.share_type = shareTypeById[security.sharetype]
assert(stock.share_type !== undefined, `Unknown share type "${stock.share_type}" for stock ${stock.id}`) assert(security.share_type !== undefined, `Unknown share type "${security.share_type}" for security ${
delete stock.sharetype security.id}`)
delete security.sharetype
{ {
// Add issuers to stock. // Add issuers to security.
const entries = objectsFromSqlResult( const entries = objectsFromSqlResult(
await connection.execute( await connection.execute(
` `
...@@ -105,14 +106,14 @@ async function generateStocks() { ...@@ -105,14 +106,14 @@ async function generateStocks() {
to_char(sc.startdate, 'YYYY-MM-DD') as start_date, to_char(sc.startdate, 'YYYY-MM-DD') as start_date,
to_char(sc.enddate, 'YYYY-MM-DD') as end_date to_char(sc.enddate, 'YYYY-MM-DD') as end_date
from stock_corporation sc from stock_corporation sc
where sc.stock = :stockId where sc.stock = :securityId
`, `,
{ {
stockId: { dir: oracledb.BIND_IN, val: stock.id, type: oracledb.INTEGER }, securityId: { dir: oracledb.BIND_IN, val: security.id, type: oracledb.INTEGER },
} }
) )
) )
stock.issuers = entries security.issuers = entries
.map(entry => { .map(entry => {
const issuerFilePath = path.join(issuersDir, `issuer_${entry.issuer_id}.json`)