diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bfe84b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__output/ +tags +notes.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0bad819 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +OUTPUT_DIR = __output/ + +.PHONY: all tex pdf clean + +all: pdf + +tex: + @go run . + +pdf: tex + @mtxrun --path=$(OUTPUT_DIR) --script context cv.tex + +clean: + @rm -rf $(OUTPUT_DIR) diff --git a/data/cv.json b/data/cv.json new file mode 100644 index 0000000..94890d9 --- /dev/null +++ b/data/cv.json @@ -0,0 +1,174 @@ +{ + "firstName": "Dan", + "lastName": "Anglin", + "jobTitle": "Software Engineer", + "contact": { + "email": "d.n.i.anglin@gmail.com", + "phone": "07XXX-XXX-XXX", + "location": "Hertfordshire, UK" + }, + "links": { + "gitlab": "gitlab.com/dananglin", + "github": "github.com/dananglin" + }, + "summary": "To be completed.", + "technologies": [ + { + "category": "Programming Languages", + "values": [ + "Go", + "Bash", + "Python" + ] + }, + { + "category": "Containerisation", + "values": [ + "Docker", + "Kubernetes", + "LXD" + ] + }, + { + "category": "Continuous Integration", + "values": [ + "Git", + "GitLab", + "Gitea", + "GitLab CI", + "Drone", + "Nexus", + "Jenkins" + ] + }, + { + "category": "Monitoring and Alerting", + "values": [ + "Prometheus", + "Alertmanager", + "Grafana" + ] + }, + { + "category": "Other Tools, Services or Frameworks", + "values": [ + "AWS", + "Openstack", + "UKCloud", + "GNU/Linux", + "Elasticsearch", + "Logstash", + "Fluentd", + "Kibana", + "Ansible", + "Puppet", + "OpenAPI v3", + "PostgreSQL", + "HAProxy", + "Kanban", + "Scrum", + "JIRA" + ] + } + ], + "employment": [ + { + "company": "Ocado Technology", + "location": "Hatfield, Hertfordshire", + "jobTitle": "Platform Automation Engineer", + "duration": { + "start": "January, 2017", + "end": "Present" + }, + "details": [ + "To be completed..." + ] + }, + { + "company": "QA Consulting (Contracted to CACI)", + "location": "London", + "jobTitle": "DevOps Consultant", + "duration": { + "start": "March, 2015", + "end": "November, 2016" + }, + "details": [ + "I've built and supported Production infrastructure hosted in UKCloud (for public sector projects) and AWS (for commercial projects).", + "My general tasks included creating Ansible playbooks for network configuration, hardening of RedHat/CentOS VMs and software installation and configuration.", + "I've set up resilient and fault-tolerant infrastructure using tools such as HAProxy and Keepalive.", + "I've built a cross-domain protective monitoring solution using the Elastic stack with Redis.", + "I've installed and configured Nagios/Icinga for infrastructure monitoring.", + "I was involved in setting up the production Docker environment for a government website." + ] + }, + { + "company": "QA Consulting (Contracted to IBM)", + "location": "Home Office, London", + "jobTitle": "DevOps Consultant", + "duration": { + "start": "October, 2014", + "end": "March, 2015 " + }, + "details": [ + "I've built and supported new development, CIT and SIT environments for new projects at the Home Office.", + "These environments were built on UKCloud using Jenkins and UKCloud's REST API.", + "Linux (RedHat/CentOS) virtual machines were provisioned and hardenend using Puppet and Jenkins.", + "I learnt how to work in an Agile environment with SCRUM; using JIRA for keeping track of my Sprint tasks.", + "Confluence was used for internal documentation and I used it to create documentation for new starters in the DevOps space." + ] + }, + { + "company": "QA Consulting", + "location": "Worthing", + "jobTitle": "Trainee DevOps Consultant", + "duration": { + "start": "September, 2014", + "end": "October, 2014" + }, + "details": [ + "I've spent eight weeks in training to become a DevOps Consultant on behalf of QA Consulting.", + "I've completed a two week intensive training course on business analysis. Trainig course covered client engagement, businnes process modelling, use case modelling and process and evaluation improvements.", + "I then completed a six week hands-on training course on the fundamentals of DevOps and Continuous Integration.", + "The DevOps training course consisted of learning about various software such as Puppet, Jenkins, Nexus, Maven, Git, Tomcat and the Atlassian tools.", + "The training also included using Vagrant and Puppet to automate the provisioning of small CI and development environments for the continuous delivery of internal Java projects." + ] + }, + { + "company": "School of Systems Engineering, University of Reading", + "location": "Reading", + "jobTitle": "Undergraduate Researcher", + "duration": { + "start": "July, 2012", + "end": "September, 2012" + }, + "details": [ + "This was a summer internship where I helped developed an interactive Java application that processes Terahertz signals in order to reveal hidden details behind walls.", + "This was aimed at those who were not specialised in Terahertz signals to use an application to aid in their research in archaeology.", + "The hidden details were captured in images or videos that could be viewed in a 2D or a 3D virtual environment.", + "During this internship I learnt new techniques for processing Terahertz signals.", + "I was able to use sample Terahertz data to reveal and display hidden details within a 3D virtual environment." + ] + } + ], + "education": [ + { + "school": "University of Reading", + "location": "Reading", + "qualification": "MEng (Hons) in Robotics, 2:1", + "duration": { + "start": "October, 2009", + "end": "July, 2013" + } + }, + { + "school": "St Angela's & St Bonaventure's Sixth Form College", + "location": "London", + "qualification": "A-Levels in Maths, Physics and Product Design", + "duration": { + "start": "September, 2007", + "end": "July, 2009" + } + } + ], + "interests": "To be completed..." +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2827b29 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module gitlab.com/dananglin/cv + +go 1.12 diff --git a/main.go b/main.go new file mode 100644 index 0000000..38f0210 --- /dev/null +++ b/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + "log" + "os" + "text/template" +) + +var ( + cvDataPath string = "data/cv.json" + cvTemplateDir string = "template/" + cvOutputDir string = "__output/" + cvOutputFileName string = "cv.tex" +) + +func main() { + + var cv Cv + cvOutputPath := cvOutputDir + cvOutputFileName + + fmap := template.FuncMap{ + "notLastElement": notLastElement, + } + + // Read the JSON data + log.Printf("INFO: Attempting to read data from %s...", cvDataPath) + data, err := ioutil.ReadFile(cvDataPath) + if err != nil { + log.Fatalf("ERROR: Unable to read data from file. %s", err.Error()) + } + log.Print("INFO: Successfully read data.") + + log.Printf("INFO: Attempting to unmarshal JSON data...") + if err = json.Unmarshal(data, &cv); err != nil { + log.Fatalf("ERROR: Unable to unmarshal JSON data. %s", err.Error()) + } + log.Println("INFO: JSON unmarshalling was successful.") + + // Create the Output tex file + log.Printf("INFO: Attempting to create output file %s...", cvOutputPath) + if err = os.MkdirAll(cvOutputDir, 0750); err != nil { + log.Fatalf("ERROR: Unable to create output directory %s. %s", cvOutputDir, err.Error()) + } + output, err := os.Create(cvOutputPath) + if err != nil { + log.Fatalf("ERROR: Unable to create output file %s. %s.", cvOutputPath, err.Error()) + } + defer output.Close() + log.Printf("INFO: Successfully created output file %s.", cvOutputPath) + + // Execute template engine and produce the resulting TEX file + log.Println("INFO: Attempting template execution...") + t := template.Must(template.New("cv.tex.tmpl").Funcs(fmap).Delims("<<", ">>").ParseGlob(cvTemplateDir + "*.tex.tmpl")) + + if err = t.Execute(output, cv); err != nil { + log.Fatalf("ERROR: Unable to execute the CV template. %s", err.Error()) + } + log.Println("INFO: Template execution successful.") +} + +// notLastElement returns true if an element of an array +// or a slice is not the last. +func notLastElement(pos, length int) bool { + return pos < length-1 +} diff --git a/model.go b/model.go new file mode 100644 index 0000000..e718632 --- /dev/null +++ b/model.go @@ -0,0 +1,46 @@ +package main + +type Cv struct { + FirstName string `json:"firstName"` + LastName string `json:"lastName"` + JobTitle string `json:"jobTitle"` + Contact Contact `json:"contact"` + Links Links `json:"links"` + Summary string `json:"summary"` + Technologies []Technologies `json:"technologies"` + Employment []Experience `json:"employment"` + Education []Experience `json:"education"` + Interests string `json:"interests"` +} + +type Contact struct { + Email string `json:"email"` + Phone string `json:"phone"` + Location string `json:"location"` +} + +type Links struct { + GitLab string `json:"gitlab"` + GitHub string `json:"github"` + Website string `json:"website,omitempty"` +} + +type Technologies struct { + Category string `json:"category"` + Values []string `json:"values"` +} + +type Experience struct { + Company string `json:"company,omitempty"` + School string `json:"school,omitempty"` + Location string `json:"location"` + JobTitle string `json:"jobTitle,omitempty"` + Qualification string `json:"qualification,omitempty"` + Duration Duration `json:"duration"` + Details []string `json:"details"` +} + +type Duration struct { + Start string `json:"start"` + End string `json:"end"` +} diff --git a/template/cv.tex.tmpl b/template/cv.tex.tmpl new file mode 100644 index 0000000..a3d11b6 --- /dev/null +++ b/template/cv.tex.tmpl @@ -0,0 +1,53 @@ +<<- /* Prepend the setup area */ ->> +<< template "cv_setup.tex.tmpl" .>> + +\starttext + \starttitleAndContact + \cvtitle{<<.FirstName>> <<.LastName>>}{<<.JobTitle>>} + \titleAndContact + {\bf Email:} <<.Contact.Email>>\blank[none] + {\bf Phone:} <<.Contact.Phone>>\blank[none] + {\bf Location:} <<.Contact.Location>>\blank[medium] + + {\bf GitLab:} \goto{<<.Links.GitLab>>}[url(https://<<.Links.GitLab>>)]\blank[none] + {\bf GitHub:} \goto{<<.Links.GitHub>>}[url(https://<<.Links.GitHub>>)]\blank[none] + \stoptitleAndContact + + \section{SUMMARY} + <<.Summary>> + + \section{SKILLS SUMMARY} + \starttabulate[|w(0.3\textwidth)lB|lp(0.7\textwidth)|] + <<$lenTech := len .Technologies>> + <> + <<$lenValues := len $tech.Values>> + \NC <<$tech.Category>> \NC <><<$val>><>, <><>\NC\NR + <>\TB[1mm]<> + <> + \stoptabulate + \section{EXPERIENCE} + <<- range .Employment>> + \jobsection{<<.Company>>}{<<.Location>>}{<<.JobTitle>>}{<<.Duration.Start>> - <<.Duration.End>>} + \startitemize + <> + \item <<.>> + <> + \stopitemize + <> + + \section{EDUCATION} + <> + \jobsection{<<.School>>}{<<.Location>>}{<<.Qualification>>}{<<.Duration.Start>> - <<.Duration.End>>} + \startitemize + <> + \item <<.>> + <> + \stopitemize + <> + + \section{OTHER INTERESTS} + <<.Interests>> + + \section{REFERENCES} + References are available upon request. +\stoptext diff --git a/template/cv_setup.tex.tmpl b/template/cv_setup.tex.tmpl new file mode 100644 index 0000000..1708c41 --- /dev/null +++ b/template/cv_setup.tex.tmpl @@ -0,0 +1,84 @@ +\version [final] + +% set main language to English +\mainlanguage [en] + +% set paper size to A4 +\setuppapersize [A4] + +% define the page layout +\setuplayout + [backspace=20mm, + bottomspace=20mm, + cutspace=0mm, + footer=0mm, + header=0mm, + height=middle, + topspace=10mm, + width=middle] +% \showframe + +% remove page numbers by setting an empty location +\setuppagenumbering[location=] + +% Colour definitions for headings +\definecolor [bluetheme][h=1155cc] +\definecolor [greytheme][h=434343] + +% Font setup +% https://wiki.contextgarden.net/Simplefonts +\definefontfamily [cvfontfamily][rm][LiberationSerif] +\definefontfamily [cvfontfamily][ss][Carlito] +\definefontfamily [cvfontfamily][tt][LiberationMono] +\definefontfamily [cvfontfamily][mm][LibertinusMath] +\setupbodyfont [cvfontfamily,ss,10pt] + +% define the font scaling when using commands such as \tfa, \tfb, etc +\definebodyfontenvironment [10pt][a=11pt,b=20pt,c=32pt] + +% cvtitle outputs the title of the CV +% arguments: +% #1 - name: name of the individual +% #2 - role: job role of the individual +\define[2]\cvtitle{ + {\bfc#1}\blank[small]{\tfb#2} +} + +% jobsection outputs the summary of a work experience +% arguments: +% #1 - company: the company where the work experience took place +% #2 - location: the location of the work experience +% #3 - role: the role of the work experience +% #4 - duration: the duration of the work experience (e.g. March, 2017 - April, 2018) +\define[4]\jobsection{ + {\bfa#1,} {\tfa#2} — {\tfa\em#3}\blank[small]\color[greytheme]{#4}\blank[line] +} +\definehead [skillssection][subsection] + +\setuphead [section][color=bluetheme] +\setuphead [skillssection][color=black] +\setuphead [section, skillssection][number=no, align=right, style=\bfa] + +% setup the format of items (bullet points) +\setupitemize + [1] + [packed] + [align=right, + symbol=1, + margin=no, + distance=1mm] + +% defineparagraphs and setupparagraphs allows us to create custom +% columns. Here we define and configure a paragraph environment for the CV title +% and contact columns. +% https://wiki.contextgarden.net/Command/defineparagraphs +% https://wiki.contextgarden.net/Command/setupparagraphs +\defineparagraphs[titleAndContact][n=2,after={\blank}] +\setupparagraphs [titleAndContact][1][width=0.6\textwidth] +\setupparagraphs [titleAndContact][2][width=0.4\textwidth] + +% use Interaction to create hyperlinks to external profiles +\setupinteraction + [state=start, + color=bluetheme, + click=]