Parse CSV files into HTML tables

This commit is contained in:
Thomas Miceli 2023-03-19 03:18:56 +01:00
parent 11b3eed250
commit 858ee3e70a
No known key found for this signature in database
GPG key ID: D86C6F6390AF050F
5 changed files with 108 additions and 13 deletions

View file

@ -3,8 +3,11 @@ package git
import (
"bufio"
"bytes"
"encoding/csv"
"fmt"
"io"
"regexp"
"strings"
)
type File struct {
@ -16,6 +19,12 @@ type File struct {
IsDeleted bool
}
type CsvFile struct {
File
Header []string
Rows [][]string
}
type Commit struct {
Hash string
Author string
@ -152,3 +161,27 @@ func parseLog(out io.Reader) []*Commit {
return commits
}
func ParseCsv(file *File) (*CsvFile, error) {
reader := csv.NewReader(strings.NewReader(file.Content))
records, err := reader.ReadAll()
if err != nil {
return nil, err
}
header := records[0]
numColumns := len(header)
for i := 1; i < len(records); i++ {
if len(records[i]) != numColumns {
return nil, fmt.Errorf("CSV file has invalid row at index %d", i)
}
}
return &CsvFile{
File: *file,
Header: header,
Rows: records[1:],
}, nil
}

View file

@ -11,6 +11,7 @@ import (
"io"
"net/http"
"opengist/internal/config"
"opengist/internal/git"
"opengist/internal/models"
"path/filepath"
"regexp"
@ -77,6 +78,21 @@ func Start() {
"isMarkdown": func(i string) bool {
return ".md" == strings.ToLower(filepath.Ext(i))
},
"isCsv": func(i string) bool {
return ".csv" == strings.ToLower(filepath.Ext(i))
},
"csvFile": func(file *git.File) *git.CsvFile {
if ".csv" != strings.ToLower(filepath.Ext(file.Filename)) {
return nil
}
csvFile, err := git.ParseCsv(file)
if err != nil {
return nil
}
return csvFile
},
"httpStatusText": http.StatusText,
"loadedTime": func(startTime time.Time) string {
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"

2
public/main.js vendored
View file

@ -18,7 +18,7 @@ document.addEventListener('DOMContentLoaded', () => {
let rev = document.querySelector('.revision-text')
if (rev) {
let fullRev = rev.innerHTML
let smallRev = fullRev.substring(0, 8)
let smallRev = fullRev.substring(0, 7)
rev.innerHTML = smallRev
rev.onmouseover = () => {

20
public/style.css vendored
View file

@ -110,4 +110,24 @@ pre {
.line-num {
@apply cursor-pointer text-slate-400 hover:text-white;
}
table.csv-table {
@apply w-full whitespace-pre text-xs;
}
table.csv-table thead {
text-align: left;
}
table.csv-table thead tr {
@apply bg-slate-800;
}
table.csv-table thead tr th {
@apply border py-2 px-1 border-slate-700;
}
table.csv-table tbody td {
@apply border py-1.5 px-1 border-slate-800;
}

View file

@ -3,6 +3,7 @@
{{ if .files }}
<div class="grid gap-y-4">
{{ range $file := .files }}
{{ $csv := csvFile $file }}
<div class="rounded-md border border-1 border-gray-700 overflow-auto">
<div class="border-b-1 border-gray-700 bg-gray-800 my-auto block">
<div class="ml-4 py-1.5 flex">
@ -20,21 +21,46 @@
This file has been truncated. <a href="/{{ $.gist.User.Username }}/{{ $.gist.Uuid }}/raw/{{ $.commit }}/{{$file.Filename}}">View the full file.</a>
</div>
{{ end }}
{{ if and (not $csv) (isCsv $file.Filename) }}
<div class="text-sm px-4 py-1.5 border-t-1 border-gray-700">
This file is not a valid CSV file.
</div>
{{ end }}
</div>
<div class="code overflow-auto">
{{ if isMarkdown $file.Filename }}
<div class="overflow-auto">
{{ if $csv }}
<table class="csv-table">
<thead>
<tr>
{{ range $csv.Header }}
<th>{{ . }}</th>
{{ end }}
</tr>
</thead>
<tbody>
{{ range $csv.Rows }}
<tr>
{{ range . }}
<td>{{ . }}</td>
{{ end }}
</tr>
{{ end }}
</table>
{{ else if isMarkdown $file.Filename }}
<div class="markdown markdown-body p-8">{{ $file.Content }}</div>
{{ else }}
{{ $fileslug := slug $file.Filename }}
{{ if ne $file.Content "" }}
<table class="table-code w-full whitespace-pre" data-filename-slug="{{ $fileslug }}" data-filename="{{ $file.Filename }}" style="font-size: 0.8em; border-spacing: 0; border-collapse: collapse;">
<tbody>
{{ $ii := "1" }}
{{ $i := toInt $ii }}
{{ range $line := lines $file.Content }}<tr><td id="file-{{ $fileslug }}-{{$i}}" class="select-none line-num px-4">{{$i}}</td><td class="line-code">{{ $line }}</td></tr>{{ $i = inc $i }}{{ end }}
</tbody>
</table>
{{ end }}
<div class="code">
{{ $fileslug := slug $file.Filename }}
{{ if ne $file.Content "" }}
<table class="table-code w-full whitespace-pre" data-filename-slug="{{ $fileslug }}" data-filename="{{ $file.Filename }}" style="font-size: 0.8em; border-spacing: 0; border-collapse: collapse;">
<tbody>
{{ $ii := "1" }}
{{ $i := toInt $ii }}
{{ range $line := lines $file.Content }}<tr><td id="file-{{ $fileslug }}-{{$i}}" class="select-none line-num px-4">{{$i}}</td><td class="line-code">{{ $line }}</td></tr>{{ $i = inc $i }}{{ end }}
</tbody>
</table>
{{ end }}
</div>
{{ end }}
</div>
</div>