omq/main.go
2023-11-20 20:34:09 +05:00

202 lines
5.6 KiB
Go

package main
import (
"log/slog"
"os"
"path/filepath"
"strings"
"time"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
func init() {
logger := slog.New(&Handler{os.Stderr, slog.LevelInfo})
slog.SetDefault(logger)
}
func main() {
renderUI()
}
func renderUI() {
// SQL-files list
files, _ := filepath.Glob(filepath.Join(SQL_FILES_DIR, "*.sql"))
for i := range files {
files[i] = filepath.Base(files[i])
}
// Servers file list
configs, _ := filepath.Glob("*.ini")
configFileDropDown := tview.NewDropDown().
SetOptions(configs, nil).
SetLabel("Файл настроек серверов").
SetCurrentOption(0)
sqlFileDropDown := tview.NewDropDown().
SetOptions(files, nil).
SetLabel("Файл SQL-скрипта").
SetCurrentOption(0)
encodingDropDown := tview.NewDropDown().
SetOptions([]string{string(EncodingWin1251), string(EncodingUtf8)}, nil).
SetLabel("Кодировка CSV-файла")
encodingDropDown.SetDisabled(true)
encodingDropDown.SetCurrentOption(0)
formatDropDown := tview.NewDropDown().
SetOptions([]string{string(ExportFormatExcel), string(ExportFormatCsv)}, func(text string, index int) {
if text == string(ExportFormatCsv) {
encodingDropDown.SetDisabled(false)
return
}
encodingDropDown.SetDisabled(true)
}).
SetLabel("Формат выгрузки").SetCurrentOption(0)
form := tview.NewForm().
AddFormItem(configFileDropDown).
AddFormItem(sqlFileDropDown).
AddFormItem(formatDropDown).
AddFormItem(encodingDropDown)
form.SetTitle("Параметры").SetBorder(true)
app := tview.NewApplication()
grid := tview.NewGrid().SetRows(-1, 1).
AddItem(form, 0, 0, 1, 1, 0, 0, true).
AddItem(tview.NewButton("Запуск").SetSelectedFunc(func() {
_, scriptFilePath := sqlFileDropDown.GetCurrentOption()
_, exportFileFormatStr := formatDropDown.GetCurrentOption()
_, configFilePath := configFileDropDown.GetCurrentOption()
_, encodingStr := encodingDropDown.GetCurrentOption()
job := &Job{
configFilePath: configFilePath,
scriptFilePath: filepath.Join(SQL_FILES_DIR, scriptFilePath),
exportFileFormat: ExportFormat(exportFileFormatStr),
encoding: Encoding(encodingStr)}
err := job.init()
if err != nil {
showError(app, err)
return
}
renderProgressUI(app, job)
}), 1, 0, 1, 1, 0, 0, false)
grid.SetBorderPadding(0, 1, 1, 1)
form.SetTitle("Параметры выгрузки").SetTitleAlign(tview.AlignLeft)
if err := app.SetRoot(grid, true).EnableMouse(true).Run(); err != nil {
panic(err)
}
}
func renderProgressUI(app *tview.Application, job *Job) {
statusTextViews := make([]*tview.TextView, 0)
maxServerNameLen := 0
for i := range job.servers {
if len(job.servers[i].Name) > maxServerNameLen {
maxServerNameLen = len([]rune(job.servers[i].Name)) + 2
}
}
rows := make([]int, len(job.servers)+3)
for i := range rows {
rows[i] = 1
}
grid := tview.NewGrid().SetRows(rows...).SetColumns(1, maxServerNameLen+2)
for i, server := range job.servers {
grid.AddItem(tview.NewTextView().SetTextAlign(tview.AlignLeft).SetText(server.Name), i+1, 1, 1, 1, 0, 0, false)
p := tview.NewTextView().SetTextAlign(tview.AlignLeft).SetText("...")
statusTextViews = append(statusTextViews, p)
grid.AddItem(p, i+1, 2, 1, 1, 0, 0, false)
}
grid.AddItem(tview.NewTextView().SetTextAlign(tview.AlignLeft).SetText(strings.Repeat("-", maxServerNameLen)), len(job.servers)+1, 1, 1, 1, 0, 0, false)
grid.AddItem(tview.NewTextView().SetTextAlign(tview.AlignLeft), len(job.servers)+1, 2, 1, 1, 0, 0, false)
statusTextView := tview.NewTextView()
grid.AddItem(statusTextView, len(job.servers)+2, 2, 1, 1, 0, 0, false)
// Last empty line
grid.AddItem(tview.NewTextView(), len(job.servers)+3, 1, 1, 1, 0, 0, false)
grid.AddItem(tview.NewTextView(), len(job.servers)+3, 2, 1, 1, 0, 0, false)
go func() {
oneMoreUpdate := false
for !job.isFinished || oneMoreUpdate {
if !oneMoreUpdate {
time.Sleep(time.Second)
}
for i, server := range job.servers {
s := []string{server.Status}
if server.Error != nil {
s = append(s, server.Error.Error())
}
statusTextViews[i].SetText(strings.Join(s, ": "))
if server.Error != nil {
statusTextViews[i].SetTextColor(tcell.ColorRed)
} else if strings.HasPrefix(server.Status, "ЗАВЕРШЕНО: ") {
statusTextViews[i].SetTextColor(tcell.ColorGreen)
}
}
statusTextView.SetText(job.status)
if strings.HasPrefix(job.status, "ЗАВЕРШЕНО") {
statusTextView.SetTextColor(tcell.ColorGreen)
}
app.ForceDraw()
if job.isFinished {
if !oneMoreUpdate {
oneMoreUpdate = true
} else {
break
}
}
}
// TODO:
/*finishButton := tview.NewButton("OK").SetSelectedFunc(func() { app.Stop() }).SetBackgroundColor(tcell.ColorDarkGreen)
grid.AddItem(finishButton, len(job.servers)+2, 1, 1, 1, 0, 0, false)
app.ForceDraw().SetFocus(finishButton)*/
showMessage(app, "Выгрузка завершена.")
}()
go func() {
err := job.launch()
if err != nil {
showError(app, err)
}
}()
if err := app.SetRoot(grid, true).SetFocus(grid).Run(); err != nil {
panic(err)
}
}
func showError(app *tview.Application, err error) {
modal := tview.NewModal().
SetText(err.Error()).
AddButtons([]string{"OK"}).
SetBackgroundColor(tcell.ColorRed).
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
os.Exit(1)
})
app.SetRoot(modal, true).SetFocus(modal)
}
func showMessage(app *tview.Application, text string) {
modal := tview.NewModal().
SetText(text).
AddButtons([]string{"OK"}).
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
os.Exit(0)
})
app.SetRoot(modal, true).SetFocus(modal).ForceDraw()
}