mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-05-24 01:12:54 -04:00
wip: recipe count for organizers
This commit is contained in:
parent
8ec60668e6
commit
01d335f73d
@ -47,3 +47,21 @@
|
||||
.v-card__title {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -24,24 +24,32 @@
|
||||
<v-spacer></v-spacer>
|
||||
<BaseButton create @click="dialog = true" />
|
||||
</v-app-bar>
|
||||
<section v-for="(itms, key, idx) in itemsSorted" :key="'header' + idx" :class="idx === 1 ? null : 'my-4'">
|
||||
<BaseCardSectionTitle :title="key"> </BaseCardSectionTitle>
|
||||
<v-row>
|
||||
<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}`">
|
||||
<section v-for="(itms, key, idx) in itemsSorted" :key="`header-${idx}`" :class="idx === 1 ? null : 'my-4'">
|
||||
<BaseCardSectionTitle :title="key" />
|
||||
<div class="grid-cols-3">
|
||||
|
||||
<v-card
|
||||
v-for="(item, index) in itms"
|
||||
:key="`cat-${index}`"
|
||||
class="left-border"
|
||||
hover
|
||||
:to="`/recipes/${itemType}/${item.slug}`"
|
||||
>
|
||||
<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 }}
|
||||
</v-icon>
|
||||
<v-card-title class="py-1">
|
||||
<v-card-title class="py-1 w-full">
|
||||
{{ item.name }}
|
||||
</v-card-title>
|
||||
<v-spacer></v-spacer>
|
||||
<ContextMenu :items="[presets.delete]" @delete="confirmDelete(item)" />
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
@ -56,6 +64,7 @@ interface GenericItem {
|
||||
id?: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
count?: number;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@ -80,7 +89,7 @@ export default defineComponent({
|
||||
// =================================================================
|
||||
// Sorted Items
|
||||
const itemsSorted = computed(() => {
|
||||
const byLetter: { [key: string]: Array<GenericItem> } = {};
|
||||
const byLetter: Record<string, Array<GenericItem>> = {};
|
||||
|
||||
if (!props.items) return byLetter;
|
||||
|
||||
@ -133,3 +142,9 @@ export default defineComponent({
|
||||
head: {},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,5 +1,6 @@
|
||||
from functools import cached_property
|
||||
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
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.plan_rules import PlanRulesOut
|
||||
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_share_token import RecipeShareToken
|
||||
from mealie.schema.recipe.recipe_timeline_events import RecipeTimelineEventOut
|
||||
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.user import GroupInDB, LongLiveTokenInDB, PrivateUser
|
||||
from mealie.schema.user.user_passwords import PrivatePasswordResetToken
|
||||
@ -73,11 +75,57 @@ class RepositoryCategories(RepositoryGeneric[CategoryOut, Category]):
|
||||
def get_empty(self):
|
||||
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]):
|
||||
def get_empty(self):
|
||||
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:
|
||||
def __init__(self, session: Session) -> None:
|
||||
|
@ -7,9 +7,9 @@ from mealie.routes._base import BaseCrudController, controller
|
||||
from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.schema import mapper
|
||||
from mealie.schema.recipe import CategoryIn, RecipeCategoryResponse
|
||||
from mealie.schema.recipe.recipe import RecipeCategory, RecipeCategoryPagination
|
||||
from mealie.schema.recipe.recipe_category import CategoryBase, CategorySave
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.schema.recipe.recipe import RecipeCategory
|
||||
from mealie.schema.recipe.recipe_category import CategoryBase, CategoryCount, CategorySave
|
||||
from mealie.schema.response.pagination import PaginationBase, PaginationQuery
|
||||
from mealie.services import urls
|
||||
from mealie.services.event_bus_service.event_types import EventCategoryData, EventOperation, EventTypes
|
||||
|
||||
@ -37,13 +37,10 @@ class RecipeCategoryController(BaseCrudController):
|
||||
def mixins(self):
|
||||
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)):
|
||||
"""Returns a list of available categories in the database"""
|
||||
response = self.repo.page_all(
|
||||
pagination=q,
|
||||
override=RecipeCategory,
|
||||
)
|
||||
response = self.repo.get_all_count_recipes(pagination=q)
|
||||
|
||||
response.set_pagination_guides(router.url_path_for("get_all"), q.dict())
|
||||
return response
|
||||
|
@ -3,11 +3,12 @@ from functools import cached_property
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.repos.repository_factory import RepositoryTags
|
||||
from mealie.routes._base import BaseCrudController, controller
|
||||
from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.schema import mapper
|
||||
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.response.pagination import PaginationQuery
|
||||
from mealie.services import urls
|
||||
@ -19,7 +20,7 @@ router = APIRouter(prefix="/tags", tags=["Organizer: Tags"])
|
||||
@controller(router)
|
||||
class TagController(BaseCrudController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
def repo(self) -> RepositoryTags:
|
||||
return self.repos.tags.by_group(self.group_id)
|
||||
|
||||
@cached_property
|
||||
@ -29,10 +30,7 @@ class TagController(BaseCrudController):
|
||||
@router.get("", response_model=RecipeTagPagination)
|
||||
async def get_all(self, q: PaginationQuery = Depends(PaginationQuery)):
|
||||
"""Returns a list of available tags in the database"""
|
||||
response = self.repo.page_all(
|
||||
pagination=q,
|
||||
override=RecipeTag,
|
||||
)
|
||||
response = self.repo.get_all_count_recipes(pagination=q)
|
||||
|
||||
response.set_pagination_guides(router.url_path_for("get_all"), q.dict())
|
||||
return response
|
||||
|
@ -4,6 +4,7 @@ from sqlalchemy.orm.session import Session
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.schema.recipe import RecipeSummary
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
|
||||
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)):
|
||||
db = get_repositories(session)
|
||||
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
|
||||
|
@ -5,7 +5,6 @@ from .recipe import (
|
||||
CreateRecipeByUrlBulk,
|
||||
Recipe,
|
||||
RecipeCategory,
|
||||
RecipeCategoryPagination,
|
||||
RecipePagination,
|
||||
RecipePaginationQuery,
|
||||
RecipeSummary,
|
||||
|
@ -53,7 +53,7 @@ class TagBase(CategoryBase):
|
||||
pass
|
||||
|
||||
|
||||
class TagOut(TagSave):
|
||||
class TagOut(TagIn):
|
||||
id: UUID4
|
||||
slug: str
|
||||
|
||||
@ -61,6 +61,17 @@ class TagOut(TagSave):
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class TagCount(TagOut):
|
||||
count: int = 0
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class CategoryCount(TagCount):
|
||||
...
|
||||
|
||||
|
||||
class RecipeTagResponse(RecipeCategoryResponse):
|
||||
pass
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user