Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,39 @@ Python:

## Events format

`drawcal` expects a JSON file containing a list of events, where each event is a
list of dates in `M/D/YYYY` format:
`drawcal` uses a structured event object format:

```json
[
["3/1/2025", "3/2/2025", "3/3/2025"],
["3/14/2025", "3/15/2025"]
{
"start_date": "3/1/2025",
"end_date": "3/3/2025",
"color": "#ee2233",
"style": "rounded",
"markers": ["3/3/2025"]
},
{
"start_date": "3/14/2025",
"end_date": "3/15/2025",
"style": "filled",
"markers": ["3/14/2025", "3/15/2025"]
}
]
```

Supported `style` values are `filled`, `rounded`, and `diagonal`. Use
`markers` to draw green marker indicators on specific dates within the event
range, or use marker-only events when you only want calendar annotations.

Legacy list-based events are still supported for backward compatibility and are
documented in [docs/events.md](docs/events.md).

## Documentation

Additional docs:

- [docs/README.md](docs/README.md)
- [docs/events.md](docs/events.md)
- [docs/rendering.md](docs/rendering.md)
- [docs/customization.md](docs/customization.md)
- [docs/python-api.md](docs/python-api.md)
13 changes: 13 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Documentation

This folder collects more detailed usage notes and examples for `drawcal`.

## Guides

- [events.md](events.md): supported event formats, including legacy, structured,
and marker-only events
- [rendering.md](rendering.md): event styles, markers, and legacy rendering
behavior
- [customization.md](customization.md): overriding default colors and other
advanced tweaks
- [python-api.md](python-api.md): calling `draw_calendar()` directly from Python
44 changes: 44 additions & 0 deletions docs/customization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Customization

`drawcal` currently uses module-level color defaults in
`drawcal.drawlib.colors`.

Example:

```python
from drawcal.drawlib import colors, draw_calendar

colors.background = "#f7f4ee"
colors.text = "#555555"
colors.border = "#e7dfcf"

draw_calendar(month=3, year=2025, events=events, outfile="drawcal.png")
```

Available color attributes include:

- `background`
- `border`
- `border_fill`
- `cell_border`
- `checkin_text`
- `checkout_text`
- `conflict`
- `conflict_border`
- `highlight`
- `highlight_fill`
- `occupied`
- `occupied_text`
- `other`
- `past`
- `past_text`
- `past_border`
- `text`
- `title_text`

Notes:

- this is global mutable state
- changes affect later renders in the same Python process
- this works today, but it is better thought of as an advanced usage pattern
than a polished public theming API
71 changes: 71 additions & 0 deletions docs/events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Events

`drawcal` supports a structured event format and also keeps the original legacy
format for backward compatibility.

## Structured Format

Structured events are objects:

```json
[
{
"start_date": "3/1/2025",
"end_date": "3/5/2025",
"style": "rounded",
"color": "#123456",
"markers": ["3/5/2025"]
}
]
```

Rules:

- `start_date` and `end_date` are inclusive
- both dates must be present together when using a range event
- `end_date` must not be earlier than `start_date`
- `markers` must be inside the event range when a range is present

Structured events are explicit. They do not create an automatic checkout day.

Example structured render:

![Rounded Event](images/rounded.png)

## Marker-Only Events

You can also draw markers without a date span:

```json
[
{
"markers": ["3/5/2025", "3/18/2025"]
}
]
```

This is useful for simple calendar annotations where you do not want a filled
range.

Example marker-only render:

![Markers Only](images/markers-only.png)

## Legacy Compatibility Format

Legacy events are lists of consecutive date strings:

```json
[
["3/1/2025", "3/2/2025", "3/3/2025"]
]
```

Rules:

- dates must use `M/D/YYYY`
- dates must be strictly increasing
- dates must be consecutive with no gaps

Legacy events preserve the original drawcal behavior, including the implicit
checkout marker on the day after the final date.
Binary file added docs/images/diagonal.png
Loading
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/images/filled.png
Loading
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/images/markers-only.png
Loading
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/images/rounded.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions docs/python-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Python API

