-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4014157
commit e265514
Showing
5 changed files
with
358 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package input |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package input | ||
|
||
import ( | ||
core "anemon/internal/core" | ||
"os" | ||
"path/filepath" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
var( | ||
work_input = ` | ||
# Back-End Intern | ||
## February 2024 -- August 2024 | ||
### TechCorp | ||
#### Internship | ||
- Assisted in developing and optimizing a key business process for expanding into new markets, collaborating with various teams to ensure compliance and smooth integration. | ||
- Participated in the migration of core backend logic from a monolithic application to a microservice architecture, improving system performance and scalability. | ||
- Enhanced system monitoring and reliability by implementing tracing mechanisms and performance objectives. | ||
# Back-End Intern | ||
## February 2024 -- August 2024 | ||
### TechCorp | ||
#### Internship | ||
- Assisted in developing and optimizing a key business process for expanding into new markets, collaborating with various teams to ensure compliance and smooth integration. | ||
- Participated in the migration of core backend logic from a monolithic application to a microservice architecture, improving system performance and scalability. | ||
- Enhanced system monitoring and reliability by implementing tracing mechanisms and performance objectives.` | ||
|
||
skill_input =` | ||
**Langage** | ||
- Langage A, Langage B, Langage C, Langage D, Langage E, Langage F, Langage G/H | ||
**Langage** | ||
- Langage A, Langage B, Langage C, Langage D, Langage E, Langage F, Langage G/H` | ||
invalid_skill_input =` | ||
- Langage A, Langage B, Langage C, Langage D, Langage E, Langage F, Langage G/H | ||
**Langag | ||
- Langage A, Langage B, Langage C, Langage D, Langage E, Langage F, Langage G/H` | ||
|
||
|
||
|
||
skill_paragraphe = core.Paragraphe{H1: "Langage", H2: "Langage A, Langage B, Langage C, Langage D, Langage E, Langage F, Langage G/H"} | ||
|
||
work_paragraphe = core.Paragraphe{H1: "Back-End Intern", H2: "February 2024 -- August 2024", | ||
H3: "TechCorp", H4: "Internship",Items: []string{ | ||
"Assisted in developing and optimizing a key business process for expanding into new markets, collaborating with various teams to ensure compliance and smooth integration.", | ||
"Participated in the migration of core backend logic from a monolithic application to a microservice architecture, improving system performance and scalability.", | ||
"Enhanced system monitoring and reliability by implementing tracing mechanisms and performance objectives."}} | ||
|
||
invalid_input = "ajsdlhsaeld##dafdbhkbhkjsd##" | ||
work_expected_result = []core.Paragraphe{work_paragraphe,work_paragraphe} | ||
skill_expected_result = []core.Paragraphe{skill_paragraphe,skill_paragraphe} | ||
|
||
paths = []struct { | ||
relativePath string | ||
content string | ||
}{ | ||
{"eng/education.md", work_input}, | ||
{"eng/project.md", work_input}, | ||
{"eng/work.md", work_input}, | ||
{"eng/skill.md", skill_input}, | ||
{"fr/education.md", work_input}, | ||
{"fr/work.md", work_input}, | ||
{"fr/skill.md", skill_input}, | ||
} | ||
|
||
) | ||
|
||
func TestParagraphe(t *testing.T) { | ||
|
||
t.Run("Work Paragraphes should return a slice of valid Paragraphe", func (t *testing.T) { | ||
got := getParagrapheFrom(work_input) | ||
want := work_expected_result | ||
if !reflect.DeepEqual(got[0], want[0]){ | ||
t.Fatalf("the first Paragraphe should be :\n%s\n got :%s",want,got) | ||
} | ||
|
||
if !reflect.DeepEqual(got[1], want[1]){ | ||
t.Fatalf("the first Paragraphe should be :\n%s\n got :%s",want,got) | ||
} | ||
}) | ||
|
||
t.Run("Invalid input should return nothing", func (t *testing.T) { | ||
result := getParagrapheFrom(invalid_input) | ||
if result != nil{ | ||
t.Fatalf("Invalid input should return nil got %v",result) | ||
} | ||
}) | ||
|
||
t.Run("Skill Paragraphe should be return from valid input", func (t *testing.T) { | ||
got := getParagrapheFrom(skill_input) | ||
want := skill_expected_result | ||
if !reflect.DeepEqual(got,want){ | ||
t.Fatalf("the first Paragraphe should be :\n%s\n got :%s",want,got) | ||
} | ||
}) | ||
|
||
t.Run("Invalid skill Paragraphe should return nil", func (t *testing.T) { | ||
got := getParagrapheFrom(invalid_skill_input) | ||
if got != nil{ | ||
t.Fatalf("Invalid input should return nil got %v",got) | ||
} | ||
}) | ||
} | ||
|
||
func TestSections(t *testing.T) { | ||
rootDir := t.TempDir() | ||
for _, p := range paths { | ||
fullPath := filepath.Join(rootDir, p.relativePath) | ||
if err := os.MkdirAll(filepath.Dir(fullPath), os.ModePerm); err != nil { | ||
t.Fatalf("Failed to create directories: %v", err) | ||
} | ||
if err := os.WriteFile(fullPath, []byte(p.content), os.ModePerm); err != nil { | ||
t.Fatalf("Failed to write file: %v", err) | ||
} | ||
} | ||
|
||
t.Run("Should return a valid list of cv", func (t *testing.T) { | ||
got,err := GetCVsFrom(rootDir) | ||
got[1].Print() | ||
if err!=nil{ | ||
t.Fatalf("Failed to getCV got %s",err) | ||
} | ||
|
||
lang_got := got[len(got)-1].Lang | ||
l_want := "fr" | ||
if lang_got != l_want{ | ||
t.Fatalf("Should have %s got %s",l_want,lang_got) | ||
} | ||
|
||
sec_got := len(got[len(got)-1].Sections) | ||
s_want := 3 | ||
if sec_got != s_want{ | ||
t.Fatalf("Should have %d got %d",s_want,sec_got) | ||
} | ||
|
||
t_sec_got := got[len(got)-1].Sections[len(got[len(got)-1].Sections)-1].Title | ||
t_s_want := "work" | ||
if t_sec_got != t_s_want{ | ||
t.Fatalf("Should have %s got %s",t_s_want,t_sec_got) | ||
} | ||
|
||
p_t_sec_got := got[len(got)-1].Sections[len(got[len(got)-1].Sections)-1].Paragraphes | ||
p_t_s_want := work_expected_result | ||
|
||
if len(p_t_sec_got) != len(p_t_s_want){ | ||
t.Fatalf("Should have len %d got %d",len(p_t_s_want),len(p_t_sec_got)) | ||
} | ||
|
||
if p_t_sec_got[len(p_t_sec_got)-1].H1 != p_t_s_want[len(p_t_s_want)-1].H1{ | ||
t.Fatalf("Should have title %s got %s",p_t_s_want[len(p_t_s_want)-1].H1,p_t_sec_got[len(p_t_sec_got)-1].H1) | ||
} | ||
|
||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package input | ||
|
||
import ( | ||
core "anemon/internal/core" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
var hashtagRegex = regexp.MustCompile(`^#+`) | ||
|
||
// `GetCVFrom` takes a root directory and extracts Markdown documents to generate a list of CV type. | ||
// | ||
// This function assumes the directory structure is a tree with a depth of 3, where each leaf | ||
// is a Markdown (.md) document. Each document may contain multiple paragraphs, but the headers | ||
// should not repeat within the same document. | ||
func GetCVsFrom(root string) ([]core.CV, error) { | ||
cvs := make([]core.CV,0) | ||
current_lang := "" | ||
current_sections := make([]core.Section,0) | ||
has_been_inside_dir := false | ||
|
||
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { | ||
if err != nil { return err } | ||
if info.IsDir(){ | ||
if has_been_inside_dir { | ||
println( cvs[len(cvs)-2].Lang ) | ||
cvs[len(cvs)-1].Sections=current_sections | ||
current_sections = make([]core.Section,0) | ||
has_been_inside_dir = false | ||
} | ||
current_lang = info.Name() | ||
cvs = append(cvs, core.CV{Lang: current_lang}) | ||
} | ||
if !info.IsDir() && strings.HasSuffix(info.Name(), ".md") { | ||
println( len(current_sections) ) | ||
has_been_inside_dir = true | ||
if current_lang == "" { return errors.New("markdown file found before lang directory") } | ||
content, err := os.ReadFile(path) | ||
if err != nil { return err } | ||
|
||
new_section := core.Section{Title: strings.TrimRight(info.Name(),".md"), | ||
Paragraphes: getParagrapheFrom(string(content))} | ||
|
||
current_sections = append(current_sections, new_section) | ||
} | ||
return nil | ||
}) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
cvs[len(cvs)-1].Sections=current_sections | ||
|
||
return cvs,nil | ||
} | ||
|
||
|
||
//Take string in the md format and return a slice of core.Paragraphe for each paragraph inside of it | ||
//We define a paragraph by text block where \n\n indicate a separation | ||
func getParagrapheFrom(s_section string)[]core.Paragraphe{ | ||
paragraphs := make([]core.Paragraphe,0) | ||
for _,paragraphe := range strings.Split(s_section, "\n\n"){ | ||
n_paragraphe,err := parse_paragraphe(paragraphe) | ||
if is_empty(n_paragraphe){ | ||
continue | ||
} | ||
if err != nil { | ||
fmt.Println("Failed to parse paragraphe ") | ||
} else{ | ||
paragraphs = append(paragraphs, n_paragraphe) | ||
} | ||
} | ||
if len(paragraphs) == 0 { return nil } | ||
return paragraphs | ||
} | ||
|
||
//Return if a paragraphs has no header and no items | ||
func is_empty(p core.Paragraphe)bool{ | ||
no_header := p.H1 == "" && p.H2 == "" && p.H3 == "" && p.H4 == "" | ||
no_items := len(p.Items)==0 | ||
return no_header && no_items | ||
} | ||
|
||
/* | ||
Parse parses a Markdown-like `paragraph` into a `Paragraph`, | ||
extracting headings and descriptions based on the number of leading hashtags or stars. | ||
Returns an error if the format is invalid. | ||
*/ | ||
func parse_paragraphe(paragraph string) (core.Paragraphe, error) { | ||
var ( | ||
n_paragraphe core.Paragraphe | ||
bulletPrefix = "- " | ||
skillAsteriskCount = 4 // Number of asterisks that signify a skill block | ||
) | ||
if len(strings.Split(paragraph, "\n\n")) > 1 { | ||
return n_paragraphe, errors.New("Tried to parse multiple paragraphs into a single section") | ||
} | ||
wasASkill := false | ||
lines := strings.Split(strings.TrimRight(paragraph, "\n"), "\n") | ||
for _, line := range lines { | ||
if len(line) == 0 { | ||
continue | ||
} | ||
nbHashtags := len(hashtagRegex.FindString(line)) | ||
if wasASkill { | ||
wasASkill = false | ||
n_paragraphe.H2 = strings.TrimLeft(line, bulletPrefix) | ||
continue | ||
} | ||
if nbHashtags == 0 && strings.HasPrefix(line, "*") && len(strings.Trim(line, "*")) == len(line)-skillAsteriskCount { | ||
n_paragraphe.H1 = strings.Trim(line, "*") | ||
wasASkill = true | ||
continue | ||
} | ||
if err := handleLine(&n_paragraphe, line, nbHashtags); err != nil { | ||
return n_paragraphe, err | ||
} | ||
} | ||
return n_paragraphe, nil | ||
} | ||
|
||
// handleLine processes a line based on the number of leading hashtags | ||
func handleLine(n_paragraphe *core.Paragraphe, line string, nbHashtags int) error { | ||
switch { | ||
case nbHashtags > 0 && line[nbHashtags] != ' ': | ||
return fmt.Errorf("Err: cannot parse this md line {%s}, '#' should be followed by space", line) | ||
case nbHashtags == 1: | ||
n_paragraphe.H1 = line[nbHashtags+1:] | ||
case nbHashtags == 2: | ||
n_paragraphe.H2 = line[nbHashtags+1:] | ||
case nbHashtags == 3: | ||
n_paragraphe.H3 = line[nbHashtags+1:] | ||
case nbHashtags == 4: | ||
n_paragraphe.H4 = line[nbHashtags+1:] | ||
case nbHashtags == 0 && len(line) > 1: | ||
if strings.HasPrefix(line, "- ") { | ||
n_paragraphe.Items = append(n_paragraphe.Items, strings.TrimLeft(line, "- ")) | ||
} | ||
case nbHashtags > 4: | ||
return fmt.Errorf("Err: cannot parse this md line {%s}", line) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package core | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
//CV with Language and Sections | ||
type CV struct { | ||
Lang string | ||
Sections []Section | ||
} | ||
|
||
type Section struct { | ||
Title string | ||
Paragraphes []Paragraphe | ||
} | ||
|
||
type Paragraphe struct { | ||
H1 string | ||
H2 string | ||
H3 string | ||
H4 string | ||
Items []string | ||
} | ||
|
||
//Print Method for CV | ||
func (cv *CV) Print() { | ||
fmt.Println("CV Language: "+ cv.Lang) | ||
fmt.Println(strings.Repeat("=", 40)) | ||
fmt.Printf("With %d Sections\n",len(cv.Sections)) | ||
for _, section := range cv.Sections { | ||
fmt.Printf("Section: %s\n", section.Title) | ||
fmt.Println(strings.Repeat("-", 40)) | ||
for _, p := range section.Paragraphes { | ||
if p.H1 != "" { fmt.Printf("H1: %s\n", p.H1) }else{fmt.Printf("No H1")} | ||
if p.H2 != "" { fmt.Printf(" H2: %s\n", p.H2) }else{fmt.Printf("No H2")} | ||
if p.H3 != "" { fmt.Printf(" H3: %s\n", p.H3) }else{fmt.Printf("No H3")} | ||
if p.H4 != "" { fmt.Printf(" H4: %s\n", p.H4) }else{fmt.Printf("No H4")} | ||
|
||
if len(p.Items) > 0 { | ||
fmt.Println(" Items:") | ||
for _, item := range p.Items { | ||
fmt.Printf(" - %s\n", item) | ||
} | ||
} | ||
fmt.Println() | ||
} | ||
fmt.Println() | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters