codeurcv 0.4.0__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {codeurcv-0.4.0 → codeurcv-0.5.0}/PKG-INFO +5 -2
  2. {codeurcv-0.4.0 → codeurcv-0.5.0}/README.md +4 -2
  3. {codeurcv-0.4.0 → codeurcv-0.5.0}/pyproject.toml +1 -1
  4. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/cli.py +1 -1
  5. codeurcv-0.5.0/src/codeurcv/core/config_loader.py +17 -0
  6. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/core/renderer.py +2 -2
  7. codeurcv-0.5.0/src/codeurcv/core/schema.py +78 -0
  8. codeurcv-0.5.0/src/codeurcv/plugins/minimalist/plugin.py +9 -0
  9. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/plugins/minimalist/template.tex +5 -5
  10. codeurcv-0.4.0/src/codeurcv/core/schema.py +0 -56
  11. codeurcv-0.4.0/src/codeurcv/plugins/minimalist/plugin.py +0 -23
  12. {codeurcv-0.4.0 → codeurcv-0.5.0}/LICENSE +0 -0
  13. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/__init__.py +0 -0
  14. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/__main__.py +0 -0
  15. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/core/constants.py +0 -0
  16. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/core/dependency_checker.py +0 -0
  17. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/core/logger.py +0 -0
  18. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/core/markdown_converter.py +0 -0
  19. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/core/plugin_loader.py +0 -0
  20. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/core/settings.py +0 -0
  21. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/core/template_loader.py +0 -0
  22. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/plugins/__init__.py +0 -0
  23. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/plugins/base.py +0 -0
  24. {codeurcv-0.4.0 → codeurcv-0.5.0}/src/codeurcv/plugins/minimalist/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeurcv
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: A Python library to generate LaTeX resumes from YAML configuration files using Jinja2 templates.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -168,8 +168,10 @@ Automate resume generation on every push using the official GitHub Action.
168
168
  ```yaml
169
169
  - uses: crackedngineer/codeurcv-action@v1
170
170
  with:
171
- file-name: config.yml
171
+ config-path: config.yml
172
172
  out-dir: output
173
+ template: minimalist
174
+ filename: john-doe-resume
173
175
  ```
174
176
 
175
177
  → [codeurcv-action on GitHub](https://github.com/crackedngineer/codeurcv-action) · [View on Marketplace](https://github.com/marketplace/actions/codeurcv-action)
@@ -185,3 +187,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) — contributions are welcome!
185
187
  ## 📜 License
186
188
 
187
189
  [MIT](LICENSE) © codeurcv contributors
190
+
@@ -142,8 +142,10 @@ Automate resume generation on every push using the official GitHub Action.
142
142
  ```yaml
143
143
  - uses: crackedngineer/codeurcv-action@v1
144
144
  with:
145
- file-name: config.yml
145
+ config-path: config.yml
146
146
  out-dir: output
147
+ template: minimalist
148
+ filename: john-doe-resume
147
149
  ```
148
150
 
149
151
  → [codeurcv-action on GitHub](https://github.com/crackedngineer/codeurcv-action) · [View on Marketplace](https://github.com/marketplace/actions/codeurcv-action)
@@ -158,4 +160,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) — contributions are welcome!
158
160
 
159
161
  ## 📜 License
160
162
 
