
    $j!s                     "   d Z ddlmZmZmZ ddlmZ ddlmZ ddl	m
Z
 ddlmZmZ ddlmZmZmZ ddlZddlZddlZdd	lmZmZ dd
lmZ ddlZddl	mZ ddlZdadad Zd Z  edd      Z!e!jE                  edgdgdg       ejF                  jI                  ejF                  jK                  e&      dd      Z'e!jQ                  d      d        Z) G d de      Z* G d de      Z+ G d de      Z, G d de      Z- G d d e      Z. G d! d"e      Z/e!ja                  d#      d$e,fd%       Z1e!ja                  d&      d$e-fd'       Z2e!jg                  d(       ed)      fd*e4d+e4fd,       Z5e!jm                  d-      d*e4fd.       Z7e!jm                  d/      d/d*e4d0e4fd1       Z8e!ja                  d/       ed)      fd$e*d*e4fd2       Z9e!jg                  d3      d4e:d$e+fd5       Z;e!jy                  d3      d4e:fd6       Z=e!ja                  d7      d4e:fd8       Z>e!jm                  d9      d*e4fd:       Z?e!jm                  d;      d*e4fd<       Z@e!jm                  d=      d0d*e4d?e:fd@       ZAdAe4dBeBdCeCfdDZDdEeEdCeEfdFZFe!jm                  dG      dHe:fdI       ZGe!jm                  dJ      d*e4fdK       ZHe!ja                  dJ       ed)      fd$e.d*e4fdL       ZIe!jg                  dM      dNe:d$e/fdO       ZJe!jy                  dM      dNe:fdP       ZKe!ja                  dQ       ed)      fd*e4fdR       ZLe!jm                  dS      d/dTe4fdU       ZMe!ja                  dV      dWe4fdX       ZNg dYg dZg d[g d\g d]g d^g d_g d`daZOe!jm                  db       ed)      fd+e4fdc       ZPe!jm                  dd      d*e4fde       ZQe!jm                  df      dg        ZR G dh die      ZSe!jm                  dj      d*e4fdk       ZTe!ja                  dj       ed)      fd$eSd*e4fdl       ZUe!jg                  dm      dne:fdo       ZVe!jy                  dp      dne:fdq       ZWe!jm                  dr      d*e4fds       ZXi dtdudvdwdxd>dydzd{d|d}d~ddzdddduddddddddddddddddi dduddddddddudduddddudduddddddddddddddddwi dd>ddddddwddwddwdd>dd>dd~dd>dd>dd>ddwdduddddddui ddddddddddddddddudduddddwdd>ddwddwdddd>dd>i ddddddddddddudduddudddddduddddudddddduddwi ddddwdd>ddudduddddddddddddddddddddddddi dddddd>dd>ddwddzddzddwddddddddddddddddddi ddddddddddudddddd>dd>ddwddd dddddddddddddddddddddddZYe!jm                  d	       ed)       ed
      fd+e4de4fd       ZZg dg dg dg dg dg dg dg dg dd	Z[e!jm                  d       ed)      fdTe4fd       Z\e!jm                  d      d*e4fd       Z]ejF                  j                  e'      r+e!j                  d eejF                  jI                  e'd            d       e!j                  d eejF                  jI                  e'd             d        e!j                  d! eejF                  jI                  e'd"            d"       dd#lm`Z` dd$l	maZa e!j                  d%      d&e`fd'       Zce!jm                  d(      d)        Zde!jm                  d*      d+        Zee!jm                  d,      d1d-e4fd.       Zfyy(2  u"   冰箱食材管理 API — FastAPI    )FastAPIHTTPExceptionQuery)CORSMiddleware)StaticFiles)FileResponse)	BaseModelField)datedatetime	timedeltaN)get_dbinit_db)seed_all)JSONResponsec                    t         Et        | d   j                        }|D ]%  }t        |      }t	        j
                  |d         |d<   t	        j
                  |d         |d<   t	        j
                  |d         |d<   |j                  d      r%t	        j
                  |j                  dd            ng |d<   |j                  d      r%t	        j
                  |j                  dd            ng |d<   |j                  d      r%t	        j
                  |j                  dd            ng |d<   |j                  d	d
      |d	<   ( |a t         S )Nrecipesingredientsstepstagsingredient_amounts[]
step_timespantry_itemsrecipe_emoji   🍳)_static_recipeslistrowsdictjsonloadsget)dbr   rs      7/home/mi/.openclaw/workspace/fridge-app/backend/main.py_get_static_recipesr'      s#   r)}))* 	>AQA#zz!M*:;AmAgJ/AgJ