## Basic Usage

```python
from drawcal import draw_calendar

events = [
{
"start_date": "3/1/2025",
"end_date": "3/5/2025",
"style": "rounded",
"markers": ["3/5/2025"],
}
]

result = draw_calendar(month=3, year=2025, events=events, outfile="drawcal.png")
```

## Return Value

`draw_calendar()` returns a dictionary like:

```python
{
"checkins": [...],
"checkouts": [...],
"conflicts": [...],
"occupied": [...],
"outfile": "drawcal.png",
}
```

Notes:

- `checkouts` is mainly relevant for legacy list-based events
- structured events only produce markers you explicitly request
- invalid event payloads raise `ValueError`

## Reading Events From JSON

```python
from drawcal.events import read_events

events = read_events("events.json")
```

`read_events()` validates the schema before returning data.
50 changes: 50 additions & 0 deletions docs/rendering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Rendering

Structured events support three span styles:

## `filled`

Draws a solid rectangular span.

![Filled Event](images/filled.png)

## `rounded`

Draws half-width rounded caps on the inside edges of the start and end dates.
This helps adjacent events avoid visually clobbering each other.

![Rounded Event](images/rounded.png)

## `diagonal`

Draws diagonal caps similar to a gantt-style slash.

![Diagonal Event](images/diagonal.png)

## Markers

Use `markers` to draw green marker circles on specific dates:

```json
{
"start_date": "3/1/2025",
"end_date": "3/5/2025",
"markers": ["3/5/2025"]
}
```

Marker-only events are also supported:

```json
{
"markers": ["3/5/2025"]
}
```

![Markers Only](images/markers-only.png)

## Legacy Behavior

Legacy list-based events still render with the original checkout-style marker on
the day after the final date. Structured events do not do this unless you add
the marker explicitly.
Binary file modified drawcal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 20 additions & 21 deletions events.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
[
[
"3/1/2025",
"3/2/2025",
"3/3/2025",
"3/4/2025",
"3/5/2025"
],
[
"3/12/2025",
"3/13/2025",
"3/14/2025",
"3/15/2025",
"3/16/2025",
"3/17/2025"
],
[
"3/24/2025",
"3/25/2025",
"3/26/2025"
]
]
{
"start_date": "3/1/2025",
"end_date": "3/6/2025",
"markers": ["3/6/2025"],
"style": "rounded"
},
{
"start_date": "3/12/2025",
"end_date": "3/18/2025",
"markers": ["3/18/2025"],
"style": "rounded"
},
{
"start_date": "3/24/2025",
"end_date": "3/27/2025",
"color": "#ff3300",
"markers": ["3/27/2025"],
"style": "rounded"
}
]
2 changes: 1 addition & 1 deletion lib/drawcal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"""

__prog__ = "drawcal"
__version__ = "0.5.9"
__version__ = "0.6.0"
__author__ = "ryan@rsgalloway.com"


Expand Down
24 changes: 19 additions & 5 deletions lib/drawcal/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def parse_args():
help="which year to draw (defaults to current year)",
)
parser.add_argument(
"-o",
"--outfile",
metavar="OUTFILE",
type=str,
Expand All @@ -97,14 +98,27 @@ def main():

args = parse_args()

if args.events:
events = read_events(args.events)
else:
events = get_events(args.month, args.year)
try:
if args.events:
events = read_events(args.events)
else:
events = get_events(args.month, args.year)
except (OSError, ValueError) as exc:
print(f"Error: {exc}", file=sys.stderr)
return 1

from drawcal.drawlib import draw_calendar

draw_calendar(month=args.month, year=args.year, events=events, outfile=args.outfile)
try:
draw_calendar(
month=args.month,
year=args.year,
events=events,
outfile=args.outfile,
)
except ValueError as exc:
print(f"Error: {exc}", file=sys.stderr)
return 1

return 0

Expand Down
Loading
Loading