Can now convert csv to proto + open proto file + filter

This commit is contained in:
cdricms
2024-04-29 21:24:13 +02:00
parent 691c063590
commit 6c5c1d594c
11 changed files with 374 additions and 79 deletions

64
app.go
View File

@@ -3,11 +3,16 @@ package main
import (
"context"
"fmt"
"os"
"git.cems.dev/cdricms/bdooc/proto"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
// App struct
type App struct {
ctx context.Context
employees *proto.EmployeeList // Caching purposes
}
// NewApp creates a new App application struct
@@ -21,7 +26,60 @@ func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
// Greet returns a greeting for the given name
func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s, It's show time!", name)
func (a *App) GetEmployees(path string, limit, page uint, column, query *string) ([]*proto.Employee, error) {
if a.employees == nil {
e, err := proto.LoadFromFile(path)
if err != nil {
return nil, err
}
a.employees = e
}
if column != nil && query != nil {
e, err := a.employees.QueryEmployeesByColumn(proto.EmployeeField(proto.SnakeCaseToPascalCase(*column)), *query)
if err != nil {
return nil, err
}
if uint(len(e)) < page*limit+limit {
return e[page*limit:], nil
}
return e[page*limit : page*limit+limit], nil
}
if uint(len(a.employees.Employees)) < page*limit+limit {
return a.employees.Employees[page*limit:], nil
}
return a.employees.Employees[page*limit : page*limit+limit], nil
}
func (a *App) SaveData(data []uint8, filename string) {
err := proto.SaveToFile(data, filename)
if err != nil {
panic(err)
}
}
func (a *App) GetProtoPath() string {
return a.OpenPath([]runtime.FileFilter{{
DisplayName: "Proto binary file",
Pattern: "*.bin",
}})
}
func (a *App) OpenPath(filters []runtime.FileFilter) string {
path, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
Filters: filters,
})
if err != nil {
panic(err)
}
return path
}
func (a *App) IsPathValid(path string) bool {
_, err := os.Stat(path)
fmt.Println(path)
return !os.IsNotExist(err)
}

View File

@@ -1,79 +1,35 @@
<script lang="ts">
import logo from './assets/images/logo-universal.png'
import {Greet} from '../wailsjs/go/main/App.js'
import { onMount } from "svelte";
import * as rt from "../wailsjs/runtime/runtime"
let resultText: string = "Please enter your name below 👇"
let name: string
import Table from "./Table.svelte";
function greet(): void {
Greet(name).then(result => resultText = result)
}
let path: string;
let page = 0;
let limitReached: boolean;
$: console.log(limitReached)
onMount(async () => {
rt.EventsOn("proto-opened", (_path: string) => {
path = _path;
})
rt.EventsOn("csv-converted", (_path: string) => {
console.log(_path)
})
});
</script>
<main>
<img alt="Wails logo" id="logo" src="{logo}">
<div class="result" id="result">{resultText}</div>
<div class="input-box" id="input">
<input autocomplete="off" bind:value={name} class="input" id="name" type="text"/>
<button class="btn" on:click={greet}>Greet</button>
</div>
{#if path}
<Table {path} {page} bind:limitReached={limitReached} />
<button disabled={page < 1} on:click={() => page--}>Prev</button>
<span>{page+1}</span>
<button disabled={limitReached} on:click={() => page++}>Next</button>
{/if}
</main>
<style>
#logo {
display: block;
width: 50%;
height: 50%;
margin: auto;
padding: 10% 0 0;
background-position: center;
background-repeat: no-repeat;
background-size: 100% 100%;
background-origin: content-box;
}
.result {
height: 20px;
line-height: 20px;
margin: 1.5rem auto;
}
.input-box .btn {
width: 60px;
height: 30px;
line-height: 30px;
border-radius: 3px;
border: none;
margin: 0 0 0 20px;
padding: 0 8px;
cursor: pointer;
}
.input-box .btn:hover {
background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
color: #333333;
}
.input-box .input {
border: none;
border-radius: 3px;
outline: none;
height: 30px;
line-height: 30px;
padding: 0 10px;
background-color: rgba(240, 240, 240, 1);
-webkit-font-smoothing: antialiased;
}
.input-box .input:hover {
border: none;
background-color: rgba(255, 255, 255, 1);
}
.input-box .input:focus {
border: none;
background-color: rgba(255, 255, 255, 1);
}
</style>

92
frontend/src/Table.svelte Normal file
View File