1V9-AfIWXW\W\]qWrdjj7KT1R&SxzA"#GHuu\GZdjj|T)BC`bAlOKL55Q_K`

155+F GfhAn !nf =An	> "    c                 p    t         &| d   j                  D cg c]  }t        |       c}a t         S c c}w )Ntips)_static_tipsr   r    )r$   ts     r&   _get_static_tipsr-   &   s/    )+F9AQ9 :s   3u   冰箱食材管理z1.0.0)titleversion*)allow_originsallow_methodsallow_headersz..frontendstartupc                  ,    t                t                y N)r   r    r(   r&   r5   r5   5   s    IJr(   c                       e Zd ZU eed<   dZeed<   dZeed<   dZeed<   dZ	eed	<   dZ
eed
<   dZeed<   dZeed<   dZeed<   dZeed<   dZeed<   y)
FoodCreatename   其他category   quantity   个unitNpurchase_dateexpiry_dater   price   冷藏storagedefaultadded_by barcodenotes)__name__
__module____qualname__str__annotations__r=   r?   floatrA   rB   rC   rD   rF   rH   rJ   rK   r8   r(   r&   r:   r:   =   sj    
IHcHeD#M3KE5GSHcGSE3Or(   r:   c                   p    e Zd ZU dZeed<   dZeed<   dZeed<   dZ	eed<   dZ
eed<   dZeed<   dZeed<   y)	
FoodUpdateNr;   r=   r?   rA   rC   rK   status)rL   rM   rN   r;   rO   rP   r=   r?   rQ   rA   rC   rK   rT   r8   r(   r&   rS   rS   J   sH    D#HcHeD#KE3FCr(   rS   c                   "    e Zd ZU eed<   eed<   y)FamilyCreater;   creator_openidN)rL   rM   rN   rO   rP   r8   r(   r&   rV   rV   S   s    
Ir(   rV   c                   0    e Zd ZU eed<   eed<   dZeed<   y)
FamilyJoininvite_codeopenidrI   nicknameN)rL   rM   rN   rO   rP   r\   r8   r(   r&   rY   rY   W   s    KHcr(   rY   c                   B    e Zd ZU eed<   dZeed<   dZeed<   dZeed<   y)	ShoppingItemr;   r>   r?   r@   rA   rG   rH   N)	rL   rM   rN   rO   rP   r?   rQ   rA   rH   r8   r(   r&   r^   r^   \   s%    
IHeD#Hcr(   r^   c                       e Zd ZU eed<   y)ShoppingUpdate	purchasedN)rL   rM   rN   boolrP   r8   r(   r&   r`   r`   b   s    Or(   r`   z/api/family/createdatac                    t               }t        j                         j                  d d }t        j                         j                  d d }|d   j	                  || j
                  |t        j                         j                         d       |d   j	                  | j                  |ddt        j                         j                         dd	       ||d
S )N      families)idr;   rZ   
created_atusersrI   r[   	family_idr\   
avatar_urlri   Treplace)rl   rZ   )
r   uuiduuid4hexinsertr;   r   now	isoformatrW   )rc   r$   fidcodes       r&   create_familyrx   h   s    	B
**,

2A
C::<BQDzNdii\d\h\h\j\t\t\vwxwK$"5"5CUWgi  zB  zF  zF  zH  zR  zR  zT  U  _c  dT22r(   z/api/family/joinc                 J   t               }t        |d   j                  d| j                  g            }|st	        dd      |d   }|d   j                  | j                  |d   | j                  dt        j                         j                         d	d
       |d   |d   dS )Nrg   zinvite_code = ?  u   邀请码无效r   rj   rh   rI   rk   Trn   r;   )rl   r;   )r   r   
rows_whererZ   r   rs   r[   r\   r   rt   ru   )rc   r$   r   fams       r&   join_familyr}   r   s    	B:))*;d>N>N=OPQDC!233
q'CwK$++CISWS`S`pr  CK  CO  CO  CQ  C[  C[  C]  ^  hl  mTCK88r(   z/api/family/{family_id}/rename.rl   r;   c                 L    t               }|d   j                  | d|i       ddiS )Nrg   r;   okTr   update)rl   r;   r$   s      r&   rename_familyr   |   s+    	BzN)fd^4$<r(   z/api/family/{family_id}c                     t               }|d   j                  |       }|st        dd      t        |d   j	                  d| g            }t        |      |D cg c]  }t        |       c}dS c c}w )Nrg   rz   u   家庭组不存在rj   family_id = ?)familymembers)r   r#   r   r   r{   r    )rl   r$   r|   r   ms        r&   
get_familyr      sl    	B
Z.

