created a basic home page #97
This commit is contained in:
parent
a3913ccdf5
commit
f41cdf78df
44
logic/stats/PublicStats.go
Normal file
44
logic/stats/PublicStats.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
|
||||||
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks/utils"
|
||||||
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandlePublicStats(handle *Handle) {
|
||||||
|
handle.Post("/stats/public/main", func(c *Context) *Error {
|
||||||
|
if handle.DataMap["PublicMainLastUpdate"] != nil && handle.DataMap["PublicMainLastUpdate"].(int64) > time.Now().UnixMilli() {
|
||||||
|
c.ShowMessage = false
|
||||||
|
return c.SendJSON(handle.DataMap["PublicMain"])
|
||||||
|
}
|
||||||
|
number_of_models, err := GetDbVar[int](c, "count(*)", "models")
|
||||||
|
if err != nil {
|
||||||
|
return c.E500M("Could not get statistics", err)
|
||||||
|
}
|
||||||
|
number_of_classfications, err := GetDbVar[int](c, "count(*)", "tasks where task_type=$1", TASK_TYPE_CLASSIFICATION)
|
||||||
|
if err != nil {
|
||||||
|
return c.E500M("Could not get statistics", err)
|
||||||
|
}
|
||||||
|
number_of_images_processed, err := GetDbVar[int](c, "count(*)", "model_data_point")
|
||||||
|
if err != nil {
|
||||||
|
return c.E500M("Could not get statistics", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DataMap["PublicMainLastUpdate"] = time.Now().UnixNano() + 60*60*1000*1000
|
||||||
|
handle.DataMap["PublicMain"] = struct {
|
||||||
|
NumberOfModels int `json:"number_of_models"`
|
||||||
|
NumberOfClassfications int `json:"number_of_classfications"`
|
||||||
|
NumberOfImagesProcessed int `json:"number_of_images_processed"`
|
||||||
|
}{
|
||||||
|
*number_of_models,
|
||||||
|
*number_of_classfications,
|
||||||
|
*number_of_images_processed,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ShowMessage = false
|
||||||
|
return c.SendJSON(handle.DataMap["PublicMain"])
|
||||||
|
})
|
||||||
|
}
|
9
logic/stats/index.go
Normal file
9
logic/stats/index.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleStats(handle *Handle) {
|
||||||
|
HandlePublicStats(handle)
|
||||||
|
}
|
@ -46,6 +46,7 @@ type Handle struct {
|
|||||||
gets []HandleFunc
|
gets []HandleFunc
|
||||||
posts []HandleFunc
|
posts []HandleFunc
|
||||||
deletes []HandleFunc
|
deletes []HandleFunc
|
||||||
|
DataMap map[string]interface{}
|
||||||
Config Config
|
Config Config
|
||||||
validate *validator.Validate
|
validate *validator.Validate
|
||||||
}
|
}
|
||||||
@ -523,7 +524,7 @@ func NewHandler(db db.Db, config Config) *Handle {
|
|||||||
var posts []HandleFunc
|
var posts []HandleFunc
|
||||||
var deletes []HandleFunc
|
var deletes []HandleFunc
|
||||||
validate := validator.New()
|
validate := validator.New()
|
||||||
x := &Handle{db, gets, posts, deletes, config, validate}
|
x := &Handle{db, gets, posts, deletes, map[string]interface{}{}, config, validate}
|
||||||
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
2
main.go
2
main.go
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"git.andr3h3nriqu3s.com/andr3/fyp/logic/db"
|
"git.andr3h3nriqu3s.com/andr3/fyp/logic/db"
|
||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models"
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models"
|
||||||
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/stats"
|
||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks"
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks"
|
||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks/runner"
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks/runner"
|
||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/users"
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/users"
|
||||||
@ -48,6 +49,7 @@ func main() {
|
|||||||
UsersEndpints(db, handle)
|
UsersEndpints(db, handle)
|
||||||
HandleModels(handle)
|
HandleModels(handle)
|
||||||
HandleTasks(handle)
|
HandleTasks(handle)
|
||||||
|
HandleStats(handle)
|
||||||
|
|
||||||
handle.Startup()
|
handle.Startup()
|
||||||
}
|
}
|
||||||
|
@ -6,22 +6,22 @@
|
|||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/"> Index </a>
|
<a href="/"> <span class="bi bi-house-fill"></span> Home </a>
|
||||||
</li>
|
</li>
|
||||||
{#if userStore.user}
|
{#if userStore.user}
|
||||||
<li>
|
<li>
|
||||||
<a href="/models"> Models </a>
|
<a href="/models"> <span class="bi bi-card-image"></span> Models </a>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
<li class="expand"></li>
|
<li class="expand"></li>
|
||||||
{#if userStore.user}
|
{#if userStore.user}
|
||||||
<li>
|
<li>
|
||||||
<a href="/user/info"> User Info </a>
|
<a href="/user/info"> <span class="bi bi-person-fill"></span> {userStore.user.username} </a>
|
||||||
<a href="/logout"> Logout </a>
|
<a href="/logout"> <span class="bi bi-box-arrow-right"></span> Logout </a>
|
||||||
</li>
|
</li>
|
||||||
{:else}
|
{:else}
|
||||||
<li>
|
<li>
|
||||||
<a href="/login"> Login </a>
|
<a href="/login"> <span class="bi bi-box-arrow-in-left"></span> Login</a>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -3,9 +3,25 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Andada+Pro:ital,wght@0,400..840;1,400..840&family=Bebas+Neue&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css"
|
||||||
|
/>
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { userStore } from 'routes/UserStore.svelte';
|
import { userStore } from 'routes/UserStore.svelte';
|
||||||
|
import { notificationStore } from './NotificationsStore.svelte';
|
||||||
|
|
||||||
const API = '/api';
|
const API = '/api';
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ export async function postFormData(url: string, body: FormData) {
|
|||||||
|
|
||||||
export async function showMessage(
|
export async function showMessage(
|
||||||
e: any,
|
e: any,
|
||||||
messages: any,
|
messages: any = notificationStore,
|
||||||
message = 'Could not complete request'
|
message = 'Could not complete request'
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (e == null) {
|
if (e == null) {
|
||||||
|
@ -1 +1,48 @@
|
|||||||
<h1>Main Web Page</h1>
|
<script>
|
||||||
|
import Spinner from 'src/lib/Spinner.svelte';
|
||||||
|
import Counter from './Counter.svelte';
|
||||||
|
import { post, showMessage } from 'src/lib/requests.svelte';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let _counters = $state(new Promise(() => {}));
|
||||||
|
|
||||||
|
async function publicStats() {
|
||||||
|
try {
|
||||||
|
const r = await post('stats/public/main', {});
|
||||||
|
_counters = Promise.resolve(r);
|
||||||
|
} catch (e) {
|
||||||
|
showMessage(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
publicStats();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="title">
|
||||||
|
<h1>Classify Your Images</h1>
|
||||||
|
<h2>A software as a service platform for image classification</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="counters">
|
||||||
|
{#await _counters}
|
||||||
|
<Spinner />
|
||||||
|
{:then counters}
|
||||||
|
<Counter value={counters.number_of_models}>Number of models created</Counter>
|
||||||
|
<Counter value={counters.number_of_classfications}>Number of images classified</Counter>
|
||||||
|
<Counter value={counters.number_of_images_processed}>Number of images processed</Counter>
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.counters {
|
||||||
|
height: 180px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
41
webpage/src/routes/Counter.svelte
Normal file
41
webpage/src/routes/Counter.svelte
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cubicOut } from 'svelte/easing';
|
||||||
|
import { tweened } from 'svelte/motion';
|
||||||
|
|
||||||
|
const { value, children }: { value: number; children: unknown } = $props();
|
||||||
|
|
||||||
|
const t_value = tweened(0, {
|
||||||
|
duration: 300,
|
||||||
|
easing: cubicOut
|
||||||
|
});
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
t_value.set(value);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="counter">
|
||||||
|
<div class="number">
|
||||||
|
{Math.floor($t_value)}
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.counter {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
box-shadow: 1px 1px 8px 2px #33333333;
|
||||||
|
border-radius: 30px;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 0 10px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.number {
|
||||||
|
font-family: 'Bebas Neue';
|
||||||
|
font-size: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,6 +1,7 @@
|
|||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Andada Pro', sans-serif;
|
||||||
|
/* font-family: 'Roboto', sans-serif; */
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
Loading…
Reference in New Issue
Block a user