fix(oo): skip junk dirs in applications Discover
Stop inventing CRM companies from node_modules/tools README trees. Only sync dossier-like folders (source-NNN-…) and invent Company from the slug token after the numeric id.
This commit is contained in:
@@ -751,7 +751,11 @@ oo applications sync --path ./applications/2026 --verbose
|
|||||||
oo applications sync --path ./applications/2026 --apply --verbose
|
oo applications sync --path ./applications/2026 --apply --verbose
|
||||||
```
|
```
|
||||||
|
|
||||||
Each `README.md` is parsed for company, role, email, phone, LinkedIn, etc.
|
Each dossier `README.md` is parsed for company, role, email, phone, LinkedIn, etc.
|
||||||
|
Discovery walks `--path` and **skips** junk trees (`node_modules`, `tools`, `.venv`,
|
||||||
|
`pdfs`, …). Only folders that look like CV application slugs (`source-NNN-…`, or
|
||||||
|
long hyphenated dossiers) are synced — not npm package READMEs.
|
||||||
|
|
||||||
The sync creates or finds contacts, opens a deal, adds members, and appends a
|
The sync creates or finds contacts, opens a deal, adds members, and appends a
|
||||||
history note. Re-running is safe: duplicate members and duplicate deal titles
|
history note. Re-running is safe: duplicate members and duplicate deal titles
|
||||||
are skipped when already present.
|
are skipped when already present.
|
||||||
|
|||||||
@@ -24,6 +24,33 @@ import (
|
|||||||
|
|
||||||
var docExt = map[string]bool{".pdf": true, ".docx": true, ".xlsx": true, ".doc": true, ".xls": true}
|
var docExt = map[string]bool{".pdf": true, ".docx": true, ".xlsx": true, ".doc": true, ".xls": true}
|
||||||
|
|
||||||
|
// Directories that are never application dossiers (WalkDir SkipDir).
|
||||||
|
var skipDirNames = map[string]bool{
|
||||||
|
"pdfs": true, "node_modules": true, ".git": true, ".venv": true, "venv": true,
|
||||||
|
"vendor": true, "tools": true, "dist": true, "build": true, ".pnpm": true,
|
||||||
|
"coverage": true, "__pycache__": true, "out": true, ".turbo": true, ".next": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var appFolderNum = regexp.MustCompile(`^\d{2,4}$`)
|
||||||
|
|
||||||
|
// looksLikeAppFolder accepts CV dossier slugs (uae-009-…, djinni-001-…, linkedin-elastic-…).
|
||||||
|
func looksLikeAppFolder(name string) bool {
|
||||||
|
if name == "" || strings.HasPrefix(name, ".") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
parts := strings.Split(name, "-")
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, p := range parts {
|
||||||
|
if appFolderNum.MatchString(p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Rare dossiers without NNN (e.g. alex-staff-cloud-platform-engineer)
|
||||||
|
return len(parts) >= 4 && len(name) >= 24
|
||||||
|
}
|
||||||
|
|
||||||
type RecruiterInfo struct {
|
type RecruiterInfo struct {
|
||||||
First, Last, JobTitle, Company, Email, Phone, LinkedIn string
|
First, Last, JobTitle, Company, Email, Phone, LinkedIn string
|
||||||
}
|
}
|
||||||
@@ -51,7 +78,7 @@ func Discover(base string) ([]string, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if d.IsDir() {
|
if d.IsDir() {
|
||||||
if d.Name() == "pdfs" {
|
if skipDirNames[d.Name()] {
|
||||||
return fs.SkipDir
|
return fs.SkipDir
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -63,6 +90,9 @@ func Discover(base string) ([]string, error) {
|
|||||||
if filepath.Clean(dir) == filepath.Clean(base) {
|
if filepath.Clean(dir) == filepath.Clean(base) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if !looksLikeAppFolder(filepath.Base(dir)) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
out = append(out, path)
|
out = append(out, path)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -97,9 +127,13 @@ func ParseReadme(path string) (Data, error) {
|
|||||||
app.Source = filepath.Base(filepath.Dir(filepath.Dir(path)))
|
app.Source = filepath.Base(filepath.Dir(filepath.Dir(path)))
|
||||||
}
|
}
|
||||||
if app.Company == "" || strings.EqualFold(app.Company, "undisclosed") || app.Company == "?" || strings.EqualFold(app.Company, "tbd") {
|
if app.Company == "" || strings.EqualFold(app.Company, "undisclosed") || app.Company == "?" || strings.EqualFold(app.Company, "tbd") {
|
||||||
|
// Prefer slug token after NNN (uae-009-dizzaract-… → Dizzaract), not source prefix.
|
||||||
parts := strings.Split(filepath.Base(filepath.Dir(path)), "-")
|
parts := strings.Split(filepath.Base(filepath.Dir(path)), "-")
|
||||||
if len(parts) >= 2 {
|
for i, p := range parts {
|
||||||
app.Company = titleWord(parts[0])
|
if appFolderNum.MatchString(p) && i+1 < len(parts) {
|
||||||
|
app.Company = titleWord(parts[i+1])
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.Summary = buildSummary(app, text)
|
app.Summary = buildSummary(app, text)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package applications
|
package applications
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
onlyoffice "github.com/eslider/go-onlyoffice"
|
onlyoffice "github.com/eslider/go-onlyoffice"
|
||||||
@@ -15,6 +17,47 @@ func TestDealTitleForApplication(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLooksLikeAppFolder(t *testing.T) {
|
||||||
|
cases := map[string]bool{
|
||||||
|
"uae-009-dizzaract-senior-backend-devops-abu-dhabi": true,
|
||||||
|
"djinni-001-devops-engineer-windows-cloud-justmarkets": true,
|
||||||
|
"linkedin-elastic-senior-go-intake-observability": true, // no NNN but long
|
||||||
|
"alex-staff-cloud-platform-engineer": true,
|
||||||
|
"http-client": false,
|
||||||
|
"node_modules": false,
|
||||||
|
"tools": false,
|
||||||
|
"README": false,
|
||||||
|
"uae-009": false, // too short
|
||||||
|
}
|
||||||
|
for name, want := range cases {
|
||||||
|
if got := looksLikeAppFolder(name); got != want {
|
||||||
|
t.Errorf("looksLikeAppFolder(%q)=%v want %v", name, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDiscoverSkipsNodeModules(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
appDir := filepath.Join(root, "uae-009-dizzaract-senior-backend-devops-abu-dhabi")
|
||||||
|
junk := filepath.Join(root, "node_modules", "http-client")
|
||||||
|
tools := filepath.Join(root, "tools", "gmail-oo-reconcile")
|
||||||
|
for _, d := range []string{appDir, junk, tools} {
|
||||||
|
if err := os.MkdirAll(d, 0o755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(d, "README.md"), []byte("# x\n"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
got, err := Discover(root)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(got) != 1 || filepath.Base(filepath.Dir(got[0])) != filepath.Base(appDir) {
|
||||||
|
t.Fatalf("Discover=%v want only %s", got, appDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHasContactInfoHelper(t *testing.T) {
|
func TestHasContactInfoHelper(t *testing.T) {
|
||||||
contact := map[string]any{
|
contact := map[string]any{
|
||||||
"commonData": []any{
|
"commonData": []any{
|
||||||
|
|||||||
Reference in New Issue
Block a user