Skip to content

Commit

Permalink
Merge branch 'mealie-next' into oidc
Browse files Browse the repository at this point in the history
  • Loading branch information
hay-kot committed Mar 10, 2024
2 parents fdfbb06 + b54cdf6 commit b23bec8
Show file tree
Hide file tree
Showing 98 changed files with 1,186 additions and 660 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ jobs:
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
# This doesn't currently work for us because it creates the PR but the workflows don't run.
# TODO: Provide a personal access token as a parameter here, that solves that problem.
# https://github.com/peter-evans/create-pull-request
with:
commit-message: "Update image tag, for release ${{ github.event.release.tag_name }}"
branch: "docs/newrelease-update-version-${{ github.event.release.tag_name }}"
Expand Down
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ tasks:
py:lint:
desc: runs python linter
cmds:
- poetry run ruff mealie
- poetry run ruff check mealie

py:check:
desc: runs all linters, type checkers, and formatters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from uuid import uuid4

import sqlalchemy as sa
from sqlalchemy.orm.session import Session
from sqlalchemy import orm

import mealie.db.migration_types
from alembic import op
Expand All @@ -23,8 +23,10 @@
depends_on = None


def populate_shopping_lists_multi_purpose_labels(shopping_lists_multi_purpose_labels_table: sa.Table, session: Session):
shopping_lists = session.query(ShoppingList).all()
def populate_shopping_lists_multi_purpose_labels(
shopping_lists_multi_purpose_labels_table: sa.Table, session: orm.Session
):
shopping_lists = session.query(ShoppingList).options(orm.load_only(ShoppingList.id, ShoppingList.group_id)).all()

shopping_lists_labels_data: list[dict] = []
for shopping_list in shopping_lists:
Expand Down Expand Up @@ -60,7 +62,7 @@ def upgrade():
)
# ### end Alembic commands ###