Y
'CC!5662g;))/I;GHG3ig,FT!W,FGG,Fs   A1z
/api/foodsrT   c                     t               }|r#t        |d   j                  d| |gd            }n!t        |d   j                  d| gd            }d|D cg c]  }t        |       c}iS c c}w )Nfoodszfamily_id = ? AND status = ?expiry_date ASCorder_byr   r   r   r{   r    )rl   rT   r$   r   r%   s        r&   
list_foodsr      st    	BBwK**+IIW]K^iz*{|BwK**?YKRc*det,!d1g,--,s   A,c                    t               }t        j                         j                         }| j                  xs |}d}| j                  rQt        j
                  | j                        }t        j                         }||z
  j                  }|dk  rd}n|dk  rd}|d   j                  || j                  | j                  | j                  | j                  | j                  xs ||| j                  | j                  | j                  | j                   | j"                  |t%        j&                         j                         d      }	d|d	S )
Nfreshr   expired   expiringr   )rl   r;   r=   r?   rA   rB   rC   rD   rF   rH   rJ   rK   rT   ri   T)r   rT   )r   r   todayru   rC   fromisoformatdaysrs   r;   r=   r?   rA   rB   rD   rF   rH   rJ   rK   r   rt   )
rc   rl   r$   r   expiryrT   edtd	days_leftrv   s
             r&   add_foodr      s   	BJJL""$E&FF 0 01ZZ\"WNN	q=F!^F
W+

		MMMM		++4u<<MM<<lln..0 C  &))r(   z/api/foods/{food_id}food_idc                    t               }|d   j                  |       }|st        dd      |j                         j	                         D ci c]  \  }}|	|| }}}d|v rTt        j                  |d         }|t        j                         z
  j                  }|dk  rd|d<   n|dk  rd	|d<   nd
|d<   |d   j                  | |       t        |d   j                  |             S c c}}w )Nr   rz   u   食材不存在rC   r   r   rT   r   r   r   )r   r#   r   
model_dumpitemsr   r   r   r   r   r    )	r   rc   r$   foodkvpatchr   r   s	            r&   update_foodr      s    	Bg;??7#DC!233"oo/557Ida1=QTIEIm 45$**,&,,	q='E(O!^(E(O%E(OwKw&7()) Js   
C+C+c                 F    t               }|d   j                  |        ddiS )Nr   r   Tr   deleter   r$   s     r&   delete_foodr      s%    	BwKw$<r(   z/api/foods/{food_id}/consumec                 ~    t               }|d   j                  | ddi       t        |d   j                  |             S )Nr   rT   consumed)r   r   r    r#   r   s     r&   consume_foodr      s:    	BwKw: 677())r(   z/api/foods/expiringc                 x   t               }t        j                         }|t        d      z   }t	        |d   j                  d| |j                         gd            }t	        |d   j                  d| gd            }|D cg c]  }t        |       c}|D cg c]  }t        |       c}dS c c}w c c}w )	Nr   r   r   zEfamily_id = ? AND status IN ('fresh','expiring') AND expiry_date <= ?r   r   z$family_id = ? AND status = 'expired')r   r   )r   r   r   r   r   r{   ru   r    )rl   r$   r   soonr   r   r%   s          r&   expiring_foodsr      s    	BJJLE9!$$D7&&O	DNN$%" '  D
 2g;)).	" *  G
 +//Qa/g<VT!W<VWW/<Vs   B2B7z/api/snapshotc                    t               }t        |d   j                  d| g            }t        |      }t	        d |D              }t	        d |D              }i }|D ]#  }|d   xs d}|j                  |d      dz   ||<   % ||||d	S )
Nr   z&family_id = ? AND status != 'consumed'c              3   2   K   | ]  }|d    dk(  sd  yw)rT   r   r>   Nr8   .0fs     r&   	<genexpr>zsnapshot.<locals>.<genexpr>   s     Aq{j'@1A   c              3   2   K   | ]  }|d    dk(  sd  yw)rT   r   r>   Nr8   r   s     r&   r   zsnapshot.<locals>.<genexpr>   s     ?akY&>!?r   r=   r<   r   r>   )totalr   r   
categories)r   r   r{   lensumr#   )	rl   r$   r   r   r   r   r   r   cats	            r&   snapshotr      s    	BG''(PS\R]^_EJEAeAAH?U??GJ 5
m'x$..a014
35  	 r(   z/api/recipes   limitc                 6   t               }t        |d   j                  d| g            }|D cg c]  }|d   	 }}t        |      }t	        ||      }t        |      }g }	|D ]  }
|
d   }|
j                  dg       }|D cg c]	  }||vs| }}|D cg c]  }||v st        ||      s| }}|D cg c]  }||vst        ||      r| }}t        |
      }t        |      |d<   t        |      |d<   ||d<   |r#t        t        |      t        |      z  d	z        nd	|d
