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 {
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>
<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}`">
<v-card-actions>
<v-icon>
{{ icon }}
</v-icon>
<v-card-title class="py-1">
{{ 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>
<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-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 w-full">
{{ item.name }}
</v-card-title>
<v-spacer></v-spacer>
<ContextMenu :items="[presets.delete]" @delete="confirmDelete(item)" />
</v-card-actions>
</v-card>
</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>

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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