session = Session(bind=op.get_bind())
session = orm.Session(bind=op.get_bind())
populate_shopping_lists_multi_purpose_labels(shopping_lists_multi_purpose_labels_table, session)


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""added user to shopping list
Revision ID: 2298bb460ffd
Revises: ba1e4a6cfe99
Create Date: 2024-02-23 16:15:07.115641
"""

from uuid import UUID

import sqlalchemy as sa
from sqlalchemy import orm

import mealie.db.migration_types
from alembic import op

# revision identifiers, used by Alembic.
revision = "2298bb460ffd"
down_revision = "ba1e4a6cfe99"
branch_labels = None
depends_on = None


def is_postgres():
return op.get_context().dialect.name == "postgresql"


def find_user_id_for_group(group_id: UUID):
bind = op.get_bind()
session = orm.Session(bind=bind)

if is_postgres():
stmt = "SELECT id FROM users WHERE group_id=:group_id AND admin = TRUE LIMIT 1"
else:
stmt = "SELECT id FROM users WHERE group_id=:group_id AND admin = 1 LIMIT 1"

with session:
try:
# try to find an admin user
user_id = session.execute(sa.text(stmt).bindparams(group_id=group_id)).scalar_one()
except orm.exc.NoResultFound:
# fallback to any user
user_id = session.execute(
sa.text("SELECT id FROM users WHERE group_id=:group_id LIMIT 1").bindparams(group_id=group_id)
).scalar_one()
return user_id


def populate_shopping_list_users():
bind = op.get_bind()
session = orm.Session(bind=bind)

with session:
list_ids_and_group_ids = session.execute(sa.text("SELECT id, group_id FROM shopping_lists")).all()
for list_id, group_id in list_ids_and_group_ids:
user_id = find_user_id_for_group(group_id)
session.execute(
sa.text(f"UPDATE shopping_lists SET user_id=:user_id WHERE id=:id").bindparams(
user_id=user_id, id=list_id
)
)


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("shopping_lists") as batch_op:
# allow nulls during migration
batch_op.add_column(sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True))
batch_op.create_index(op.f("ix_shopping_lists_user_id"), ["user_id"], unique=False)
batch_op.create_foreign_key("fk_user_shopping_lists", "users", ["user_id"], ["id"])
# ### end Alembic commands ###

populate_shopping_list_users()

# forbid nulls after migration
with op.batch_alter_table("shopping_lists") as batch_op:
batch_op.alter_column("user_id", nullable=False)


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "shopping_lists", type_="foreignkey")
op.drop_index(op.f("ix_shopping_lists_user_id"), table_name="shopping_lists")
op.drop_column("shopping_lists", "user_id")
# ### end Alembic commands ###
16 changes: 9 additions & 7 deletions dev/code-generation/gen_py_pytest_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,34 @@

from fastapi import FastAPI
from jinja2 import Template
from pydantic import BaseModel
from utils import PROJECT_DIR, CodeTemplates, HTTPRequest, RouteObject
from pydantic import BaseModel, ConfigDict
from utils import PROJECT_DIR, CodeTemplates, HTTPRequest, RouteObject, RequestType

CWD = Path(__file__).parent

OUTFILE = PROJECT_DIR / "tests" / "utils" / "api_routes" / "__init__.py"


class PathObject(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
route_object: RouteObject
http_verbs: list[HTTPRequest]

class Config:
arbitrary_types_allowed = True


def get_path_objects(app: FastAPI):
paths = []

for key, value in app.openapi().items():
if key == "paths":
for key, value in value.items():
for key, value2 in value.items():
verbs = []
for k, v in value2.items():
verbs.append(HTTPRequest(request_type=k, **v))

paths.append(
PathObject(
route_object=RouteObject(key),
http_verbs=[HTTPRequest(request_type=k, **v) for k, v in value.items()],
http_verbs=verbs,
)
)

Expand Down
25 changes: 19 additions & 6 deletions dev/code-generation/gen_ts_locales.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import dotenv
import requests
from jinja2 import Template
from pydantic import Extra
from pydantic import ConfigDict
from requests import Response
from utils import CodeDest, CodeKeys, inject_inline, log

Expand Down Expand Up @@ -56,7 +56,7 @@ class LocaleData:
"zh-TW": LocaleData(name="繁體中文 (Chinese traditional)"),
}

LOCALE_TEMPLATE = """// This Code is auto generated by gen_global_components.py
LOCALE_TEMPLATE = """// This Code is auto generated by gen_ts_locales.py
export const LOCALES = [{% for locale in locales %}
{
name: "{{ locale.name }}",
Expand All @@ -70,6 +70,8 @@ class LocaleData:


class TargetLanguage(MealieModel):
model_config = ConfigDict(populate_by_name=True, extra="allow")

id: str
name: str
locale: str
Expand All @@ -78,10 +80,6 @@ class TargetLanguage(MealieModel):
twoLettersCode: str
progress: float = 0.0

class Config:
extra = Extra.allow
allow_population_by_field_name = True


class CrowdinApi:
project_name = "Mealie"
Expand Down Expand Up @@ -152,6 +150,7 @@ def get_progress(self) -> dict:
datetime_dir = PROJECT_DIR / "frontend" / "lang" / "dateTimeFormats"
locales_dir = PROJECT_DIR / "frontend" / "lang" / "messages"
nuxt_config = PROJECT_DIR / "frontend" / "nuxt.config.js"
reg_valid = PROJECT_DIR / "mealie" / "schema" / "_mealie" / "validators.py"

"""
This snippet walks the message and dat locales directories and generates the import information
Expand All @@ -175,6 +174,19 @@ def inject_nuxt_values():
inject_inline(nuxt_config, CodeKeys.nuxt_local_dates, all_date_locales)


def inject_registration_validation_values():
all_langs = []
for match in locales_dir.glob("*.json"):
lang_string = f'"{match.stem}",'
all_langs.append(lang_string)

# sort
all_langs.sort()

log.debug(f"injecting locales into user registration validation -> {reg_valid}")
inject_inline(reg_valid, CodeKeys.nuxt_local_messages, all_langs)


def generate_locales_ts_file():
api = CrowdinApi("")
models = api.get_languages()
Expand All @@ -193,6 +205,7 @@ def main():

generate_locales_ts_file()
inject_nuxt_values()
inject_registration_validation_values()


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion dev/code-generation/gen_ts_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# ============================================================

template = """// This Code is auto generated by gen_global_components.py
template = """// This Code is auto generated by gen_ts_types.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 %}
Expand Down
24 changes: 10 additions & 14 deletions dev/code-generation/utils/route.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import re
from enum import Enum
from typing import Optional