<   t        |      dk(  |d<   ||d<   |	j                  |        |	j                  d        |	d t        |d       }	g }|	D ]  }
|j                  |
j                  dd      |
d   |
d   |
j                  dd      |
j                  dg       |
j                  dd      |
j                  dd      |
d
   |
d   |
d   d d |
d   |
d   g d        ||dS c c}w c c}w c c}w c c}w )Nr   0family_id = ? AND status IN ('fresh','expiring')r;   r   r   matched_counttotal_countmissingd   completenessr   can_make	conflictsc                     | d    | d    fS )Nr   r   r8   xs    r&   <lambda>z!suggest_recipes.<locals>.<lambda>  s    :>1B0BC r(   key   rh   descriptionr   r   r   cooking_time
difficultyr>   )rh   r;   r   r   r   r   r   r   r   r   r   r   r   )r   my_ingredients)r   r   r{   setcheck_conflictsr'   r#   match_genericr    r   roundappendsortmax)rl   r   r$   r   r   
food_namesfood_setconflicts_cacher   resultsr%   ingspantryineededmatchedr   rdlight_resultss                      r&   suggest_recipesr     sU   	BG'':	 E &++!F)+J+:H%b*5O!"%GG ~r*!5Qf_!55$TXq(9S1TT$](9-PQS[B\1]]!W!'l?K=9HNU3w<#f+#=#CDTW>W*:);r LLCLDns5!}%GM %%a.!F)AmDTEE.&9EE&"%quu^Q7O%%a0n-1Z=|BQ'/]+

 
	 %
CCK , 6T]s/   H 	H
HH*H4	H>HH
ingredientr   returnc                 @    h dh dd}| |v rt        ||    |z        S y)uQ   通用食材匹配：叶菜类匹配各种绿叶菜，猪肉匹配猪肉制品等>   	   大白菜	   小白菜   油菜   生菜   白菜   芹菜   茼蒿   菠菜   青菜   韭菜	   卷心菜	   娃娃菜	   油麦菜	   空心菜>      猪肉	   五花肉	   猪肉末)	   叶菜类r   F)rb   )r   r   generic_maps      r&   r   r   0  s4     g4K [ K
+h677r(   r   c                     g }t        |      D ]]  \  }}||dz   d D ]M  }t        | d   j                  d||||g            }|s(t        |d         }||g|d<   |j	                  |       O _ |S )u'   检查当前食材之间的相冲关系r>   Nfood_conflictsz:(food_a = ? AND food_b = ?) OR (food_a = ? AND food_b = ?)r   involved)	enumerater   r{   r    r   )r$   r   r   r   abrowr%   s           r&   r   r   :  s    I*% 	$1AaCD! 	$Ar*+66LAq! C QL!"A*  #	$	$ r(   z/api/recipes/{recipe_id}	recipe_idc                 x   t               }|d   j                  |       }|st        dd      t        |      }t	        j
                  |d         |d<   t	        j
                  |d         |d<   t	        j
                  |d         |d<   |j                  d      r%t	        j
                  |j                  dd            ng |d<   |j                  d	      r%t	        j
                  |j                  d	d            ng |d	<   |j                  d
      r%t	        j
                  |j                  d
d            ng |d
<   |S )Nr   rz   u   菜谱不存在r   r   r   r   r   r   r   )r   r#   r   r    r!   r"   )r  r$   r%   s      r&   
get_reciper  L  s   	B
9)$AC!233QAzz!M"23AmAgJ'AgJ

1V9%AfIOPuuUiOjdjj/CT)JKprA?@uu\?Rdjj|T!:;XZAlOCD55CX

155#>?^`AnHr(   z/api/shopping-listc                     t               }t        |d   j                  d| gd            }d|D cg c]  }t        |       c}iS c c}w )Nshopping_listr   zpurchased ASC, created_at DESCr   r   r   rl   r$   r   r%   s       r&   list_shoppingr  ^  sJ    	B?#..Vv.wxDt,!d1g,--,   Ac           
          t               }|d   j                  || j                  | j                  | j                  | j
                  dt        j                         j                         d      }d|iS )Nr
  Frl   r;   r?   rA   rH   ra   ri   rh   )	r   rs   r;   r?   rA   rH   r   rt   ru   )rc   rl   r$   sids       r&   add_shoppingr  d  sc    	B
_

