diff --git a/main.ipynb b/main.ipynb
index 962826f..50cf559 100644
--- a/main.ipynb
+++ b/main.ipynb
@@ -102,6 +102,8 @@
"import email_task as email_task\n",
"from pydantic import EmailStr\n",
"import secrets\n",
+ "from pymongo import ReturnDocument\n",
+ "\n",
"logging.basicConfig(level=logging.INFO)\n",
"logger = logging.getLogger(__name__)"
]
@@ -531,35 +533,109 @@
"source": [
"@app.get(\"/cron/5\")\n",
"async def cron_every_five_min(token: str = Security(get_token)):\n",
- " exhibition = await exhibition_collection.find_one_and_update(\n",
- " {\"prod\": PROD, \"status\": 0, \"date_finish\": {\"$lt\": datetime.now(timezone.utc) + timedelta(hours=3)}},\n",
- " {\"$set\": {\"status\": 2}}, return_document=False)\n",
- " if exhibition:\n",
- " tasks = []\n",
- " async for doc in delivery_collection.find({'exhibition_id': str(exhibition.get('_id')), 'status': 'Получено'},{'_id':0}):\n",
- " new_doc = {**doc, 'delivery_type':'', 'track':'', 'status': 'Возврат художнику', 'receiver': doc.get('sender'), 'sender': doc.get('receiver')}\n",
- " tasks.append(delivery_collection.update_one({\"prod\": PROD, 'status': 'Возврат художнику', 'post_id': doc.get('post_id'),'exhibition_id': doc.get('exhibition_id')},{'$set':new_doc},upsert=True))\n",
- " asyncio.gather(*tasks)\n",
- " else:\n",
- " exhibition = await exhibition_collection.find_one_and_update(\n",
- " {\"prod\": PROD, \"datetime_start\": {\"$lt\": datetime.now(timezone.utc) + timedelta(hours=3)}},\n",
- " {\"$set\": {\"status\": 1}}, return_document=False)\n",
- " if exhibition:\n",
- " if exhibition.get('vk') == [] or exhibition.get('tg') == []:\n",
- " await service_social_exhibition(exhibition)\n",
- " tasks = []\n",
- " async for doc in delivery_collection.find({'exhibition_id': str(exhibition.get('_id')), 'status': 'Получено'},{'_id':0}):\n",
- " post = await posts_collection.find_one({'_id': ObjectId(doc.get('post_id'))})\n",
- " new_doc = {**doc, 'exhibition': exhibition, 'status': 'На выставке', 'user': post.get('user'), 'post': post}\n",
- " tasks.append(delivery_collection.update_one({\"prod\": PROD, 'status': 'На выставке', 'post_id': doc.get('post_id'),'exhibition_id': doc.get('exhibition_id')},{'$set':new_doc},upsert=True))\n",
- " tasks.append(notify_in_profile(NotifyItem(\n",
- " user_id=str(post.get('user',{}).get('_id')),\n",
- " type_item='exhibtion',\n",
- " title=f'Выставка {doc.get('exhibition',{}).get('title')}',\n",
- " link=f'/profile/exhibition',\n",
- " text=f'Ваши картины на выставке'\n",
- " )))\n",
- " asyncio.gather(*tasks)\n",
+ " now = datetime.now(timezone.utc)\n",
+ " limit_dt = now + timedelta(hours=3)\n",
+ "\n",
+ " prod_filter = {\"prod\": PROD}\n",
+ "\n",
+ " # 1) If there's an exhibition that is going to be finished soon -> move deliveries to \"Возврат художнику\"\n",
+ " exhibition_finished = await exhibition_collection.find_one_and_update(\n",
+ " {**prod_filter, \"date_finish\": {\"$lt\": limit_dt}},\n",
+ " {\"$set\": {\"status\": 2}},\n",
+ " return_document=ReturnDocument.AFTER,\n",
+ " )\n",
+ " if exhibition_finished:\n",
+ " exhibition_id_str = str(exhibition_finished[\"_id\"])\n",
+ "\n",
+ " async for doc in delivery_collection.find(\n",
+ " {\"exhibition_id\": exhibition_id_str, \"status\": \"Получено\", **prod_filter},\n",
+ " {\"_id\": 0},\n",
+ " ):\n",
+ " sender = doc.get(\"sender\")\n",
+ " post_id = doc.get(\"post_id\")\n",
+ " exhibition_id = doc.get(\"exhibition_id\")\n",
+ "\n",
+ " new_doc = {\n",
+ " **doc,\n",
+ " \"delivery_type\": \"\",\n",
+ " \"track\": \"\",\n",
+ " \"status\": \"Возврат художнику\",\n",
+ " \"receiver\": sender,\n",
+ " \"sender\": doc.get(\"receiver\"),\n",
+ " }\n",
+ "\n",
+ " await delivery_collection.update_one(\n",
+ " {**prod_filter, \"status\": \"Возврат художнику\", \"post_id\": post_id, \"exhibition_id\": exhibition_id},\n",
+ " {\"$set\": new_doc},\n",
+ " upsert=True,\n",
+ " )\n",
+ "\n",
+ " return {}\n",
+ " tasks = []\n",
+ " async for exhibition_to_start in exhibition_collection.find(\n",
+ " {**prod_filter}\n",
+ " ):\n",
+ " exhibition_id_str = str(exhibition_to_start[\"_id\"])\n",
+ " exhibition_title = exhibition_to_start.get(\"title\")\n",
+ "\n",
+ " notified_user_id = None\n",
+ " notified_post_user_id = None\n",
+ "\n",
+ " async for doc in delivery_collection.find(\n",
+ " {\"exhibition_id\": exhibition_id_str, \"status\": \"Получено\", **prod_filter},\n",
+ " {\"_id\": 0},\n",
+ " ):\n",
+ " post_id = doc.get(\"post_id\")\n",
+ " exhibition_id = doc.get(\"exhibition_id\")\n",
+ "\n",
+ " post = await posts_collection.find_one({\"_id\": ObjectId(post_id)})\n",
+ "\n",
+ " # Use user from post; keep them even if multiple deliveries exist\n",
+ " if post and post.get(\"user\") and not notified_post_user_id:\n",
+ " notified_post_user_id = str(post[\"user\"].get(\"_id\"))\n",
+ "\n",
+ " new_doc = {\n",
+ " **doc,\n",
+ " \"exhibition\": exhibition_to_start,\n",
+ " \"status\": \"На выставке\",\n",
+ " \"user\": post.get(\"user\") if post else None,\n",
+ " \"post\": post,\n",
+ " **prod_filter\n",
+ " }\n",
+ " print(new_doc)\n",
+ "\n",
+ " tasks.append(delivery_collection.update_one(\n",
+ " {**prod_filter, \"status\": \"На выставке\", \"post_id\": post_id, \"exhibition_id\": exhibition_id},\n",
+ " {\"$set\": new_doc},\n",
+ " upsert=True,\n",
+ " ))\n",
+ "\n",
+ " # NOTE: your original code used `post` from inside the loop (could be last item / even undefined).\n",
+ " # This version notifies once using the first found post user.\n",
+ " if notified_post_user_id:\n",
+ " await notify_in_profile(\n",
+ " NotifyItem(\n",
+ " user_id=notified_post_user_id,\n",
+ " type_item=\"exhibtion\",\n",
+ " title=f\"Выставка {exhibition_title}\",\n",
+ " link=\"/profile/exhibition\",\n",
+ " text=\"Ваши картины на выставке\",\n",
+ " )\n",
+ " )\n",
+ " asyncio.gather(tasks)\n",
+ " # 2) Otherwise find an exhibition that should start soon -> move deliveries to \"На выставке\"\n",
+ " exhibition_to_start = await exhibition_collection.find_one_and_update(\n",
+ " {**prod_filter, \"status\": 0, \"datetime_start\": {\"$lt\": limit_dt}},\n",
+ " {\"$set\": {\"status\": 1}},\n",
+ " return_document=ReturnDocument.AFTER,\n",
+ " )\n",
+ "\n",
+ " if not exhibition_to_start:\n",
+ " return {}\n",
+ "\n",
+ " # If vk/tg arrays are empty -> send social invitation\n",
+ " if exhibition_to_start.get(\"vk\") == [] or exhibition_to_start.get(\"tg\") == []:\n",
+ " await service_social_exhibition(bson_clear(exhibition_to_start))\n",
" return {}\n"
]
},
@@ -2946,7 +3022,7 @@
{
"cell_type": "code",
"execution_count": null,
- "id": "44ea690c",
+ "id": "aabb40cb",
"metadata": {},
"outputs": [],
"source": [
@@ -2956,9 +3032,16 @@
" current_user: Dict = Security(get_current_user, scopes=[\"create:posts\"])\n",
"):\n",
" await posts_collection.delete_one({'_id': ObjectId(post_id), 'user._id': current_user.get('_id')})\n",
- " return {'ok': True} \n",
- " \n",
- "\n",
+ " return {'ok': True} \n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d38cafed",
+ "metadata": {},
+ "outputs": [],
+ "source": [
"@app.post(\"/api/post\", response_model=dict)\n",
"async def post_painting(\n",
" background_tasks: BackgroundTasks,\n",
@@ -3013,9 +3096,16 @@
" return doc_out\n",
" except Exception as e:\n",
" print(e)\n",
- " raise HTTPException(status_code=500, detail=str(e))\n",
- "\n",
- "\n",
+ " raise HTTPException(status_code=500, detail=str(e))\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "44ea690c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
"@app.put(\"/api/post/{post_id}\", response_model=dict)\n",
"async def put_painting(\n",
" background_tasks: BackgroundTasks,\n",
@@ -3067,7 +3157,6 @@
" if res is None:\n",
" raise HTTPException(status_code=404, detail=\"Картина не найдена\")\n",
"\n",
- "\n",
" doc_out = {\n",
" \"_id\":post_id,\n",
" \"id\":post_id,\n",
@@ -3075,12 +3164,18 @@
" }\n",
" doc_out = bson_clear(doc_out)\n",
" \n",
- "\n",
" background_tasks.add_task(update_social_post, doc_out)\n",
" background_tasks.add_task(replace_new_medias, bson_clear(doc_out), links_media)\n",
- " return doc_out\n",
- "\n",
- "\n",
+ " return doc_out\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3f18f6a6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
"@app.post(\"/api/posts/media\", response_model=dict)\n",
"async def post_media(\n",
" medias: Optional[List[UploadFile]] = File(None),\n",
@@ -3096,7 +3191,7 @@
{
"cell_type": "code",
"execution_count": null,
- "id": "abe852d2",
+ "id": "9c463130",
"metadata": {},
"outputs": [],
"source": [
@@ -3136,9 +3231,16 @@
" background_tasks.add_task(replace_news_medias, doc_out)\n",
" return doc_out\n",
" except Exception as e:\n",
- " raise HTTPException(status_code=500, detail=str(e))\n",
- "\n",
- "\n",
+ " raise HTTPException(status_code=500, detail=str(e))\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "de77638d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
"@app.put(\"/api/news/{news_id}\", response_model=dict)\n",
"async def put_news(\n",
" background_tasks: BackgroundTasks,\n",
@@ -3177,9 +3279,16 @@
" **{k: v for k, v in update_doc.items()}\n",
" }\n",
" doc_out = bson_clear(doc_out)\n",
- " return doc_out\n",
- "\n",
- "\n",
+ " return doc_out\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "abe852d2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
"@app.post(\"/api/news/media\", response_model=dict)\n",
"async def news_media(\n",
" medias: Optional[List[UploadFile]] = File(None),\n",
diff --git a/main.py b/main.py
index 4c2f986..0be0c80 100644
--- a/main.py
+++ b/main.py
@@ -41,6 +41,8 @@ from templates import templates
import email_task as email_task
from pydantic import EmailStr
import secrets
+from pymongo import ReturnDocument
+
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@@ -335,35 +337,109 @@ async def cron_every_day(token: str = Security(get_token)):
@app.get("/cron/5")
async def cron_every_five_min(token: str = Security(get_token)):
- exhibition = await exhibition_collection.find_one_and_update(
- {"prod": PROD, "status": 0, "date_finish": {"$lt": datetime.now(timezone.utc) + timedelta(hours=3)}},
- {"$set": {"status": 2}}, return_document=False)
- if exhibition:
- tasks = []
- async for doc in delivery_collection.find({'exhibition_id': str(exhibition.get('_id')), 'status': 'Получено'},{'_id':0}):
- new_doc = {**doc, 'delivery_type':'', 'track':'', 'status': 'Возврат художнику', 'receiver': doc.get('sender'), 'sender': doc.get('receiver')}
- tasks.append(delivery_collection.update_one({"prod": PROD, 'status': 'Возврат художнику', 'post_id': doc.get('post_id'),'exhibition_id': doc.get('exhibition_id')},{'$set':new_doc},upsert=True))
- asyncio.gather(*tasks)
- else:
- exhibition = await exhibition_collection.find_one_and_update(
- {"prod": PROD, "datetime_start": {"$lt": datetime.now(timezone.utc) + timedelta(hours=3)}},
- {"$set": {"status": 1}}, return_document=False)
- if exhibition:
- if exhibition.get('vk') == [] or exhibition.get('tg') == []:
- await service_social_exhibition(exhibition)
- tasks = []
- async for doc in delivery_collection.find({'exhibition_id': str(exhibition.get('_id')), 'status': 'Получено'},{'_id':0}):
- post = await posts_collection.find_one({'_id': ObjectId(doc.get('post_id'))})
- new_doc = {**doc, 'exhibition': exhibition, 'status': 'На выставке', 'user': post.get('user'), 'post': post}
- tasks.append(delivery_collection.update_one({"prod": PROD, 'status': 'На выставке', 'post_id': doc.get('post_id'),'exhibition_id': doc.get('exhibition_id')},{'$set':new_doc},upsert=True))
- tasks.append(notify_in_profile(NotifyItem(
- user_id=str(post.get('user',{}).get('_id')),
- type_item='exhibtion',
- title=f'Выставка {doc.get('exhibition',{}).get('title')}',
- link=f'/profile/exhibition',
- text=f'Ваши картины на выставке'
- )))
- asyncio.gather(*tasks)
+ now = datetime.now(timezone.utc)
+ limit_dt = now + timedelta(hours=3)
+
+ prod_filter = {"prod": PROD}
+
+ # 1) If there's an exhibition that is going to be finished soon -> move deliveries to "Возврат художнику"
+ exhibition_finished = await exhibition_collection.find_one_and_update(
+ {**prod_filter, "date_finish": {"$lt": limit_dt}},
+ {"$set": {"status": 2}},
+ return_document=ReturnDocument.AFTER,
+ )
+ if exhibition_finished:
+ exhibition_id_str = str(exhibition_finished["_id"])
+
+ async for doc in delivery_collection.find(
+ {"exhibition_id": exhibition_id_str, "status": "Получено", **prod_filter},
+ {"_id": 0},
+ ):
+ sender = doc.get("sender")
+ post_id = doc.get("post_id")
+ exhibition_id = doc.get("exhibition_id")
+
+ new_doc = {
+ **doc,
+ "delivery_type": "",
+ "track": "",
+ "status": "Возврат художнику",
+ "receiver": sender,
+ "sender": doc.get("receiver"),
+ }
+
+ await delivery_collection.update_one(
+ {**prod_filter, "status": "Возврат художнику", "post_id": post_id, "exhibition_id": exhibition_id},
+ {"$set": new_doc},
+ upsert=True,
+ )
+
+ return {}
+ tasks = []
+ async for exhibition_to_start in exhibition_collection.find(
+ {**prod_filter}
+ ):
+ exhibition_id_str = str(exhibition_to_start["_id"])
+ exhibition_title = exhibition_to_start.get("title")
+
+ notified_user_id = None
+ notified_post_user_id = None
+
+ async for doc in delivery_collection.find(
+ {"exhibition_id": exhibition_id_str, "status": "Получено", **prod_filter},
+ {"_id": 0},
+ ):
+ post_id = doc.get("post_id")
+ exhibition_id = doc.get("exhibition_id")
+
+ post = await posts_collection.find_one({"_id": ObjectId(post_id)})
+
+ # Use user from post; keep them even if multiple deliveries exist
+ if post and post.get("user") and not notified_post_user_id:
+ notified_post_user_id = str(post["user"].get("_id"))
+
+ new_doc = {
+ **doc,
+ "exhibition": exhibition_to_start,
+ "status": "На выставке",
+ "user": post.get("user") if post else None,
+ "post": post,
+ **prod_filter
+ }
+ print(new_doc)
+
+ tasks.append(delivery_collection.update_one(
+ {**prod_filter, "status": "На выставке", "post_id": post_id, "exhibition_id": exhibition_id},
+ {"$set": new_doc},
+ upsert=True,
+ ))
+
+ # NOTE: your original code used `post` from inside the loop (could be last item / even undefined).
+ # This version notifies once using the first found post user.
+ if notified_post_user_id:
+ await notify_in_profile(
+ NotifyItem(
+ user_id=notified_post_user_id,
+ type_item="exhibtion",
+ title=f"Выставка {exhibition_title}",
+ link="/profile/exhibition",
+ text="Ваши картины на выставке",
+ )
+ )
+ asyncio.gather(tasks)
+ # 2) Otherwise find an exhibition that should start soon -> move deliveries to "На выставке"
+ exhibition_to_start = await exhibition_collection.find_one_and_update(
+ {**prod_filter, "status": 0, "datetime_start": {"$lt": limit_dt}},
+ {"$set": {"status": 1}},
+ return_document=ReturnDocument.AFTER,
+ )
+
+ if not exhibition_to_start:
+ return {}
+
+ # If vk/tg arrays are empty -> send social invitation
+ if exhibition_to_start.get("vk") == [] or exhibition_to_start.get("tg") == []:
+ await service_social_exhibition(bson_clear(exhibition_to_start))
return {}
@@ -2216,7 +2292,7 @@ async def post_delete(
):
await posts_collection.delete_one({'_id': ObjectId(post_id), 'user._id': current_user.get('_id')})
return {'ok': True}
-
+
@app.post("/api/post", response_model=dict)
async def post_painting(
@@ -2326,7 +2402,6 @@ async def put_painting(
if res is None:
raise HTTPException(status_code=404, detail="Картина не найдена")
-
doc_out = {
"_id":post_id,
"id":post_id,
@@ -2334,7 +2409,6 @@ async def put_painting(
}
doc_out = bson_clear(doc_out)
-
background_tasks.add_task(update_social_post, doc_out)
background_tasks.add_task(replace_new_medias, bson_clear(doc_out), links_media)
return doc_out
diff --git a/templates/exhibition/index.html b/templates/exhibition/index.html
index 33b7fb9..1ede15d 100644
--- a/templates/exhibition/index.html
+++ b/templates/exhibition/index.html
@@ -43,11 +43,14 @@
Место проведения: {{ exhibition.get('place') }} Картины: {{posts|length}}{{exhibition.title}}
+
+ Релиз выставки
+
Место проведения: {{ exhibition.get('place') }}