Skip to content

Commit

Permalink
Feature/group based notifications (mealie-recipes#918)
Browse files Browse the repository at this point in the history
* fix group page

* setup group notification for backend

* update type generators

* script to auto-generate schema exports

* setup frontend CRUD interface

* remove old notifications UI

* drop old events api

* add test functionality

* update naming for fields

* add event dispatcher functionality

* bump to python 3.10

* bump python version

* purge old event code

* use-async apprise

* set mealie logo as image

* unify styles for buttons rows

* add links to banners
  • Loading branch information
hay-kot authored Jan 10, 2022
1 parent 50a341e commit 190773c
Show file tree
Hide file tree
Showing 74 changed files with 1,992 additions and 1,229 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/backend-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
- name: Set up python
uses: actions/setup-python@v2
with:
python-version: 3.9
python-version: "3.10"
#----------------------------------------------
# ----- install & configure poetry -----
#----------------------------------------------
Expand Down
1 change: 0 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,6 @@ ignore-comments=yes

# Ignore docstrings when computing similarities.
ignore-docstrings=yes
w54
# Ignore imports when computing similarities.
ignore-imports=no

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
###############################################
# Base Image
###############################################
FROM python:3.9-slim as python-base
FROM python:3.10-slim as python-base

ENV MEALIE_HOME="/app"

Expand Down
12 changes: 10 additions & 2 deletions dev/code-generation/_gen_utils.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
from __future__ import annotations

import re
from dataclasses import dataclass
from pathlib import Path
from typing import Tuple

import black
import isort
from jinja2 import Template


def render_python_template(template_file: Path, dest: Path, data: dict) -> str:
def render_python_template(template_file: Path | str, dest: Path, data: dict) -> str:
"""Render and Format a Jinja2 Template for Python Code"""
tplt = Template(template_file.read_text())
if isinstance(template_file, Path):
tplt = Template(template_file.read_text())
else:
tplt = Template(template_file)

text = tplt.render(data=data)
text = black.format_str(text, mode=black.FileMode())
dest.write_text(text)
isort.file(dest)


@dataclass
Expand Down
1 change: 1 addition & 0 deletions dev/code-generation/_static.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path

CWD = Path(__file__).parent
PROJECT_DIR = Path(__file__).parent.parent.parent


class Directories:
Expand Down
99 changes: 99 additions & 0 deletions dev/code-generation/gen_frontend_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from pathlib import Path

from jinja2 import Template
from pydantic2ts import generate_typescript_defs

# ============================================================
# Global Compoenents Generator

template = """// This Code is auto generated by gen_global_components.py
{% for name in global %} import {{ name }} from "@/components/global/{{ name }}.vue";
{% endfor %}
{% for name in layout %} import {{ name }} from "@/components/layout/{{ name }}.vue";
{% endfor %}
declare module "vue" {
export interface GlobalComponents {
// Global Components
{% for name in global %} {{ name }}: typeof {{ name }};
{% endfor %} // Layout Components
{% for name in layout %} {{ name }}: typeof {{ name }};
{% endfor %}
}
}
export {};
"""

CWD = Path(__file__).parent
PROJECT_DIR = Path(__file__).parent.parent.parent


def generate_global_components_types() -> None:
destination_file = PROJECT_DIR / "frontend" / "types" / "components.d.ts"

component_paths = {
"global": PROJECT_DIR / "frontend" / "components" / "global",
"layout": PROJECT_DIR / "frontend" / "components" / "Layout",
}

def render_template(template: str, data: dict) -> None:
template = Template(template)
return template.render(**data)

def build_data() -> dict:
data = {}
for name, path in component_paths.items():
components = [component.stem for component in path.glob("*.vue")]
data[name] = components

return data

def write_template(text: str) -> None:
destination_file.write_text(text)

text = render_template(template, build_data())
write_template(text)


# ============================================================
# Pydantic To Typescript Generator


def generate_typescript_types() -> None:
def path_to_module(path: Path):
path: str = str(path)

path = path.removeprefix(str(PROJECT_DIR))
path = path.removeprefix("/")
path = path.replace("/", ".")

return path

schema_path = PROJECT_DIR / "mealie" / "schema"
types_dir = PROJECT_DIR / "frontend" / "types" / "api-types"

for module in schema_path.iterdir():

if not module.is_dir() or not module.joinpath("__init__.py").is_file():
continue

ts_out_name = module.name.replace("_", "-") + ".ts"

out_path = types_dir.joinpath(ts_out_name)

print(module) # noqa
try:
path_as_module = path_to_module(module)
generate_typescript_defs(path_as_module, str(out_path), exclude=("CamelModel"))
except Exception as e:
print(f"Failed to generate {module}") # noqa
print(e) # noqa


if __name__ == "__main__":
print("\n-- Starting Global Components Generator --") # noqa
generate_global_components_types()

print("\n-- Starting Pydantic To Typescript Generator --") # noqa
generate_typescript_types()
35 changes: 35 additions & 0 deletions dev/code-generation/gen_schema_exports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from _gen_utils import render_python_template
from _static import PROJECT_DIR

template = """# GENERATED CODE - DO NOT MODIFY BY HAND
{% for file in data.files %}from .{{ file }} import *
{% endfor %}
"""

SCHEMA_PATH = PROJECT_DIR / "mealie" / "schema"


def generate_init_files() -> None:

for schema in SCHEMA_PATH.iterdir():
if not schema.is_dir():
print(f"Skipping {schema}")
continue

print(f"Generating {schema}")
init_file = schema.joinpath("__init__.py")

module_files = [
f.stem for f in schema.iterdir() if f.is_file() and f.suffix == ".py" and not f.stem.startswith("_")
]
render_python_template(template, init_file, {"files": module_files})


def main():
print("Starting...")
generate_init_files()
print("Finished...")


if __name__ == "__main__":
main()
58 changes: 0 additions & 58 deletions dev/scripts/gen_global_components.py

This file was deleted.

37 changes: 0 additions & 37 deletions dev/scripts/types_gen.py

This file was deleted.

41 changes: 0 additions & 41 deletions frontend/api/class-interfaces/event-notifications.ts

This file was deleted.

18 changes: 18 additions & 0 deletions frontend/api/class-interfaces/group-event-notifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { BaseCRUDAPI } from "../_base";
import { GroupEventNotifierCreate, GroupEventNotifierOut } from "~/types/api-types/group";

const prefix = "/api";

const routes = {
eventNotifier: `${prefix}/groups/events/notifications`,
eventNotifierId: (id: string | number) => `${prefix}/groups/events/notifications/${id}`,
};

export class GroupEventNotifierApi extends BaseCRUDAPI<GroupEventNotifierOut, GroupEventNotifierCreate> {
baseRoute = routes.eventNotifier;
itemRoute = routes.eventNotifierId;

async test(itemId: string) {
return await this.requests.post(`${this.baseRoute}/${itemId}/test`, {});
}
}
Loading

0 comments on commit 190773c

Please sign in to comment.