Skip to content

Commit

Permalink
WIP: inserting tables with \tableinput command (#197)
Browse files Browse the repository at this point in the history
* add markdown formatting functions

* add tableinput command

* replace tableinput command to LXCOM_SIMPLE_REPROCESS

* simplify csv2md processing

* resolve_lx_tableinput now returns html insted of markdown

* csv processing returns always html
handling DimensionMismatch of the header

* add tests for tableinput

* document tableinput command

* make requested changes

* fix tableinput behaviour, docs and tests
  • Loading branch information
cserteGT3 authored and tlienart committed Sep 2, 2019
1 parent d61a3e8 commit afb833e
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 0 deletions.
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ version = "0.2.0"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
JuDocTemplates = "6793090a-55ae-11e9-0511-73b91164f4ea"
LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589"
Expand Down
50 changes: 50 additions & 0 deletions docs/src/man/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Most of what is presented here is also shown in that example.
* [Inserting a figure](#Inserting-a-figure-1)
* [Linking a file](#Linking-a-file-1)
* [Inserting markdown](#Inserting-markdown-1)
* [Inserting a table](#Inserting-a-table-1)
* [Page variables](#Page-variables-1)
* [Local page variables](#Local-page-variables-1)
* [Default variables](#Default-variables-1)
Expand Down Expand Up @@ -681,6 +682,55 @@ This is the index then some **markdown** in a side file.

**Note**: if you don't specify a file extension, `.md` is appended to the specified path.

### Inserting a table

You can insert tables directly from CSV files with the `\tableinput{header}{path}` command.
If you generate the file on-the-fly, you should follow this example:
`````judoc
```julia:./tableinput/gen
testcsv = "h1,h2,h3
152,some string, 1.5f0
0,another string,2.87"
write("assets/pages/tableinput/testcsv.csv", testcsv)
```
`````
Then you can insert the table with:
`````judoc
\tableinput{}{./tableinput/testcsv.csv}
`````
Which will result in:

| h1 | h2 | h3 |
| --- | -------------- | ----- |
| 152 | some string | 1.5f0 |
| 0 | another string | 2.87 |
In this case given no header was specified in the call, a header was generated from the first line in the CSV (here: h1, h2, h3).

If you're file doesn't have a header, you can specify it in the call:
`````judoc
```julia:./tableinput/gen
testcsv = "152,some string, 1.5f0
0,another string,2.87"
write("assets/pages/tableinput/testcsv2.csv", testcsv)
\tableinput{custom h1,custom h2,custom h3}{./tableinput/testcsv2.csv}
`````

| custom h1 | custom h2 | custom h3 |
| --------- | -------------- | --------- |
| 152 | some string | 1.5f0 |
| 0 | another string | 2.87 |

With the above in mind, you can also include existing CSV files.

!!! note

The look of the table will be defined by your CSS stylesheet.

There's a couple of rules that you have to keep in mind when using the `\tableinput{}{}` command:
* Columns must be separated with comma (`,`).
* If a header is specified, its length must match the number of columns of the file.

## Page variables

Page variables are a way to interact with the HTML templating.
Expand Down
1 change: 1 addition & 0 deletions src/JuDoc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ using JuDocTemplates

using Markdown
using Dates # see jd_vars
using DelimitedFiles: readdlm

import LiveServer

Expand Down
67 changes: 67 additions & 0 deletions src/converter/lx_simple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,72 @@ function resolve_lx_figalt(lxc::LxCom)::String
end


"""
$SIGNATURES
Internal function to resolve a `\\tableinput{header}{rpath}` (finds a csv and includes it with header).
"""
function resolve_lx_tableinput(lxc::LxCom)::String
rpath = strip(content(lxc.braces[2]))
header = strip(content(lxc.braces[1]))
path = resolve_assets_rpath(rpath; canonical=false)
fdir, fext = splitext(path)
# copy-paste from resolve_lx_figalt()
# A. a path with extension --> use that
# there can be a relative path set but the user may mean
# that it's in the subfolder /output/ (if generated by code) so should look
# both in the relpath and if not found and if /output/ not already the last subdir
syspath = joinpath(PATHS[:folder], split(path, '/')...)
if isfile(syspath)
return csv2html(syspath, header)
end
# now try in the output dir just in case (provided we weren't already looking there)
p1, p2 = splitdir(fdir)
if splitdir(p1)[2] != "output"
candpath = joinpath(p1, "output", p2 * fext)
syspath = joinpath(PATHS[:folder], split(candpath, '/')...)
isfile(syspath) && return csv2html(syspath, header)
end
return html_err("table matching '$path' not found")
end


"""
$SIGNATURES
Internal function to process an array of strings to markdown table (in one single string).
If header is empty, the first row of the file will be used for header.
"""
function csv2html(path, header)::String
csvcontent = readdlm(path, ',', String, header=false)
nrows, ncols = size(csvcontent)
io = IOBuffer()
# writing the header
if ! isempty(header)
# header provided
newheader = split(header, ",")
hs = size(newheader,1)
hs != ncols && return html_err("header size ($hs) and number of columns ($ncols) do not match")
write(io, prod("| " * h * " " for h in newheader))
rowrange = 1:nrows
else
# header from csv file
write(io, prod("| " * csvcontent[1, i] * " " for i in 1:ncols))
rowrange = 2:nrows
end
# writing end of header & header separator
write(io, "|\n|", repeat( " ----- |", ncols), "\n")
# writing content
for i in rowrange
for j in 1:ncols
write(io, "| ", csvcontent[i,j], " ")
end
write(io, "|\n")
end
return md2html(String(take!(io)))
end


"""
$SIGNATURES
Expand All @@ -94,6 +160,7 @@ const LXCOM_SIMPLE = Dict{String, Function}(
"\\output" => resolve_lx_output, # include plain output generated by code
"\\figalt" => resolve_lx_figalt, # include a figure (may or may not have been generated)
"\\file" => resolve_lx_file, # include a file
"\\tableinput" => resolve_lx_tableinput, # include table from a csv file
)


Expand Down
1 change: 1 addition & 0 deletions src/jd_vars.jl
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ the site. See [`resolve_lxcom`](@ref).
GLOBAL_LXDEFS["\\figalt"] = LxDef("\\figalt", 2, EMPTY_SS)
GLOBAL_LXDEFS["\\fig"] = LxDef("\\fig", 1, subs("\\figalt{}{#1}"))
GLOBAL_LXDEFS["\\file"] = LxDef("\\file", 2, subs("[#1]()"))
GLOBAL_LXDEFS["\\tableinput"] = LxDef("\\tableinput", 2, EMPTY_SS)
# text formatting
GLOBAL_LXDEFS["\\underline"] = LxDef("\\underline", 1,
subs("~~~<span style=\"text-decoration:underline;\">!#1</span>~~~"))
Expand Down
76 changes: 76 additions & 0 deletions test/converter/lx_simple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,79 @@ end
<p>View $(J.html_err("file matching '/assets/blih.pdf' not found")) here.</p>
""")
end

@testset "table: source with header" begin
testcsv = "h1,h2,h3\nstring1, 1.567, 0\n,,\n l i n e ,.158,99999999"
write(joinpath(J.PATHS[:assets], "testcsv.csv"), testcsv)
# no header specified
h = raw"""
A table:
\tableinput{}{/assets/testcsv.csv}
Done.
""" |> seval
shouldbe = """<p>A table: <table><tr><th>h1</th><th>h2</th><th>h3</th></tr>
<tr><td>string1</td><td>1.567</td><td>0</td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td>l i n e</td><td>.158</td><td>99999999</td></tr></table>
Done.</p>"""
@test isapproxstr(h, shouldbe)
# header specified
h = raw"""
A table:
\tableinput{A,B,C}{/assets/testcsv.csv}
Done.
""" |> seval
shouldbe = """<p>A table: <table><tr><th>A</th><th>B</th><th>C</th></tr>
<tr><td>h1</td><td>h2</td><td>h3</td></tr>
<tr><td>string1</td><td>1.567</td><td>0</td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td>l i n e</td><td>.158</td><td>99999999</td></tr></table>
Done.</p>"""
@test isapproxstr(h, shouldbe)
# wrong header
h = raw"""
A table:
\tableinput{,}{/assets/testcsv.csv}
Done.
""" |> seval
shouldbe = """<p>A table: <p><span style=\"color:red;\">// header size (2) and number of columns (3) do not match //</span></p>
Done.</p>"""
@test isapproxstr(h, shouldbe)
end

@testset "table: source without header" begin
testcsv = "string1, 1.567, 0\n,,\n l i n e ,.158,99999999"
write(joinpath(J.PATHS[:assets], "testcsv.csv"), testcsv)
# no header specified
h = raw"""
A table:
\tableinput{}{/assets/testcsv.csv}
Done.
""" |> seval
shouldbe = """<p>A table: <table><tr><th>string1</th><th>1.567</th><th>0</th></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td>l i n e</td><td>.158</td><td>99999999</td></tr></table>
Done.</p>"""
@test isapproxstr(h, shouldbe)
# header specified
h = raw"""
A table:
\tableinput{A,B,C}{/assets/testcsv.csv}
Done.
""" |> seval
shouldbe = """<p>A table: <table><tr><th>A</th><th>B</th><th>C</th></tr>
<tr><td>string1</td><td>1.567</td><td>0</td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td>l i n e</td><td>.158</td><td>99999999</td></tr></table>
Done.</p>"""
@test isapproxstr(h, shouldbe)
# wrong header
h = raw"""
A table:
\tableinput{A,B}{/assets/testcsv.csv}
Done.
""" |> seval
shouldbe = """<p>A table: <p><span style=\"color:red;\">// header size (2) and number of columns (3) do not match //</span></p>
Done.</p>"""
@test isapproxstr(h, shouldbe)
end

0 comments on commit afb833e

Please sign in to comment.