wip: recipe count for organizers

This commit is contained in:
Hayden 2022-11-27 10:04:51 -09:00
parent 8ec60668e6
commit 01d335f73d
No known key found for this signature in database
GPG Key ID: 17CF79474E257545
8 changed files with 133 additions and 36 deletions

View File

@ -47,3 +47,21 @@
.v-card__title { .v-card__title {
word-break: normal !important; word-break: normal !important;
} }
.grid-cols-3 {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 1rem;
}
@media screen and (max-width: 960px) {
.grid-cols-3 {
grid-template-columns: 1fr 1fr;
}
}
@media screen and (max-width: 600px) {
.grid-cols-3 {
grid-template-columns: 1fr;
}
}

View File

@ -24,24 +24,32 @@
<v-spacer></v-spacer> <v-spacer></v-spacer>
<BaseButton create @click="dialog = true" /> <BaseButton create @click="dialog = true" />
</v-app-bar> </v-app-bar>
<section v-for="(itms, key, idx) in itemsSorted" :key="'header' + idx" :class="idx === 1 ? null : 'my-4'"> <section v-for="(itms, key, idx) in itemsSorted" :key="`header-${idx}`" :class="idx === 1 ? null : 'my-4'">
<BaseCardSectionTitle :title="key"> </BaseCardSectionTitle> <BaseCardSectionTitle :title="key" />
<v-row> <div class="grid-cols-3">
<v-col v-for="(item, index) in itms" :key="'cat' + index" cols="12" :sm="12" :md="6" :lg="4" :xl="3">
<v-card class="left-border" hover :to="`/recipes/${itemType}/${item.slug}`"> <v-card
v-for="(item, index) in itms"
:key="`cat-${index}`"
class="left-border"
hover
:to="`/recipes/${itemType}/${item.slug}`"
>
<v-card-actions> <v-card-actions>
<v-icon> <v-chip v-if="item.count != undefined" small class="ml-auto accent">
{{ item.count }}
</v-chip>
<v-icon v-else>
{{ icon }} {{ icon }}
</v-icon> </v-icon>
<v-card-title class="py-1"> <v-card-title class="py-1 w-full">
{{ item.name }} {{ item.name }}
</v-card-title> </v-card-title>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<ContextMenu :items="[presets.delete]" @delete="confirmDelete(item)" /> <ContextMenu :items="[presets.delete]" @delete="confirmDelete(item)" />
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-col> </div>
</v-row>
</section> </section>
</div> </div>
</template> </template>
@ -56,6 +64,7 @@ interface GenericItem {
id?: string; id?: string;
name: string; name: string;
slug: string; slug: string;
count?: number;
} }
export default defineComponent({ export default defineComponent({
@ -80,7 +89,7 @@ export default defineComponent({
// ================================================================= // =================================================================
// Sorted Items // Sorted Items
const itemsSorted = computed(() => { const itemsSorted = computed(() => {
const byLetter: { [key: string]: Array<GenericItem> } = {}; const byLetter: Record<string, Array<GenericItem>> = {};
if (!props.items) return byLetter; if (!props.items) return byLetter;
@ -133,3 +142,9 @@ export default defineComponent({
head: {}, head: {},
}); });
</script> </script>
<style lang="css">
.w-full {
width: 100%;
}
</style>

View File

@ -1,5 +1,6 @@
from functools import cached_property from functools import cached_property
from sqlalchemy import func
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from mealie.db.models.group import Group, GroupMealPlan, ReportEntryModel, ReportModel from mealie.db.models.group import Group, GroupMealPlan, ReportEntryModel, ReportModel
@ -47,11 +48,12 @@ from mealie.schema.labels import MultiPurposeLabelOut
from mealie.schema.meal_plan.new_meal import ReadPlanEntry from mealie.schema.meal_plan.new_meal import ReadPlanEntry
from mealie.schema.meal_plan.plan_rules import PlanRulesOut from mealie.schema.meal_plan.plan_rules import PlanRulesOut
from mealie.schema.recipe import Recipe, RecipeCommentOut, RecipeToolOut from mealie.schema.recipe import Recipe, RecipeCommentOut, RecipeToolOut
from mealie.schema.recipe.recipe_category import CategoryOut, TagOut from mealie.schema.recipe.recipe_category import CategoryCount, CategoryOut, TagCount, TagOut
from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUnit from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUnit
from mealie.schema.recipe.recipe_share_token import RecipeShareToken from mealie.schema.recipe.recipe_share_token import RecipeShareToken
from mealie.schema.recipe.recipe_timeline_events import RecipeTimelineEventOut from mealie.schema.recipe.recipe_timeline_events import RecipeTimelineEventOut
from mealie.schema.reports.reports import ReportEntryOut, ReportOut from mealie.schema.reports.reports import ReportEntryOut, ReportOut
from mealie.schema.response.pagination import PaginationBase, PaginationQuery
from mealie.schema.server import ServerTask from mealie.schema.server import ServerTask
from mealie.schema.user import GroupInDB, LongLiveTokenInDB, PrivateUser from mealie.schema.user import GroupInDB, LongLiveTokenInDB, PrivateUser
from mealie.schema.user.user_passwords import PrivatePasswordResetToken from mealie.schema.user.user_passwords import PrivatePasswordResetToken
@ -73,11 +75,57 @@ class RepositoryCategories(RepositoryGeneric[CategoryOut, Category]):
def get_empty(self): def get_empty(self):
return self.session.query(Category).filter(~Category.recipes.any()).all() return self.session.query(Category).filter(~Category.recipes.any()).all()
def get_all_count_recipes(self, pagination: PaginationQuery) -> PaginationBase[TagCount]:
q = self.session.query(Category, func.count(RecipeModel.id).filter(RecipeModel.recipe_category)).group_by(
Category.id
)
fltr = self._filter_builder()
q = q.filter_by(**fltr)
q, count, total_pages = self.add_pagination_to_query(q, pagination)
try:
data: list[tuple[Category, int]] = q.all()
except Exception as e:
self._log_exception(e)
self.session.rollback()
raise e
return PaginationBase(
page=pagination.page,
per_page=pagination.per_page,
total=count,
total_pages=total_pages,
items=[CategoryCount(**s[0].__dict__, count=s[1]) for s in data],
)
class RepositoryTags(RepositoryGeneric[TagOut, Tag]): class RepositoryTags(RepositoryGeneric[TagOut, Tag]):
def get_empty(self): def get_empty(self):
return self.session.query(Tag).filter(~Tag.recipes.any()).all() return self.session.query(Tag).filter(~Tag.recipes.any()).all()
def get_all_count_recipes(self, pagination: PaginationQuery) -> PaginationBase[TagCount]:
q = self.session.query(self.model, func.count(RecipeModel.id)).join(RecipeModel.tags).group_by(self.model)
fltr = self._filter_builder()
q = q.filter_by(**fltr)
q, count, total_pages = self.add_pagination_to_query(q, pagination)
try:
data: list[tuple[Tag, int]] = q.all()
except Exception as e:
self._log_exception(e)
self.session.rollback()
raise e
return PaginationBase(
page=pagination.page,
per_page=pagination.per_page,
total=count,
total_pages=total_pages,
items=[TagCount(**s[0].__dict__, count=s[1]) for s in data],
)
class AllRepositories: class AllRepositories:
def __init__(self, session: Session) -> None: def __init__(self, session: Session) -> None:

View File

@ -7,9 +7,9 @@ from mealie.routes._base import BaseCrudController, controller
from mealie.routes._base.mixins import HttpRepo from mealie.routes._base.mixins import HttpRepo
from mealie.schema import mapper from mealie.schema import mapper
from mealie.schema.recipe import CategoryIn, RecipeCategoryResponse from mealie.schema.recipe import CategoryIn, RecipeCategoryResponse
from mealie.schema.recipe.recipe import RecipeCategory, RecipeCategoryPagination from mealie.schema.recipe.recipe import RecipeCategory
from mealie.schema.recipe.recipe_category import CategoryBase, CategorySave from mealie.schema.recipe.recipe_category import CategoryBase, CategoryCount, CategorySave
from mealie.schema.response.pagination import PaginationQuery from mealie.schema.response.pagination import PaginationBase, PaginationQuery
from mealie.services import urls from mealie.services import urls
from mealie.services.event_bus_service.event_types import EventCategoryData, EventOperation, EventTypes from mealie.services.event_bus_service.event_types import EventCategoryData, EventOperation, EventTypes
@ -37,13 +37,10 @@ class RecipeCategoryController(BaseCrudController):
def mixins(self): def mixins(self):
return HttpRepo(self.repo, self.logger) return HttpRepo(self.repo, self.logger)
@router.get("", response_model=RecipeCategoryPagination) @router.get("", response_model=PaginationBase[CategoryCount])
def get_all(self, q: PaginationQuery = Depends(PaginationQuery)): def get_all(self, q: PaginationQuery = Depends(PaginationQuery)):
"""Returns a list of available categories in the database""" """Returns a list of available categories in the database"""
response = self.repo.page_all( response = self.repo.get_all_count_recipes(pagination=q)
pagination=q,
override=RecipeCategory,
)
response.set_pagination_guides(router.url_path_for("get_all"), q.dict()) response.set_pagination_guides(router.url_path_for("get_all"), q.dict())
return response return response

View File

@ -3,11 +3,12 @@ from functools import cached_property
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import UUID4 from pydantic import UUID4
from mealie.repos.repository_factory import RepositoryTags
from mealie.routes._base import BaseCrudController, controller from mealie.routes._base import BaseCrudController, controller
from mealie.routes._base.mixins import HttpRepo from mealie.routes._base.mixins import HttpRepo
from mealie.schema import mapper from mealie.schema import mapper
from mealie.schema.recipe import RecipeTagResponse, TagIn from mealie.schema.recipe import RecipeTagResponse, TagIn
from mealie.schema.recipe.recipe import RecipeTag, RecipeTagPagination from mealie.schema.recipe.recipe import RecipeTagPagination
from mealie.schema.recipe.recipe_category import TagSave from mealie.schema.recipe.recipe_category import TagSave
from mealie.schema.response.pagination import PaginationQuery from mealie.schema.response.pagination import PaginationQuery
from mealie.services import urls from mealie.services import urls
@ -19,7 +20,7 @@ router = APIRouter(prefix="/tags", tags=["Organizer: Tags"])
@controller(router) @controller(router)
class TagController(BaseCrudController): class TagController(BaseCrudController):
@cached_property @cached_property
def repo(self): def repo(self) -> RepositoryTags:
return self.repos.tags.by_group(self.group_id) return self.repos.tags.by_group(self.group_id)
@cached_property @cached_property
@ -29,10 +30,7 @@ class TagController(BaseCrudController):
@router.get("", response_model=RecipeTagPagination) @router.get("", response_model=RecipeTagPagination)
async def get_all(self, q: PaginationQuery = Depends(PaginationQuery)): async def get_all(self, q: PaginationQuery = Depends(PaginationQuery)):
"""Returns a list of available tags in the database""" """Returns a list of available tags in the database"""
response = self.repo.page_all( response = self.repo.get_all_count_recipes(pagination=q)
pagination=q,
override=RecipeTag,
)
response.set_pagination_guides(router.url_path_for("get_all"), q.dict()) response.set_pagination_guides(router.url_path_for("get_all"), q.dict())
return response return response

View File

@ -4,6 +4,7 @@ from sqlalchemy.orm.session import Session
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.repos.all_repositories import get_repositories from mealie.repos.all_repositories import get_repositories
from mealie.schema.recipe import RecipeSummary from mealie.schema.recipe import RecipeSummary
from mealie.schema.response.pagination import PaginationQuery
router = APIRouter() router = APIRouter()
@ -18,3 +19,13 @@ async def get_untagged_recipes(count: bool = False, session: Session = Depends(g
async def get_uncategorized_recipes(count: bool = False, session: Session = Depends(generate_session)): async def get_uncategorized_recipes(count: bool = False, session: Session = Depends(generate_session)):
db = get_repositories(session) db = get_repositories(session)
return db.recipes.count_uncategorized(count=count, override_schema=RecipeSummary) return db.recipes.count_uncategorized(count=count, override_schema=RecipeSummary)
@router.get("/summar/whatever")
async def get_whatever(session: Session = Depends(generate_session)):
db = get_repositories(session)
payload = db.tags.get_all_count_recipes(
PaginationQuery(),
)
return payload

View File

@ -5,7 +5,6 @@ from .recipe import (
CreateRecipeByUrlBulk, CreateRecipeByUrlBulk,
Recipe, Recipe,
RecipeCategory, RecipeCategory,
RecipeCategoryPagination,
RecipePagination, RecipePagination,
RecipePaginationQuery, RecipePaginationQuery,
RecipeSummary, RecipeSummary,

View File

@ -53,7 +53,7 @@ class TagBase(CategoryBase):
pass pass
class TagOut(TagSave): class TagOut(TagIn):
id: UUID4 id: UUID4
slug: str slug: str
@ -61,6 +61,17 @@ class TagOut(TagSave):
orm_mode = True orm_mode = True
class TagCount(TagOut):
count: int = 0
class Config:
orm_mode = True
class CategoryCount(TagCount):
...
class RecipeTagResponse(RecipeCategoryResponse): class RecipeTagResponse(RecipeCategoryResponse):
pass pass