from humps import camelize
from pydantic import BaseModel, Extra, Field
from pydantic import BaseModel, ConfigDict, Field
from slugify import slugify


Expand Down Expand Up @@ -34,33 +33,30 @@ class ParameterIn(str, Enum):


class RouterParameter(BaseModel):
model_config = ConfigDict(extra="allow")

required: bool = False
name: str
location: ParameterIn = Field(..., alias="in")

class Config:
extra = Extra.allow


class RequestBody(BaseModel):
required: bool = False
model_config = ConfigDict(extra="allow")

class Config:
extra = Extra.allow
required: bool = False


class HTTPRequest(BaseModel):
model_config = ConfigDict(extra="allow", populate_by_name=True)

request_type: RequestType
description: str = ""
summary: str
requestBody: Optional[RequestBody]
request_body: RequestBody | None = None

parameters: list[RouterParameter] = []
tags: list[str] | None = []

class Config:
extra = Extra.allow

def list_as_js_object_string(self, parameters, braces=True):
if len(parameters) == 0:
return ""
Expand All @@ -71,11 +67,11 @@ def list_as_js_object_string(self, parameters, braces=True):
return ", ".join(parameters)

def payload(self):
return "payload" if self.requestBody else ""
return "payload" if self.request_body else ""

def function_args(self):
all_params = [p.name for p in self.parameters]
if self.requestBody:
if self.request_body:
all_params.append("payload")
return self.list_as_js_object_string(all_params)

Expand Down
2 changes: 1 addition & 1 deletion dev/code-generation/utils/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def push_line(self, string: str) -> None:
self._next_line += 1


def get_indentation_of_string(line: str, comment_char: str = "//") -> str:
def get_indentation_of_string(line: str, comment_char: str = "//|#") -> str:
return re.sub(rf"{comment_char}.*", "", line).removesuffix("\n")


Expand Down
10 changes: 5 additions & 5 deletions docs/docs/contributors/developers-guide/code-contributions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
## We Develop with Github
We use github to host code, to track issues and feature requests, as well as accept pull requests.

## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests
Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests:
## We Use [Github Flow](https://docs.github.com/en/get-started/using-github/github-flow), So All Code Changes Happen Through Pull Requests
Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://docs.github.com/en/get-started/using-github/github-flow)). We actively welcome your pull requests:

1. Fork the repo and create your branch from `mealie-next`.
2. Checkout the Discord, the PRs page, or the Projects page to get an idea of what's already being worked on.
Expand All @@ -28,8 +28,8 @@ We use GitHub issues to track public bugs. Report a bug by [opening a new issue]

- A quick summary and/or background
- Steps to reproduce
- Be specific!
- Give sample code if you can. [This stackoverflow question](http://stackoverflow.com/q/12488905/180626) includes sample code that *anyone* with a base R setup can run to reproduce what I was seeing
* Be specific!
* Give sample code if you can. [This stackoverflow question](http://stackoverflow.com/q/12488905/180626) includes sample code that *anyone* with a base R setup can run to reproduce what I was seeing
- What you expected would happen
- What actually happens
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
Expand All @@ -41,4 +41,4 @@ People *love* thorough bug reports. I'm not even kidding.
By contributing, you agree that your contributions will be licensed under its AGPL License.

## References
This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md)
This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebookarchive/draft-js/blob/main/CONTRIBUTING.md)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ PostgreSQL might be considered if you need to support many concurrent users. In
version: "3.7"
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v1.1.0 # (3)
image: ghcr.io/mealie-recipes/mealie:v1.3.1 # (3)
container_name: mealie
ports:
- "9925:9000" # (1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ SQLite is a popular, open source, self-contained, zero-configuration database th
version: "3.7"
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v1.1.0 # (3)
image: ghcr.io/mealie-recipes/mealie:v1.3.1 # (3)
container_name: mealie
ports:
- "9925:9000" # (1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default defineComponent({
})
async function getShoppingLists() {
const { data } = await api.shopping.lists.getAll();
const { data } = await api.shopping.lists.getAll(1, -1, { orderBy: "name", orderDirection: "asc" });
if (data) {
shoppingLists.value = data.items ?? [];
}
Expand Down
Loading

0 comments on commit b23bec8

Please sign in to comment.