diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d162fe1..687c86c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,6 @@ name: Build and Deploy run-name: ${{ github.ref_name }} - "${{ github.event.head_commit.message }}" on: - # TODO: other environments? pull_request: paths: - 'generator/**' @@ -83,6 +82,7 @@ jobs: with: sparse-checkout: | quartzModified + scripts - name: Clone Quartz 4 run: git clone https://github.com/jackyzha0/quartz.git - uses: actions/setup-node@v4 @@ -126,13 +126,45 @@ jobs: compression-level: 6 overwrite: true include-hidden-files: false - +# clean-bucket: +# needs: [build-quartz, generate-markdown] +# if: | +# contains(github.event.pull_request.labels.*.name, 'clean bucket') +# && always() && !cancelled() +# && (needs.generate-markdown.result == 'success') +# && needs.build-quartz.result == 'success' +# runs-on: ubuntu-latest +# permissions: +# id-token: write +# contents: read +# actions: write +# steps: +# # - uses: actions/checkout@v4 +# # with: +# # sparse-checkout: | +# # terraform +# # static +# # - name: Setup Terraform +# # uses: hashicorp/setup-terraform@v3 +# # with: +# # terraform_version: "1.14.6" +# - name: Configure AWS Credentials +# uses: aws-actions/configure-aws-credentials@v6.0.0 +# with: +# role-to-assume: ${{ secrets.AWS_RUNNER_ROLE }} +# aws-region: ${{ env.AWS_REGION }} +# role-session-name: reeceSubdomainBucketCleanup +# # TODO: ensure this does not mess up the terraform! +# - name: clean out s3 bucket # Do not do this often because it is more expensive +# run: aws s3 rm s3://${{ env.TF_VAR_subdomain }}.${{ env.TF_VAR_domain }} --recursive deploy: needs: [build-quartz, generate-markdown] if: | - always() && !cancelled() + (github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'force deploy')) + && always() && !cancelled() && (needs.generate-markdown.result == 'success') && needs.build-quartz.result == 'success' +# && (needs.clean-bucket.result == 'success' || needs.clean-bucket.result == 'skipped') runs-on: ubuntu-latest permissions: id-token: write @@ -167,13 +199,15 @@ jobs: mv -f static/* terraform/public/static/ fi fi - # TODO: ONLY REPLACE FILES THAT HAVE CHANGED! + - name: Change file timestamps + run: go run scripts/changeTimestamps/... -path=quartz/public -date=03-22-2026 # TODO: ensure works correctly - name: initialize terraform run: terraform init -backend-config='bucket=${{ vars.TERRAFORM_STATE_BUCKET }}' -backend-config='region=${{ env.AWS_REGION }}' working-directory: terraform - name: plan terraform run: terraform plan -var-file='deploy.tfvars' -var 'cloudflare_api_token=${{secrets.CLOUDFLARE_API_TOKEN}}' -var 'cloudflare_zone_id=${{secrets.CLOUDFLARE_ZONE_ID}}' -var 'aws_region=${{env.AWS_REGION}}' -out=plan.tf working-directory: terraform + # TODO: ensure changed fileNames actually delete the old files! (should but unsure with terraform resources on s3 objects specifically) - name: apply terraform run: terraform apply -auto-approve plan.tf working-directory: terraform diff --git a/generate.sh b/generate.sh index 701b2f8..02290b6 100755 --- a/generate.sh +++ b/generate.sh @@ -1,2 +1,19 @@ -chmod -R 777 ./quartz -go run ./generator # TODO: delete file if unused \ No newline at end of file +# Execute commands in a subshell so that the final directory is the initial +( + current_dir_name=$(basename "$PWD") + case "$current_dir_name" in + "CV") + echo "working from CV directory" + ;; + "scripts") + echo "working from scripts directory" + cd ../ || exit 1 + ;; + *) + echo "unhandled working directory: $current_dir_name" + exit 1 + ;; + esac + chmod -R 777 ./quartz + go run ./generator +) diff --git a/generator/aliases.go b/generator/aliases.go new file mode 100644 index 0000000..5f048cd --- /dev/null +++ b/generator/aliases.go @@ -0,0 +1,36 @@ +package main + +import "strings" + +var aliases = map[string]linkableAlias{} // Map of link to link + +func initAliases() { + newLinkableAlias("IaC", smIAC) // TODO: ADD TAGS? + newLinkableAlias("CiCd", smCiCd) + newLinkableAlias("CI/CD", smCiCd) // TODO: NOT WORKING + newLinkableAlias("k8s", techs["Kubernetes"]) // TODO: ok? + newLinkableAlias("KMS", cloudServices["Key Management Service"]) // TODO: ok? + newLinkableAlias("ASM", langs["Assembly"]) // TODO: ok? + newLinkableAlias("WebAssembly", langs["WASM"]) // TODO: ok? +} +func newLinkableAlias(name string, linkTo Linkable) { + out := linkableAlias{name: name, finalLink: linkTo} + aliases[name] = out + addLinkable(strings.ToLower(name), out) +} + +type linkableAlias struct { + name string + finalLink Linkable +} + +func (pg linkableAlias) Title() string { + return pg.name +} + +func (pg linkableAlias) Dst() string { // TODO: ptr? + return pg.finalLink.Dst() +} +func (pg linkableAlias) EntryType() string { + return pg.name +} diff --git a/generator/blogPost.go b/generator/blogPost.go index fb8d4ad..111195e 100644 --- a/generator/blogPost.go +++ b/generator/blogPost.go @@ -54,6 +54,9 @@ func (post BlogPost) Bytes() []byte { func (post BlogPost) Link() string { return fmt.Sprintf(`[%s](blog/%s)`, post.Title, withoutSpaces(post.Title)) } +func (pg BlogPost) Dst() string { + return dstFor("blog", withoutSpaces(pg.Title)) +} const blogPostDir = "blogPosts/" diff --git a/generator/cache.go b/generator/cache.go index 257f637..da38d7b 100644 --- a/generator/cache.go +++ b/generator/cache.go @@ -1,5 +1,7 @@ package main +import "strings" + var caches = map[string]*CachePage{} type CachePage struct { @@ -7,11 +9,25 @@ type CachePage struct { *tracked } -func (pg *CachePage) Link() string { +func Link(linkable Linkable) string { + return linkFor(linkable.Title(), linkable.Dst()) +} + +func (pg *CachePage) Dst() string { if pg == nil { return "NO_LINK" } - return linkFor(pg.Name, "cv", "cache", withoutSpaces(pg.Name)) + return dstFor("cv", "cache", withoutSpaces(pg.Name)) +} + +func (pg *CachePage) Title() string { + if pg == nil { + return "NO_TITLE" + } + return pg.Name +} +func dstFor(elems ...string) string { + return strings.Join(elems, "/") } func (pg *CachePage) EntryType() string { return "Cache" @@ -23,5 +39,6 @@ func NewCache(name string) *CachePage { tracked: newTracked(), } caches[name] = out + addLinkable(strings.ToLower(name), out) return out } diff --git a/generator/client.go b/generator/client.go index a3e102f..10b5085 100644 --- a/generator/client.go +++ b/generator/client.go @@ -1,6 +1,7 @@ package main import ( + "appli.ng/cv/generator/tags" "appli.ng/cv/generator/utils" "fmt" "maps" @@ -13,12 +14,25 @@ var clientsOrder = []string{} type Client struct { Name string + Description string // TODO: ADD THIS? Info []string // Information points on a client. // TODO: ADD INFO TO ALL CLIENTS AND DISPLAY Projects []*Project projectsSet utils.Set[string] //Languages []string // Calculated later // Parent company *CompanyPage + Tags tags.Field +} + +func (pg *Client) Title() string { + if pg == nil { + return "NO_NAME" + } + return pg.Name +} +func (pg *Client) WithTags(tags ...tags.Tag) *Client { + pg.Tags = append(pg.Tags, tags...) + return pg } func (pg *Client) NameValue() string { @@ -29,32 +43,39 @@ func (pg *Client) ProjectTypeInfo() projectTypeInfo { return professionalProjectTypeInfo{client: pg} } -func (pg *Client) Link() string { +const noLinkText = "NO_LINK" + +func (pg *Client) Dst() string { if pg == nil { - return "NO_LINK" + return noLinkText } - return linkFor(pg.Name, "cv", "client", withoutSpaces(pg.Name)) + return dstFor("cv", "client", withoutSpaces(pg.Name)) } func (pg *Client) EntryType() string { return "Client" } -func NewClient(name string, info ...string) *Client { +func NewClient(name string) *Client { out := &Client{Name: name, Projects: []*Project{}, Info: nil, projectsSet: utils.Set[string]{}} if _, exists := clients[name]; exists { panic("client already exists") } - out.Info = info clients[name] = out + addLinkable(strings.ToLower(name), out) clientsOrder = append(clientsOrder, name) return out } +func (pg *Client) WithInfo(info ...string) *Client { + pg.Info = info + return pg +} + func (pg *Client) Bytes() []byte { builder := strings.Builder{} builder.WriteString(frontmatterFor(pg.Name, "Client")) if pg.Info != nil && len(pg.Info) > 0 { - builder.WriteString("# Responsibilities and Achievements \n") + builder.WriteString("# Notable Information\n") for _, info := range pg.Info { builder.WriteString(fmt.Sprintf("- %s\n", info)) } @@ -62,14 +83,13 @@ func (pg *Client) Bytes() []byte { if pg.Projects != nil && len(pg.Projects) > 0 { builder.WriteString("# Projects\n") for _, proj := range pg.Projects { - builder.WriteString(fmt.Sprintf("- %s\n", proj.Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(proj))) } } ls := languagesFor(pg.Projects) if len(ls) > 0 { builder.WriteString("# Languages\n") builder.WriteString("Language | Usage Frequency[[" + withoutSpaces(pg.Name) + "#^usageFrequencies\\|*]]\n") - // TODO: remove frequency??? builder.WriteString(":-- | :--\n") freqs := make(map[Frequency][]string, 6) for name, freq := range ls { @@ -81,7 +101,7 @@ func (pg *Client) Bytes() []byte { } for f := 5; f >= 0; f-- { for _, name := range freqs[Frequency(f)] { - builder.WriteString(fmt.Sprintf("%s | %s\n", langs[name].Link(), Frequency(f).String())) + builder.WriteString(fmt.Sprintf("%s | %s\n", Link(langs[name]), Frequency(f).String())) } } } @@ -120,7 +140,7 @@ func (c *Client) WithProjects(projs ...*Project) *Client { techs[l].AddClient(c) } for l := range maps.Keys(proj.CloudProviders) { - providers[l].AddClient(c) // TODO: cloud services? + providers[l].AddClient(c) } for l := range maps.Keys(proj.Platforms) { platforms[l].AddClient(c) @@ -164,47 +184,85 @@ func (pg *Client) GetAllLowest() (outCaches map[string]*CachePage, outDbs map[st return } +func initClientsLast() { + +} + var ( - jdClient = NewClient("John Deere", - "Fortune 100 Agricultural Business (Think: Green Tractors)", // TODO: FIX ALL POINTS - ) - sourceAlliesClient = NewClient("Source Allies", - "Source Allies internal projects", // TODO: FIX ALL POINTS - ) - critColaClient = NewClient("CritCola", - "ADD SUMMARY POINTS", // TODO: FIX ALL POINTS - ) - wellAwareClient = NewClient("Well Aware NC", - "ADD SUMMARY POINTS", // TODO: FIX ALL POINTS - ) - clarkClient = NewClient("Chapel Hill Masters Student in Public Health", - "ADD SUMMARY POINTS", // TODO: FIX ALL POINTS - ) - charityClient = NewClient("Undisclosed Charity", - "ADD SUMMARY POINTS", // TODO: FIX ALL POINTS - ) - teiClient = NewClient("TEI", - "ADD SUMMARY POINTS", // TODO: FIX ALL POINTS - ) - taeClient = NewClient("Talley Associates of Engineering", // TODO: JS photo parser - "ADD SUMMARY POINTS", // TODO: FIX ALL POINTS - ) - mafcClient = NewClient("Monroe Aquatics and Fitness Center", - "ADD SUMMARY POINTS", // TODO: FIX ALL POINTS - ) + jdClient = NewClient("John Deere") + simpsonUniversityClient = NewClient("Simpson University") + sourceAlliesClient = NewClient("Source Allies") + critColaClient = NewClient("CritCola") + arrowNailClient = NewClient("ArrowNail LLC") + wellAwareClient = NewClient("Well Aware NC") + clarkClient = NewClient("Chapel Hill Masters Student in Public Health") + wildlifeRClient = NewClient("Wildlife mapping with R") + charityClient = NewClient("Undisclosed Charity") + teiClient = NewClient("TEI") + taeClient = NewClient("Talley Associates of Engineering") + mafcClient = NewClient("Monroe Aquatics and Fitness Center") ) func initClientsAfterProjectsComplete() { - jdClient = jdClient.WithProjects(polygonBuilderProject, ogreProject, renderProject, statsProject, billingProject, explorerProject, wqdbProject, supportProject, scudsProject, ufoProject, goweProject, tileGenProject, GhaRunnersProject) - sourceAlliesClient = sourceAlliesClient.WithProjects(simpsonUnivProject) // TODO: USE! - // TODO: SIMPSON COLLEGE // TODO: USE! - critColaClient = critColaClient.WithProjects(CritColaProject) - wellAwareClient = wellAwareClient.WithProjects(WellAwareProject) - clarkClient = clarkClient.WithProjects(MastersDataAnalysisProject) - charityClient = charityClient.WithProjects(CharityProject) - teiClient = teiClient.WithProjects(teiProjects) // TODO: add projects (like NM, TX, IA, NC?) - taeClient = taeClient.WithProjects(taeProjects) // TODO: JS photo parser - mafcClient = mafcClient.WithProjects(mafcProjects) // TODO: Indoor and outdoor pool? + jdClient = jdClient. + WithProjects(polygonBuilderProject, ogreProject, renderProject, statsProject, billingProject, explorerProject, wqdbProject, supportProject, scudsProject, ufoProject, goweProject, tileGenProject, GhaRunnersProject).WithInfo( + "Most senior consulting engineer on a high-performance, global-scale, team of 4-8 at John Deere’s Intelligent Solutions Group; responsible for architecture, implementation, testing, optimization, and support of a complex set of diverse cloud services utilizing geospatiotemporal agribusiness data", + "Architected, implemented, and maintained cloud infrastructure and services for ingest, distributed processing, storage, manipulation, and retrieval of data for, datastores totalling over 50PB", + "Created multiple "+Link(lookup("ECS"))+" clusters for use in, and consuming data from, John Deere "+Link(lookup("AI"))+" platforms", + "Designed "+Link(lookup("CUDA"))+" "+Link(lookup("C"))+"/[C++](cv/language/Cpp) Kernels used through "+Link(lookup("CGo"))+" for statistics and image processing via GPU", + "Improved a mission-critical image manipulation API from 65% reliability to 99.9999% success rate", + "Spearheaded implementation of an "+Link(lookup("ECS"))+" cluster using an advanced "+Link(lookup("topology"))+" algorithm (from a PhD thesis), achieving >100x performance gains over its original implementation on TB-scale datasets", + "Built a "+Link(lookup("Go"))+"-based compiler that transformed nested "+Link(lookup("JSON"))+" instructions into machine-executable operations across clusters of "+Link(lookup("EC2"))+" instances for for retrieving and manipulating geospatial agricultural data", + "__Saved \\$18M of a \\$28M budget (64%)__ in 2024, while still increasing service stability and throughput", + "Ensured maximum service uptime via careful design and rollout of "+Link(lookup("CI-CD"))+" pipelines operated via self-hosted GitHub Actions runners, in conjunction with Infrastructure as Code via "+Link(lookup("Terraform")), + "Setup monitoring, dashboards, alerting, traces, and profiling ("+Link(lookup("Datadog"))+"/"+Link(lookup("Grafana"))+"/"+Link(lookup("Cloudwatch"))+")", + "Responsible for educating engineers on infrastructure, codebase, domain, and best practices", + "Utilized primarily "+Link(lookup("Go"))+", "+Link(lookup("Terraform"))+", "+Link(lookup("Bash"))+", and "+Link(lookup("Docker"))+" on "+Link(lookup("AWS"))+", but also used "+Link(lookup("Scala"))+", "+Link(lookup("Github Actions"))+", "+Link(lookup("C"))+"/[C++](cv/language/Cpp) with "+Link(lookup("CUDA"))+", "+Link(lookup("DroneCI"))+", "+Link(lookup("Python"))+", "+Link(lookup("Javascript"))+", "+Link(lookup("Typescript"))+", "+Link(lookup("Kotlin"))+", and more", + "Platforms utilized: AWS (>30 separate services), "+Link(lookup("Datadog"))+", "+Link(lookup("LogCentral"))+", "+Link(lookup("Rally"))+", "+Link(lookup("Azure DevOps"))+", "+Link(lookup("Github"))+", "+Link(lookup("DroneCI"))+", "+Link(lookup("Grafana"))+", "+Link(lookup("Prometheus"))+", "+Link(lookup("Confluence"))+", and more"). + WithTags(tags.AgTech) + sourceAlliesClient = sourceAlliesClient. + WithProjects(jamfProject, smallImprovementsProject, internalResumeGeneratorProject). // TODO: USE OTHERS + WithInfo( + "Source Allies internal projects", + "Upgraded company internal payment gateway to a newer version of "+Link(lookup("Java"))+" "+Link(lookup("Spring")), + "Secured all company machines via "+Link(lookup("JAMF"))+" to ensure the protection of company and client data", + "Designed and created a "+Link(lookup("Slack"))+" bot integration with "+Link(lookup("Small Improvements"))+" to automate monthly announcements, and employee creation and completion of personal and professional goals", + "Redesigned "+Link(lookup("Jira"))+" workflows streamlining the hiring process and onboarding systems for remote coworkers", + ). + WithTags(tags.Consulting) + simpsonUniversityClient = simpsonUniversityClient. + WithProjects(simpsonUnivProject). + WithInfo("Upgraded the University’s payment and donation gateway. Remediated resulting bugs"). + WithTags(tags.Education) + critColaClient = critColaClient. + WithProjects(CritColaProject). + WithInfo( + "Consulted on hosting game servers and "+Link(lookup("Discord"))+" bots for a large online community such that they could be deployed or destroyed, on short notice with persistent game data utilizing "+Link(lookup("Gitlab CI"))+", "+Link(lookup("Terraform"))+", "+Link(lookup("Cloudflare"))+", and "+Link(lookup("AWS"))+" ("+Link(lookup("EC2"))+", "+Link(lookup("EBS"))+", "+Link(lookup("IAM"))+")", + "Created a final product with a spin-up time of approximately 3 minutes", + ).WithTags(tags.Entertainment) + wellAwareClient = wellAwareClient.WithProjects(WellAwareProject). + WithInfo("Designed, created, and hosted a website for Well Aware NC, a University of North Carolina Chapel Hill affiliated nonprofit focused on the testing of well water contaminants within North Carolina"). + WithTags(tags.PublicHealth, tags.Education) + clarkClient = clarkClient.WithProjects(MastersDataAnalysisProject). + WithInfo("Created programs for a student doing research for his Masters Degree in Public Health. Provided data on E.Coli samples from different waterways, the programs checked the statistical validity on different E.Coli indicating kits"). + WithTags(tags.PublicHealth, tags.Education) + charityClient = charityClient.WithProjects(CharityProject). + WithInfo("Designed, created, and hosted a website for an undisclosed local charity"). + WithTags(tags.Charity) + teiClient = teiClient.WithProjects(teiProjects). + WithInfo("Internal company work for TEI"). // TODO: WEBSITE? // TODO: add projects (like NM, TX, IA, NC?) + WithTags(tags.Telecom) + taeClient = taeClient.WithProjects(taePhotoImporter). + WithInfo("Internal company work for Talley Associates of Engineering"). + WithTags(tags.Telecom) + mafcClient = mafcClient.WithProjects(mafcProjects). + WithInfo("Lifeguarding work, both at the indoor and outdoor pools of the fitness center"). // TODO: Indoor and outdoor pool? + WithTags(tags.FirstAid) + arrowNailClient = arrowNailClient.WithProjects(ArrowNailProject). + WithInfo("Used "+Link(lookup("Serverless"))+" services on "+Link(lookup("AWS"))+" to support a "+Link(lookup("React"))+" geospatial web app, Node API via "+Link(lookup("lambda"))+" functions, and an "+Link(lookup("Aurora"))+" database. Provided IT and Systems Administration Support", + "Set up "+Link(lookup("CI-CD"))+" pipeline in "+Link(lookup("Gitlab CI"))+" to make future deployments seamless"). + WithTags(tags.Construction) + wildlifeRClient = wildlifeRClient.WithProjects(WildlifeRProject). + WithInfo("Utilized spatiotemporal data for wildlife in a specified area over a specified date range in order to produce population density maps"). + WithTags(tags.Ecology) } - -var () diff --git a/generator/common.go b/generator/common.go index 32f1300..9e6ff36 100644 --- a/generator/common.go +++ b/generator/common.go @@ -75,32 +75,32 @@ func bytesForAll(item showAll, showFrequencies bool) string { } for f := 5; f >= 0; f-- { for _, name := range freqs[Frequency(f)] { - b.WriteString(fmt.Sprintf("%s | %s\n", langs[name].Link(), Frequency(f).String())) + b.WriteString(fmt.Sprintf("%s | %s\n", Link(langs[name]), Frequency(f).String())) } } } if len(tempC) > 0 { b.WriteString("# Caches\n") for name, _ := range tempC { - b.WriteString(fmt.Sprintf("%s\n", caches[name].Link())) + b.WriteString(fmt.Sprintf("%s\n", Link(caches[name]))) } } if len(tempD) > 0 { b.WriteString("# Databases\n") for name, _ := range tempD { - b.WriteString(fmt.Sprintf("%s\n", dbs[name].Link())) + b.WriteString(fmt.Sprintf("%s\n", Link(dbs[name]))) } } if len(tempP) > 0 { b.WriteString("# Platforms\n") for name, _ := range tempP { - b.WriteString(fmt.Sprintf("%s\n", platforms[name].Link())) + b.WriteString(fmt.Sprintf("%s\n", Link(platforms[name]))) } } if len(tempT) > 0 { b.WriteString("# Technologies\n") for name, _ := range tempT { - b.WriteString(fmt.Sprintf("%s\n", techs[name].Link())) + b.WriteString(fmt.Sprintf("%s\n", Link(techs[name]))) } } if len(tempS) > 0 { @@ -108,9 +108,9 @@ func bytesForAll(item showAll, showFrequencies bool) string { for name, svcs := range tempS { tempSvcs := make([]string, len(svcs)) for i, svc := range slices.Collect(maps.Keys(svcs)) { - tempSvcs[i] = fmt.Sprintf("%s\n", cloudServices[svc].Link()) + tempSvcs[i] = fmt.Sprintf("%s\n", Link(cloudServices[svc])) } - b.WriteString(fmt.Sprintf("%s: %s\n", providers[name].Link(), strings.Join(tempSvcs, ", "))) + b.WriteString(fmt.Sprintf("%s: %s\n", Link(providers[name]), strings.Join(tempSvcs, ", "))) } } if showFrequencies { @@ -148,28 +148,28 @@ func newTracked() *tracked { Tags: utils.Set[string]{}, } } -func (pg *tracked) Bytes(title string, typ string) []byte { +func (pg *tracked) Bytes(title string, sm *SubjectMatter, tags ...string) []byte { builder := strings.Builder{} if title != "" { - builder.WriteString(frontmatterFor(title, typ)) + builder.WriteString(frontmatterFor(title, tags...)) } if pg.Companies != nil && len(pg.Companies) > 0 { builder.WriteString("# Companies\n") for _, com := range pg.Companies { - builder.WriteString(fmt.Sprintf("- %s\n", com.Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(com))) } } if pg.Positions != nil && len(pg.Positions) > 0 { builder.WriteString("# Positions\n") for _, pos := range pg.Positions { - builder.WriteString(fmt.Sprintf("- %s\n", pos.Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(pos))) } } if pg.Clients != nil && len(pg.Clients) > 0 { builder.WriteString("# Clients\n") for _, cli := range pg.Clients { - builder.WriteString(fmt.Sprintf("- %s\n", cli.Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(cli))) } } if pg.Projects != nil && len(pg.Projects) > 0 { @@ -177,9 +177,13 @@ func (pg *tracked) Bytes(title string, typ string) []byte { //builder.WriteString("Project | Usage Frequency | Project Type\n") //builder.WriteString(":-- | :--: | :--\n") for _, proj := range pg.Projects { - builder.WriteString(fmt.Sprintf("- %s\n", proj.Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(proj))) } } + if sm != nil { + builder.WriteString("\n\nSubject Matter: (HIDE THIS STRING)" + sm.Dst() + "\n") + sm.Dst() + } return []byte(builder.String()) } @@ -208,25 +212,25 @@ func (tr *tracked) String() string { if tr.Projects != nil { b.WriteString("# Projects\n") for _, proj := range tr.Projects { - b.WriteString(fmt.Sprintf("- %s\n", proj.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(proj))) } } if tr.Positions != nil { b.WriteString("# Positions\n") for _, pos := range tr.Positions { // TODO: ENSURE THESE ARE IN ORDER - b.WriteString(fmt.Sprintf("- %s at %s\n", pos.Link(), pos.company.Link())) + b.WriteString(fmt.Sprintf("- %s at %s\n", Link(pos), Link(pos.company))) } } if tr.Companies != nil { b.WriteString("# Companies\n") for _, item := range tr.Companies { - b.WriteString(fmt.Sprintf("- %s\n", item.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(item))) } } if tr.Clients != nil { b.WriteString("# Clients\n") for _, client := range tr.Clients { - b.WriteString(fmt.Sprintf("- %s\n", client.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(client))) } } return b.String() @@ -305,7 +309,59 @@ func FrequencyFromString(s string) Frequency { } } +var ( + _ Linkable = &CachePage{} + _ Linkable = &Client{} + _ Linkable = NoLinkable{} + _ Linkable = &CompanyPage{} + _ Linkable = &DbPage{} + _ Linkable = &SchoolPage{} + _ Linkable = &Interest{} // TODO: ptr ok? + _ Linkable = &LanguagePage{} + _ Linkable = &MiscSkill{} + _ Linkable = &PlatformPage{} + _ Linkable = &Position{} + _ Linkable = &Project{} + _ Linkable = &CloudProviderPage{} + _ Linkable = &CloudServicePage{} + _ Linkable = SubjectMatter("") + _ Linkable = &TechologyPage{} +) + type Linkable interface { // TODO: use??? EntryType() string - Link() string + // Link() string + Dst() string // TODO: impl + Title() string +} + +var linkables = map[string]Linkable{} // TODO: USE THIS +type NoLinkable struct{} + +func (n NoLinkable) Dst() string { + return "error.md" // TODO: ok? +} + +func (n NoLinkable) Title() string { + return "FIX_ME" +} + +func (n NoLinkable) EntryType() string { + panic("nilEntryType") +} + +func (n NoLinkable) Link() string { + return fixmeLink +} + +func addLinkable(name string, item Linkable) { + linkables[strings.ToLower(name)] = item +} + +func lookup(name string) Linkable { + if item, ok := linkables[strings.ToLower(name)]; ok { + return item + } + panic("lookup failed for " + name) // TODO: del? + return NoLinkable{} } diff --git a/generator/company.go b/generator/company.go index 65e3927..06dea7f 100644 --- a/generator/company.go +++ b/generator/company.go @@ -41,16 +41,21 @@ type CompanyPage struct { //Languages []string // Calculated later } -func (pg *CompanyPage) NameValue() string { - return pg.Name +func (pg *CompanyPage) Dst() string { + return dstFor("cv", "company", withoutSpaces(pg.Name)) } -func (pg *CompanyPage) Link() string { +func (pg *CompanyPage) Title() string { if pg == nil { - return "NO_LINK" + return noLinkText } - return linkFor(pg.Name, "cv", "company", withoutSpaces(pg.Name)) + return pg.Name +} + +func (pg *CompanyPage) NameValue() string { + return pg.Name } + func (pg *CompanyPage) EntryType() string { return "Company" } @@ -75,6 +80,7 @@ func NewCompany(name string, startMo, startYr int, endMo, endYr *int) *CompanyPa } } companies[name] = out + addLinkable(strings.ToLower(name), out) companiesOrder = append(companiesOrder, name) return out } @@ -88,7 +94,7 @@ func (pg *CompanyPage) Bytes() []byte { // TODO: NOT PROPERLY SORTED // TODO: ORDERING? // TODO: POS NOT WORKING ON DEERE CLIENT PAGE - builder.WriteString(fmt.Sprintf("- %s\n", pos.Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(pos))) } } clis := pg.Clients() @@ -96,7 +102,7 @@ func (pg *CompanyPage) Bytes() []byte { if clis != nil && len(clis) > 0 { builder.WriteString("# Clients\n") for _, client := range clis { - builder.WriteString(fmt.Sprintf("- %s\n", client.Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(client))) } } // resolve projects @@ -104,7 +110,7 @@ func (pg *CompanyPage) Bytes() []byte { if len(ps) > 0 { builder.WriteString("# Projects\n") for _, proj := range ps { - builder.WriteString(fmt.Sprintf("- %s\n", proj.Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(proj))) } } // Resolve languages @@ -122,7 +128,7 @@ func (pg *CompanyPage) Bytes() []byte { } for f := 5; f >= 0; f-- { for _, name := range freqs[Frequency(f)] { - builder.WriteString(fmt.Sprintf("- %s\n", langs[name].Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(langs[name]))) } } } @@ -216,7 +222,7 @@ func (pg *CompanyPage) GetAllLowest() (outCaches map[string]*CachePage, outDbs m func initCompaniesAfterPositions() { _ = NewCompany("Source Allies", 5, 2022, nil, nil). - WithPositions(sai1, sai2, sai3) + WithPositions(sai1, sai2, sai3) // TODO: SAI not showing SAI as a client _ = NewCompany("Freelance", 1, 2012, utils.Pointer(6), utils.Pointer(2022)). // TODO: RENAME WithPositions(freelancePosition) _ = NewCompany("TEI", 1, 2019, utils.Pointer(3), utils.Pointer(2020)). // TODO: ENSURE DATES ARE RIGHT diff --git a/generator/db.go b/generator/db.go index 411d386..92267f8 100644 --- a/generator/db.go +++ b/generator/db.go @@ -1,5 +1,7 @@ package main +import "strings" + var dbs = map[string]*DbPage{} type DbPage struct { @@ -7,11 +9,15 @@ type DbPage struct { *tracked } -func (pg *DbPage) Link() string { +func (pg *DbPage) Dst() string { + return dstFor("cv", "db", withoutSpaces(pg.Name)) +} + +func (pg *DbPage) Title() string { if pg == nil { - return "NO_LINK" + return noLinkText } - return linkFor(pg.Name, "cv", "db", withoutSpaces(pg.Name)) + return pg.Name } func (pg *DbPage) EntryType() string { @@ -27,6 +33,7 @@ func NewDb(name string) *DbPage { // out.EquivalentLink = cloudServices[name].Link() // TODO: ????????? //} dbs[name] = out + addLinkable(strings.ToLower(name), out) // TODO: will services like DynamoDB overwrite this? return out } diff --git a/generator/education.go b/generator/education.go index 25a688d..1845958 100644 --- a/generator/education.go +++ b/generator/education.go @@ -28,6 +28,7 @@ func NewSchool(name string) *SchoolPage { SubjectMatters: map[SubjectMatter]struct{}{}, } schools[name] = out + addLinkable(strings.ToLower(name), out) return out } func (pg *SchoolPage) WithSummary(info string) *SchoolPage { @@ -51,11 +52,15 @@ func (pg *SchoolPage) ProjectTypeInfo() projectTypeInfo { return schoolProjectTypeInfo{school: pg} } -func (pg *SchoolPage) Link() string { +func (pg *SchoolPage) Dst() string { + return dstFor("cv", "school", withoutSpaces(pg.Name)) +} + +func (pg *SchoolPage) Title() string { if pg == nil { - return "NO_LINK" + return noLinkText } - return linkFor(pg.Name, "cv", "school", withoutSpaces(pg.Name)) + return pg.Name } func (pg *SchoolPage) EntryType() string { @@ -77,7 +82,7 @@ func (sp *SchoolPage) Bytes() []byte { // Write all projects b.WriteString("# Projects\n") for _, proj := range sp.Projects { - b.WriteString(fmt.Sprintf("- %s\n", proj.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(proj))) } // Write all extracurriculars b.WriteString("# Extracurriculars and positions held\n") @@ -96,7 +101,7 @@ func (sp *SchoolPage) Bytes() []byte { if len(sp.SubjectMatters) > 0 { b.WriteString("# Related Subject Matters\n") for sm, _ := range sp.SubjectMatters { - b.WriteString(fmt.Sprintf("- %s\n", sm.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(sm))) } } @@ -165,10 +170,10 @@ func NewEcPosition(title, notes string) EcPosition { var ( schoolCata = NewSchool("Central Academy of Technology and Arts"). - WithSummary("Magnet High School, Engineering"). - WithSubjectMatters(smEducation, smStatics, smElectronics). - WithDegree(DegHS). - WithExtracurriculars( + WithSummary("Magnet High School, Engineering"). + WithSubjectMatters(smEducation, smStatics, smElectronics). + WithDegree(DegHS). + WithExtracurriculars( NewExtracurricular("Soccer", fixmeLink, NewEcPosition("Varsity", "Sophomore-Senior year"), NewEcPosition("Junior Varsity", "Freshman year"), @@ -190,11 +195,11 @@ var ( NewExtracurricular("Beta club", fixmeLink), ) schoolNCSU = NewSchool("North Carolina State University"). - WithSummary("Undergraduate studies"). - WithDegree(DegNE). - WithDegree(DegMath). - WithSubjectMatters(smNuclearEngineering, smParticlePhysics, smFluidMechanics, smThermodynamics, smEducation, smStatics, smElectronics). - WithExtracurriculars( + WithSummary("Undergraduate studies"). + WithDegree(DegNE). + WithDegree(DegMath). + WithSubjectMatters(smNuclearEngineering, smParticlePhysics, smFluidMechanics, smThermodynamics, smEducation, smStatics, smElectronics). + WithExtracurriculars( NewExtracurricular("American Nuclear Society", fixmeLink), NewExtracurricular("88.1 WKNC FM HD1 Raleigh", fixmeLink, NewEcPosition("DJ", "Sophomore-Junior years"), diff --git a/generator/interests.go b/generator/interests.go index 9553c7e..ef428bb 100644 --- a/generator/interests.go +++ b/generator/interests.go @@ -1,5 +1,7 @@ package main +import "strings" + var interests = map[string]*Interest{} type Interest struct { @@ -13,6 +15,7 @@ func NewInterest(name string) *Interest { tracked: newTracked(), } interests[name] = out + addLinkable(strings.ToLower(name), out) return out } @@ -20,8 +23,15 @@ func (pg *Interest) EntryType() string { return "Interest" } -func (i *Interest) Link() string { - return linkForInterest(i.Name) +func (pg *Interest) Dst() string { + return dstFor("cv", "interest", withoutSpaces(pg.Name)) +} + +func (pg *Interest) Title() string { + if pg == nil { + return noLinkText + } + return pg.Name } func linkForInterest(name string) string { diff --git a/generator/language.go b/generator/language.go index b6bc99c..e9451f2 100644 --- a/generator/language.go +++ b/generator/language.go @@ -30,11 +30,18 @@ func (pg *LanguagePage) WithSubjectMatters(sms ...SubjectMatter) *LanguagePage { return pg } -func (pg *LanguagePage) Link() string { +func (pg *LanguagePage) Dst() string { + return dstFor("cv", "language", withoutSpaces(pg.Name)) +} + +func (pg *LanguagePage) Title() string { if pg == nil { - return "NO_LINK" + return noLinkText + } + if pg.Name == "Cpp" { + return "C++" // TODO: escape plusses? } - return linkFor(pg.Name, "cv", "language", withoutSpaces(pg.Name)) + return pg.Name } func (pg *LanguagePage) Bytes() []byte { @@ -44,13 +51,13 @@ func (pg *LanguagePage) Bytes() []byte { builder.WriteString("# Companies\n") for companyName := range pg.Companies { - builder.WriteString(fmt.Sprintf("- %s\n", companies[companyName].Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(companies[companyName]))) } } if pg.Clients != nil && len(pg.Clients) > 0 { builder.WriteString("# Clients\n") for name := range pg.Clients { - builder.WriteString(fmt.Sprintf("- %s\n", clients[name].Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(clients[name]))) } } if pg.Projects != nil && len(pg.Projects) > 0 { @@ -80,7 +87,7 @@ func (pg *LanguagePage) Bytes() []byte { panic("unknown project type: " + v) } - builder.WriteString(fmt.Sprintf("%s | %s | %s\n", projects[name].Link(), Frequency(f).String(), projTypeStr)) + builder.WriteString(fmt.Sprintf("%s | %s | %s\n", Link(projects[name]), Frequency(f).String(), projTypeStr)) } } } @@ -117,6 +124,7 @@ func NewLanguage(name string) *LanguagePage { SubjectMattersField: SubjectMattersField{utils.Set[SubjectMatter]{}}, } langs[name] = out + addLinkable(strings.ToLower(name), out) return out } diff --git a/generator/main.go b/generator/main.go index 9ff3bc7..17d84b3 100644 --- a/generator/main.go +++ b/generator/main.go @@ -1,6 +1,7 @@ package main import ( + "appli.ng/cv/generator/utils" "fmt" "maps" "os" @@ -18,6 +19,9 @@ import ( // TODO: sort projects on projects page? (maybe chronological?) (alphabetical?) // TODO: backlinks only on the subject matter pages???? // TODO: PUT TAGS ALL OVER PROJECTS???!!!!!! +// TODO: Folder page should alphabetize contents on list + +// TODO: add TDD? OOP? functional? func main() { for _, dir := range []string{"cv", "blog", "note"} { @@ -42,8 +46,8 @@ func main() { initClientsAfterProjectsComplete() // Sets client on projects as well initPositionsAfterProjects() initCompaniesAfterPositions() // Must be done after positions and project setup, but before projects pages. What about clients? - // TODO: POPULATE SUBJECT MATTERS ON TECHS, SERVICES, MISCSKILLS? + initAliases() // TODO: ok ???? // Start creating actual pages createLanguagesPages() @@ -96,16 +100,19 @@ func createMainCVPage() { // TODO: TAGS EVERYWHERE???? // TODO: https://quartz.jzhao.xyz/configuration PAGE TITLE // TODO: THEMEING // https://quartz.jzhao.xyz/configuration // TODO: FORCE DARK MODE + // TODO: figure out how to change rich text preview (when sent via discord, snapchat, or RCS + // TODO: ADD QR CODES???? + // TODO: MODIFY FAVICON + // TODO: MODIFY TAB TITLES b := strings.Builder{} b.WriteString(frontmatterFor("CV")) // TODO: HEADER AREA FOR LINKS TO CV, RESUME, BLOG, NOTES? - b.WriteString("Welcome to my CV! It is a living document that is updated occasionally.\n\n") // Why does this need 2 newlines? // TODO: move down? - b.WriteString("Looking for a resume instead? [Download it here](Resume.pdf)ENSURE WORKING\n\n") // TODO: ENSURE OK - b.WriteString("Feel free to check out the [source code](https://github.com/reeceappling/CV) for this website\n") // TODO: ENSURE OK + b.WriteString("Welcome to my CV! It is a living document that is updated occasionally.\n\n") // Why does this need 2 newlines? + b.WriteString("Looking for a resume instead? [Download it here](static/Resume_Reece_Appling.pdf)ENSURE WORKING\n\n") // TODO: ENSURE OK + b.WriteString("Feel free to check out the [source code](https://github.com/reeceappling/CV) for this website\n") b.WriteString("# About\n") - b.WriteString(fixmeLink + "\n") // TODO: SUMMARY/About b.WriteString("# Work History ([Companies](companies.md), [Positions](positions.md))\n") // TODO: RENAME @@ -117,7 +124,7 @@ func createMainCVPage() { // TODO: TAGS EVERYWHERE???? if endDate == "current" { endDate = "" } - b.WriteString(fmt.Sprintf("%s | %s | %s | %s\n", company.Positions[0].Name, company.Link(), company.Positions[len(company.Positions)-1].Start.String(), endDate)) + b.WriteString(fmt.Sprintf("%s | %s | %s | %s\n", company.Positions[0].Name, Link(company), company.Positions[len(company.Positions)-1].Start.String(), endDate)) } b.WriteString("# Education\n") @@ -130,7 +137,7 @@ func createMainCVPage() { // TODO: TAGS EVERYWHERE???? for i, d := range school.Degrees { temp[i] = d.StringShort() } - b.WriteString(fmt.Sprintf("- %s %s\n", school.Link(), strings.Join(temp, ", "))) + b.WriteString(fmt.Sprintf("- %s %s\n", Link(school), strings.Join(temp, ", "))) } b.WriteString("# Certifications\n") @@ -152,31 +159,33 @@ func createMainCVPage() { // TODO: TAGS EVERYWHERE???? b.WriteString("[see all languages LINK BROKEN](language/)\n") // TODO: delete? b.WriteString("## Preferred (in order)\n") for _, name := range []string{"Go", "Typescript", "Terraform", "Bash"} { - b.WriteString(fmt.Sprintf("- %s\n", langs[name].Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(langs[name]))) } b.WriteString("## All\n") b.WriteString(alphabetizedLinksCompressed(langs, "language")) - b.WriteString("# Databases\n[Full Page](dbs.md)\n\n") // TODO: why does this take 2 newlines?? + b.WriteString("# Databases\n[Full Page](dbs.md)\n\n") b.WriteString(alphabetizedLinksCompressed(dbs, "db")) - b.WriteString("# Caches\n[Full Page](caches.md)\n\n") // TODO: why does this take 2 newlines?? + b.WriteString("# Caches\n[Full Page](caches.md)\n\n") b.WriteString(alphabetizedLinksCompressed(caches, "cache")) b.WriteString("# Cloud Providers\n[Full Page](providers.md)\n") b.WriteString("## Preferred\n") for _, name := range []string{"AWS"} { - b.WriteString(fmt.Sprintf("- %s\n", providers[name].Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(providers[name]))) } b.WriteString("## All\n") b.WriteString(alphabetizedLinksCompressed(providers, "provider")) // TODO: ADD GCP! Azure! b.WriteString("# Technologies and Libraries\n[Full Page](technologies.md)\n\n") // TODO: why does this take 2 newlines?? - b.WriteString(alphabetizedLinksCompressed(techs, "technology")) // TODO: dir correct? + b.WriteString(alphabetizedLinksCompressed(techs, "technology")) - b.WriteString("## Containerization\n\n") // TODO: why does this take 2 newlines?? - b.WriteString("## Distributed Computing\n\n") // TODO: why does this take 2 newlines?? + b.WriteString("## Containerization\n\n") + b.WriteString(fixmeLink + "\n") // TODO: this! + b.WriteString("## Distributed Computing\n\n") + b.WriteString(fixmeLink + "\n") // TODO: this! // TODO: POPULATE THIS AREA!!!!!!!!!!!!! @@ -225,7 +234,7 @@ func createMainCVPage() { // TODO: TAGS EVERYWHERE???? alphabetizedSkills := slices.Collect(maps.Keys(miscSkills)) sort.Strings(alphabetizedSkills) for _, name := range alphabetizedSkills { - b.WriteString(fmt.Sprintf("- %s\n", miscSkills[name].Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(miscSkills[name]))) } b.WriteString("# Interests\n") // TODO: move???? alphabetizedInterests := slices.Collect(maps.Keys(interests)) @@ -239,7 +248,7 @@ func createMainCVPage() { // TODO: TAGS EVERYWHERE???? return string(alphabetizedSms[i]) < string(alphabetizedSms[j]) }) for _, name := range alphabetizedSms { - b.WriteString(fmt.Sprintf("- %s\n", name.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(name))) } WriteFile("cv.md", b.String()) } @@ -257,7 +266,7 @@ func createLanguagesPages() { b.WriteString("# Preferred (in order)\n") for _, name := range []string{"Go", "Typescript", "Terraform", "Bash"} { l := langs[name] - b.WriteString(fmt.Sprintf("- %s\n", l.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(l))) } b.WriteString("# All\n") b.WriteString("[see all languages](language/)\n") // TODO: delete? @@ -293,21 +302,21 @@ func createProjectsPages() { for _, proj := range projects { switch proj.TypeInfo.Type() { case projectTypePersonal: - bPersonal.WriteString(fmt.Sprintf("- %s\n", proj.Link())) + bPersonal.WriteString(fmt.Sprintf("- %s\n", Link(proj))) case projectTypeProfessional: - pr := proj.Link() + pr := Link(proj) var co, cl = "none", "none" client := proj.TypeInfo.getClient() if client != nil { - cl = client.Link() + cl = Link(client) if client.company != nil { - co = client.company.Link() + co = Link(client.company) } } bProfessional.WriteString(fmt.Sprintf("%s | %s | %s\n", pr, co, cl)) case projectTypeSchool: - bSchool.WriteString(fmt.Sprintf("- %s\n", proj.Link())) // TODO: school link??? + bSchool.WriteString(fmt.Sprintf("- %s\n", Link(proj))) // TODO: school link??? default: panic("unknown project type") @@ -334,7 +343,7 @@ func createSchoolsPages() { b := strings.Builder{} b.WriteString(frontmatterFor("Schools")) for _, school := range schools { - b.WriteString(fmt.Sprintf("# %s\n", school.Link())) + b.WriteString(fmt.Sprintf("# %s\n", Link(school))) // Write all degrees b.WriteString("- Degrees:") for i, deg := range school.Degrees { @@ -347,7 +356,7 @@ func createSchoolsPages() { // Write all projects b.WriteString("- Projects:\n") for _, proj := range school.Projects { - b.WriteString(fmt.Sprintf("- - %s\n", proj.Link())) + b.WriteString(fmt.Sprintf("- - %s\n", Link(proj))) } b.WriteString("- Extracurriculars:\n") // Write all extracurriculars @@ -397,7 +406,7 @@ func createPositionsPages() { if end == "current" { end = "" } - b.WriteString(fmt.Sprintf(" %s | %s | %s | %s\n", pos.Link(), pos.company.Name, pos.Start.String(), end)) + b.WriteString(fmt.Sprintf(" %s | %s | %s | %s\n", Link(pos), pos.company.Name, pos.Start.String(), end)) } b.WriteString("Earliest") @@ -424,9 +433,9 @@ func createClientsPages() { client := clients[clientName] compLink := linkFor(client.Name, "cv", "company", withoutSpaces(client.Name)) if client.company != nil { - compLink = client.company.Link() + compLink = Link(client.company) } - b.WriteString(fmt.Sprintf("%s | %s \n", client.Link(), compLink)) + b.WriteString(fmt.Sprintf("%s | %s \n", Link(client), compLink)) } err := os.WriteFile(root+"clients.md", []byte(b.String()), 777) if err != nil { @@ -452,7 +461,7 @@ func createCompaniesPages() { for _, companyName := range companiesOrder { // TODO: Consider linking every position from here, but on separate lines company := companies[companyName] - b.WriteString(fmt.Sprintf("%s | %s | %s | %s\n", company.Positions[0].Name, company.Link(), company.Start.String(), company.End.String())) + b.WriteString(fmt.Sprintf("%s | %s | %s | %s\n", company.Positions[0].Name, Link(company), company.Start.String(), company.End.String())) } err := os.WriteFile(root+"companies.md", []byte(b.String()), 777) if err != nil { @@ -476,7 +485,7 @@ func createPlatformsPages() { sort.Strings(alphabetizedPlatforms) for _, platName := range alphabetizedPlatforms { platform := platforms[platName] - b.WriteString(fmt.Sprintf("- %s\n", platform.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(platform))) } err := os.WriteFile(root+"platforms.md", []byte(b.String()), 777) if err != nil { @@ -491,7 +500,7 @@ func createPlatformsPages() { if len(platform.SubjectMatters) == 0 { panic("no subject matter on platform " + name) } - WriteCVFile("platform/"+withoutSpaces(name)+".md", string(platform.Bytes(name, "Platform"))) + WriteCVFile("platform/"+withoutSpaces(name)+".md", string(platform.Bytes(name, nil, "Platform"))) } } @@ -503,7 +512,7 @@ func createDbsPages() { sort.Strings(alphabetized) for _, name := range alphabetized { db := dbs[name] - b.WriteString(fmt.Sprintf("- %s\n", db.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(db))) } err := os.WriteFile(root+"dbs.md", []byte(b.String()), 777) if err != nil { @@ -515,7 +524,8 @@ func createDbsPages() { } } for name, db := range dbs { - WriteCVFile("db/"+withoutSpaces(name)+".md", string(db.Bytes(name, "Database"))) + + WriteCVFile("db/"+withoutSpaces(name)+".md", string(db.Bytes(name, utils.Pointer(smDatabase), "Database"))) } } func createCachesPages() { @@ -526,7 +536,7 @@ func createCachesPages() { sort.Strings(alphabetized) for _, name := range alphabetized { cache := caches[name] - b.WriteString(fmt.Sprintf("- %s\n", cache.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(cache))) } err := os.WriteFile(root+"caches.md", []byte(b.String()), 777) if err != nil { @@ -538,7 +548,7 @@ func createCachesPages() { } } for name, cache := range caches { - WriteCVFile("cache/"+withoutSpaces(name)+".md", string(cache.Bytes(name, "Cache"))) + WriteCVFile("cache/"+withoutSpaces(name)+".md", string(cache.Bytes(name, utils.Pointer(smCaching), "Cache"))) } } @@ -550,7 +560,7 @@ func createProvidersPages() { b.WriteString("# Preferred (in order)\n") for _, name := range []string{"AWS"} { provider := providers[name] - b.WriteString(fmt.Sprintf("- %s\n", provider.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(provider))) // TODO: SUB-SERVICES!!!!! } b.WriteString("# All\n") @@ -576,7 +586,7 @@ func createServicesPages() { b.WriteString("Service | Provider\n") b.WriteString(":-- | :--\n") for _, service := range cloudServices { // TODO: alphabetize?!!!!! - b.WriteString(fmt.Sprintf("%s | %s\n", service.Link(), service.provider)) + b.WriteString(fmt.Sprintf("%s | %s\n", Link(service), service.provider)) } err := os.WriteFile(root+"services.md", []byte(b.String()), 777) if err != nil { @@ -588,7 +598,7 @@ func createServicesPages() { } } for name, service := range cloudServices { - WriteCVFile("service/"+withoutSpaces(name)+".md", string(service.Bytes(name, "Cloud Service"))) + WriteCVFile("service/"+withoutSpaces(name)+".md", string(service.Bytes(name, nil, "Cloud Service"))) } } @@ -600,7 +610,7 @@ func createTechnologiesPages() { sort.Strings(alphabetized) for _, name := range alphabetized { tech := techs[name] - b.WriteString(fmt.Sprintf("- %s\n", tech.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(tech))) } err := os.WriteFile(root+"technologies.md", []byte(b.String()), 777) if err != nil { @@ -615,7 +625,8 @@ func createTechnologiesPages() { if len(tech.SubjectMatters) == 0 { panic("no subject matter on technology " + name) } - WriteCVFile("technology/"+withoutSpaces(name)+".md", string(tech.Bytes(name, "Technology"))) + typePlusTags := append([]string{"Technology"}, tech.Tags.AsSlice()...) + WriteCVFile("technology/"+withoutSpaces(name)+".md", string(tech.Bytes(name, nil, typePlusTags...))) } } @@ -627,7 +638,7 @@ func createMiscSkillsPages() { sort.Strings(alphabetized) for _, name := range alphabetized { skill := miscSkills[name] - b.WriteString(fmt.Sprintf("- %s\n", skill.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(skill))) } err := os.WriteFile(root+"miscSkills.md", []byte(b.String()), 777) if err != nil { @@ -639,7 +650,7 @@ func createMiscSkillsPages() { } } for name, skill := range miscSkills { - WriteCVFile("miscSkill/"+withoutSpaces(name)+".md", string(skill.Bytes(name, "Misc Skill"))) + WriteCVFile("miscSkill/"+withoutSpaces(name)+".md", string(skill.Bytes(name, nil, "Misc Skill"))) } } func createInterestsPages() { @@ -660,7 +671,7 @@ func createInterestsPages() { } } for name, interest := range interests { - WriteCVFile("interest/"+withoutSpaces(name)+".md", string(interest.Bytes(name, "Interest"))) + WriteCVFile("interest/"+withoutSpaces(name)+".md", string(interest.Bytes(name, nil, "Interest"))) } } func createSubjectMatterPages() { @@ -672,7 +683,7 @@ func createSubjectMatterPages() { return string(alphabetizedSms[i]) < string(alphabetizedSms[j]) }) for _, name := range alphabetizedSms { - b.WriteString(fmt.Sprintf("- %s\n", name.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(name))) } err := os.WriteFile(root+"subjectMatters.md", []byte(b.String()), 777) if err != nil { diff --git a/generator/miscSkills.go b/generator/miscSkills.go index b1eb67e..6a05553 100644 --- a/generator/miscSkills.go +++ b/generator/miscSkills.go @@ -7,11 +7,15 @@ type MiscSkill struct { *tracked } -func (pg *MiscSkill) Link() string { +func (pg *MiscSkill) Dst() string { + return dstFor("cv", "miscSkill", withoutSpaces(pg.Name)) +} + +func (pg *MiscSkill) Title() string { if pg == nil { - return "NO_LINK" + return noLinkText } - return linkFor(pg.Name, "cv", "miscSkill", withoutSpaces(pg.Name)) // TODO: plural?? + return pg.Name } func (pg *MiscSkill) EntryType() string { diff --git a/generator/note.go b/generator/note.go index 2b54cd7..e80875a 100644 --- a/generator/note.go +++ b/generator/note.go @@ -18,22 +18,22 @@ func createNotesPages() { WriteFile("Notes.md", b.String()) // create all note pages for _, note := range notes { - WriteFile(fmt.Sprintf(`note/%s.md`, withoutSpaces(note.Title)), string(note.Bytes())) + WriteFile(fmt.Sprintf(`note/%s.md`, withoutSpaces(note.TitleText)), string(note.Bytes())) } } type Note struct { - Title string + TitleText string CreationDate dayMonthYr ModifiedDate *dayMonthYr ContentFile string Tags []string } -func (note Note) Bytes() []byte { +func (note *Note) Bytes() []byte { b := strings.Builder{} b.WriteString("---\n") - b.WriteString("title: " + note.Title + "\n") + b.WriteString("title: " + note.TitleText + "\n") b.WriteString("draft: false\n") if len(note.Tags) > 0 { b.WriteString("tags:\n") @@ -51,8 +51,18 @@ func (note Note) Bytes() []byte { return []byte(b.String()) } -func (note Note) Link() string { - return fmt.Sprintf(`[%s](note/%s)`, note.Title, withoutSpaces(note.Title)) +func (note *Note) Link() string { + return fmt.Sprintf(`[%s](note/%s)`, note.Title, withoutSpaces(note.TitleText)) +} +func (pg *Note) Dst() string { + return dstFor("note", withoutSpaces(pg.TitleText)) +} + +func (pg *Note) Title() string { + if pg == nil { + return noLinkText + } + return pg.TitleText } const notesDir = "notes/" @@ -65,7 +75,7 @@ func newNote(title string, creationDate dayMonthYr, modifiedDate *dayMonthYr, co panic("note " + contentFileName + " already exists") } notes = append(notes, Note{ - Title: title, + TitleText: title, CreationDate: creationDate, ModifiedDate: modifiedDate, ContentFile: contentFileName, diff --git a/generator/platform.go b/generator/platform.go index 220b844..ddbfc8d 100644 --- a/generator/platform.go +++ b/generator/platform.go @@ -1,5 +1,7 @@ package main +import "strings" + var platforms = map[string]*PlatformPage{} type PlatformPage struct { // Datadog, Github, etc @@ -12,11 +14,15 @@ func (pg *PlatformPage) EntryType() string { return "Platform" } -func (pg *PlatformPage) Link() string { +func (pg *PlatformPage) Dst() string { + return dstFor("cv", "platform", withoutSpaces(pg.Name)) +} + +func (pg *PlatformPage) Title() string { if pg == nil { - return "NO_LINK" + return noLinkText } - return linkFor(pg.Name, "cv", "platform", withoutSpaces(pg.Name)) + return pg.Name } func (pg *PlatformPage) WithSubjectMatters(sms ...SubjectMatter) *PlatformPage { @@ -34,6 +40,7 @@ func NewPlatform(name string) *PlatformPage { SubjectMattersField: SubjectMattersField{SubjectMatters: map[SubjectMatter]struct{}{}}, } platforms[name] = out + addLinkable(strings.ToLower(name), out) return out } @@ -58,10 +65,24 @@ func setupPlatformSubjectMatters() { WithSubjectMatters(smBackend, smNetworking) NewPlatform("Gitlab"). WithSubjectMatters(smCiCd) - NewPlatform("Azure DevOps"). // TODO: USE - WithSubjectMatters(smDevOps) - NewPlatform("Jira"). // TODO: USE - WithSubjectMatters(smDevOps) - NewPlatform("Confluence"). // TODO: USE - WithSubjectMatters(smDocumentation) + NewPlatform("Azure DevOps"). + WithSubjectMatters(smDevSecOps) + NewPlatform("Jira"). + WithSubjectMatters(smDevSecOps) + NewPlatform("Confluence"). + WithSubjectMatters(smDocumentation) + NewPlatform("Slack"). + WithSubjectMatters(smCommunication) + NewPlatform("Small Improvements"). + WithSubjectMatters(smObservability) + NewPlatform("Teams"). + WithSubjectMatters(smCommunication) + NewPlatform("Jamf"). + WithSubjectMatters(smCybersecurity) + NewPlatform("DroneCI"). + WithSubjectMatters(smCiCd) + NewPlatform("Discord"). + WithSubjectMatters(smCommunication) + NewPlatform("Rally"). + WithSubjectMatters(smObservability) } diff --git a/generator/position.go b/generator/position.go index ffe27e6..6140759 100644 --- a/generator/position.go +++ b/generator/position.go @@ -1,6 +1,7 @@ package main import ( + "appli.ng/cv/generator/tags" "appli.ng/cv/generator/utils" "fmt" "strings" @@ -9,30 +10,34 @@ import ( var positionsMap = map[string]*Position{} // Map of companyName+positionName to position type Position struct { - Name string - Start monthYr - End *monthYr // None == current - //Clients []*Client // clients may contain projects which are outside of this position + Name string + Start monthYr + End *monthYr // None == current Projects map[string]*Project // Parent company *CompanyPage - miscSkills utils.Set[string] // TODO: MISC SKILLS?????? + miscSkills utils.Set[string] // TODO: DISPLAY???? SubjectMatters utils.Set[SubjectMatter] // TODO: DISPLAY THIS??? // TODO: maybe dont have this on here + Tags tags.Field } func (pg *Position) NameValue() string { // The name to be used on the link. NOT the URL - return pg.Name // TODO: or mapName? + return pg.Name } func (pg *Position) EntryType() string { return "Position" } -func (pg *Position) Link() string { +func (pg *Position) Dst() string { + return dstFor("cv", "position", withoutSpaces(pg.Name)) +} + +func (pg *Position) Title() string { if pg == nil { - return "NO_LINK" + return noLinkText } - return linkFor(pg.Name, "cv", "position", withoutSpaces(pg.MapName())) + return pg.Name } func (pg *Position) WithMiscSkills(skills ...string) *Position { if pg == nil { @@ -87,16 +92,16 @@ func NewPosition(name string, startMo, startYr int, endMo, endYr *int) *Position func (pg *Position) Bytes() []byte { builder := strings.Builder{} - builder.WriteString(frontmatterFor(pg.Name)) + builder.WriteString(frontmatterFor(pg.Name, pg.Tags.AsStrings()...)) // TODO: Start/end // Company and clients - builder.WriteString(fmt.Sprintf("Company: %s\n", pg.company.Link())) + builder.WriteString(fmt.Sprintf("Company: %s\n", Link(pg.company))) if len(pg.Projects) > 0 { builder.WriteString("# Clients and Projects\n") builder.WriteString("Client | Project\n") builder.WriteString(":-- | :--\n") for _, pr := range pg.Projects { - builder.WriteString(fmt.Sprintf("%s | %s\n", pr.TypeInfo.getClient().Link(), pr.Link())) + builder.WriteString(fmt.Sprintf("%s | %s\n", Link(pr.TypeInfo.getClient()), Link(pr))) } } else { builder.WriteString("NO CLIENTS FOUND. FIXME\n ") @@ -105,14 +110,14 @@ func (pg *Position) Bytes() []byte { if len(pg.miscSkills) > 0 { builder.WriteString("# Misc Skills\n") for skill, _ := range pg.miscSkills { - builder.WriteString(fmt.Sprintf("- %s\n", miscSkills[skill].Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(miscSkills[skill]))) } } // SubjectMatters if len(pg.SubjectMatters) > 0 { builder.WriteString("# Related Subject Matters\n") for sm, _ := range pg.SubjectMatters { - builder.WriteString(fmt.Sprintf("- %s\n", sm.Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(sm))) } } @@ -129,6 +134,11 @@ func (pg *Position) MapName() string { return pg.company.Name + " " + pg.Name } +func (pg *Position) WithTags(tags ...tags.Tag) *Position { + pg.Tags = append(pg.Tags, tags...) + return pg +} + func (pg *Position) WithProjects(projs ...*Project) *Position { pg.Projects = map[string]*Project{} for _, proj := range projs { @@ -221,29 +231,41 @@ var ( sai1, sai2, sai3, freelancePosition, positionTEI, positionTAE, positionSeniorLifeguard, positionLifeguard *Position ) +// TODO; ensure all professional projects are added to positions func initPositionsAfterProjects() { // TODO: ANY MISC SKILLS positionLifeguard = NewPosition("Lifeguard", 1, 2013, utils.Pointer(6), utils.Pointer(2014)). // TODO: ensure dates are right - WithSubjectMatters(smFirstAid) + WithSubjectMatters(smFirstAid). + WithProjects(mafcProjects). + WithTags(tags.FirstAid) positionSeniorLifeguard = NewPosition("Senior Lifeguard", 6, 2014, utils.Pointer(8), utils.Pointer(2016)). // TODO: ensure dates are right - WithSubjectMatters(smFirstAid) + WithSubjectMatters(smFirstAid). + WithProjects(mafcProjects). + WithTags(tags.FirstAid) positionTAE = NewPosition("Civil Structural Engineer and Tower Climber", 5, 2017, utils.Pointer(3), utils.Pointer(2018)). // TODO: ensure dates are right WithMiscSkills("Excel", "Climbing", "AutoDesk Inventor", "AutoCAD", "Autodesk Revit", "Drafting"). - WithSubjectMatters(smCivilEngineering, smStructuralEngineering, smStatics) + WithSubjectMatters(smCivilEngineering, smStructuralEngineering, smStatics). + WithProjects(taePhotoImporter). + WithTags(tags.CivilEngineering, tags.StructuralEngineering, tags.Telecom) positionTEI = NewPosition("Cell Tower Inspector and Tower Climber", 1, 2019, utils.Pointer(3), utils.Pointer(2020)). // TODO: ensure dates are right WithMiscSkills("Climbing", "Drafting"). - WithSubjectMatters(smCivilEngineering, smStructuralEngineering, smStatics) + WithSubjectMatters(smCivilEngineering, smStructuralEngineering, smStatics). + WithProjects(teiProjects). + WithTags(tags.CivilEngineering, tags.StructuralEngineering, tags.Telecom) freelancePosition = NewPosition("Software Engineer", 1, 2012, utils.Pointer(5), utils.Pointer(2022)). WithSubjectMatters(smFullStack). - WithProjects(WellAwareProject, CharityProject, CritColaProject, MastersDataAnalysisProject) + WithProjects(WellAwareProject, CharityProject, CritColaProject, MastersDataAnalysisProject, WildlifeRProject, ArrowNailProject). + WithTags(tags.SWE) sai1 = NewPosition("Software Engineer", 5, 2022, utils.Pointer(6), utils.Pointer(2023)). WithSubjectMatters(smFullStack). - WithProjects(tileGenProject, renderProject, statsProject, explorerProject, wqdbProject, supportProject, scudsProject, ufoProject, goweProject, simpsonUnivProject) // TODO: MOVE PROJECTS AROUND + WithProjects(explorerProject, supportProject, scudsProject, simpsonUnivProject, jamfProject, smallImprovementsProject, internalResumeGeneratorProject). + WithTags(tags.SWE) // TODO: can the same project be on multiple positions? sai2 = NewPosition("Senior Software Engineer", 6, 2023, utils.Pointer(2), utils.Pointer(2025)). WithSubjectMatters(smBackend). - WithProjects(polygonBuilderProject, GhaRunnersProject) + WithProjects(tileGenProject, rasterRenderProject, polygonBuilderProject, GhaRunnersProject, statsProject, ufoProject, renderProject). + WithTags(tags.SWE) sai3 = NewPosition("Senior Software Engineer and Tech Lead", 2, 2025, nil, nil). WithSubjectMatters(smBackend). - WithProjects(ogreProject, billingProject) - + WithProjects(ogreProject, billingProject, goweProject, wqdbProject). + WithTags(tags.SWE) } diff --git a/generator/project.go b/generator/project.go index c7f2d47..94a1d96 100644 --- a/generator/project.go +++ b/generator/project.go @@ -1,6 +1,7 @@ package main import ( + "appli.ng/cv/generator/tags" "appli.ng/cv/generator/utils" "fmt" "maps" @@ -29,96 +30,18 @@ func (pt projectType) isCoursework() bool { } -type projectTypeInfo interface { - Type() projectType - Finalize(*Project) - getClient() *Client - setClient(*Client) projectTypeInfo - getSchool() *SchoolPage - setSchool(page *SchoolPage) projectTypeInfo -} -type schoolProjectTypeInfo struct { - school *SchoolPage -} - -func (schoolProjectTypeInfo) Type() projectType { - return projectTypeSchool -} -func (i schoolProjectTypeInfo) Finalize(pr *Project) { - i.school.withProjects(pr) -} -func (i schoolProjectTypeInfo) getClient() *Client { - return nil -} -func (i schoolProjectTypeInfo) getSchool() *SchoolPage { - return i.school -} -func (i schoolProjectTypeInfo) setClient(cli *Client) projectTypeInfo { - return i -} -func (i schoolProjectTypeInfo) setSchool(s *SchoolPage) projectTypeInfo { - i.school = s - return i -} - -type professionalProjectTypeInfo struct { - client *Client -} - -func (professionalProjectTypeInfo) Type() projectType { - return projectTypeProfessional -} -func (i professionalProjectTypeInfo) Finalize(pr *Project) { - i.client.WithProjects(pr) -} -func (i professionalProjectTypeInfo) getClient() *Client { - return i.client -} -func (i professionalProjectTypeInfo) getSchool() *SchoolPage { - return nil -} -func (i professionalProjectTypeInfo) setClient(cli *Client) projectTypeInfo { - i.client = cli - return i -} -func (i professionalProjectTypeInfo) setSchool(s *SchoolPage) projectTypeInfo { - return i -} - -type personalProjectTypeInfo struct{} - -func (i personalProjectTypeInfo) getClient() *Client { - return nil -} -func (i personalProjectTypeInfo) getSchool() *SchoolPage { - return nil -} -func (i personalProjectTypeInfo) setClient(cli *Client) projectTypeInfo { - return i -} -func (i personalProjectTypeInfo) setSchool(s *SchoolPage) projectTypeInfo { - return i -} - -func (personalProjectTypeInfo) Type() projectType { - return projectTypePersonal -} -func (personalProjectTypeInfo) Finalize(*Project) {} - -type projectStatus string - const ( statusComplete projectStatus = "Complete" statusBuilding projectStatus = "Building" statusMaintaining projectStatus = "Maintaining" statusNotOnTeam projectStatus = "No longer part of project" - statusShelved projectStatus = "Shelved or not actively working on this project" // TODO: change + statusShelved projectStatus = "Shelved or not actively working on this project" ) type Project struct { Name string Summary string - Status projectStatus // TODO: USE THESE!!!! + Status projectStatus TypeInfo projectTypeInfo link *string @@ -129,21 +52,34 @@ type Project struct { Technologies utils.Set[string] Platforms utils.Set[string] MiscSkills utils.Set[string] - // TODO: MORE! + RelatedInterests utils.Set[string] SubjectMatters utils.Set[SubjectMatter] - Tags utils.Set[Tag] + Tags utils.Set[tags.Tag] + size projectSize // TODO: PROJECT SIZE. Maybe get rid of? } +type projectSize string + +const ( + projectSizeS projectSize = "small" + projectSizeM projectSize = "medium" + projectSizeL projectSize = "large" +) + func (pg *Project) NameValue() string { return pg.Name } -func (pg *Project) Link() string { +func (pg *Project) Dst() string { + return dstFor("cv", "project", withoutSpaces(pg.Name)) +} + +func (pg *Project) Title() string { if pg == nil { - return "NO_LINK" + return noLinkText } - return linkFor(pg.Name, "cv", "project", withoutSpaces(pg.Name)) + return pg.Name } func (pg *Project) WithStatus(status projectStatus) *Project { @@ -207,28 +143,28 @@ func (pg *Project) EntryType() string { func (pg *Project) Bytes() []byte { builder := strings.Builder{} - builder.WriteString(frontmatterFor(pg.Name, "Project")) - // PERSONAL/Professional + pgTags := []string{"Project"} t := pg.TypeInfo.Type() switch t { case projectTypeSchool: - builder.WriteString("Coursework-related Project\n") + pgTags = append(pgTags, "Coursework-Related Project") case projectTypeProfessional: - builder.WriteString("Professional Project\n") + pgTags = append(pgTags, "Professional Project") case projectTypePersonal: - builder.WriteString("Personal Project\n") + pgTags = append(pgTags, "Personal Project") default: panic("unknown project type: " + string(t)) } - // Client/company/school + builder.WriteString(frontmatterFor(pg.Name, pgTags...)) // TODO; do these tags need both Project and Project/*? + // Client/company/school if cli := pg.TypeInfo.getClient(); cli != nil { - builder.WriteString(fmt.Sprintf("Client: %s\n", cli.Link())) + builder.WriteString(fmt.Sprintf("Client: %s\n\n", Link(cli))) if comp := cli.company; comp != nil { - builder.WriteString(fmt.Sprintf("Company: %s\n", comp.Link())) + builder.WriteString(fmt.Sprintf("Company: %s\n", Link(comp))) } } else if sch := pg.TypeInfo.getSchool(); sch != nil { - builder.WriteString(fmt.Sprintf("School: %s\n", sch.Link())) + builder.WriteString(fmt.Sprintf("School: %s\n", Link(sch))) } // SUMMARY @@ -256,7 +192,7 @@ func (pg *Project) Bytes() []byte { } for f := 5; f >= 0; f-- { for _, name := range freqs[Frequency(f)] { - builder.WriteString(fmt.Sprintf("%s | %s\n", langs[name].Link(), Frequency(f).String())) + builder.WriteString(fmt.Sprintf("%s | %s\n", Link(langs[name]), Frequency(f).String())) } } } @@ -264,14 +200,14 @@ func (pg *Project) Bytes() []byte { if len(pg.Dbs) > 0 { builder.WriteString("# Databases\n") for db, _ := range pg.Dbs { - builder.WriteString(fmt.Sprintf("- %s\n", dbs[db].Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(dbs[db]))) } } // CACHES if len(pg.Caches) > 0 { builder.WriteString("# Caches\n") for name, _ := range pg.Caches { - builder.WriteString(fmt.Sprintf("- %s\n", caches[name].Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(caches[name]))) } } // Cloud Providers! @@ -280,9 +216,9 @@ func (pg *Project) Bytes() []byte { for provName, services := range pg.CloudProviders { svcStrings := make([]string, len(services)) for i, svc := range slices.Collect(maps.Keys(services)) { - svcStrings[i] = cloudServices[svc].Link() + svcStrings[i] = Link(cloudServices[svc]) } - builder.WriteString(fmt.Sprintf("- %s\n", providers[provName].Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(providers[provName]))) builder.WriteString(strings.Join(svcStrings, ", ") + "\n") } } @@ -290,35 +226,35 @@ func (pg *Project) Bytes() []byte { if len(pg.Technologies) > 0 { builder.WriteString("# Technologies\n") for name, _ := range pg.Technologies { - builder.WriteString(fmt.Sprintf("- %s\n", techs[name].Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(techs[name]))) } } // Platforms if len(pg.Platforms) > 0 { builder.WriteString("# Platforms\n") for name, _ := range pg.Platforms { - builder.WriteString(fmt.Sprintf("- %s\n", platforms[name].Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(platforms[name]))) } } // Misc skills if len(pg.MiscSkills) > 0 { builder.WriteString("# Misc Skills\n") for skill, _ := range pg.MiscSkills { - builder.WriteString(fmt.Sprintf("- %s\n", miscSkills[skill].Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(miscSkills[skill]))) } } // SubjectMatters if len(pg.SubjectMatters) > 0 { builder.WriteString("# Related Subject Matters\n") for sm, _ := range pg.SubjectMatters { - builder.WriteString(fmt.Sprintf("- %s\n", sm.Link())) + builder.WriteString(fmt.Sprintf("- %s\n", Link(sm))) } } // Related Interests if len(pg.RelatedInterests) > 0 { builder.WriteString("# Related Interests\n") for interest, _ := range pg.RelatedInterests { - builder.WriteString(fmt.Sprintf("- %s\n", linkForInterest(interest))) + builder.WriteString(fmt.Sprintf("- %s\n", linkForInterest(interest))) // TODO: remove interests because of overlap with subject matters??? } } builder.WriteString(definitionsArea()) @@ -402,7 +338,6 @@ func (p *Project) WithCloudProvider(name string, services ...string) *Project { return p } func (p *Project) WithDbs(names ...string) *Project { - // TODO: Aurora, DynamoDB, DocumentDB, RDS should all point to the AWS service!!!!??? (probably not) for _, dbName := range names { if !p.Dbs.Contains(dbName) { p.Dbs.Add(dbName) @@ -441,7 +376,7 @@ func (p *Project) WithTechnologies(names ...string) *Project { } return p } -func (p *Project) WithTags(tags ...Tag) *Project { +func (p *Project) WithTags(tags ...tags.Tag) *Project { p.Tags.Add(tags...) return p } @@ -495,23 +430,23 @@ func (p *Project) GetAllLowest() (outCaches map[string]*CachePage, outDbs map[st return } -func NewPersonalProject(name string, summary string, link *string) *Project { - return newProject(name, summary, personalProjectTypeInfo{}, link) +func NewPersonalProject(name string, summary string, link *string, size projectSize) *Project { + return newProject(name, summary, personalProjectTypeInfo{}, link, size) } -func NewProfessionalProject(name string, summary string, client *Client, link *string) *Project { - out := newProject(name, summary, client.ProjectTypeInfo(), link) +func NewProfessionalProject(name string, summary string, client *Client, link *string, size projectSize) *Project { + out := newProject(name, summary, client.ProjectTypeInfo(), link, size) client.WithProjects(out) return out } -func NewSchoolProject(name string, summary string, school *SchoolPage, link *string) *Project { - out := newProject(name, summary, school.ProjectTypeInfo(), link) +func NewSchoolProject(name string, summary string, school *SchoolPage, link *string, size projectSize) *Project { + out := newProject(name, summary, school.ProjectTypeInfo(), link, size) school.withProjects(out) return out } -func newProject(name string, summary string, info projectTypeInfo, link *string) *Project { +func newProject(name string, summary string, info projectTypeInfo, link *string, size projectSize) *Project { if strings.Contains(name, ".") || strings.Contains(name, "%") { panic("invalid character in project " + name) } @@ -528,12 +463,14 @@ func newProject(name string, summary string, info projectTypeInfo, link *string) Platforms: utils.Set[string]{}, SubjectMatters: map[SubjectMatter]struct{}{}, RelatedInterests: map[string]struct{}{}, - Tags: utils.Set[Tag]{}, + Tags: utils.Set[tags.Tag]{}, + size: size, } if _, exists := projects[name]; exists { panic("project already exists") } projects[name] = out + addLinkable(strings.ToLower(name), out) return out } @@ -549,46 +486,55 @@ var nfcScannerUrl = "github.com/reeceappling/nfcScanner" // TODO: ensure ok var coreShufflerUrl = "github.com/reeceappling/coreShuffler" // TODO: ensure ok var ( - projectAgentSwarm = NewPersonalProject("AI Agent Swarm", fixmeLink, nil) - polygonBuilderProject = NewProfessionalProject("Polygon Builder", fixmeLink, jdClient, nil) - tileGenProject = NewProfessionalProject("Tile Generator", fixmeLink, jdClient, nil) - GhaRunnersProject = NewProfessionalProject("Github Actions GPU Runners", "FIX SUMMARY", jdClient, nil) - ogreProject = NewProfessionalProject("Organizational Geospatial Rollup Engine", fixmeLink, jdClient, nil) - renderProject = NewProfessionalProject("Render Cluster", fixmeLink, jdClient, nil) // TODO: MORE! - statsProject = NewProfessionalProject("Statistics Cluster", fixmeLink, jdClient, nil) - billingProject = NewProfessionalProject("Billing Cluster", fixmeLink, jdClient, nil) - explorerProject = NewProfessionalProject("Transform Explorer", fixmeLink, jdClient, nil) - wqdbProject = NewProfessionalProject("Work Queue Database", fixmeLink, jdClient, nil) - supportProject = NewProfessionalProject("Support Cluster", fixmeLink, jdClient, nil) - scudsProject = NewProfessionalProject("Scuds API", fixmeLink, jdClient, nil) - ufoProject = NewProfessionalProject("UFO API", fixmeLink, jdClient, nil) - goweProject = NewProfessionalProject("Gowe Builder", fixmeLink, jdClient, nil) - simpsonUnivProject = NewProfessionalProject("Simpson University", fixmeLink, sourceAlliesClient, nil) // TODO: MORE! - mushDbProject = NewPersonalProject("MushDb", fixmeLink, &mushDbUrl) - cvProject = NewPersonalProject("Personal Site and CV", "This project! A generator which creates markdown files that can be viewed via Obsidian, or published to the web.", &cvUrl) // TODO: make multiple strings an available option for summary - linksPage = NewPersonalProject("Personal Links Page", "A page to put all my links", nil) // TODO: LINKS PAGE - measurementsProject = NewPersonalProject("Measurements", fixmeLink, &measurementsUrl) - nfcScannerProject = NewPersonalProject("Nfc Scanner", fixmeLink, &nfcScannerUrl) - teiProjects = NewProfessionalProject("Tei Projects", fixmeLink, teiClient, nil) // TODO: maybe add an actual project - taeProjects = NewProfessionalProject("Tae Projects", fixmeLink, taeClient, nil) // TODO: maybe add actual projects? - mafcProjects = NewProfessionalProject("Mafc Projects", fixmeLink, mafcClient, nil) // TODO: maybe add actual projects? - coreShufflerProject = NewPersonalProject("Simulate Core Shuffler", fixmeLink, &coreShufflerUrl) - CharityProject = NewProfessionalProject("Charity Site", fixmeLink, charityClient, nil) - WellAwareProject = NewProfessionalProject("Well Aware NC", fixmeLink, clarkClient, nil) // TODO: change client to the lab??? - CritColaProject = NewProfessionalProject("CritCola", fixmeLink, critColaClient, nil) // TODO: ADD OTHER CRITCOLA PROJECTS - MastersDataAnalysisProject = NewProfessionalProject("Masters Data Analysis", fixmeLink, clarkClient, nil) - capstoneProject = NewSchoolProject("Capstone Project-Uranium Silicide Accident Tolerant Fuel cycle design for Duke Energy Catawba Nuclear Plant", "FIX M_E", schoolNCSU, &coreShufflerUrl) - projectLinAlgCryptography = NewSchoolProject("Linear algebra cryptography algorithm", "FIX M_E", schoolNCSU, nil) - cherenkovProject = NewSchoolProject("Cherenkov radiation sensor", fixmeLink, schoolNCSU, nil) - roboticsTeamProject = NewSchoolProject("Robotics team 3720", fixmeLink, schoolCata, nil) - cncLaserCutterProject = NewSchoolProject("CNC Laser Cutter", fixmeLink, schoolCata, nil) - aerospaceFinalProject = NewSchoolProject("Aerospace senior design course", "Designed, created, and tested a rocket from scratch", schoolCata, nil) - miscSmallPersonalProjects = NewPersonalProject("Misc small personal projects", "A conglomeration of personal projects which did not each deserve their own entry", nil) + projectAgentSwarm = NewPersonalProject("AI Agent Swarm", fixmeLink, nil, projectSizeS) + polygonBuilderProject = NewProfessionalProject("Polygon Builder", fixmeLink, jdClient, nil, projectSizeL) + tileGenProject = NewProfessionalProject("Tile Generator", fixmeLink, jdClient, nil, projectSizeL) + GhaRunnersProject = NewProfessionalProject("Github Actions GPU Runners", "FIX SUMMARY", jdClient, nil, projectSizeM) + ogreProject = NewProfessionalProject("Organizational Geospatial Rollup Engine", fixmeLink, jdClient, nil, projectSizeL) + renderProject = NewProfessionalProject("Render Cluster", fixmeLink, jdClient, nil, projectSizeL) // TODO: MORE! + statsProject = NewProfessionalProject("Statistics Cluster", fixmeLink, jdClient, nil, projectSizeL) + billingProject = NewProfessionalProject("Billing Cluster", fixmeLink, jdClient, nil, projectSizeL) + explorerProject = NewProfessionalProject("Transform Explorer", fixmeLink, jdClient, nil, projectSizeM) + wqdbProject = NewProfessionalProject("Work Queue Database", fixmeLink, jdClient, nil, projectSizeM) + supportProject = NewProfessionalProject("Support Cluster", fixmeLink, jdClient, nil, projectSizeM) + scudsProject = NewProfessionalProject("Scuds API", fixmeLink, jdClient, nil, projectSizeL) + ufoProject = NewProfessionalProject("UFO API", fixmeLink, jdClient, nil, projectSizeL) + goweProject = NewProfessionalProject("Gowe Builder", fixmeLink, jdClient, nil, projectSizeL) + rasterRenderProject = NewProfessionalProject("Raster Render", fixmeLink, jdClient, nil, projectSizeL) + simpsonUnivProject = NewProfessionalProject("Simpson University", fixmeLink, simpsonUniversityClient, nil, projectSizeS) // TODO: MORE! + jamfProject = NewProfessionalProject("JAMF companywide setup", fixmeLink, sourceAlliesClient, nil, projectSizeM) + smallImprovementsProject = NewProfessionalProject("Small Improvements Bot", fixmeLink, sourceAlliesClient, nil, projectSizeS) + internalResumeGeneratorProject = NewProfessionalProject("Source Allies Internal Consultant Resume Generator", fixmeLink, sourceAlliesClient, nil, projectSizeM) + mushDbProject = NewPersonalProject("MushDb", fixmeLink, &mushDbUrl, projectSizeL) + cvProject = NewPersonalProject("Personal Site and CV", "This project! A generator which creates markdown files that can be viewed via Obsidian, or published to the web.", &cvUrl, projectSizeM) // TODO: make multiple strings an available option for summary + linksPage = NewPersonalProject("Personal Links Page", "A page to put all my links", nil, projectSizeS) // TODO: LINKS PAGE + measurementsProject = NewPersonalProject("Measurements", fixmeLink, &measurementsUrl, projectSizeM) + nfcScannerProject = NewPersonalProject("Nfc Scanner", fixmeLink, &nfcScannerUrl, projectSizeS) + teiProjects = NewProfessionalProject("Tei Projects", fixmeLink, teiClient, nil, projectSizeS) // TODO: maybe add an actual project + taePhotoImporter = NewProfessionalProject("Tae Field Photograph Importer", fixmeLink, taeClient, nil, projectSizeS) + mafcProjects = NewProfessionalProject("Mafc Projects", fixmeLink, mafcClient, nil, projectSizeS) // TODO: maybe add actual projects? + coreShufflerProject = NewPersonalProject("Simulate Core Shuffler", fixmeLink, &coreShufflerUrl, projectSizeM) + CharityProject = NewProfessionalProject("Charity Site", fixmeLink, charityClient, nil, projectSizeM) + WellAwareProject = NewProfessionalProject("Well Aware NC", fixmeLink, clarkClient, nil, projectSizeM) // TODO: change client to the lab??? + CritColaProject = NewProfessionalProject("CritCola", fixmeLink, critColaClient, nil, projectSizeM) // TODO: ADD OTHER CRITCOLA PROJECTS + ArrowNailProject = NewProfessionalProject("Hail History Tracker", fixmeLink, arrowNailClient, nil, projectSizeM) + WildlifeRProject = NewProfessionalProject("Wildlife R Data Analysis", fixmeLink, wildlifeRClient, nil, projectSizeM) + MastersDataAnalysisProject = NewProfessionalProject("Masters Data Analysis", fixmeLink, clarkClient, nil, projectSizeM) + capstoneProject = NewSchoolProject("Capstone Project-Uranium Silicide Accident Tolerant Fuel cycle design for Duke Energy Catawba Nuclear Plant", "FIX M_E", schoolNCSU, &coreShufflerUrl, projectSizeL) + reactorAnalysisFinal = NewSchoolProject("Reactor Analysis Exam", "Reactor analysis final exam code", schoolNCSU, utils.Pointer(fixmeLink), projectSizeM) // TODO: ADD REAL LINK! + monteCarloProject = NewSchoolProject("MonteCarlo nuclear scattering and decay project", "FIX_ME", schoolNCSU, utils.Pointer(fixmeLink), projectSizeM) // TODO: ADD REAL LINK! + // TODO; FORTRAN SCHOOL PROJECT FOR ATM + projectLinAlgCryptography = NewSchoolProject("Linear algebra cryptography algorithm", "FIX M_E", schoolNCSU, nil, projectSizeM) + cherenkovProject = NewSchoolProject("Cherenkov radiation sensor", fixmeLink, schoolNCSU, nil, projectSizeM) + roboticsTeamProject = NewSchoolProject("Robotics team 3720", fixmeLink, schoolCata, nil, projectSizeM) + cncLaserCutterProject = NewSchoolProject("CNC Laser Cutter", fixmeLink, schoolCata, nil, projectSizeM) + aerospaceFinalProject = NewSchoolProject("Aerospace senior design course", "Designed, created, and tested a rocket from scratch", schoolCata, nil, projectSizeM) + miscSmallPersonalProjects = NewPersonalProject("Misc small personal projects", "A conglomeration of personal projects which did not each deserve their own entry", nil, projectSizeS) ) func initProjectsFinal() { projectAgentSwarm = projectAgentSwarm.WithStatus(statusMaintaining). - WithSummary("SUMMARY HERE"). // TODO: MORE! + WithSummary("Create an AI coding agent swarm with "+Link(lookup("Langgraph"))+" and "+Link(lookup("Poolside AI"))). WithLang("Go", Extensively, smBackend). WithInterests("Agentic AI", "Agent Swarms"). // TODO: any more? WithPlatforms("Poolside AI"). @@ -597,7 +543,7 @@ func initProjectsFinal() { //WithTags(tagAI, tagBackend). finalize() polygonBuilderProject = polygonBuilderProject.WithStatus(statusMaintaining). - WithSummary("SUMMARY HERE"). // TODO: MORE! + WithSummary("Service that utilizes a modified AlphaShapes algorithm to take inputs of millions of datapoints of geospatiotemporal agribusiness data, and output multipolygons representing field operations and subsets thereof."). WithLang("Go", Extensively, smBackend, smScripting). WithLang("Scala", Often, smBackend). WithLang("Terraform", Regularly, smIAC). @@ -611,14 +557,15 @@ func initProjectsFinal() { WithLang("Python", Rarely, smScripting). WithLang("Rust", Minimal, smScripting). WithCloudProvider("AWS", - "ECS", "S3", "EC2", "IAM", "SecretsManager", "DynamoDB", "DAX", "Cloudwatch", "Lambda", "ECR", "Route53", "Kinesis", "SQS", "SNS", "ApiGateway", // TODO: FARGATE NOT BEING ON THIS LIST IS CAUSING PROBLEMS + "ECS", "S3", "EC2", "IAM", "SecretsManager", "DynamoDB", "DAX", "Cloudwatch", "Lambda", "ECR", "Route53", "Kinesis", "SQS", "SNS", "ApiGateway", "EBS", "ELB", "ALB", ). WithDbs("Aurora", "DynamoDB", "Postgres"). WithCaches("Redis", "memcached"). - WithTechnologies("Github Actions", "Parquet", "Avro"). + WithTechnologies("Github Actions", "Parquet", "Avro", "REST API"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "Azure DevOps", "Jira", "Confluence"). - WithSubjectMatters(smBackend, smTopology, smLinearAlgebra, smClusterComputing, smDistributedComputing, smContainerization, smIAC, smCiCd). + WithSubjectMatters(smApi, smGeospatial, smBackend, smTopology, smLinearAlgebra, smClusterComputing, smDistributedComputing, smContainerization, smIAC, smCiCd). //WithTags(tagBackend, tagTopology, tagLinearAlgebra, tagClusterComputing, tagDistributedComputing, tagContainerization, tagIAC, tagCiCd) + WithMiscSkills("GIS", "QGIS"). finalize() tileGenProject = tileGenProject.WithStatus(statusMaintaining). WithSummary("SUMMARY HERE"). // TODO: MORE! @@ -632,9 +579,9 @@ func initProjectsFinal() { ). WithDbs("DynamoDB", "Postgres"). WithCaches("Redis", "memcached", "DAX"). - WithTechnologies("CUDA", "Github Actions", "Parquet", "Avro", "LocalStack"). + WithTechnologies("CUDA", "Github Actions", "Parquet", "Avro", "LocalStack", "REST API"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "Azure DevOps", "Jira", "Confluence"). - WithSubjectMatters(smBackend, smLinearAlgebra, smClusterComputing, smDistributedComputing, smContainerization, smIAC, smCiCd). + WithSubjectMatters(smApi, smGeospatial, smBackend, smLinearAlgebra, smClusterComputing, smDistributedComputing, smContainerization, smIAC, smCiCd). finalize() GhaRunnersProject = GhaRunnersProject.WithStatus(statusMaintaining). WithSummary("SUMMARY HERE"). // TODO: MORE! @@ -645,8 +592,8 @@ func initProjectsFinal() { "ECS", "S3", "EC2", "IAM", "SecretsManager", "Cloudwatch", "Lambda", "ECR", "Route53", // Route53 add networking sm ). WithTechnologies("Github Actions"). - WithPlatforms("Datadog", "Logcentral", "Github", "Azure DevOps", "Jira", "Confluence"). - WithSubjectMatters(smBackend, smCiCd, smDevOps, smContainerization, smIAC, smCiCd). + WithPlatforms("Datadog", "Logcentral", "Github", "Azure DevOps", "Jira", "Confluence", "Teams"). + WithSubjectMatters(smBackend, smCiCd, smDevSecOps, smContainerization, smIAC, smCiCd). finalize() ogreProject = ogreProject.WithStatus(statusBuilding). WithSummary("SUMMARY HERE"). // TODO: MORE! @@ -657,12 +604,12 @@ func initProjectsFinal() { WithLang("SQL", Some). WithDbs("Aurora", "DynamoDB", "Postgres"). WithCaches("Redis", "memcached"). - WithTechnologies("Github Actions", "Parquet", "Avro"). + WithTechnologies("Github Actions", "Parquet", "Avro", "REST API"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "Azure DevOps", "Jira", "Confluence"). WithCloudProvider("AWS", "ECS", "S3", "EC2", "IAM", "SecretsManager", "DynamoDB", "DAX", "Cloudwatch", "Lambda", "ECR", "Route53", "Kinesis", "SQS", "SNS", ). - WithSubjectMatters(smBackend, smCiCd, smClusterComputing, smDistributedComputing, smContainerization, smIAC). + WithSubjectMatters(smApi, smGeospatial, smBackend, smCiCd, smClusterComputing, smDistributedComputing, smContainerization, smIAC). finalize() renderProject = renderProject.WithStatus(statusMaintaining). WithSummary("SUMMARY HERE"). // TODO: MORE! @@ -671,14 +618,14 @@ func initProjectsFinal() { WithLang("Docker", Regularly). WithLang("Bash", Some). WithLang("SQL", Some). - WithTechnologies("Github Actions", "Parquet", "Avro", "GraphQL"). + WithTechnologies("Github Actions", "Parquet", "Avro", "REST API", "GraphQL"). WithDbs("Postgres"). WithCaches("Redis", "memcached"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "GraphQL Apollo", "Azure DevOps", "Jira", "Confluence"). WithCloudProvider("AWS", "ECS", "S3", "EC2", "IAM", "SecretsManager", "DynamoDB", "DAX", "Cloudwatch", "Lambda", "ECR", "Route53", "Kinesis", "SQS", "SNS", ). - WithSubjectMatters(smBackend, smCiCd, smClusterComputing, smDistributedComputing, smContainerization, smIAC, smGraphics). + WithSubjectMatters(smApi, smGeospatial, smBackend, smCiCd, smClusterComputing, smDistributedComputing, smContainerization, smIAC, smGraphics). finalize() statsProject = statsProject.WithStatus(statusMaintaining). WithSummary("SUMMARY HERE"). // TODO: MORE! @@ -694,12 +641,12 @@ func initProjectsFinal() { WithLang("Rust", Minimal). WithDbs("Aurora", "Postgres"). WithCaches("Redis", "memcached"). - WithTechnologies("CUDA", "Github Actions", "Parquet", "Avro", "GraphQL", "SIMD"). + WithTechnologies("CGo", "Swagger", "OpenAPI", "CUDA", "Github Actions", "Parquet", "Avro", "GraphQL", "SIMD", "REST API"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "GraphQL Apollo", "Azure DevOps", "Jira", "Confluence"). WithCloudProvider("AWS", "ECS", "S3", "EC2", "IAM", "SecretsManager", "DynamoDB", "DAX", "Cloudwatch", "Lambda", "ECR", "Route53", "Kinesis", "SQS", "SNS", ). - WithSubjectMatters(smBackend, smCiCd, smClusterComputing, smDistributedComputing, smContainerization, smIAC, smGPU, smStatistics). + WithSubjectMatters(smApi, smGeospatial, smBackend, smCiCd, smClusterComputing, smDistributedComputing, smContainerization, smIAC, smGPU, smStatistics). finalize() billingProject = billingProject.WithStatus(statusBuilding). WithSummary("SUMMARY HERE"). // TODO: MORE! @@ -714,9 +661,9 @@ func initProjectsFinal() { ). WithDbs("DynamoDB", "Postgres"). WithCaches("Redis", "memcached"). - WithTechnologies("Github Actions", "Parquet", "Avro"). + WithTechnologies("Github Actions", "Parquet", "Avro", "REST API"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "Azure DevOps", "Jira", "Confluence"). - WithSubjectMatters(smCiCd, smClusterComputing, smDistributedComputing, smContainerization, smIAC, smStatistics, smBackend). + WithSubjectMatters(smApi, smGeospatial, smCiCd, smClusterComputing, smDistributedComputing, smContainerization, smIAC, smStatistics, smBackend). finalize() explorerProject = explorerProject.WithStatus(statusMaintaining). WithSummary("SUMMARY HERE"). // TODO: MORE! @@ -730,12 +677,12 @@ func initProjectsFinal() { ). WithDbs("Aurora", "DynamoDB", "Postgres"). WithCaches("Redis", "memcached"). - WithTechnologies("Github Actions"). + WithTechnologies("Github Actions", "REST API"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "Azure DevOps", "Jira", "Confluence"). - WithSubjectMatters(smCiCd, smContainerization, smIAC, smFullStack). + WithSubjectMatters(smApi, smGeospatial, smCiCd, smContainerization, smIAC, smFullStack). finalize() wqdbProject = wqdbProject.WithStatus(statusComplete). - WithSummary("SUMMARY HERE"). // TODO: MORE! + WithSummary("Work queue database for the various builders"). // TODO: list builders WithLang("SQL", Often). WithLang("Go", Often). WithLang("Scala", Often). @@ -750,7 +697,7 @@ func initProjectsFinal() { WithSubjectMatters(smCiCd, smIAC, smBackend). finalize() supportProject = supportProject.WithStatus(statusComplete). - WithSummary("SUMMARY HERE"). // TODO: MORE! + WithSummary("Support API cluster for troubleshooting"). WithLang("SQL", Regularly). WithLang("Go", Often). WithLang("Terraform", Often). @@ -761,7 +708,7 @@ func initProjectsFinal() { ). WithTechnologies("Github Actions"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "Azure DevOps", "Jira", "Confluence"). - WithSubjectMatters(smCiCd, smContainerization, smIAC, smBackend). + WithSubjectMatters(smApi, smCiCd, smContainerization, smIAC, smBackend). finalize() scudsProject = scudsProject.WithStatus(statusComplete). WithSummary("SUMMARY HERE"). // TODO: MORE! @@ -775,12 +722,12 @@ func initProjectsFinal() { ). WithDbs("ElasticSearch"). WithCaches("Redis"). - WithTechnologies("ElasticSearch", "Github Actions", "Parquet", "Avro"). + WithTechnologies("ElasticSearch", "Github Actions", "Parquet", "Avro", "REST API"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "Azure DevOps", "Jira", "Confluence"). - WithSubjectMatters(smCiCd, smContainerization, smIAC, smStatistics, smBackend). + WithSubjectMatters(smApi, smGeospatial, smCiCd, smContainerization, smIAC, smStatistics, smBackend). finalize() ufoProject = ufoProject.WithStatus(statusMaintaining). - WithSummary("SUMMARY HERE"). // TODO: MORE! + WithSummary("Unified Field Operations API Cluster"). WithLang("Scala", Extensively). WithLang("Go", Regularly). WithLang("Terraform", Regularly). @@ -790,9 +737,9 @@ func initProjectsFinal() { "ECS", "Fargate", "S3", "EC2", "IAM", "SecretsManager", "DynamoDB", "DAX", "Cloudwatch", "Lambda", "ECR", "Route53", "Kinesis", "SQS", "SNS", ). WithCaches("Redis"). - WithTechnologies("Github Actions"). + WithTechnologies("Github Actions", "REST API"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "Azure DevOps", "Jira", "Confluence"). - WithSubjectMatters(smCiCd, smContainerization, smIAC, smStatistics, smBackend). + WithSubjectMatters(smApi, smGeospatial, smCiCd, smContainerization, smIAC, smStatistics, smBackend). finalize() goweProject = goweProject.WithStatus(statusMaintaining). WithSummary("SUMMARY HERE"). // TODO: MORE! @@ -801,14 +748,24 @@ func initProjectsFinal() { WithLang("Bash", Some). WithLang("Docker", Regularly). WithCloudProvider("AWS", - "ECS", "Fargate", "S3", "EC2", "IAM", "SecretsManager", "DynamoDB", "DAX", "Cloudwatch", "Lambda", "ECR", "Route53", "Kinesis", "SQS", "SNS", + "ECS", "Fargate", "VPC", "VPN", "S3", "EC2", "IAM", "SecretsManager", "DynamoDB", "DAX", "Cloudwatch", "Lambda", "ECR", "Route53", "Kinesis", "SQS", "SNS", "Kinesis", ). WithDbs("DuckDB", "Aurora", "Postgres"). WithCaches("Redis"). WithTechnologies("Github Actions", "Parquet", "Avro"). WithPlatforms("Datadog", "Logcentral", "Github", "ServiceNow", "Azure DevOps", "Jira", "Confluence"). - WithSubjectMatters(smCiCd, smContainerization, smIAC, smClusterComputing, smDistributedComputing, smLinearAlgebra, smBackend). + WithSubjectMatters(smGeospatial, smCiCd, smContainerization, smIAC, smClusterComputing, smDistributedComputing, smLinearAlgebra, smBackend). finalize() + rasterRenderProject = rasterRenderProject. // TODO: this whole thing! + WithSummary("Tile renderer API service cluster. Later replaced by the Render Cluster"). // TODO: link to render cluster + WithLang("Scala", Extensively). + WithLang("Terraform", Regularly). + WithLang("Bash", Some). + WithStatus(statusComplete). + WithPlatforms("DroneCI", "Rally"). + WithSubjectMatters(smBackend). + WithCloudProvider("AWS", "ECS", "EC2", "API Gateway", "IAM", "ELB", "ALB", "Corretto"). + finalize() simpsonUnivProject = simpsonUnivProject.WithStatus(statusComplete). // TODO: MORE! WithSummary("SUMMARY HERE"). // TODO: MORE! WithLang("Html", Regularly). @@ -816,9 +773,8 @@ func initProjectsFinal() { WithLang("Javascript", Some). WithSubjectMatters(smFrontend). WithTechnologies("Drupal"). - WithPlatforms("Confluence"). // TODO: SOURCE JAMF + WithPlatforms("Confluence"). finalize() - // TODO: sai project for JAMF mushDbProject = mushDbProject.WithStatus(statusBuilding). WithSummary("SUMMARY HERE"). // TODO: MORE! WithLang("Go", Extensively). @@ -829,14 +785,14 @@ func initProjectsFinal() { WithLang("Docker Compose", Often). //WithLang("Kubernetes", Some). // TODO: lang or tech? WithDbs("mongodb"). - WithTechnologies("Github Actions", "React", "NextJs", "RFID", "NFC", "I2C", "SPI", "Cloudflare Tunnels", "Kubernetes"). + WithTechnologies("Github Actions", "React", "NextJs", "RFID", "NFC", "I2C", "SPI", "Cloudflare Tunnels", "Kubernetes", "REST API"). WithPlatforms("Github", "Cloudflare", "Grafana"). WithCloudProvider("GCP", "Google Cloud DNS", "Identity Platform"). - WithSubjectMatters(smContainerization, smDistributedComputing, smMycology, smFullStack). + WithSubjectMatters(smApi, smContainerization, smDistributedComputing, smMycology, smFullStack). WithInterests(string(smMycology)). finalize() cvProject = cvProject.WithStatus(statusBuilding). - WithSummary("This project! A generator which creates markdown files that can be viewed via Obsidian, or published to the web."). // TODO: MORE! + WithSummary("This project! A generator which creates markdown files that can be viewed via Obsidian, or published to the web."). WithLang("Go", Extensively). WithLang("Terraform", Often). WithLang("Bash", Some). @@ -846,10 +802,9 @@ func initProjectsFinal() { WithLang("CSS", Rarely). WithLang("SCSS", Rarely, smFrontend). WithTechnologies("Github Actions", "Obsidian", "Markdown", "Quartz", "Quartz 4"). - // TODO: add info to technologies???? WithPlatforms("Github"). WithCloudProvider("AWS", "S3", "IAM", "Cloudfront", "Cloudfront Functions", "ACM"). - WithSubjectMatters(smCiCd, smFrontend, smDevOps, smServerless). + WithSubjectMatters(smCiCd, smFrontend, smDevSecOps, smServerless). finalize() linksPage = linksPage. WithStatus(statusComplete). @@ -867,13 +822,15 @@ func initProjectsFinal() { nfcScannerProject = nfcScannerProject.WithStatus(statusBuilding). WithSummary("SUMMARY HERE"). // TODO: MORE! WithLang("Go", Extensively). - WithTechnologies("Github Actions", "Webhooks", "Websockets", "Server-Sent Events", "Pub-Sub", "NFC", "I2C", "SPI", "RFID"). + WithTechnologies("Github Actions", "Webhooks", "Websockets", "Server-Sent Events", "Pub-Sub", "NFC", "I2C", "SPI", "RFID", "REST API"). WithPlatforms("Github"). - WithSubjectMatters(smContainerization, smDistributedComputing). + WithSubjectMatters(smApi, smContainerization, smDistributedComputing). WithInterests(string(smMycology)). finalize() coreShufflerProject = coreShufflerProject.WithStatus(statusComplete). - WithSummary("SUMMARY HERE"). // TODO: MORE! + // TODO: MORE! LINKS! + WithSummary("Related to capstone project. Takes fuel assembly layout input, allows you to shuffle them, and outputs the CASMO and SIMULATE files necessary to run a cycle with the new layout."). + // TODO: ADD A PICTURE!!!!! WithLang("Javascript", Extensively). WithLang("Html", Extensively). WithLang("CSS", Extensively). @@ -897,7 +854,7 @@ func initProjectsFinal() { WithCloudProvider("AWS", "S3", "IAM", "Route53"). WithTechnologies("Gitlab CI", "React", "NodeJS"). - WithSubjectMatters(smCiCd, smDevOps, smFrontend). + WithSubjectMatters(smCiCd, smDevSecOps, smFrontend). finalize() CritColaProject = CritColaProject.WithStatus(statusComplete). WithSummary("SUMMARY HERE"). // TODO: this @@ -907,10 +864,10 @@ func initProjectsFinal() { WithLang("Bash", Some). WithLang("Javascript", Rarely). WithTechnologies("Gitlab CI"). - WithPlatforms("Gitlab"). + WithPlatforms("Gitlab", "Discord"). WithCloudProvider("AWS", "S3", "EC2", "IAM", "SecretsManager", "ECR", "Route53"). - WithSubjectMatters(smBackend, smCiCd, smDevOps, smScripting). + WithSubjectMatters(smBackend, smCiCd, smDevSecOps, smScripting). WithInterests("Gaming"). finalize() MastersDataAnalysisProject = MastersDataAnalysisProject.WithStatus(statusComplete). @@ -930,16 +887,30 @@ func initProjectsFinal() { WithSubjectMatters(smNuclearEngineering, smLinearAlgebra, smFluidMechanics, smThermodynamics, smParticlePhysics). WithInterests(string(smNuclearEngineering)). finalize() + reactorAnalysisFinal = reactorAnalysisFinal. + WithLang("Javascript", Extensively). + WithStatus(statusComplete). + WithSummary("Final exam for the final course of my Nuclear Engineering Bachelors degree"). // TODO: this! LInk! + WithSubjectMatters(smNuclearEngineering, smThermodynamics, smFluidMechanics). + finalize() + monteCarloProject = monteCarloProject. + WithSummary(fixmeLink). + WithStatus(statusComplete). + WithInterests(string(smParticlePhysics)). // TODO: ok? + WithLang("Java", Often). + WithSubjectMatters(smParticlePhysics, smNuclearEngineering). + finalize() projectLinAlgCryptography = projectLinAlgCryptography. - WithSummary("Created and analyzed the efficacy of a cryptographic algorithm utilizing basic matrix mathematics. STATS"). // TODO: this + WithSummary("Created and analyzed the efficacy of a cryptographic algorithm utilizing basic matrix mathematics."). WithMiscSkills("Linear Algebra"). WithInterests("Cryptography"). WithSubjectMatters(smCybersecurity, smCryptography). WithInterests(string(smCryptography), string(smCybersecurity)). finalize() cherenkovProject = cherenkovProject.WithStatus(statusComplete). - WithSummary("Designed and built a sensor for Cherenkov radiation, which was tested by lowering the sensor into the core of the PULSTAR reactor at "+schoolNCSU.Link()+". The sensor utilized and Arduino for signal processing."). // TODO: this + WithSummary("Designed and built a sensor for Cherenkov radiation, which was tested by lowering the sensor into the core of the PULSTAR reactor at "+Link(schoolNCSU)+". The sensor utilized and Arduino for signal processing."). WithLang("Python", Often). + WithLang("Fortran", Often). WithTechnologies("Arduino"). WithPlatforms("Github"). WithSubjectMatters(smNuclearEngineering, smLinearAlgebra, smParticlePhysics, smStatistics, smEmbeddedSystems, smElectronics). @@ -969,29 +940,172 @@ func initProjectsFinal() { WithLang("Javascript", Minimal). WithSubjectMatters(smFluidMechanics, smElectronics, smChemistry). finalize() + teiProjects = teiProjects. + WithSummary("A catch-all for all projects at my position at TEI."). + WithStatus(statusComplete). + WithSubjectMatters(smStructuralEngineering, smCivilEngineering). + finalize() + taePhotoImporter = taePhotoImporter. + WithSummary("Script to import photos taken in the field into the proper format and structure for use in engineering documentation"). + WithLang("Javascript", Some). + WithStatus(statusComplete). + WithSubjectMatters(smStructuralEngineering, smCivilEngineering). + finalize() + mafcProjects = mafcProjects. + WithSummary("Lifeguarding stuff, I guess."). + WithStatus(statusComplete). + WithSubjectMatters(smFirstAid). + finalize() + ArrowNailProject = ArrowNailProject. + WithLang("Javascript", Extensively). + WithLang("Html", Regularly). + WithLang("CSS", Regularly). + WithSummary("Geospatial mapping app to track historical hail instances for use by a roofing company"). + WithStatus(statusShelved). + WithTechnologies("React", "REST API"). + WithSubjectMatters(smApi, smGeospatial). + WithCloudProvider("AWS", "Lambda", "RDS"). // TODO: MORE STUFF? + finalize() + WildlifeRProject = WildlifeRProject. + WithStatus(statusComplete). + WithLang("R", Often, smGeospatial). + WithSummary("Utilized spatiotemporal data for wildlife in a specified area over a specified date range in order to produce population density maps"). + WithSubjectMatters(smGeospatial, smStatistics). + finalize() + jamfProject = jamfProject. + WithSummary("JAMF companywide setup. " + fixmeLink). + WithStatus(statusComplete). + WithPlatforms("Jamf"). + WithSubjectMatters(smCybersecurity). + finalize() + smallImprovementsProject = smallImprovementsProject. + WithLang("Javascript", Often). + WithSummary("Created a Small Improvements Slack bot for Source Allies to track goal creation and achievement"). + WithCloudProvider("AWS", "Lambda", "DynamoDB", "SAM", "Cloudformation"). // TODO: AWS + WithPlatforms("Slack", "Small Improvements", "Github", "Jira", "Confluence", "Slack"). + WithTechnologies("Github Actions", "REST API"). + WithStatus(statusComplete). + WithSubjectMatters(smApi, smBackend, smServerless). + finalize() + internalResumeGeneratorProject = internalResumeGeneratorProject. + WithSummary("Updated company internal resume generator"). + // TODO: any aws in here??? + WithStatus(statusComplete). + WithLang("Java", Often, smBackend). + WithLang("Html", Some). + WithLang("CSS", Some). + WithLang("Javascript", Some). + WithTechnologies("Spring", "REST API"). + WithSubjectMatters(smApi, smFullStack). + WithTechnologies("Github Actions"). + WithCloudProvider("AWS", "Route53"). // TODO: add route53 everywhere needed + finalize() + // TODO: root-finding algorithms project miscSmallPersonalProjects = miscSmallPersonalProjects. WithSummary("A catch-all for my miscellaneous personal projects I didn't want to add entire pages for"). WithStatus(statusShelved). WithLang("Python", Regularly, smScripting). WithLang("Fortran", Some, smScripting, smThermodynamics, smFluidMechanics, smParticlePhysics, smNuclearEngineering, smEducation, smEmbeddedSystems, smStatistics, smLinearAlgebra, smStatics). WithLang("Solidity", Some, smCryptocurrency). - WithLang("Go", Extensively, smThermodynamics). + WithLang("Go", Extensively, smThermodynamics, smScripting, smBackend). WithLang("Java", Some, smBackend). - WithLang("Javascript", Often, smFrontend). - WithLang("Typescript", Often, smFullStack). - WithTechnologies("Kubernetes"). - WithSubjectMatters(smCryptocurrency). + WithLang("Javascript", Often, smFrontend, smScripting). + WithLang("Typescript", Often, smFullStack, smScripting). + WithLang("Kotlin", Rarely, smBackend). + WithLang("Rust", Rarely, smBackend). + WithLang("Lua", Rarely, smScripting). + WithLang("Batch", Rarely, smScripting). + WithLang("Assembly", Rarely, smEmbeddedSystems). + WithLang("WASM", Rarely, smFrontend). + WithLang("ARM Assembly", Rarely, smEmbeddedSystems). + WithLang("ARM Assembly", Rarely, smEmbeddedSystems). + WithLang("Zig", Rarely, smBackend). + WithLang("Cobol", Rarely, smBackend). + WithLang("LISP", Rarely, smBackend). + WithLang("LABVIEW", Rarely, smScripting). // TODO: ok or put elsewhere? + WithLang("MATLAB", Rarely, smScripting). // TODO: ok or put elsewhere? + WithLang("Perl", Rarely, smScripting). + WithLang("PowerShell", Rarely, smScripting). + WithLang("Ruby", Rarely, smScripting). + WithTechnologies("Kubernetes", "Kafka", "ORC"). + WithSubjectMatters(smApi, smCryptocurrency). finalize() - teiProjects = teiProjects. - WithSummary("A catch-all for all projects at my position at TEI."). - WithStatus(statusComplete). - WithSubjectMatters(smStructuralEngineering, smCivilEngineering) - taeProjects = taeProjects. - WithSummary("A catch-all for all projects at my position at Talley Associates of Engineering."). - WithStatus(statusComplete). - WithSubjectMatters(smStructuralEngineering, smCivilEngineering) - mafcProjects = mafcProjects. - WithSummary("Lifeguarding stuff, I guess."). - WithStatus(statusComplete). - WithSubjectMatters(smFirstAid) } + +type projectTypeInfo interface { + Type() projectType + Finalize(*Project) + getClient() *Client + setClient(*Client) projectTypeInfo + getSchool() *SchoolPage + setSchool(page *SchoolPage) projectTypeInfo +} +type schoolProjectTypeInfo struct { + school *SchoolPage +} + +func (schoolProjectTypeInfo) Type() projectType { + return projectTypeSchool +} +func (i schoolProjectTypeInfo) Finalize(pr *Project) { + i.school.withProjects(pr) +} +func (i schoolProjectTypeInfo) getClient() *Client { + return nil +} +func (i schoolProjectTypeInfo) getSchool() *SchoolPage { + return i.school +} +func (i schoolProjectTypeInfo) setClient(cli *Client) projectTypeInfo { + return i +} +func (i schoolProjectTypeInfo) setSchool(s *SchoolPage) projectTypeInfo { + i.school = s + return i +} + +type professionalProjectTypeInfo struct { + client *Client +} + +func (professionalProjectTypeInfo) Type() projectType { + return projectTypeProfessional +} +func (i professionalProjectTypeInfo) Finalize(pr *Project) { + i.client.WithProjects(pr) +} +func (i professionalProjectTypeInfo) getClient() *Client { + return i.client +} +func (i professionalProjectTypeInfo) getSchool() *SchoolPage { + return nil +} +func (i professionalProjectTypeInfo) setClient(cli *Client) projectTypeInfo { + i.client = cli + return i +} +func (i professionalProjectTypeInfo) setSchool(s *SchoolPage) projectTypeInfo { + return i +} + +type personalProjectTypeInfo struct{} + +func (i personalProjectTypeInfo) getClient() *Client { + return nil +} +func (i personalProjectTypeInfo) getSchool() *SchoolPage { + return nil +} +func (i personalProjectTypeInfo) setClient(cli *Client) projectTypeInfo { + return i +} +func (i personalProjectTypeInfo) setSchool(s *SchoolPage) projectTypeInfo { + return i +} + +func (personalProjectTypeInfo) Type() projectType { + return projectTypePersonal +} +func (personalProjectTypeInfo) Finalize(*Project) {} + +type projectStatus string diff --git a/generator/provider.go b/generator/provider.go index d06f8ca..c58a2dc 100644 --- a/generator/provider.go +++ b/generator/provider.go @@ -37,21 +37,25 @@ func (pg *CloudProviderPage) Bytes() []byte { b.WriteString(frontmatterFor(pg.Name, "Cloud Provider")) b.WriteString("# Services\n") for _, svc := range pg.Services { - b.WriteString(fmt.Sprintf("- %s\n", svc.Link())) + b.WriteString(fmt.Sprintf("- %s\n", Link(svc))) } - b.WriteString("\n" + string(pg.tracked.Bytes("", ""))) + b.WriteString("\n" + string(pg.tracked.Bytes("", nil, ""))) // TODO: ok? return []byte(b.String()) } -func (pg *CloudProviderPage) Link() string { - if pg == nil { - return "NO_LINK" - } - return linkFor(pg.Name, "cv", "provider", withoutSpaces(pg.Name)) -} func (pg *CloudProviderPage) EntryType() string { return "Cloud Provider" } +func (pg *CloudProviderPage) Dst() string { + return dstFor("cv", "provider", withoutSpaces(pg.Name)) +} + +func (pg *CloudProviderPage) Title() string { + if pg == nil { + return noLinkText + } + return pg.Name +} func NewCloudProvider(name string, services ...string) *CloudProviderPage { prov, exists := providers[name] @@ -60,9 +64,6 @@ func NewCloudProvider(name string, services ...string) *CloudProviderPage { } svcs := make(map[string]*CloudServicePage, len(services)) for _, svc := range services { - if svc == "Fargate" { - println("fargate found") - } serv, existing := cloudServices[svc] if !existing || serv == nil { serv = NewService(svc, name) @@ -79,6 +80,7 @@ func NewCloudProvider(name string, services ...string) *CloudProviderPage { } subjectMatters[smCloudComputing][prov.EntryType()].Add() // TODO: ok? providers[name] = prov + addLinkable(name, prov) return prov } diff --git a/generator/service.go b/generator/service.go index f826477..62bf1a1 100644 --- a/generator/service.go +++ b/generator/service.go @@ -1,6 +1,9 @@ package main -import "appli.ng/cv/generator/utils" +import ( + "appli.ng/cv/generator/utils" + "strings" +) var cloudServices = map[string]*CloudServicePage{} @@ -23,11 +26,15 @@ func (pg *CloudServicePage) WithSubjectMatters(sms ...SubjectMatter) *CloudServi return pg } -func (pg *CloudServicePage) Link() string { +func (pg *CloudServicePage) Dst() string { + return dstFor("cv", "service", withoutSpaces(pg.Name)) +} + +func (pg *CloudServicePage) Title() string { if pg == nil { - return "NO_LINK" + return noLinkText } - return linkFor(pg.Name, "cv", "service", withoutSpaces(pg.Name)) + return pg.Name } func NewService(name string, provider string) *CloudServicePage { @@ -41,6 +48,7 @@ func NewService(name string, provider string) *CloudServicePage { } subjectMatters[smCloudComputing][out.EntryType()].Add() // TODO: ok? cloudServices[name] = out + addLinkable(strings.ToLower(name), out) return out } @@ -48,16 +56,77 @@ func setupServiceSubjectMatters() { NewService("Cloudwatch", "AWS"). WithSubjectMatters(smObservability) NewService("Lambda", "AWS"). - WithSubjectMatters(smServerless) + WithSubjectMatters(smServerless, smCloudComputing) NewService("Fargate", "AWS"). - WithSubjectMatters(smServerless) + WithSubjectMatters(smServerless, smCloudComputing) NewService("Aurora", "AWS"). - WithSubjectMatters(smServerless) + WithSubjectMatters(smServerless, smDatabase) + //WithTags("Database") // TODO: ok??? NewService("DynamoDB", "AWS"). + WithSubjectMatters(smServerless, smDatabase) + NewService("RDS", "AWS"). + WithSubjectMatters(smDatabase) + NewService("DocumentDB", "AWS"). + WithSubjectMatters(smDatabase) + NewService("DAX", "AWS"). + WithSubjectMatters(smCaching) + NewService("Elasticache", "AWS"). // TODO: use! + WithSubjectMatters(smCaching) + //NewService("DocumentDB", "AWS"). // TODO: use if used + // WithSubjectMatters(smServerless) + NewService("IAM", "AWS"). + WithSubjectMatters(smCybersecurity) + NewService("ECS", "AWS"). + WithSubjectMatters(smCloudComputing, smClusterComputing) + // TODO: ASGs? CapacityProviders? + // TODO: Glue? + NewService("EC2", "AWS"). + WithSubjectMatters(smCloudComputing) + NewService("Secrets Manager", "AWS"). + WithSubjectMatters(smCybersecurity) + NewService("Key Management Service", "AWS"). + WithSubjectMatters(smCybersecurity) + NewService("Cloudfront", "AWS"). + WithSubjectMatters(smFullStack) // TODO: OK? + NewService("ECS", "AWS"). + WithSubjectMatters(smCloudComputing, smClusterComputing) + NewService("ECR", "AWS"). + WithSubjectMatters(smCloudComputing, smClusterComputing, smContainerization) + NewService("SAM", "AWS"). WithSubjectMatters(smServerless) - NewService("SAM", "AWS"). // TODO: use somewhere - WithSubjectMatters(smServerless) + NewService("EBS", "AWS"). + WithSubjectMatters(smBackend) + //NewService("EFS", "AWS"). // TODO: use somewhere when used + // WithSubjectMatters(smBackend) + //NewService("EKS", "AWS"). // TODO: use somewhere when used + // WithSubjectMatters(smBackend, smClusterComputing) + NewService("Kinesis", "AWS"). // TODO: use somewhere when used + WithSubjectMatters(smBackend, smClusterComputing) // TODO: event driven??? NewService("Cloudformation", "AWS"). // TODO: use somewhere WithSubjectMatters(smIAC) + NewService("Chime", "AWS"). // TODO: use somewhere + WithSubjectMatters(smCommunication) + NewService("SQS", "AWS"). // TODO: use somewhere + WithSubjectMatters(smBackend) // TODO: event driven??? + NewService("SNS", "AWS"). // TODO: use somewhere + WithSubjectMatters(smBackend) // TODO: event driven??? + //NewService("SES", "AWS"). // TODO: use somewhere when used + // WithSubjectMatters(smBackend) + NewService("VPC", "AWS"). // TODO: use somewhere when used + WithSubjectMatters(smBackend, smNetworking) + NewService("VPN", "AWS"). // TODO: use somewhere when used + WithSubjectMatters(smBackend, smNetworking) + //NewService("MQ", "AWS"). // TODO: use somewhere + // WithSubjectMatters(smBackend) // TODO: event driven??? + //NewService("Cognito", "AWS"). // TODO: use somewhere when used + // WithSubjectMatters(smCommunication) + //NewService("Athena", "AWS"). // TODO: use somewhere when used + // WithSubjectMatters(smBackend) + //NewService("Bedrock", "AWS"). // TODO: use somewhere when used + // WithSubjectMatters(smBackend, smAI) + NewService("Corretto", "AWS"). // TODO: use somewhere when used + WithSubjectMatters(smBackend) + NewService("S3", "AWS"). + WithSubjectMatters(smBackend) } diff --git a/generator/subjectMatter.go b/generator/subjectMatter.go index cab55fd..eee48c8 100644 --- a/generator/subjectMatter.go +++ b/generator/subjectMatter.go @@ -9,10 +9,18 @@ var subjectMatters = map[SubjectMatter]map[string]utils.Set[string]{} // Map of type SubjectMatter string -func (sm SubjectMatter) Link() string { - return linkFor(string(sm), "cv", "subjectMatter", withoutSpaces(string(sm))) +func (sm SubjectMatter) Dst() string { + if string(sm) == "CI-CD" { + dstFor("cv", "subjectMatter", withoutSpaces(string(sm))) + return linkFor("CI-CD", "cv", "subjectMatter", withoutSpaces(string(sm))) // TODO; FIX SO IT SAYS CI/CD + } + return dstFor("cv", "subjectMatter", withoutSpaces(string(sm))) +} + +func (sm SubjectMatter) Title() string { + return string(sm) } -func (pg *SubjectMatter) EntryType() string { +func (pg SubjectMatter) EntryType() string { return "Subject Matter" } func (sm SubjectMatter) Bytes() []byte { @@ -28,6 +36,7 @@ func NewSubjectMatter(sm string) SubjectMatter { if _, exists := subjectMatters[out]; !exists { subjectMatters[out] = map[string]utils.Set[string]{} } + addLinkable(strings.ToLower(sm), out) return out } @@ -48,15 +57,15 @@ func withSubjectMatters(pg Linkable, setIn utils.Set[SubjectMatter], sms ...Subj if current, exists := subjectMatters[sm]; exists { newInternalMap := current if currentSet, setExists := current[pg.EntryType()]; setExists { - currentSet.Add(pg.Link()) + currentSet.Add(Link(pg)) newInternalMap[pg.EntryType()] = currentSet // TODO: use new set? } else { - newInternalMap[pg.EntryType()] = utils.SetFrom(pg.Link()) + newInternalMap[pg.EntryType()] = utils.SetFrom(Link(pg)) } subjectMatters[sm] = newInternalMap } else { subjectMatters[sm] = map[string]utils.Set[string]{ - pg.EntryType(): utils.SetFrom(pg.Link()), + pg.EntryType(): utils.SetFrom(Link(pg)), } } } @@ -71,7 +80,7 @@ var ( smDistributedComputing = NewSubjectMatter("Distributed Computing") smContainerization = NewSubjectMatter("Containerization") smIAC = NewSubjectMatter("Infrastructure As Code") - smDevOps = NewSubjectMatter("DevOps") + smDevSecOps = NewSubjectMatter("DevSecOps") smCiCd = NewSubjectMatter("CI-CD") smStatistics = NewSubjectMatter("Statistics") smTopology = NewSubjectMatter("Topology") @@ -92,10 +101,12 @@ var ( smMycology = NewSubjectMatter("Mycology") smFrontend = NewSubjectMatter("Frontend") smBackend = NewSubjectMatter("Backend") + smApi = NewSubjectMatter("API") // TODO: USE THIS EVERYWHERE smFullStack = NewSubjectMatter("Full Stack") smCloudComputing = NewSubjectMatter("Cloud Computing") smObservability = NewSubjectMatter("Observability") smServerless = NewSubjectMatter("Serverless") + smDatabase = NewSubjectMatter("Serverless") smLogging = NewSubjectMatter("Logging") smFirstAid = NewSubjectMatter("First Aid") smScripting = NewSubjectMatter("Scripting") @@ -104,6 +115,9 @@ var ( smNetworking = NewSubjectMatter("Networking") // TODO: maybe get rid of smChemistry = NewSubjectMatter("Chemistry") smDocumentation = NewSubjectMatter("Documentation") + smGeospatial = NewSubjectMatter("Geospatial") + smCommunication = NewSubjectMatter("Communication") + smCaching = NewSubjectMatter("Caching") ) func setupSubjectMatters() { @@ -117,4 +131,6 @@ func setupSubjectMatters() { setupServiceSubjectMatters() setupTechSubjectMatters() setupPlatformSubjectMatters() + linkables["cicd"] = smCiCd + linkables["ci/cd"] = smCiCd } diff --git a/generator/tags.go b/generator/tags.go deleted file mode 100644 index 63763ca..0000000 --- a/generator/tags.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import "appli.ng/cv/generator/utils" - -type Tag string - -var tags = utils.Set[Tag]{} - -func NewTag(name string) Tag { - out := Tag(name) - tags.Add(out) - return out -} - -var ( - tagRobotics = NewTag("Robotics") - tagCryptocurrency = NewTag("Cryptocurrency") // TODO: USE SOMEWHERE - tagClusterComputing = NewTag("Cluster Computing") - tagDistributedComputing = NewTag("Distributed Computing") - tagContainerization = NewTag("Containerization") - tagIAC = NewTag("Infrastructure As Code") - tagDevOps = NewTag("DevOps") - tagCiCd = NewTag("CI-CD") - tagStatistics = NewTag("Statistics") - tagTopology = NewTag("Topology") - tagParticlePhysics = NewTag("Particle Physics") - tagNuclearEngineering = NewTag("Nuclear Engineering") - tagCivilEngineering = NewTag("Civil Engineering") - tagStructuralEngineering = NewTag("Structural Engineering") - tagThermodynamics = NewTag("Thermodynamics") - tagFluidMechanics = NewTag("Fluid Mechanics") - tagCybersecurity = NewTag("Cybersecurity") - tagCryptography = NewTag("Cryptography") - tagEmbeddedSystems = NewTag("Embedded Systems") - tagElectronics = NewTag("Electronics") - tagAI = NewTag("AI") - tagLinearAlgebra = NewTag("Linear Algebra") - tagGraphics = NewTag("Graphics") - tagGPU = NewTag("GPU") - tagMycology = NewTag("Mycology") - tagFrontend = NewTag("Frontend") - tagBackend = NewTag("Backend") - tagFullStack = NewTag("Full Stack") - tagCloudComputing = NewTag("Cloud Computing") - tagObservability = NewTag("Observability") - tagServerless = NewTag("Serverless") - tagLogging = NewTag("Logging") - tagFirstAid = NewTag("First Aid") - tagScripting = NewTag("Scripting") - tagEducation = NewTag("Education") - tagStatics = NewTag("Statics") - tagNetworking = NewTag("Networking") // TODO: maybe get rid of -) diff --git a/generator/tags/tags.go b/generator/tags/tags.go new file mode 100644 index 0000000..8e1ab94 --- /dev/null +++ b/generator/tags/tags.go @@ -0,0 +1,72 @@ +package tags + +import "appli.ng/cv/generator/utils" + +type Tag string + +var tags = utils.Set[Tag]{} + +func NewTag(name string) Tag { + out := Tag(name) + tags.Add(out) + return out +} + +type Field []Tag + +func (tags Field) AsStrings() []string { + out := make([]string, len(tags)) + for i, tag := range tags { + out[i] = string(tag) + } + return out +} + +var ( + AgTech = NewTag("AgTech") + Consulting = NewTag("Consulting") + Robotics = NewTag("Robotics") + Cryptocurrency = NewTag("Cryptocurrency") // TODO: USE SOMEWHERE + ClusterComputing = NewTag("Cluster Computing") + DistributedComputing = NewTag("Distributed Computing") + Containerization = NewTag("Containerization") + IAC = NewTag("Infrastructure As Code") + DevOps = NewTag("DevOps") + CiCd = NewTag("CI-CD") + Statistics = NewTag("Statistics") + Topology = NewTag("Topology") + ParticlePhysics = NewTag("Particle Physics") + NuclearEngineering = NewTag("Nuclear Engineering") + CivilEngineering = NewTag("Civil Engineering") + StructuralEngineering = NewTag("Structural Engineering") + Telecom = NewTag("Telecommunications") + SWE = NewTag("Software Engineering") + Thermodynamics = NewTag("Thermodynamics") + FluidMechanics = NewTag("Fluid Mechanics") + Cybersecurity = NewTag("Cybersecurity") + Cryptography = NewTag("Cryptography") + EmbeddedSystems = NewTag("Embedded Systems") + Electronics = NewTag("Electronics") + AI = NewTag("AI") + LinearAlgebra = NewTag("Linear Algebra") + Graphics = NewTag("Graphics") + GPU = NewTag("GPU") + Mycology = NewTag("Mycology") + Frontend = NewTag("Frontend") + Backend = NewTag("Backend") + FullStack = NewTag("Full Stack") + CloudComputing = NewTag("Cloud Computing") + Observability = NewTag("Observability") + Serverless = NewTag("Serverless") + Logging = NewTag("Logging") + FirstAid = NewTag("First Aid") + Scripting = NewTag("Scripting") + Statics = NewTag("Statics") + Networking = NewTag("Networking") // TODO: maybe get rid of + Charity = NewTag("Charity") + Entertainment = NewTag("Entertainment") + Education = NewTag("Education") + Construction = NewTag("Construction") + PublicHealth = NewTag("Public Health") + Ecology = NewTag("Ecology") +) diff --git a/generator/technology.go b/generator/technology.go index cbef927..2d53c55 100644 --- a/generator/technology.go +++ b/generator/technology.go @@ -1,6 +1,9 @@ package main -import "appli.ng/cv/generator/utils" +import ( + "appli.ng/cv/generator/utils" + "strings" +) var techs = map[string]*TechologyPage{} @@ -10,11 +13,15 @@ type TechologyPage struct { // React, Github actions, etc SubjectMatters utils.Set[SubjectMatter] } -func (pg *TechologyPage) Link() string { +func (pg *TechologyPage) Dst() string { + return dstFor("cv", "technology", withoutSpaces(pg.Name)) +} + +func (pg *TechologyPage) Title() string { if pg == nil { - return "NO_LINK" + return noLinkText } - return linkFor(pg.Name, "cv", "technology", withoutSpaces(pg.Name)) + return pg.Name } func (pg *TechologyPage) EntryType() string { @@ -44,6 +51,7 @@ func NewTechnology(name string, subjectMatters ...SubjectMatter) *TechologyPage SubjectMatters: utils.SetFrom(subjectMatters...), } techs[name] = out + addLinkable(strings.ToLower(name), out) return out } @@ -56,8 +64,10 @@ func setupTechSubjectMatters() { NewTechnology("LLM", smAI) NewTechnology("AI", smAI) NewTechnology("AI Agents", smAI) - NewTechnology("OpenAI API spec", smAI, smBackend) + NewTechnology("OpenAI API spec", smAI, smBackend).WithTags("API") NewTechnology("CUDA", smGPU, smGraphics) + NewTechnology("CGo", smBackend) + NewTechnology("REST API", smBackend, smApi).WithTags("API") // TODO: use this everywhere necessary... NewTechnology("Avro", smBackend).WithTags("DataFormat") NewTechnology("Parquet", smBackend).WithTags("DataFormat") NewTechnology("JSON", smFullStack).WithTags("DataFormat") @@ -67,7 +77,6 @@ func setupTechSubjectMatters() { NewTechnology("YAML", smFullStack, smCiCd).WithTags("DataFormat") NewTechnology("TOML", smFullStack).WithTags("DataFormat") NewTechnology("CUDA", smGPU, smGraphics) - // TODO: GRAPHQL???? NewTechnology("Kubernetes", smBackend, smDistributedComputing, smContainerization, smIAC, smCloudComputing, smNetworking) // TODO: ansible? chef? NewTechnology("RFID", smRobotics, smEmbeddedSystems, smElectronics) @@ -79,7 +88,7 @@ func setupTechSubjectMatters() { NewTechnology("Quartz 4", smFrontend) NewTechnology("Quartz", smFrontend) NewTechnology("NextJs", smFullStack) - NewTechnology("GraphQL", smBackend, smNetworking) + NewTechnology("GraphQL", smBackend, smNetworking).WithTags("API") NewTechnology("Obsidian", smDocumentation) NewTechnology("SIMD", smBackend, smRobotics, smEmbeddedSystems) NewTechnology("Cloudflare Tunnels", smNetworking) @@ -89,18 +98,23 @@ func setupTechSubjectMatters() { NewTechnology("Markdown", smDocumentation) NewTechnology("Websockets", smFullStack, smNetworking) NewTechnology("Server-Sent Events", smFullStack, smNetworking) - // TODO: ADD NDSF(?) FILES FOR NUC STUFF NewTechnology("SIMULATE3", smNuclearEngineering, smParticlePhysics) NewTechnology("CASMO4e", smNuclearEngineering, smParticlePhysics) NewTechnology("Pub-Sub", smBackend, smNetworking) - NewTechnology("Git", smFullStack, smDevOps) + NewTechnology("Git", smFullStack, smDevSecOps) NewTechnology("Webhooks", smFullStack, smNetworking) NewTechnology("JQuery", smFrontend) NewTechnology("Arduino", smEmbeddedSystems, smElectronics, smRobotics) NewTechnology("PWM", smEmbeddedSystems, smElectronics, smRobotics) NewTechnology("G and M codes", smElectronics, smRobotics) - NewTechnology("OpenApi", smDocumentation) // TODO: USE - NewTechnology("Swagger", smDocumentation) // TODO: USE + NewTechnology("OpenApi", smDocumentation).WithTags("API") + NewTechnology("Swagger", smDocumentation).WithTags("API") + NewTechnology("Spring", smBackend) + //NewTechnology("Spark", smBackend) // TODO: apache arrow + //NewTechnology("Arrow", smBackend) // TODO: apache arrow + //NewTechnology("ORC", smBackend) // TODO: apache ORC + NewTechnology("Kafka", smBackend) // TODO: event driven? + // } // TODO: list all backlinks???? diff --git a/quartzModified/.gitignore b/quartzModified/.gitignore index b38445e..b2ca155 100644 --- a/quartzModified/.gitignore +++ b/quartzModified/.gitignore @@ -2,6 +2,8 @@ !quartz.config.ts !quartz.layout.ts !quartz/components/Footer.tsx +!quartz/components/ContentMeta.tsx +!quartz/components/PageList.tsx !quartz/components/scripts/darkmode.inline.ts !quartz/components/styles/darkmode.scss !quartz/components/styles/footer.scss \ No newline at end of file diff --git a/quartzModified/quartz.layout.ts b/quartzModified/quartz.layout.ts index ed0cdf2..a6211a7 100644 --- a/quartzModified/quartz.layout.ts +++ b/quartzModified/quartz.layout.ts @@ -8,9 +8,13 @@ export const sharedPageComponents: SharedLayout = { afterBody: [], footer: Component.Footer({ links: { + "Home": "https://reece.appli.ng", + "CV": "https://reece.appli.ng/cv", + "Blog": "https://reece.appli.ng/Blog", + "My Links": "https://links.reece.appli.ng", GitHub: "https://github.com/reeceappling", "Linkedin": "https://linkedin.com/reeceappling", - "Other Links": "https://links.reece.appli.ng" + "Notes": "https://reece.appli.ng/Notes", }, }), } @@ -50,7 +54,11 @@ export const defaultContentPageLayout: PageLayout = { // components for pages that display lists of pages (e.g. tags or folders) export const defaultListPageLayout: PageLayout = { - beforeBody: [Component.Breadcrumbs(), Component.ArticleTitle(), Component.ContentMeta()], + beforeBody: [ + Component.Breadcrumbs(), + Component.ArticleTitle(), + Component.ContentMeta(), + ], left: [ Component.PageTitle(), Component.MobileOnly(Component.Spacer()), diff --git a/quartzModified/quartz/components/ContentMeta.tsx b/quartzModified/quartz/components/ContentMeta.tsx new file mode 100644 index 0000000..d013ba5 --- /dev/null +++ b/quartzModified/quartz/components/ContentMeta.tsx @@ -0,0 +1,59 @@ +//import { Date, getDate } from "./Date" +import { QuartzComponentConstructor, QuartzComponentProps } from "./types" +import readingTime from "reading-time" +import { classNames } from "../util/lang" +import { i18n } from "../i18n" +import { JSX } from "preact" +import style from "./styles/contentMeta.scss" + +interface ContentMetaOptions { + /** + * Whether to display reading time + */ + showReadingTime: boolean + showComma: boolean +} + +const defaultOptions: ContentMetaOptions = { + showReadingTime: false, // Set to true to reenable reading time ContentMetadata + showComma: true, +} + +export default ((opts?: Partial) => { + // Merge options with defaults + const options: ContentMetaOptions = { ...defaultOptions, ...opts } + + function ContentMetadata({ cfg, fileData, displayClass }: QuartzComponentProps) { + const text = fileData.text + + if (text) { + const segments: (string | JSX.Element)[] = [] + + // Disable dates at the top of pages + // if (fileData.dates) { + // segments.push() + // } + + // Display reading time if enabled + if (options.showReadingTime) { + const { minutes, words: _words } = readingTime(text) + const displayedTime = i18n(cfg.locale).components.contentMeta.readingTime({ + minutes: Math.ceil(minutes), + }) + segments.push({displayedTime}) + } + + return ( +

