Skip to content

Commit

Permalink
V0.2.0 august 2nd 2021
Browse files Browse the repository at this point in the history
See release for details
  • Loading branch information
Descent098 committed Aug 2, 2021
2 parents 9a5b071 + 0b6dcc3 commit 6a63cf2
Show file tree
Hide file tree
Showing 399 changed files with 3,487 additions and 113,663 deletions.
52 changes: 52 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,57 @@
# Changelog

## V0.2.0; August 2nd 2021

The focus for this release is to add new features and themes

### Features

- Added Resume Generator
- Setup remote theme support
- Added new section; gallery
- Supports multiple images being put together into a gallery
- Google analytics config option
- Custom Favicon
- Added new section creation CLI
- When initializing with a theme that is not downloaded it will try to be downloaded on initialization instead of first build
- Added support for all standard markdown file extensions (.md, .markdown, .mdown, .mkdn, .mkd, .mdwn)
- Added support for many image file extensions as content (.jpg, .png, .jpeg, .gif, .svg, .webp, .apng, .jfif, .pjpeg, .pjp)
- Added many new markdown extensions
- [footnotes](https://python-markdown.github.io/extensions/footnotes/)
- [tables](https://python-markdown.github.io/extensions/tables/)
- [toc (Table of contents)](https://python-markdown.github.io/extensions/toc/)
- [abbr(abbreviations)](https://python-markdown.github.io/extensions/abbreviations/)
- [def_list(Definition lists)](https://python-markdown.github.io/extensions/definition_lists/)
- [sane_lists(Sane lists)](https://python-markdown.github.io/extensions/sane_lists/)
- [mdx_math(Latex/formulas)](https://github.com/mitya57/python-markdown-math)
- Created filters module for creating custom Jinja filters (will be an exposed API for adding your own in v0.3.0)
- [Created several custom filters for optimizing & simplifying theme development](https://ezcv.readthedocs.io/en/latest/theme-development/#available-custom-filters)
- [split_to_sublists; Takes a list and splits it into sublists of size n](https://ezcv.readthedocs.io/en/latest/theme-development/#split_to_sublists)
- [get_image_path; Takes in the path to an image and returns it in usable format to use in img tags as src attribute](https://ezcv.readthedocs.io/en/latest/theme-development/#get_image_path)
- [get_filename_without_extension; Takes in path and returns filename without extension](https://ezcv.readthedocs.io/en/latest/theme-development/#get_filename_without_extension)
- [pretty_datetime; A utility function for pretty printing dates provided for jobs/getting a degree/volunteering etc](https://ezcv.readthedocs.io/en/latest/theme-development/#pretty_datetime)
- [pretty_defaultdict; Returns a prettyprinted form of a defaultdict](https://ezcv.readthedocs.io/en/latest/theme-development/#pretty_defaultdict)

### Themes

- Moved existing themes to new repo https://github.com/QU-UP/ezcv-themes
- Added new themes
- [cv](https://ezcv.readthedocs.io/en/latest/included-themes/#resume)
- [Grayscale](https://ezcv.readthedocs.io/en/latest/included-themes/#grayscale)
- [Paradigm Shift](https://ezcv.readthedocs.io/en/latest/included-themes/#paradigm-shift)
- [Lens](https://ezcv.readthedocs.io/en/latest/included-themes/#lens)

### Bug fixes

- Fixed markdown files with different standard extensions not being recognized
- Fixed markdown files with capitalized extensions not being recognized

### Documentation Improvements

- Added documentation for new features
- Added additional onboarding videos/tutorials
- Added section for finding help/support

## V0.1.1; January 10th 2020

Fixes following initial launch
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ Usage:
ezcv [-h] [-v] [-p]
ezcv init [<name>] [<theme>]
ezcv build [-d OUTPUT_DIR] [-p]
ezcv theme [-l] [-c] [<theme>]
ezcv theme [-l] [-c] [-s SECTION_NAME] [<theme>]


Options:
Expand All @@ -146,6 +146,7 @@ Options:
-c, --copy copy the provided theme, or defined site theme
-p, --preview preview the current state of the site
-d OUTPUT_DIR, --dir OUTPUT_DIR The folder name to export the site to
-s SECTION_NAME, --section SECTION_NAME The section name to initialize
```
See the [CLI Documentation](https://ezcv.readthedocs.io/en/latest/cli/) for additional details
Expand Down
16 changes: 15 additions & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Usage:
ezcv [-h] [-v] [-p]
ezcv init [<name>] [<theme>]
ezcv build [-d OUTPUT_DIR] [-p]
ezcv theme [-l] [-c] [<theme>]
ezcv theme [-l] [-c] [-s SECTION_NAME] [<theme>]


Options:
Expand All @@ -17,6 +17,7 @@ Options:
-c, --copy copy the provided theme, or defined site theme
-p, --preview preview the current state of the site
-d OUTPUT_DIR, --dir OUTPUT_DIR The folder name to export the site to
-s SECTION_NAME, --section SECTION_NAME The section name to initialize
```
## Init
Expand Down Expand Up @@ -83,6 +84,7 @@ There are two optional flags and one positional argument:
- First it will check if a ```<theme>``` argument has been passed, and if it has it will copy that theme
- Then it will check if there's a ```config.yml``` file in the current directory and copy that one
- Then it will just default to exporting the dimension theme
- ```-s``` Used to create a new section in a theme


**Examples**
Expand All @@ -104,3 +106,15 @@ ezcv theme -c
```bash
ezcv theme -c aerial
```

*Create a new section called books in the current theme*

```bash
ezcv theme -s books
```

or

```bash
ezcv theme --section="books"
```
259 changes: 259 additions & 0 deletions docs/contributor-docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
# Contributor/Developer Docs

This section of the documentation is intended for people who are looking to write code for the `ezcv` codebase. This section is intended for developers who are looking to dive deeper into the technical details of the system, and those who are looking to contribute directly.

## Contribution guidelines

Below are details for submitting your code to the `ezcv` codebase. If you are just looking to modify some of the themes (aside from the `base` theme and `dimension` theme), then review the [theme development](/theme-development) section instead of this page.

### Code standards

Any submitted code is expected to:
1. Be documented inline, and update user documentation if it's a new feature
- Inline docs at minimum entails writing docstrings on **ALL** functions/classes using the existing [numpy style docs](https://numpydoc.readthedocs.io/en/latest/format.html#overview) (you can just follow what other methods/classes do)
- User documentation should be concise but descriptive. People don't need all the technical details, but they do need to know enough to use your code
2. Not break any existing features/syntax

If you are unsure if you're on the right track to submitting code feel free to post in the [discussion board](https://github.com/Descent098/ezcv/discussions) for a second pair of eyes.

## Themes

Details about creating themes and submitting them for development can be found in the [theme development](/theme-development) section of the docs. All themes (aside from the `base` theme and `dimension` theme) have their code at [https://github.com/QU-UP/ezcv-themes](https://github.com/QU-UP/ezcv-themes).

## Content parsing

In ezcv content parsing is done based on file extension. There is a base class in ```ezcv.content``` that is used to dispatch file parsing to subclasses based on the extensions they support. So for example if a file has a ``.md`` extension the ```Content``` class will have` Content.get_available_extensions()` called which will look into the values of it's child classes ```Content.extension``` attribute to see if they match. The resulting dictionary can then be used to dispatch to the correct class.

For example this snippet is adapted from ```ezcv.content.get_section_content()```:

```python
import os

from ezcv.content import Content

content = [] # Empty list to be filled with content later
extension_handlers = Content.get_available_extensions()

for file_name in os.listdir("content/education"): # Iterate through /content/education and get the content from each file
if not examples and file_name.startswith("example"):
continue
else:
extension = "." + file_name.lower().split(".")[-1] # Get the file extension
if extension_handlers[extension]: # Checking if there exists a Content subclass capable of handling the file
extension_handler = extension_handlers[extension]() # Instantiate the proper extension

# Get the content and add it to the list
metadata, html = extension_handler.get_content(os.path.join(section_folder, file_name))
content.append([metadata, html])
print(content) # All the content from the files will be here in lists of lists
```

### Content base class

The `Content` class can be found in ```ezcv.content```, and is only really used to subclass content parsers and to get the available extensions with ```Content.get_available_extensions()```. Details on subclassing can be found [here](#creating-parser-for-new-extensions).

### Included extensions

Below are details about the included extensions. This is a broad overview, and it is a good idea to look specifically at the implementations in [content.py](https://github.com/Descent098/ezcv/blob/master/ezcv/content.py).

#### Markdown files

Used to parse markdown files in the `Projects`, `Education`, `Work Experience`, and `Volunteering Experience` sections. Details about implementation can be found in the [content.py](https://github.com/Descent098/ezcv/blob/master/ezcv/content.py).

**Extensions**: .md, .markdown, .mdown, .mkdn, .mkd, .mdwn

##### Usage

To use this class directly you can use:

```python
metadata, html = Markdown().get_content(file_path: str)
```

The `metadata` return variable is a defaultdict of the [YAML Frontmatter](https://assemble.io/docs/YAML-front-matter.html) of the markdown file and `html` return variable is the raw HTML export of the content from the markdown file.

#### Image

Used in the gallery section. Details about implementation can be found in the [content.py](https://github.com/Descent098/ezcv/blob/master/ezcv/content.py).

**Extensions**: .jpg, .png, .jpeg, .gif, .svg, .webp, .apng, .jfif, .pjpeg, .pjp (Note only .png, .jpg and .jpeg are tested, rest are supported based on [this list of support](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img))

##### Usage

To use this class directly you can use:

```python
tags, html = Image().get_content(file_path:str)
```

The file path is then available at:

```python
tags[0]["file_path"]
```

### Creating parser for new extensions

To create a new parser for a set of extensions you will need to subclass the `Content` class and make sure you have:

1. A list attribute called extensions, that is a list of strings with the extensions you support (i.e. [".md", ".markdown"])
2. A function called \_\_metadata\_\_() that returns a defaultdict of the appropriate metadata for a file (can use `lambda:False` to initialize the defauldict)
3. A function called \_\_html\_\_() that returns a string of the HTML from the parsed content
4. A function called get_content() that returns the value of `\_\_metadata\_\_(), \_\_html\_\_()` as a tuple

While it is optional to have \_\_metadata\_\_() and \_\_html\_\_() return anything of value (\_\_html\_\_() in the `Image` class does very little), it is important because anyone who tries to call those methods directly will recieve a `NotImplementedError`, since it will invoke the methods from the base `Content` class.

A minimal example of a custom parser to handle files with the ".extension" and ".ext" extensions might look like:

```python
from collections import defaultdict # Used to give dicts default args
from dataclasses import dataclass # Used to improve class performance
from typing import DefaultDict, List, Tuple # Used to provide accurate type hints


from ezcv.content import Content

@dataclass
class ExtensionParser(Content):
extensions:List[str] = (".extension", ".ext") # Put all the relevent extensions that are handled by your parser

def __metadata__(self) -> defaultdict:
metadata:defaultdict = defaultdict(lambda:False)
... # Code to scrape relevent metadata and add to defaultdict
return metadata

def __html__(self, file_path:str) -> str:
html = ""
... # Append markup to html variable
return html

# This function is what actually gets called by default in the ezcv code
def get_content(self, file_path: str) -> Tuple[defaultdict, str]:
if not os.path.exists(file_path): # If file doesn't exist
raise FileNotFoundError(f"Could not find file: {file_path}\n")
html = self.__html__(file_path)
metadata = self.__metadata__()
return metadata, html
```

If you would like to submit a parser you developed to be added to the main `ezcv` API please add it into `ezcv.content` and submit a pull request.

## CLI Entrypoints

`ezcv`'s command line interface has several entrypoints the details for which can be found in ```ezcv.cli```. Essentially each entrypoint is it's own function, and after the cli is called [docopt](https://github.com/docopt/docopt) is used to parse the arguments and dispatch to the corresponding functions.

### How exporting sites works

If a user uses ```ezcv build``` then it just calls the ```ezcv.core.generate_site()``` method, and if a ```--dir``` argument is provided it also passes that to the method.

### How generating previews works

Preview generation uses the same methods and calls as site exporting, the only difference is it uses the system defined temporary directory as defined by the [tempfile.TemporaryDirectory()](https://docs.python.org/3/library/tempfile.html#tempfile.TemporaryDirectory) class and passes that to ```ezcv.core.generate_site()```.

## Filters

Filters are used inside templates to do... pretty much anything that python can do. They are injected into the Jinja environments and are used to do everything from capitalizing strings to rendering HTML. They are incredibly useful for times when Jinja doesn't quite do enough for your use case. `ezcv` has [several custom filters built in](/theme-development#available-custom-filters).

There are specific sections of the jinja documentation [dedicated to filters](https://jinja.palletsprojects.com/en/3.0.x/templates/#filters), but I will explain the basics of developing them.

### Filter syntax

To write a filter you will need to have at least 1 argument being passed in. When someone uses your filter anything they put left of the pipe (`|`) will be passed as the first variable.

#### Single argument filter

A filter is just a basic python function. So if you wanted to make a filter that takes in an int and then doubles it and returns it's string form you would do something like:

```python
def double_it(n:int) -> str:
"""Takes in an int, doubles it and returns it's string form"""
return str(2*n)
```

Then make sure to add it to the environment by going into ```ezcv.filters.inject_filters()```, and adding the function object to the `filters` variable.

You can then use the filter like so:

```jinja2
<h2> 2 * 2 = {{ 2 | double_it }}</h2>
```

#### Multi-argument filter

Multi argument filters have very similar syntax to single argument filters, the main place the syntax deviates is in how you call it with jinja. Changing our `double_it()` example from before to taking in 2 integers `n` and `m` and then multiplying them by each other like so:

```python
def multiply(n:int, m:int) -> str:
"""Takes in two numbers and multiplies them by each other"""
return str(n * m)
```

We then make sure to add it to the environment by going into ```ezcv.filters.inject_filters()```, and adding the function object to the `filters` variable.

Now we can use the filter like so:

```jinja2
<h2> 4 * 6 = {{ 4 | multiply(6) }}</h2>
```

The variable to the left of the pipe is automatically put as the first variable (`n`), and then every subsequent variable is structured like a standard python function call. So with a function like:

```python
def multiply_multiple_numbers(n:int, m:int, z:int) -> str:
"""Takes in three numbers and multiplies them by each other"""
return str(n * m * z)
```

You would invoke it like this:

```jinja2
<h2> 4 * 6 * 2 = {{ 4 | multiply_multiple_numbers(6, 2) }}</h2>
```

You can also use python standard unpacking to allow arbitrary amounts of arguments, such as:

```python
def multiply_many_numbers(*numbers:int) -> str:
"""Takes in an arbitrary amount of numbers and multiplies them by each other"""
result = 1

for number in numbers:
result *= number

return str(result)
```

Which would be called after being added to ```ezcv.filters.inject_filters()``` with any number of arugment like this:

```
<h2> 2 * 3 * 4 * 5 * 6 * 7 = {{ 2 | multiply_many_numbers(3, 4, 5, 6, 7) }}</h2>
```

The same goes for using keyword arguments, just keep in mind you still **must** have that first argument passed. So something like:

```python
def string_values(n:int, **kwargs) -> str:
"""Takes in arguments and prints them"""

return str(kwargs)
```

Could be called like this after being added to ```ezcv.filters.inject_filters()```:

```jinja
<h2> {{ 2 | string_values(arg_1 = "wow", arg_2 = "wowee", arg_3="zooweemama") }}</h2>
```

Notice our first number is just a throwaway value, but it is necessary in this case.

More details about jinja filters can be found [here](https://ttl255.com/jinja2-tutorial-part-4-template-filters/#writingyourownfilters)

### Updating existing filters
To update existing filters head to ```ezcv.filters``` and locate the filter you want to change. Be sure to familiarize yourself with the [syntax](#filter-syntax) first.

### How to add new custom filters

To add new filters you will need to add the function to ```ezcv.filters```, and then add the function object to the `filters` list local variable inside ```ezcv.filters.inject_filters()```.

**THE ABILITY TO DEFINE AD-HOC CUSTOM FILTERS IS UNDER DEVELOPMENT AND WILL BE RELEASED IN VERSION 0.3.0**

Binary file added docs/img/markdown-extensions/abbr.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/markdown-extensions/def.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/markdown-extensions/footnotes.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/markdown-extensions/tables.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/markdown-extensions/toc.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/remote-editing/create-content-1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/remote-editing/create-content-2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/remote-editing/drag-n-drop-1.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/remote-editing/drag-n-drop-2.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/remote-editing/edit-file-gh.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6a63cf2

Please sign in to comment.