@@ -0,0 +1,92 @@
<script lang="ts">
import { GetEmployees } from "../wailsjs/go/main/App.js";
// import type { proto } from "../wailsjs/go/models";
// let employees: proto.Employee[] = [];
export let path: string;
export let page: number;
let limit: number = 10;
export let limitReached: boolean;
let _column: string | null = null;
let _query: string | null = null;
let column: string | null = null;
let query: string | null = null;
async function listEmployees(path: string, limit: number, page: number, column?: string, query?: string) {
try {
const employees = await GetEmployees(path, limit, page, column, query);
limitReached = employees.length < limit;
return employees
} catch (error) {
throw error.message;
}
}
</script>
<section>
{#await listEmployees(path, limit, page, column, query)}
<p>Loading...</p>
{:then employees}
<div>
<select
name="query"
on:change={({ currentTarget }) => (_column = currentTarget.value)}
>
{#each Object.keys(employees[0]) as key}
<option value={key}>{key}</option>
{/each}
</select>
<input placeholder="Query" type="text" bind:value={_query} />
<button
type="button"
on:click={() => {
column = _column;
query = _query;
}}>Search</button
>
</div>
<table>
<tr>
{#each Object.keys(employees[0]) as key}
<th>{key}</th>
{/each}
</tr>
{#each employees as employee}
<tr>
{#each Object.values(employee) as value}
<td>{value}</td>
{/each}
</tr>
{/each}
</table>
{:catch error}
<p>{error}</p>
{/await}
</section>
<style>
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
th,
td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
th {
background-color: #000000;
}
tr:nth-child(even) {
background-color: #343434;
}
tr:hover {
background-color: #110909;
}
</style>

View File

@@ -1,4 +1,14 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {proto} from '../models';
import {frontend} from '../models';
export function Greet(arg1:string):Promise<string>;
export function GetEmployees(arg1:string,arg2:number,arg3:number,arg4:any,arg5:any):Promise<Array<proto.Employee>>;
export function GetProtoPath():Promise<string>;
export function IsPathValid(arg1:string):Promise<boolean>;
export function OpenPath(arg1:Array<frontend.FileFilter>):Promise<string>;
export function SaveData(arg1:Array<number>,arg2:string):Promise<void>;

View File

@@ -2,6 +2,22 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function Greet(arg1) {
return window['go']['main']['App']['Greet'](arg1);
export function GetEmployees(arg1, arg2, arg3, arg4, arg5) {
return window['go']['main']['App']['GetEmployees'](arg1, arg2, arg3, arg4, arg5);
}
export function GetProtoPath() {
return window['go']['main']['App']['GetProtoPath']();
}
export function IsPathValid(arg1) {
return window['go']['main']['App']['IsPathValid'](arg1);
}
export function OpenPath(arg1) {
return window['go']['main']['App']['OpenPath'](arg1);
}
export function SaveData(arg1, arg2) {
return window['go']['main']['App']['SaveData'](arg1, arg2);
}

87
frontend/wailsjs/go/models.ts Executable file
View File

@@ -0,0 +1,87 @@
export namespace proto {
export class Employee {
emp_id?: number;
name_prefix?: string;
first_name?: string;
middle_initial?: string;
last_name?: string;
gender?: number;
email?: string;
fathers_name?: string;
mothers_name?: string;
mothers_maiden_name?: string;
birthdate?: string;
birth_time?: string;
weight_kg?: number;
joining_date?: string;
joining_quarter?: number;
joining_half?: number;
joining_year?: number;
joining_month?: number;
joining_month_name?: number;
joining_month_name_short?: number;
joining_month_day?: number;
joining_week_day?: number;
joining_week_day_short?: number;
years_of_service?: number;
salary?: number;
latest_hike_percentage?: string;
ssn?: string;
phone_number?: string;
place_name?: string;
county?: string;
city?: string;
state?: string;
zip?: number;
region?: string;
username?: string;
password?: string;
static createFrom(source: any = {}) {
return new Employee(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.emp_id = source["emp_id"];
this.name_prefix = source["name_prefix"];
this.first_name = source["first_name"];
this.middle_initial = source["middle_initial"];
this.last_name = source["last_name"];
this.gender = source["gender"];
this.email = source["email"];
this.fathers_name = source["fathers_name"];
this.mothers_name = source["mothers_name"];
this.mothers_maiden_name = source["mothers_maiden_name"];
this.birthdate = source["birthdate"];
this.birth_time = source["birth_time"];
this.weight_kg = source["weight_kg"];
this.joining_date = source["joining_date"];
this.joining_quarter = source["joining_quarter"];
this.joining_half = source["joining_half"];
this.joining_year = source["joining_year"];
this.joining_month = source["joining_month"];
this.joining_month_name = source["joining_month_name"];
this.joining_month_name_short = source["joining_month_name_short"];
this.joining_month_day = source["joining_month_day"];
this.joining_week_day = source["joining_week_day"];
this.joining_week_day_short = source["joining_week_day_short"];
this.years_of_service = source["years_of_service"];
this.salary = source["salary"];
this.latest_hike_percentage = source["latest_hike_percentage"];
this.ssn = source["ssn"];
this.phone_number = source["phone_number"];
this.place_name = source["place_name"];
this.county = source["county"];
this.city = source["city"];
this.state = source["state"];
this.zip = source["zip"];
this.region = source["region"];
this.username = source["username"];
this.password = source["password"];
}
}
}

2
go.mod
View File

@@ -15,7 +15,7 @@ require (
github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
github.com/leaanthony/gosod v1.0.3 // indirect
github.com/leaanthony/slicer v1.6.0 // indirect
github.com/leaanthony/u v1.1.0 // indirect
github.com/leaanthony/u v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect

4
go.sum
View File

@@ -26,8 +26,8 @@ github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRC
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js=
github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI=
github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=

57
main.go
View File

@@ -2,10 +2,17 @@ package main
import (
"embed"
"encoding/csv"
"os"
"git.cems.dev/cdricms/bdooc/parsing"
p "git.cems.dev/cdricms/bdooc/proto"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/menu"
"github.com/wailsapp/wails/v2/pkg/menu/keys"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
//go:embed all:frontend/dist
@@ -14,6 +21,55 @@ var assets embed.FS
func main() {
// Create an instance of the app structure
app := NewApp()
appMenu := menu.NewMenu()
fileMenu := appMenu.AddSubmenu("File")
fileMenu.AddText("Open proto file", keys.CmdOrCtrl("o"), func(_ *menu.CallbackData) {
path := app.GetProtoPath()
runtime.EventsEmit(app.ctx, "proto-opened", path)
})
fileMenu.AddText("Convert CSV to Proto", keys.CmdOrCtrl("k"), func(_ *menu.CallbackData) {
path := app.OpenPath([]runtime.FileFilter{{
DisplayName: "CSV file",
Pattern: "*.csv",
}})
employeesProtoChan := make(chan *p.EmployeeList)
go func() {
// Parse CSV to Go Struct
file, err := os.Open(path)
if err != nil {
panic(err)
}
defer file.Close()
reader := csv.NewReader(file)
employees, err := parsing.UnmarshalEmployees(reader)
if err != nil {
panic(err)
}
// Convert CSV's Go struct to Proto
employeesProto := parsing.MapToProto(employees)
employeesProtoChan <- employeesProto
}()
// Get the filepath from the user.
filePath, err := runtime.SaveFileDialog(app.ctx, runtime.SaveDialogOptions{
Title: "Destination for Proto file.",
Filters: []runtime.FileFilter{{
DisplayName: "Proto binary file",
Pattern: "*.bin",
}},
})
if err != nil {
panic(err)
}
// Await the conversion
employeesProto := <-employeesProtoChan
// Save
employeesProto.SaveToFile(filePath)
runtime.EventsEmit(app.ctx, "csv-converted", filePath)
})
// Create application with options
err := wails.Run(&options.App{
@@ -23,6 +79,7 @@ func main() {
AssetServer: &assetserver.Options{
Assets: assets,
},
Menu: appMenu,
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
OnStartup: app.startup,
Bind: []interface{}{

View File

@@ -6,6 +6,15 @@ import (
// "git.cems.dev/cdricms/bdooc/parsing"
)
func SaveToFile(data []uint8, filename string) error {
EmployeeList := &EmployeeList{}
err := proto.Unmarshal(data, EmployeeList)
if err != nil {
return err
}
return EmployeeList.SaveToFile(filename)
}
func (el *EmployeeList) SaveToFile(filename string) error {
data, err := proto.Marshal(el)
if err != nil {

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"math"
"reflect"
"strings"
)
func GetMax[T float64 | float32 | int | uint | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64](a, b T) T {
@@ -62,6 +63,15 @@ const (
FieldPassword = "Password"
)
func SnakeCaseToPascalCase(s string) string {
words := strings.Split(s, "_")
for i, word := range words {
words[i] = strings.ToUpper(word[0:1]) + strings.ToLower(word[1:])
}
return strings.Join(words, "")
}
func (e *Employee) Copy() Employee {
return Employee{
EmpId: e.GetEmpId(),