$
$		MM		MMlln..0& C #;r(   z/api/shopping-list/{item_id}item_idc                     t               }|d   j                  | d|j                  i       |d   j                  |       S )Nr
  ra   )r   r   ra   r#   )r  rc   r$   s      r&   update_shoppingr  r  s=    	Bwdnn(EFo""7++r(   c                 F    t               }|d   j                  |        ddiS )Nr
  r   Tr   )r  r$   s     r&   remove_shoppingr  x  s&    	Bw'$<r(   z /api/shopping-list/from-expiringc                    t               }t        j                         }|t        d      z   }t	        |d   j                  d| g            }d}|D ]o  }t	        |d   j                  d| |d   g            }|r)|d   j                  | |d   d	|d
   ddt        j                         j                         d       |d	z  }q d|iS )u'   一键将过期食材加入采购清单r   r   r   z2family_id = ? AND status IN ('expiring','expired')r   r
  z,family_id = ? AND name = ? AND purchased = 0r;   r>   rA   systemFr  added)
r   r   r   r   r   r{   rs   r   rt   ru   )rl   r$   r   r   r   r  r   exists           r&   auto_add_expiring_to_shoppingr  ~  s     
BJJLE9!$$D7&&<	 D E R(33:&	"
  &&&&	&	$"&lln668(  QJE!" Ur(   z	/api/tipsr=   c                 r    t               }t        |      }| r|D cg c]  }|d   | k(  s| }}d|iS c c}w )Nr=   r*   )r   r-   )r=   r$   r*   r,   s       r&   get_tipsr    sC    	BBD=a1Z=H#<==D> >s   44z	/api/scanrJ   c                     | dddS )u0   模拟扫码：返回提示让用户补充信息u!   请补充食材名称和保质期u<   可在包装上查看保质期，或在名称栏手动输入)rJ   message
suggestionr8   )rJ   s    r&   scan_barcoder!    s     6T r(   ):r   r   r   r   r   r   r   r   r   r   r   r   	   西兰花   花菜u   菜花	   西红柿   番茄   黄瓜   青椒   辣椒   茄子   豆角	   四季豆   豇豆   秋葵   苦瓜   冬瓜   南瓜   丝瓜	   西葫芦   萝卜	   胡萝卜   土豆   洋葱   大蒜   生姜   山药   红薯   芋头   藕   竹笋   芦笋   玉米u   葱u   姜u   蒜u   香菜u   薄荷u   罗勒u   紫苏u   芥蓝u   菜心u	   豌豆苗   豆芽u	   韭菜苔u   蒜苔u   蒜苗u   大葱)%   苹果   梨   橙子   橘子   柚子   柠檬   香蕉   葡萄   草莓   蓝莓   樱桃   芒果	   猕猴桃   西瓜	   哈密瓜   桃子   李子   杏   榴莲   荔枝   龙眼   柿子   石榴	   火龙果	   百香果   椰子	   牛油果u   菠萝rY  u   甘蔗u   山竹u   杨梅u   枇杷u   桑葚u	   无花果u   木瓜u   释迦)r      牛肉   羊肉   鸡肉   鸭肉u   鹅肉r      排骨   猪蹄u   猪肝u   猪肚u   猪腰   鸡腿u   鸡胸   鸡翅u   鸡爪u   牛腩u   牛腱u   牛排u   羊排   肉馅u   肉末u   里脊u	   梅花肉   腊肉   腊肠   香肠   培根   火腿	   午餐肉u   肉)   虾   鱼   螃蟹   虾仁u   贝	   三文鱼   带鱼   草鱼   鲈鱼   鱿鱼u   墨鱼u   章鱼u   蛤蜊u   生蚝u   扇贝u   海参u   鲍鱼u   龙虾u	   皮皮虾u   鳕鱼u	   黄花鱼u   鲳鱼u   鳗鱼u   鲫鱼u   鲤鱼u   黑鱼   海带   紫菜)   鸡蛋u   鸭蛋u   鹅蛋u	   鹌鹑蛋   牛奶   酸奶   奶酪   黄油u   奶油u   炼乳u   奶粉	   咸鸭蛋   皮蛋u   蛋)   豆腐   豆皮   豆干r@     腐竹u   豆浆u	   豆腐乳u   千张u   素鸡u   面筋u   纳豆u   豆)    盐   糖   酱油   醋   料酒   蚝油	   豆瓣酱u	   甜面酱u	   番茄酱u	   辣椒酱u	   沙茶酱   味精   鸡精   花椒u   胡椒r(  u   八角u   桂皮u   香叶u   孜然u   咖喱u   淀粉u   生粉u   香油u	   芝麻油u	   老干妈   火锅底料u   泡椒u   剁椒u   豆豉u   榨菜u   酱)u   米u   面u   粉   馒头u   包子   饺子   馄饨   汤圆   粽子   年糕   面条u   挂面u	   方便面   面包u   吐司u   饼干u   麦片u   燕麦   米饭u   面粉u   大米u   小米u   糯米)   蔬菜   水果   肉类   海鲜   蛋奶	   豆制品   调料   主食z/api/classifyc                 l    t         j                         D ]  \  }}|D ]  }|| v s||dc c S   dddS )u   根据食材名自动分类)r=   r   r<   N)CATEGORY_KEYWORDSr   )r;   r   keywordskws       r&   classify_foodr    sN     +002 8X 	8BTz$'B77	88 !T22r(   z/api/weekly-reportc                    t               }t        j                         }|t        |j	                               z
  }|j                         }t        |d   j                  d| |g            }t        d |D              }i }|D ]7  }|d   xs d}	|j                  |	d      |j                  dd      xs dz   ||	<   9 ||j                         t        |d	      t        |      ||D cg c]  }t        |       c}d