+ {segments} +

+ ) + } else { + return null + } + } + + ContentMetadata.css = style + + return ContentMetadata +}) satisfies QuartzComponentConstructor diff --git a/quartzModified/quartz/components/PageList.tsx b/quartzModified/quartz/components/PageList.tsx new file mode 100644 index 0000000..819c8a5 --- /dev/null +++ b/quartzModified/quartz/components/PageList.tsx @@ -0,0 +1,115 @@ +import { FullSlug, isFolderPath, resolveRelative } from "../util/path" +import { QuartzPluginData } from "../plugins/vfile" +//import { Date, getDate } from "./Date" +import { getDate } from "./Date" +import { QuartzComponent, QuartzComponentProps } from "./types" +import { GlobalConfiguration } from "../cfg" + +export type SortFn = (f1: QuartzPluginData, f2: QuartzPluginData) => number + +export function byDateAndAlphabetical(cfg: GlobalConfiguration): SortFn { + return (f1, f2) => { + // Sort by date/alphabetical + if (f1.dates && f2.dates) { + // sort descending + return getDate(cfg, f2)!.getTime() - getDate(cfg, f1)!.getTime() + } else if (f1.dates && !f2.dates) { + // prioritize files with dates + return -1 + } else if (!f1.dates && f2.dates) { + return 1 + } + + // otherwise, sort lexographically by title + const f1Title = f1.frontmatter?.title.toLowerCase() ?? "" + const f2Title = f2.frontmatter?.title.toLowerCase() ?? "" + return f1Title.localeCompare(f2Title) + } +} + +export function byDateAndAlphabeticalFolderFirst(cfg: GlobalConfiguration): SortFn { + return (f1, f2) => { + // Sort folders first + const f1IsFolder = isFolderPath(f1.slug ?? "") + const f2IsFolder = isFolderPath(f2.slug ?? "") + if (f1IsFolder && !f2IsFolder) return -1 + if (!f1IsFolder && f2IsFolder) return 1 + + // If both are folders or both are files, sort by date/alphabetical + if (f1.dates && f2.dates) { + // sort descending + return getDate(cfg, f2)!.getTime() - getDate(cfg, f1)!.getTime() + } else if (f1.dates && !f2.dates) { + // prioritize files with dates + return -1 + } else if (!f1.dates && f2.dates) { + return 1 + } + + // otherwise, sort lexographically by title + const f1Title = f1.frontmatter?.title.toLowerCase() ?? "" + const f2Title = f2.frontmatter?.title.toLowerCase() ?? "" + return f1Title.localeCompare(f2Title) + } +} + +type Props = { + limit?: number + sort?: SortFn +} & QuartzComponentProps + +export const PageList: QuartzComponent = ({ cfg, fileData, allFiles, limit, sort }: Props) => { + const sorter = sort ?? byDateAndAlphabeticalFolderFirst(cfg) + let list = allFiles.sort(sorter) + if (limit) { + list = list.slice(0, limit) + } + + return ( + + ) +} + +PageList.css = ` +.section h3 { + margin: 0; +} + +.section > .tags { + margin: 0; +} +` diff --git a/scripts/changeTimestamps/main.go b/scripts/changeTimestamps/main.go new file mode 100644 index 0000000..5c5dd50 --- /dev/null +++ b/scripts/changeTimestamps/main.go @@ -0,0 +1,61 @@ +package changeTimestamps + +import ( + "flag" + "fmt" + "io/fs" + "log" + "os" + "path/filepath" + "strconv" + "strings" + "time" +) + +func main() { + endPath := flag.String("path", "", "directory path to change timestamps within") + dateStr := flag.String("date", "03-21-2026", "date to use for all files") + flag.Parse() + if endPath == nil || *endPath == "" { + panic("path is required") + } + dateStrs := strings.Split(*dateStr, "-") + if len(dateStrs) != 3 { + panic("date format is invalid. Must be MM-DD-YYYY") + } + mo, err := strconv.ParseInt(dateStrs[0], 10, 8) + if err != nil { + panic("month must be an integer") + } + dy, err := strconv.ParseInt(dateStrs[1], 10, 8) + if err != nil { + panic("day must be an integer") + } + yr, err := strconv.ParseInt(dateStrs[2], 10, 64) + if err != nil { + panic("year must be an integer") + } + modificationTime := time.Date(int(yr), time.Month(mo), int(dy), 0, 0, 0, 0, time.UTC) + + wd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + root := wd + "/" + *endPath // TODO: ensure ok + err = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + fmt.Printf("Error accessing path %s: %v\n", path, err) + return err + } + + if d.IsDir() { + return nil + } + // Process the file. Change the access and modification times + return os.Chtimes(path, modificationTime, modificationTime) // TODO: ensure this also changes creation time + }) + if err != nil { + log.Fatalf("Error walking the path %s: %v\n", root, err) + } + +} diff --git a/static/Resume_Reece_Appling.pdf b/static/Resume_Reece_Appling.pdf new file mode 100644 index 0000000..b982910 Binary files /dev/null and b/static/Resume_Reece_Appling.pdf differ