diff --git a/internal/adapters/input/cli.go b/internal/adapters/input/cli.go new file mode 100644 index 0000000..de1ab6f --- /dev/null +++ b/internal/adapters/input/cli.go @@ -0,0 +1 @@ +package input diff --git a/internal/adapters/input/input_test.go b/internal/adapters/input/input_test.go new file mode 100644 index 0000000..33a504d --- /dev/null +++ b/internal/adapters/input/input_test.go @@ -0,0 +1,56 @@ +package input + +import ( + core "anemon/internal/core" + "reflect" + "testing" +) + +func TestParagraphe(t *testing.T) { + 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.` + + work_paragraphe1 := 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_paragraphe1,work_paragraphe1} + + t.Run("Work Paragraphes should return a slice of valid Paragraphe", func (t *testing.T) { + result := getParagrapheFrom(work_input) + + if !reflect.DeepEqual(result[0], work_paragraphe1){ + t.Fatalf("the first Paragraphe should be :\n%s\n got :%s", work_expected_result[0].Items[0],result[0].Items[0]) + } + + if !reflect.DeepEqual(result[1], work_paragraphe1){ + t.Fatalf("the second Paragraphe should be :\n%s\n got :%s", work_expected_result[0].Items[0],result[0].Items[0]) + } + }) + + t.Run("Invalid inout should return nothing", func (t *testing.T) { + result := getParagrapheFrom(invalid_input) + + if result != nil{ + t.Fatalf("Invalid input should return nil got %s",result) + } + }) + +} diff --git a/internal/adapters/input/markdown_tree_reader.go b/internal/adapters/input/markdown_tree_reader.go new file mode 100644 index 0000000..57e7b95 --- /dev/null +++ b/internal/adapters/input/markdown_tree_reader.go @@ -0,0 +1,130 @@ +package input + +import ( + core "anemon/internal/core" + "errors" + "fmt" + "os" + "path/filepath" + "regexp" + "strings" +) + +// `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,4) + //Walk + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir(){ + current_lang = info.Name() + cvs = append(cvs, core.CV{Lang: current_lang}) + } + if !info.IsDir() && strings.HasSuffix(info.Name(), ".md") { + 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:info.Name(), Paragraphes: getParagrapheFrom(string(content))} + current_sections = append(current_sections, new_section) + if info.Name()=="skill.md"{//Walk is in alphabetique order, therefore skill is last + cvs[len(cvs)-1].Sections=current_sections + } + } + return nil + }) + if err != nil { + return nil, err + } + + 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 +} + +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 description 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){ + + n_paragraphe := core.Paragraphe{} + if len(strings.Split(paragraph, "\n\n")) > 1{ + return n_paragraphe, errors.New("Tried to parse mutiple paragraph into a single section") + } + hashtag_regex, _ := regexp.Compile("^#+") + wasASkill := false + for _, line := range strings.Split(strings.TrimRight(paragraph, "\n"), "\n") { + nb_hashtag := len(hashtag_regex.FindString(line)) + + if len(line) == 0{ continue } + + if wasASkill { + wasASkill = false + n_paragraphe.H2= strings.TrimLeft(line,"- ") + } + + if nb_hashtag == 0 && string(line[0])=="*" && len(strings.Trim(line,"*")) == len(line) - 4 {//Trim should **tt** -> tt + n_paragraphe.H1= strings.Trim(line,"*") + wasASkill = true + } + + + switch{ + case nb_hashtag>0 && line[nb_hashtag] != ' ': + return n_paragraphe, errors.New("Err: cannot parse this md line{"+line+"} # should be followed by space") + case nb_hashtag == 1: + n_paragraphe.H1=line[nb_hashtag+1:] + case nb_hashtag == 2: + n_paragraphe.H2=line[nb_hashtag+1:] + case nb_hashtag == 3: + n_paragraphe.H3=line[nb_hashtag+1:] + case nb_hashtag == 4: + n_paragraphe.H4=line[nb_hashtag+1:] + case nb_hashtag == 0 && len(line)>1: + if line[:2] == "- "{ + n_paragraphe.Items = append(n_paragraphe.Items, strings.TrimLeft(line,"- ")) + } + case nb_hashtag > 4: + return n_paragraphe, errors.New("Err: cannot parse this md line{"+line+"}") + } + } + return n_paragraphe, nil +} + diff --git a/internal/core/ports.go b/internal/core/ports.go new file mode 100644 index 0000000..68469b0 --- /dev/null +++ b/internal/core/ports.go @@ -0,0 +1,20 @@ +package core + +//Test +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 +} diff --git a/internal/walker/cv.go b/internal/walker/cv.go index ffd93a4..315d044 100644 --- a/internal/walker/cv.go +++ b/internal/walker/cv.go @@ -12,9 +12,8 @@ import ( func WalkCV(root string) (map[string]map[string]string, error) { fileMap := make(map[string]string) err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } + println(info.Name()) + println(info.IsDir()) if !info.IsDir() && strings.HasSuffix(info.Name(), ".md") { relativePath := strings.TrimSuffix(strings.TrimPrefix(path, root+string(os.PathSeparator)), ".md") content, err := os.ReadFile(path)