161
- [MIT](LICENSE) © codeurcv contributors
163
+ [MIT](LICENSE) © codeurcv contributors
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codeurcv"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "A Python library to generate LaTeX resumes from YAML configuration files using Jinja2 templates."
5
5
  authors = [
6
6
  { name = "crackedngineer", email = "subhomoyrchoudhury@gmail.com" },
@@ -58,7 +58,7 @@ def run(
58
58
 
59
59
  renderer = ResumeRenderer()
60
60
 
61
- renderer.render(config_path=input, output_path=output, name=name, template=template)
61
+ renderer.render(config_path=input, output_dir=output, out_filename=name, template=template)
62
62
 
63
63
  console.print("[bold green]Done![/bold green]")
64
64
 
@@ -0,0 +1,17 @@
1
+ import json
2
+ import yaml
3
+ from pathlib import Path
4
+
5
+ LOADERS = {
6
+ ".yml": yaml.safe_load,
7
+ ".yaml": yaml.safe_load,
8
+ ".json": json.loads,
9
+ }
10
+
11
+ def load_config(path: Path) -> dict:
12
+ loader = LOADERS.get(path.suffix.lower())
13
+ if loader is None:
14
+ raise ValueError(
15
+ f"Unsupported file type '{path.suffix}'. Use .yml, .yaml, or .json"
16
+ )
17
+ return loader(path.read_text())
@@ -1,6 +1,5 @@
1
1
  import tempfile
2
2
  import shutil
3
- import yaml
4
3
  import logging
5
4
  import subprocess
6
5
  from pathlib import Path
@@ -13,6 +12,7 @@ from codeurcv.core.schema import ResumeConfig
13
12
  from codeurcv.core.settings import console
14
13
  from codeurcv.core.dependency_checker import check_dependencies
15
14
  from codeurcv.core.constants import DEFAULT_OUTPUT_FILENAME
15
+ from codeurcv.core.config_loader import load_config
16
16
 
17
17
  class ResumeRenderer:
18
18
  def __init__(self):
@@ -31,7 +31,7 @@ class ResumeRenderer:
31
31
  # STEP 1
32
32
  raw_data = self._step(
33
33
  "Configuration loaded",
34
- lambda: yaml.safe_load(config_path.read_text()),
34
+ lambda: load_config(config_path),
35
35
  )
36
36
 
37
37
  # STEP 2
@@ -0,0 +1,78 @@
1
+ from typing import List, Optional
2
+ from pydantic import BaseModel, Field, GetCoreSchemaHandler
3
+ from pydantic_core import core_schema
4
+ from codeurcv.core.markdown_converter import MarkdownConverter
5
+ import logging
6
+
7
+ converter = MarkdownConverter()
8
+ class ResumeSection(BaseModel):
9
+ """Base model for resume sections with optional ordering."""
10
+ priority: Optional[int] = None
11
+
12
+ class MarkdownContent(str):
13
+ """A string type that will be rendered as bold markdown content."""
14
+ def __new__(cls, content):
15
+ converted_content = converter.convert(content)
16
+ return super().__new__(cls, converted_content)
17
+
18
+ def __init__(self, content):
19
+ try:
20
+ super().__init__()
21
+ except Exception as e:
22
+ logging.error(f"Error initializing MarkdownContent: {e}")
23
+
24
+ @classmethod
25
+ def __get_pydantic_core_schema__(
26
+ cls, source_type: any, handler: GetCoreSchemaHandler
27
+ ) -> core_schema.CoreSchema:
28
+ return core_schema.no_info_plain_validator_function(
29
+ cls,
30
+ serialization=core_schema.plain_serializer_function_ser_schema(str),
31
+ )
32
+
33
+ class BasicDetails(BaseModel):
34
+ name: str
35
+ email: str
36
+ phone: Optional[str] = None
37
+ website: Optional[str] = None
38
+ github: Optional[str] = None
39
+ linkedin: Optional[str] = None
40
+ location: Optional[str] = None
41
+
42
+ class Education(ResumeSection):
43
+ institution: str
44
+ location: str
45
+ degree: str
46
+ year: int
47
+ gpa: Optional[str] = None
48
+ additional_information: List[MarkdownContent] = Field(default_factory=list)
49
+
50
+ class Job(ResumeSection):
51
+ company: str
52
+ role: str
53
+ start: str
54
+ end: Optional[str] = "Present"
55
+ location: Optional[str] = None
56
+ achievements: List[MarkdownContent] = Field(default_factory=list)
57
+ technologies: List[MarkdownContent] = Field(default_factory=list)
58
+
59
+ class Project(ResumeSection):
60
+ name: str
61
+ description: List[MarkdownContent] = Field(default_factory=list)
62
+ start: str
63
+ end: Optional[str] = None
64
+ technologies: List[MarkdownContent] = Field(default_factory=list)
65
+ link: Optional[str] = None
66
+
67
+ class Skill(ResumeSection):
68
+ category: str
69
+ featured: List[MarkdownContent] = Field(default_factory=list)
70
+
71
+ class ResumeConfig(BaseModel):
72
+ basic_details: BasicDetails
73
+ summary: Optional[MarkdownContent] = None
74
+ education: List[Education] = Field(default_factory=list)
75
+ work: List[Job] = Field(default_factory=list)
76
+ projects: List[Project] = Field(default_factory=list)
77
+ skills: List[Skill] = Field(default_factory=list)
78
+ location: Optional[str] = None
@@ -0,0 +1,9 @@
1
+ from pathlib import Path
2
+ from codeurcv.plugins.base import TemplatePlugin
3
+
4
+ class MinimalistTemplate(TemplatePlugin):
5
+ name = "minimalist"
6
+ description = "Clean minimalist resume style"
7
+
8
+ def template_path(self) -> Path:
9
+ return Path(__file__).parent / "template.tex"
@@ -128,7 +128,7 @@
128
128
  ((* for school in education *))
129
129
  \resumeSubheading
130
130
  { ((( school.institution ))) }{ ((( school.location ))) }
131
- { ((( school.degree ))); GPA: ((( school.gpa ))) }{ ((( school.duration ))) }
131
+ { ((( school.degree ))); GPA: ((( school.gpa ))) }{ ((( school.year | string ))) }
132
132
  ((* endfor *))
133
133
  \resumeSubHeadingListEnd
134
134
 
@@ -138,7 +138,7 @@
138
138
  ((* for job in work *))
139
139
  \resumeSubheading
140
140
  { ((( job.company ))) }{ ((( job.location ))) }
141
- { ((( job.role ))) }{ ((( job.duration ))) }
141
+ { ((( job.role ))) }{ ((( job.start ))) - ((( job.end ))) }
142
142
  \resumeItemListStart
143
143
  ((* for item in job.achievements *))
144
144
  \resumeItem{ ((( item | safe ))) }
@@ -155,7 +155,7 @@
155
155
  ((* for proj in projects *))
156
156
  \resumeProjectHeading
157
157
  {\href{ ((( proj.link ))) }{ \textbf{((( proj.name )))}}((* if proj.technologies *)) $|$ \emph{((( proj.technologies | join(', ') )))}((* endif *))}
158
- {((( proj.duration )))}
158
+ {((( proj.start ))) - ((( proj.end )))}
159
159
  \resumeItemListStart
160
160
  ((* for item in proj.description *))
161
161
  \resumeItem{((( item | safe )))}
@@ -167,10 +167,10 @@
167
167
  %-----------TECHNICAL SKILLS-----------------
168
168
  \section{Technical Skills}
169
169
  \resumeSubHeadingListStart
170
- ((* for category in skills *))
170
+ ((* for skill in skills *))
171
171
  \item\small
172
172
  \begin{tabular*}{0.97\textwidth}{l@{\extracolsep{\fill}}r}
173
- \textbf{ ((( category.name|capitalize )))}: {((( category.featured_skills | join(", ") )))} \\
173
+ \textbf{ ((( skill.category|capitalize )))}: {((( skill.featured | join(", ") )))} \\
174
174
  \end{tabular*}\vspace{-7pt}
175
175
  ((* endfor *))
176
176
  \resumeSubHeadingListEnd
@@ -1,56 +0,0 @@
1
- from typing import List, Optional
2
- from pydantic import BaseModel, Field, field_validator
3
-
4
- from codeurcv.core.constants import DEFAULT_TEMPLATE
5
-
6
- class ResumeSection(BaseModel):
7
- """Base model for resume sections with optional ordering."""
8
- priority: Optional[int] = None
9
-
10
- class BasicDetails(BaseModel):
11
- name: str
12
- email: str
13
- phone: Optional[str] = None
14
- website: Optional[str] = None
15
- github: Optional[str] = None
16
- linkedin: Optional[str] = None
17
- location: Optional[str] = None
18
-
19
- class Education(ResumeSection):
20
- institution: str
21
- location: str
22
- degree: str
23
- duration: str
24
- gpa: Optional[str] = None
25
- additional_information: List[str] = Field(default_factory=list)
26
-
27
- class Job(ResumeSection):
28
- company: str
29
- role: str
30
- duration: str
31
- achievements: List[str] = Field(default_factory=list)
32
- technologies: List[str] = Field(default_factory=list)
33
-
34
- class Project(ResumeSection):
35
- name: str
36
- description: List[str] = Field(default_factory=list)
37
- technologies: List[str] = Field(default_factory=list)
38
- link: Optional[str] = None
39
- date: Optional[str] = None
40
-
41
- class Skill(ResumeSection):
42
- name: str
43
- featured_skills: List[str] = Field(default_factory=list)
44
-
45
- class ResumeConfig(BaseModel):
46
- basic_details: BasicDetails
47
- summary: Optional[str] = None
48
- education: List[Education] = Field(default_factory=list)
49
- work: List[Job] = Field(default_factory=list)
50
- projects: List[Project] = Field(default_factory=list)
51
- skills: List[Skill] = Field(default_factory=list)
52
-
53
- @field_validator("filename")
54
- @classmethod
55
- def strip_pdf_extension(cls, v: str) -> str:
56
- return v.removesuffix(".pdf")
@@ -1,23 +0,0 @@
1
- from pathlib import Path
2
-
3
- from codeurcv.core.schema import ResumeConfig
4
- from codeurcv.plugins.base import TemplatePlugin
5
- from codeurcv.core.markdown_converter import MarkdownConverter
6
-
7
-
8
- class MinimalistTemplate(TemplatePlugin):
9
- name = "minimalist"
10
- description = "Clean minimalist resume style"
11
-
12
- def template_path(self) -> Path:
13
- return Path(__file__).parent / "template.tex"
14
-
15
- def preprocess(self, data: ResumeConfig) -> ResumeConfig:
16
- converter = MarkdownConverter()
17
- if data.summary:
18
- data.summary = converter.convert(data.summary)
19
- for job in data.work:
20
- job.achievements = [converter.convert(achievement) for achievement in job.achievements]
21
- for proj in data.projects:
22
- proj.description = [converter.convert(desc) for desc in proj.description]
23
- return data
File without changes