S c c}w )u   本周费用统计r   r   z$family_id = ? AND purchase_date >= ?c              3   H   K   | ]  }|j                  d d      xs d  yw)rD   r   N)r#   r   s     r&   r   z weekly_report.<locals>.<genexpr>  s#     61gq!&Q&6s    "r=   r<   r   rD      )
week_startweek_endr   countby_categoryr   )r   r   r   r   weekdayru   r   r{   r   r#   r   r   r    )
rl   r$   r   r  week_start_strr   r   r  r   r   s
             r&   weekly_reportr    s    
BJJLE88J))+NG''.	N# E
 666EK N
m'x&??32aeeGQ6G6L1MCN
 %OO%uaU"#()a$q')  *s   &C=z/api/healthc                  L    dt        j                         j                         dS )Nr   )rT   time)r   rt   ru   r8   r(   r&   healthr    s    HLLN$<$<$>??r(   c                   4    e Zd ZU eed<   dZeed<   dZeed<   y)OrderCreaterecipe_namerI   noteu   匿名requested_byN)rL   rM   rN   rO   rP   r  r  r8   r(   r&   r  r    s    D#N L# r(   r  z/api/ordersc                     t               }t        |d   j                  d| gd            }d|D cg c]  }t        |       c}iS c c}w )Nordersr   zfulfilled ASC, created_at DESCr   r   r  s       r&   list_ordersr    sI    	B8'')Oo'pqD-1tAw-..-r  c           	      @   t               }t        |d   j                  d|| j                  g            }|rt	        dd      |d   j                  || j                  | j                  | j                  dt        j                         j                         d       ddiS )	Nr  z3family_id = ? AND recipe_name = ? AND fulfilled = 0i  u   这道菜已经许愿过了F)rl   r  r  r  	fulfilledri   r   T)r   r   r{   r  r   rs   r  r  r   rt   ru   )rc   rl   r$   existings       r&   create_orderr    s    	BBxL++=	D$$% H C!>??xL''))		lln..0  $<r(   z/api/orders/{order_id}/fulfillorder_idc                 L    t               }|d   j                  | ddi       ddiS )Nr  r  Tr   r   r  r$   s     r&   fulfill_orderr    s,    	BxL;"56$<r(   z/api/orders/{order_id}c                 F    t               }|d   j                  |        ddiS )Nr  r   Tr   r  s     r&   delete_orderr  !  s%    	BxL!$<r(   z/api/shopping-suggestionsc                 B   t               }t        j                         }t        |d   j	                  d| gdd            }t        |d   j	                  d| gdd            }|D cg c]  }t        |       c}|D cg c]  }t        |       c}d	d
S c c}w c c}w )u9   基于已过期食材和点菜记录，推荐采购清单r   z%family_id = ? AND status = 'consumed'zcreated_at DESC
   )r   r   r  zfamily_id = ? AND fulfilled = 0r   uK   以上是家人最近用掉的食材和想吃的菜，采购时可以参考)recently_usedpending_orderstip)r   r   r   r   r{   r    )rl   r$   r   r   r  cos          r&   shopping_suggestionsr  )  s     
