mirror of
https://github.com/thomiceli/opengist.git
synced 2024-12-23 04:52:40 +00:00
Parse CSV files into HTML tables
This commit is contained in:
parent
11b3eed250
commit
858ee3e70a
5 changed files with 108 additions and 13 deletions
|
@ -3,8 +3,11 @@ package git
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
|
@ -16,6 +19,12 @@ type File struct {
|
||||||
IsDeleted bool
|
IsDeleted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CsvFile struct {
|
||||||
|
File
|
||||||
|
Header []string
|
||||||
|
Rows [][]string
|
||||||
|
}
|
||||||
|
|
||||||
type Commit struct {
|
type Commit struct {
|
||||||
Hash string
|
Hash string
|
||||||
Author string
|
Author string
|
||||||
|
@ -152,3 +161,27 @@ func parseLog(out io.Reader) []*Commit {
|
||||||
|
|
||||||
return commits
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"opengist/internal/config"
|
"opengist/internal/config"
|
||||||
|
"opengist/internal/git"
|
||||||
"opengist/internal/models"
|
"opengist/internal/models"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -77,6 +78,21 @@ func Start() {
|
||||||
"isMarkdown": func(i string) bool {
|
"isMarkdown": func(i string) bool {
|
||||||
return ".md" == strings.ToLower(filepath.Ext(i))
|
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,
|
"httpStatusText": http.StatusText,
|
||||||
"loadedTime": func(startTime time.Time) string {
|
"loadedTime": func(startTime time.Time) string {
|
||||||
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
|
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
|
||||||
|
|
2
public/main.js
vendored
2
public/main.js
vendored
|
@ -18,7 +18,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
let rev = document.querySelector('.revision-text')
|
let rev = document.querySelector('.revision-text')
|
||||||
if (rev) {
|
if (rev) {
|
||||||
let fullRev = rev.innerHTML
|
let fullRev = rev.innerHTML
|
||||||
let smallRev = fullRev.substring(0, 8)
|
let smallRev = fullRev.substring(0, 7)
|
||||||
rev.innerHTML = smallRev
|
rev.innerHTML = smallRev
|
||||||
|
|
||||||
rev.onmouseover = () => {
|
rev.onmouseover = () => {
|
||||||
|
|
20
public/style.css
vendored
20
public/style.css
vendored
|
@ -111,3 +111,23 @@ pre {
|
||||||
.line-num {
|
.line-num {
|
||||||
@apply cursor-pointer text-slate-400 hover:text-white;
|
@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;
|
||||||
|
}
|
30
templates/pages/gist.html
vendored
30
templates/pages/gist.html
vendored
|
@ -3,6 +3,7 @@
|
||||||
{{ if .files }}
|
{{ if .files }}
|
||||||
<div class="grid gap-y-4">
|
<div class="grid gap-y-4">
|
||||||
{{ range $file := .files }}
|
{{ range $file := .files }}
|
||||||
|
{{ $csv := csvFile $file }}
|
||||||
<div class="rounded-md border border-1 border-gray-700 overflow-auto">
|
<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="border-b-1 border-gray-700 bg-gray-800 my-auto block">
|
||||||
<div class="ml-4 py-1.5 flex">
|
<div class="ml-4 py-1.5 flex">
|
||||||
|
@ -20,11 +21,35 @@
|
||||||
This file has been truncated. <a href="/{{ $.gist.User.Username }}/{{ $.gist.Uuid }}/raw/{{ $.commit }}/{{$file.Filename}}">View the full file.</a>
|
This file has been truncated. <a href="/{{ $.gist.User.Username }}/{{ $.gist.Uuid }}/raw/{{ $.commit }}/{{$file.Filename}}">View the full file.</a>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ 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>
|
</div>
|
||||||
<div class="code overflow-auto">
|
{{ end }}
|
||||||
{{ if isMarkdown $file.Filename }}
|
</div>
|
||||||
|
<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>
|
<div class="markdown markdown-body p-8">{{ $file.Content }}</div>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
|
<div class="code">
|
||||||
{{ $fileslug := slug $file.Filename }}
|
{{ $fileslug := slug $file.Filename }}
|
||||||
{{ if ne $file.Content "" }}
|
{{ 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;">
|
<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;">
|
||||||
|
@ -35,6 +60,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue