Intermediate commit
- Following info is now parsed from yaml, specified with --config flag default path for config: ./data/resume.yaml - Summary - Experience - Education - Skills - Languages Need to update sample YAML with mock data
This commit is contained in:
parent
67215cf921
commit
8b7978f7b5
|
@ -0,0 +1 @@
|
||||||
|
resume.yaml
|
27
main.go
27
main.go
|
@ -2,9 +2,12 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -13,9 +16,18 @@ import (
|
||||||
//go:embed "static/css/*.css" "templates/*.html"
|
//go:embed "static/css/*.css" "templates/*.html"
|
||||||
var static embed.FS
|
var static embed.FS
|
||||||
var templates map[string]*template.Template
|
var templates map[string]*template.Template
|
||||||
|
var configFile string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
readConfig("")
|
cfgFile := flag.String("config", "./data/resume.yaml", "Path to the configuration YAML")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
_, err := readConfig(*cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error(fmt.Sprintf("error reading configuration: %s", err.Error()))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
configFile = *cfgFile
|
||||||
staticFileServer := http.FileServer(http.FS(static))
|
staticFileServer := http.FileServer(http.FS(static))
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc("/", home)
|
mux.HandleFunc("/", home)
|
||||||
|
@ -24,7 +36,7 @@ func main() {
|
||||||
mux.Handle("/static/", staticFileServer)
|
mux.Handle("/static/", staticFileServer)
|
||||||
|
|
||||||
slog.Info("Starting go-resume server, listening on port 3000")
|
slog.Info("Starting go-resume server, listening on port 3000")
|
||||||
err := http.ListenAndServe(":3000", mux)
|
err = http.ListenAndServe("0.0.0.0:3000", mux)
|
||||||
slog.Error(err.Error())
|
slog.Error(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +55,7 @@ func home(w http.ResponseWriter, r *http.Request) {
|
||||||
slog.Error(err.Error())
|
slog.Error(err.Error())
|
||||||
http.Error(w, "Server error", http.StatusInternalServerError)
|
http.Error(w, "Server error", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
data, err := readConfig("")
|
data, err := readConfig(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error(err.Error())
|
slog.Error(err.Error())
|
||||||
http.Error(w, "Server error", http.StatusInternalServerError)
|
http.Error(w, "Server error", http.StatusInternalServerError)
|
||||||
|
@ -54,9 +66,18 @@ func home(w http.ResponseWriter, r *http.Request) {
|
||||||
data.Theme = urlSlice[len(urlSlice)-1]
|
data.Theme = urlSlice[len(urlSlice)-1]
|
||||||
}
|
}
|
||||||
data.Year = time.Now().Format("2006")
|
data.Year = time.Now().Format("2006")
|
||||||
|
tmpl.Funcs(templateFunctions())
|
||||||
err = tmpl.Execute(w, *data)
|
err = tmpl.Execute(w, *data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error(err.Error())
|
slog.Error(err.Error())
|
||||||
http.Error(w, "Server error", http.StatusInternalServerError)
|
http.Error(w, "Server error", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func templateFunctions() template.FuncMap {
|
||||||
|
return template.FuncMap{
|
||||||
|
"html": func(raw string) template.HTML {
|
||||||
|
return template.HTML(raw)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
91
resume.go
91
resume.go
|
@ -1,5 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
)
|
||||||
|
|
||||||
type Resume struct {
|
type Resume struct {
|
||||||
Theme string `yaml:"theme"`
|
Theme string `yaml:"theme"`
|
||||||
Meta *Meta `yaml:"meta"`
|
Meta *Meta `yaml:"meta"`
|
||||||
|
@ -8,16 +13,45 @@ type Resume struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Profile struct {
|
type Profile struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Title string `yaml:"title"`
|
Title string `yaml:"title"`
|
||||||
City string `yaml:"city"`
|
City string `yaml:"city"`
|
||||||
Phone string `yaml:"phone"`
|
Phone string `yaml:"phone"`
|
||||||
Email string `yaml:"email"`
|
Email string `yaml:"email"`
|
||||||
Socials Socials `yaml:"socials"`
|
Socials Socials `yaml:"socials"`
|
||||||
Photo string `yaml:"photo"`
|
Photo string `yaml:"photo"`
|
||||||
|
Summary string `yaml:"summary"`
|
||||||
|
Experience []Job `yaml:"experience"`
|
||||||
|
Education []Education `yaml:"education"`
|
||||||
|
Skills []Skill `yaml:"skills"`
|
||||||
|
Projects any `yaml:"projects"`
|
||||||
|
Languages []Language `yaml:"languages"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type job struct {
|
func (p *Profile) GetTagWidth(t string) string {
|
||||||
|
l := 0
|
||||||
|
switch t {
|
||||||
|
case "skills":
|
||||||
|
for _, skill := range p.Skills {
|
||||||
|
if len(skill.Name) > l {
|
||||||
|
l = len(skill.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "languages":
|
||||||
|
for _, skill := range p.Languages {
|
||||||
|
if len(skill.Name) > l {
|
||||||
|
l = len(skill.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%dch", l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (job *Job) html(raw string) template.HTML {
|
||||||
|
return template.HTML(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Job struct {
|
||||||
Company string `yaml:"company"`
|
Company string `yaml:"company"`
|
||||||
Location string `yaml:"location"`
|
Location string `yaml:"location"`
|
||||||
Title string `yaml:"title"`
|
Title string `yaml:"title"`
|
||||||
|
@ -25,24 +59,40 @@ type job struct {
|
||||||
Description []string `yaml:"description"`
|
Description []string `yaml:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type jobs []job
|
type Education struct {
|
||||||
|
|
||||||
type education struct {
|
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
City string `yaml:"city"`
|
|
||||||
Degree string `yaml:"degree"`
|
Degree string `yaml:"degree"`
|
||||||
Period string `yaml:"period"`
|
|
||||||
Faculty string `yaml:"faculty"`
|
Faculty string `yaml:"faculty"`
|
||||||
|
City string `yaml:"city"`
|
||||||
|
Period string `yaml:"period"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type educations []education
|
type Skill struct {
|
||||||
|
|
||||||
type skill struct {
|
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Level string `yaml:"level"`
|
Level int `yaml:"level"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type skills []skill
|
func _visualLevel(level int) template.HTML {
|
||||||
|
tagString := ""
|
||||||
|
if level > 5 {
|
||||||
|
level = 5
|
||||||
|
} else if level < 1 {
|
||||||
|
level = 1
|
||||||
|
}
|
||||||
|
for i := 0; i < level; i++ {
|
||||||
|
tagString += "<span class=\"tag is-info is-success is-size-6\" style=\"color: black\">☆</span>"
|
||||||
|
}
|
||||||
|
return template.HTML(tagString)
|
||||||
|
}
|
||||||
|
func (skill *Skill) VisualLevel(level int) template.HTML {
|
||||||
|
return _visualLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (language *Language) VisualLevel(level int) template.HTML {
|
||||||
|
return _visualLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
type skills []Skill
|
||||||
|
|
||||||
type Social struct {
|
type Social struct {
|
||||||
Media string `yaml:"media"`
|
Media string `yaml:"media"`
|
||||||
|
@ -60,3 +110,8 @@ type Meta struct {
|
||||||
Robots string `yaml:"robots"`
|
Robots string `yaml:"robots"`
|
||||||
ThemeColor string `yaml:"theme-color"`
|
ThemeColor string `yaml:"theme-color"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Language struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Level int `yaml:"level"`
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ body {
|
||||||
.education .item,
|
.education .item,
|
||||||
.projects .item,
|
.projects .item,
|
||||||
.skills .item,
|
.skills .item,
|
||||||
|
.languages .item,
|
||||||
.awards .item {
|
.awards .item {
|
||||||
padding: .5em 0; }
|
padding: .5em 0; }
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ body {
|
||||||
.education,
|
.education,
|
||||||
.projects,
|
.projects,
|
||||||
.skills,
|
.skills,
|
||||||
|
.languages,
|
||||||
.awards {
|
.awards {
|
||||||
.item {
|
.item {
|
||||||
padding: .5em 0;
|
padding: .5em 0;
|
||||||
|
|
|
@ -38,6 +38,7 @@ body {
|
||||||
.education .item,
|
.education .item,
|
||||||
.projects .item,
|
.projects .item,
|
||||||
.skills .item,
|
.skills .item,
|
||||||
|
.languages .item,
|
||||||
.awards .item {
|
.awards .item {
|
||||||
padding: .5em 0; }
|
padding: .5em 0; }
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,22 @@
|
||||||
<head>
|
<head>
|
||||||
{{ template "meta" . }}
|
{{ template "meta" . }}
|
||||||
<link rel="stylesheet" href="/static/css/{{ .Theme }}-style.css" />
|
<link rel="stylesheet" href="/static/css/{{ .Theme }}-style.css" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
{{ if .Profile.Skills }}
|
||||||
|
.skilltag {
|
||||||
|
display: inline-block;
|
||||||
|
width: {{ .Profile.GetTagWidth "skills" }};
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Profile.Languages }}
|
||||||
|
.languagetag {
|
||||||
|
display: inline-block;
|
||||||
|
width: {{ .Profile.GetTagWidth "languages" }};
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="body-app">
|
<body id="body-app">
|
||||||
|
@ -21,7 +37,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-2">
|
<div class="column is-2">
|
||||||
<figure class="image image is-128x128">
|
<figure class="image image is-128x128">
|
||||||
<img alt="Profile photo" class="is-rounded" src="{{ .Profile.Photo }}" /> <!--<%= content.data.profile.photo %>" />-->
|
<img alt="Profile photo" class="is-rounded" src="{{ .Profile.Photo }}" />
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
<div class="column has-text-grey-light has-text-right-in-desktop">
|
<div class="column has-text-grey-light has-text-right-in-desktop">
|
||||||
|
@ -55,55 +71,72 @@
|
||||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||||
SUMMARY
|
SUMMARY
|
||||||
</div>
|
</div>
|
||||||
<div class="wrapper"><%- content.data.summary %></div>
|
<div class="wrapper">{{ .Profile.Summary }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ with .Profile.Experience }}
|
||||||
<div class="experience">
|
<div class="experience">
|
||||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||||
EXPERIENCE
|
EXPERIENCE
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% content.data.experiences.forEach(function(experience){ %>
|
<!--<% content.data.experiences.forEach(function(experience){ %>-->
|
||||||
|
{{ range . }}
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="is-size-5">
|
<div class="is-size-5">
|
||||||
<b><%= experience.company %>,</b>
|
<!-- <div class="columns is-gapless">-->
|
||||||
<span class="has-text-weight-semi-bold">
|
<!-- <div class="column">-->
|
||||||
<%= experience.location %> — <i><%= experience.title %></i>
|
<span><b>{{ .Company }}</b></span>
|
||||||
</span>
|
<!-- </div>-->
|
||||||
|
<!-- <div class="column">-->
|
||||||
|
<span class="has-text-weight-semi-bold"> - <i>{{ .Title }}</i></span>
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
</div>
|
</div>
|
||||||
<div class="is-size-7 tag is-primary"><%= experience.period %></div>
|
<div>{{ .Location }}</div>
|
||||||
<% if (experience.jobdesc.length > 0) { %>
|
<div class="is-size-6 tag is-primary">{{ .Period }}</div>
|
||||||
|
{{ with .Description }}
|
||||||
<ul style="list-style: disc; margin-left: 1em">
|
<ul style="list-style: disc; margin-left: 1em">
|
||||||
<% experience.jobdesc.forEach(function(job){ %>
|
{{ range . }}
|
||||||
<li><%= job %></li>
|
<li>{{ . | html }}</li>
|
||||||
<% }); %>
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
<% } %>
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
<% }); %>
|
{{ end }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with .Profile.Education }}
|
||||||
<div class="education">
|
<div class="education">
|
||||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||||
EDUCATION
|
EDUCATION
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% content.data.educations.forEach(function(education){ %>
|
{{ range . }}
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="is-size-5">
|
<div class="is-size-5">
|
||||||
<b><%= education.name %></b>,
|
<!-- <div class="columns is-gapless">-->
|
||||||
<span class="has-text-weight-semi-bold">
|
<!-- <div class="column">-->
|
||||||
<%= education.city %> — <i><%= education.degree %></i>
|
<span><b>{{ .Degree }}</b>, </span>
|
||||||
</span>
|
<!-- </div>-->
|
||||||
|
<!-- <div class="column">-->
|
||||||
|
<span class="has-text-weight-semi-bold">{{ .Name }}</span>
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</div>
|
||||||
|
<div>{{ .Faculty }}
|
||||||
|
<div class="is-size-6 tag is-primary ml-5">{{ .Period }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="is-size-7 tag is-primary"><%= education.period %></div>
|
|
||||||
<div><%= education.faculty %></div>
|
|
||||||
</div>
|
</div>
|
||||||
<% }); %>
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
{{ if or .Profile.Projects (or .Profile.Skills .Profile.Languages) }}
|
||||||
<div class="column">
|
<div class="column">
|
||||||
|
{{ with .Profile.Projects }}
|
||||||
<div class="projects">
|
<div class="projects">
|
||||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||||
PROJECTS
|
PROJECTS
|
||||||
|
@ -116,34 +149,57 @@
|
||||||
—
|
—
|
||||||
<i><%= proj.company %></i>
|
<i><%= proj.company %></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="is-size-7 tag is-primary"><%= proj.period %></div>
|
<div class="is-size-6 tag is-primary"><%= proj.period %></div>
|
||||||
</div>
|
</div>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with .Profile.Skills }}
|
||||||
<div class="skills">
|
<div class="skills">
|
||||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||||
SKILLS
|
SKILLS
|
||||||
</div>
|
</div>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<% content.data.skills.forEach(function(skill){ %>
|
{{ range . }}
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div><%= skill.name %></div>
|
<div class="field is-grouped is-grouped-multiline">
|
||||||
|
<div class="control">
|
||||||
|
<div class="tags has-addons">
|
||||||
|
<span class="skilltag tag is-size-6">{{ .Name }}</span>
|
||||||
|
{{ .VisualLevel .Level }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% }); %>
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with .Profile.Languages }}
|
||||||
<div class="languages">
|
<div class="languages">
|
||||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||||
LANGUAGES
|
LANGUAGES
|
||||||
</div>
|
</div>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<%= content.data.languages.join(', ') %>
|
{{ range . }}
|
||||||
|
<div class="item">
|
||||||
|
<div class="field is-grouped is-grouped-multiline">
|
||||||
|
<div class="control">
|
||||||
|
<div class="tags has-addons">
|
||||||
|
<span class="languagetag tag is-size-6">{{ .Name }}</span>
|
||||||
|
{{ .VisualLevel .Level }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
Loading…
Reference in New Issue