BJJLEBwK**/)"" +  H
 "X,)))I;"! *  F
 ,44a$q'4,23q473\ 43s   'B?Brw     rx     ry  rz  Z   rv     r{  <   r|  r      r   r   r   r   r   r   r   r   r   r   r  r   r  r   r   r"  r#  r$  r%  r&  r'  r(  r)  r*  r+  u	   荷兰豆r,  r-  r.  r/  r0  r1  r2  r3  r4  u	   白萝卜r5  r6  r7  r8  r9  r:  r;  r<  r=  r>  r?  u   香菇u   蘑菇u	   金针菇u	   杏鲍菇u   木耳u   银耳r}  r~  r  r@  r  rA  rB  rC  rD  rE  rF  rG  rH  rI  rJ  rK  rL  rM  rN  rO  rP  rQ  rR  rS  rT  rU  rV  rW  rX  rY  rZ  r[  r   r\  r]  r^  r_  r   r`  ra  rb  u	   鸡胸肉rc  r   u	   牛肉末rd  rg  ri  rh  re  rf  rj  rk  rl  rm  rn  u   贝类ro  rp  rq  rr  rs  r  r  r  r  r  r  r  r  r  u   粉条   u   粉丝ru  im  rt  u	   木耳干u	   银耳干r  i  )r  r  r  r  r  r  r  r  r  r  u	   干辣椒z/api/shelf-liferE   rF   c                    |dk(  rdnd}t         j                  |       }|rI|dk(  rt        d|dz        }t        j                         t        |      z   j                         }d||dS t         j                         D ][  \  }}|| v s| |v s|dk(  rt        d|dz        n|}t        j                         t        |      z   j                         }d|||dc S  d	d
dS )u3   根据食材名和储存方式建议保质期天数u   冷冻r   r>   r   r   T)foundr   suggested_date)r  r   r  r   Fu0   未找到建议，请根据包装或经验填写)r  r  )
SHELF_LIFEr#   r   r   r   r   ru   r   )r;   rF   frozen_multr   	suggestedr   valds           r&   suggest_shelf_lifer  e  s      8+"K>>$Dhr4!8$DZZ\I4$88CCE	tyII$$& [S$;$#+$+x$7Ba SA	q(99DDFI!1	VYZZ	[
 'YZZr(   )   斤r@   u   把   颗u   根)r  r@   r  u   串)r     块   条kg)r  u   只r  r  )r@      盒   瓶r  )r  r  r     包)r  r     袋r  r  )r  r  r  r  r@   )r@   r  r  r  r  )	r  r  r  r  r  r  r  r  r<   z
