Pietro Rodolfo Masera 2023-05-15 13:37:27 +02:00
commit 4ed823f7bb
6 changed files with 77 additions and 13 deletions

1
company_generic.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M80 976V495l280-119v80l200-81v121h320v480H80Zm60-60h680V556.199H500V464l-200 80v-79l-160 71v380Zm310-100h60V656h-60v160Zm-160 0h60V656h-60v160Zm320 0h60V656h-60v160Zm270-320H700l40-320h100l40 320ZM140 916h680-680Z"/></svg>

After

Width:  |  Height:  |  Size: 317 B

View file

@ -9,7 +9,7 @@
"lint": "eslint . --fix --ignore-path .gitignore" "lint": "eslint . --fix --ignore-path .gitignore"
}, },
"dependencies": { "dependencies": {
"@mdi/font": "7.0.96", "@mdi/font": "^7.2.96",
"core-js": "^3.29.0", "core-js": "^3.29.0",
"roboto-fontface": "*", "roboto-fontface": "*",
"vue": "^3.2.0", "vue": "^3.2.0",

View file

@ -13,7 +13,11 @@ export interface Company {
tags: string[]; tags: string[];
ticker: string; ticker: string;
website: string; website: string;
logoSrc: string
} }
export const getCompanies = (): Promise<Company[]> => export const getCompanies = (): Promise<Company[]> =>
fetch(BACKEND_URL + '/companies').then(r => r.json()) fetch(BACKEND_URL + '/companies').then(r => r.json()).then(list => list.map((e: Company) => ({
...e,
logoSrc: `${BACKEND_URL}/companies/logos/${e.ticker}`
})));

View file

@ -9,13 +9,28 @@
<template v-else v-for="company in companies" :key="company.ticker"> <template v-else v-for="company in companies" :key="company.ticker">
<v-col cols="12" md="6" lg="4"> <v-col cols="12" md="6" lg="4">
<v-card class="ma-1 fill-height"> <v-card class="ma-1 fill-height">
<v-card-item>
<v-card-title>{{ company['short name'] }}</v-card-title> <div class="d-flex stretch align-center">
<v-card-subtitle>{{ company['company name'] }}</v-card-subtitle> <div class="flex-0 pa-3">
</v-card-item> <img style="max-height: 36px; min-height: 24px;" :src="company.logoSrc"/>
</div>
<v-card-item style="flex: 1 !important">
<v-card-title>{{ company['short name'] }}</v-card-title>
<v-card-subtitle>{{ company['company name'] }}</v-card-subtitle>
</v-card-item>
</div>
<v-card-text> <v-card-text>
{{ company.description }} <p class="text--primary">
{{ company.description }}
</p>
<div class="pt-2 pb-2" v-for="m in metrics" :key="m.title">
<div class="d-inline-flex justify-space-between" style="width: 100%">
<strong>{{ m.title }}</strong>
<span class="text-right">{{ m.value(company) }}{{ m.symbol ?? '' }}</span>
</div>
<v-progress-linear :color="m.color" :model-value="m.percentage(company)"></v-progress-linear>
</div>
</v-card-text> </v-card-text>
<div class="px-4"> <div class="px-4">
@ -28,7 +43,7 @@
{{ formatCurrency(company['market cap']) }} {{ formatCurrency(company['market cap']) }}
</v-chip> </v-chip>
<template v-for="tag in company.tags" :key="tag"> <template v-for="tag in company.tags" :key="tag">
<v-chip label class="ma-1">{{ tag }}</v-chip> <v-chip label class="ma-1">{{ tag }}</v-chip>
</template> </template>
</div> </div>
</v-card> </v-card>
@ -40,11 +55,44 @@
<script lang="ts" setup> <script lang="ts" setup>
import { getCompanies, Company } from '@/api'; import { getCompanies, Company } from '@/api';
import { ref, reactive } from 'vue'; import { ref, reactive, computed, watch } from 'vue';
const loading = ref(true); const loading = ref(true);
const companies = reactive<Company[]>([]); const companies = reactive<Company[]>([]);
interface Metric {
title: string,
color: string,
minValue: number,
maxValue: number,
value: (c: Company) => number // in [0, 100],
symbol?: string
}
const metricsData = reactive<Metric[]>([
{
title: 'Metric 1',
color: 'green',
minValue: 0,
maxValue: 100,
value: _ => 20
},
{
title: 'Length of ticker',
color: 'orange',
minValue: 0,
maxValue: 5,
value: c => c.ticker.length,
symbol: ' chars'
},
]);
const metrics = computed<(Metric & { percentage: (c: Company) => number })[]>(() => metricsData.map(e => ({
...e,
percentage: (c: Company) => (e.value(c) - e.minValue) * 100 / (e.maxValue - e.minValue)
})));
getCompanies().then(cs => { getCompanies().then(cs => {
loading.value = false; loading.value = false;
companies.push(...cs); companies.push(...cs);

View file

@ -192,10 +192,10 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@mdi/font@7.0.96": "@mdi/font@^7.2.96":
version "7.0.96" version "7.2.96"
resolved "https://registry.yarnpkg.com/@mdi/font/-/font-7.0.96.tgz#9853c222623072f5575b4039c8c195ea929b61fc" resolved "https://registry.yarnpkg.com/@mdi/font/-/font-7.2.96.tgz#af800d9fe3b424f85ad45e9baa755bd003ab4986"
integrity sha512-rzlxTfR64hqY8yiBzDjmANfcd8rv+T5C0Yedv/TWk2QyAQYdc66e0kaN1ipmnYU3RukHRTRcBARHzzm+tIhL7w== integrity sha512-e//lmkmpFUMZKhmCY9zdjRe4zNXfbOIJnn6xveHbaV2kSw5aJ5dLXUxcRt1Gxfi7ZYpFLUWlkG2MGSFAiqAu7w==
"@nodelib/fs.scandir@2.1.5": "@nodelib/fs.scandir@2.1.5":
version "2.1.5" version "2.1.5"

View file

@ -26,6 +26,17 @@ def companies() -> object:
return jsonify(get_companies(ROOT_DIR)) return jsonify(get_companies(ROOT_DIR))
@app.route('/companies/logos/<ticker>')
def get_company_logo(ticker: str):
logo_dir: str = os.path.join(ROOT_DIR, 'scraper', 'logos', 'logos')
logo_name: str = str(ticker).upper() + '.png'
if os.path.exists(os.path.join(logo_dir, logo_name)):
return send_from_directory(logo_dir, logo_name)
return send_from_directory(ROOT_DIR, 'company_generic.svg')
if __name__ == '__main__': if __name__ == '__main__':
build_frontend() build_frontend()
app.run() app.run()