Can now convert csv to proto + open proto file + filter
This commit is contained in:
64
app.go
64
app.go
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
92
frontend/src/Table.svelte
Normal 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>
|
||||
12
frontend/wailsjs/go/main/App.d.ts
vendored
12
frontend/wailsjs/go/main/App.d.ts
vendored
@@ -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>;
|
||||
|
||||
@@ -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
87
frontend/wailsjs/go/models.ts
Executable 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
2
go.mod
@@ -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
4
go.sum
@@ -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
57
main.go
@@ -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{}{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user