/api/unitsc                 :    t         j                  | g d      }d|iS )u$   根据食材分类建议常用单位)r@   r  r  units)UNIT_MAPr#   )r=   r  s     r&   suggest_unitsr    s      LL#89EUr(   z/api/daily-recommendc                    t               }t        |d   j                  d| g            }|D cg c]  }|d   	 }}t        |      }t	        ||      }t        |      }g }|D ]  }	|	d   |	j                  dg       }
D cg c]	  }||
vs| }}|D cg c]  }||v st        ||      s| }}|rt        |      t        |      z  nd}t        fd|D              }|dz  |rd	nd
z   }|D cg c]  }||vst        ||      r| }}t        |	      }|d<   |
|d<   |	j                  dg       |d<   t        |dz        |d<   ||d<   t        |      d
k(  |d<   ||d<   ||d<   |j                  |        |j                  d        g }|D ]o  }	|j                  |	j                  dd
      |	d   |	d   |	j                  dd      |	j                  dg       |	j                  dd
      |	d   |	d   |	d   d d d	       q t               }g }|D ]>  }	|	d   |vr%|j                  |	d          |j                  |	       t        |      dk\  s> n |ddS c c}w c c}w c c}w c c}w )Nr   r   r;   r   r   r>   c              3   @   K   | ]  }|d    dk(  xr |d   v   yw)rT   r   r;   Nr8   )r   r   r   s     r&   r   z"daily_recommend.<locals>.<genexpr>  s+     Zq1X;*4J6d9JJZs   r   r  r   r   r   r   r   scorer   c                     | d    S )Nr  r8   r   s    r&   r   z!daily_recommend.<locals>.<lambda>  s    qzk r(   r   rh   r   r   r   r   r   )	rh   r;   r   r   r   r   r   r   r   r   u!   快过期的食材优先推荐哦)recommendationsr  )r   r   r{   r   r   r'   r#   r   r   anyr    r   r   r   add)rl   r$   r   r   r   r   r   r   scoredr%   r   r   r   r   ratiohas_expiringr  r   rdicttop_allseentopr   s                         @r&   daily_recommendr    s   	BG'':	 E &++!F)+J+:H%b*5O!"%GF ~r*!5Qf_!55$TXq(9S1TT.4Gs6{*!ZTYZZ\rq9$](9-PQS[B\1]]Q#m &nfb)f %eck 2n"iLA-jg,ke'* KK)K*G %%a.!F)AmDTEE.&9EE&"%quu^Q7On-1Z=|BQ'
 	 5D
C V9D HHQvYJJqMs8q=  #+NOO_ , 6T
 ^s/   I	III$,I$1	I);I)I)z/csscss)	directory)r;   z/jsjsz/iconsicons)Request)HTMLResponsehttprequestc                 .   K    ||        d {   S 7 wr7   r8   )r  	call_nexts     r&   auth_middlewarer    s     w''''s   z/manifest.jsonc                  \    t        t        j                  j                  t        d            S )Nzmanifest.jsonr   ospathjoinF_DIRr8   r(   r&   manifestr    s    BGGLL@AAr(   z/sw.jsc                  `    t        t        j                  j                  t        d      d      S )Nzsw.jszapplication/javascript)
media_typer  r8   r(   r&   swr	    s    BGGLL8E]^^r(   z/{full_path:path}	full_pathc                     | j                  d      rt        d      t        t        j                  j                  t        d            S )Nzapi/rz   z
index.html)
startswithr   r   r  r  r  r  )r
  s    r&   spa_fallbackr    s7     '$$BGGLL=>>r(   r7   )r   )rI   )g__doc__fastapir   r   r   fastapi.middleware.corsr   fastapi.staticfilesr   fastapi.responsesr   pydanticr	   r
   r   r   r   r!   rp   r  databaser   r   	seed_datar   	functoolsr   gzipgzip_moduler   r+   r'   r-   appadd_middlewarer  r  dirname__file__r  on_eventr5   r:   rS   rV   rY   r^   r`   postrx   r}   putrO   r   r#   r   r   r   intr   r   r   r   r   r   r   r   rb   r   r   r   r  r  r  r  r  r  r  r!  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  isdirmountr  r  
middlewarer  r  r	  r  r8   r(   r&   <module>r$     s   ( 1 1 2 + * % . .   	 $   *    (':   >#se\_[`  a 	RWW__X.jA i   9  
9 Y  
3 3  3 
9j 9 9 	)*.3Cj S   +
 	"#H# H $H .# .s . . ,05c
 *: *# * *> 	 * *J * !*$ "#  $
 
()*# * ** 	Xc X  X&   ( +Ds +D3 +D +DZc S T D T $ 	#$#  %" 	.S . .
 
6;Cj |     	'(,S , , ),
 *+S  ,
 
,-38: S  .@ s   +#  Rz2[)~ht' . #Cj 3 3 3 	S  : @ @
!) !
 /3 / /
 -5:3Z { s  & 	)*C  +
 $%3  & 	$%C  &,#a#2#'#-5r#;CR#ITVX#Zbdf# 	# a	# "*1	# /7	# <DQ	# ITUV	# Ydef	#
 a#
 a#
 "-b#
 3;A#
 @H#
 MXYZ#
 ]efg# # a# "*1# /7# <DQ# IQRS# V^_`# #  # %-a# 2:1# ?G# LTUW# Zbce# a# a# "*2# 0;B# ALR# b# B# !)"# /7# =Eb# KSTV# Yabd# 
1# # $Q# )1!# a# 1# *1# /:1# ?G# LTUV# a# 1# '# ,4Q# 9A"# b# # &r# ,4R# :B2# HPQS# a# 1# '# ,4Q# 9A!# FNq# S^_`#  a!#  b!#  #+A!#  08!#  =B1!#  GOPQ!#  T\]^!#  aijk!#" a##" 2##"  +B##" 1<R##" BJ2##" P[\]##$ %#( a)#( 1)#( ')#( ,4Q)#( 9A!)#* +#* a+#* "*1+#* /7+#* <G+#* LTUV+#, -#,  -#, %-a-#. b/#. B/#. !)"/#. /7/#. =Eb/#0 1#4 
15#4 Q5#4 !!5#4 &.q5#4 3;A5#4 @KA5#6 a7#6 17#6 '7#6 ,4Q7#: a;#: 1;#: ';#: ,4Q;#: 9A!;#: FNq;#< b=#< B=#< !)"=#@ cA#@ SA#@ #+CA#@ 2:3A#@ ALSA#@ S^_bA#B CC#B '*SPS_bscSCVYE#
J 	#(:eHo [S [ [ [( 2*))*-001
 "'* C   	 5Ps 5P !5Pr 77==IIfkBGGLL,FGeITIIe[277<<t+DEDIQIIhbggll5'.JKRYIZ
  .^^F(w ( ( 	WWB B 	WWX_ _ 	WW !? ? "?1 r(   