expiring subs
Browse files- App/Android/Android.py +29 -11
- App/Payments/PaymentsRoutes.py +11 -4
- App/Payments/Schema.py +10 -6
- App/Payments/utils.py +11 -0
- App/Subscriptions/background_tasks.py +35 -0
- App/Templates/Templates.py +9 -4
- App/Users/Schema.py +4 -0
- App/Users/UserRoutes.py +26 -2
- App/app.py +14 -0
App/Android/Android.py
CHANGED
@@ -46,7 +46,7 @@ class AndroidClient:
|
|
46 |
@require_base_url
|
47 |
async def logout_user(self, phone: str) -> APIResponse:
|
48 |
"""Logout a user by phone."""
|
49 |
-
response = await self.client.post(f"/logout/{phone}")
|
50 |
return APIResponse(**response.json())
|
51 |
|
52 |
@require_base_url
|
@@ -67,7 +67,7 @@ class AndroidClient:
|
|
67 |
@require_base_url
|
68 |
async def get_active_users(self) -> APIResponse:
|
69 |
"""Retrieve all active users."""
|
70 |
-
response = await self.client.get("/users/active")
|
71 |
print(response.json())
|
72 |
return APIResponse(**response.json())
|
73 |
|
@@ -76,7 +76,7 @@ class AndroidClient:
|
|
76 |
"""Enable or disable a user."""
|
77 |
path = f"/users/{phone_number}/remove-session"
|
78 |
try:
|
79 |
-
response = await self.client.post(path, json=None)
|
80 |
return APIResponse(**response.json())
|
81 |
except:
|
82 |
pass
|
@@ -85,7 +85,7 @@ class AndroidClient:
|
|
85 |
async def _disable_request(self, request: SetUserStatusRequest) -> APIResponse:
|
86 |
"""disable a user."""
|
87 |
path = f"/users/{request.phone}/disable"
|
88 |
-
response = await self.client.post(path, json=None)
|
89 |
|
90 |
return APIResponse(**response.json())
|
91 |
|
@@ -93,20 +93,20 @@ class AndroidClient:
|
|
93 |
async def enable_user(self, request: SetUserStatusRequest) -> APIResponse:
|
94 |
"""Enable or disable a user."""
|
95 |
path = f"/users/{request.phone}/enable"
|
96 |
-
response = await self.client.post(path, json=None)
|
97 |
return APIResponse(**response.json())
|
98 |
|
99 |
@require_base_url
|
100 |
async def get_users_list(self) -> APIResponse:
|
101 |
"""Retrieve all users (active and inactive)."""
|
102 |
-
response = await self.client.get("/users")
|
103 |
return APIResponse(**response.json())
|
104 |
|
105 |
@require_base_url
|
106 |
async def get_user_stats(self, phone: Optional[str] = None) -> APIResponse:
|
107 |
"""Retrieve user statistics for a specific user or all users."""
|
108 |
url = f"/user/stats/{phone}" if phone else "/user/stats"
|
109 |
-
response = await self.client.get(url)
|
110 |
return APIResponse(**response.json())
|
111 |
|
112 |
@require_base_url
|
@@ -126,15 +126,33 @@ class AndroidClient:
|
|
126 |
"phoneNumbers": phone_numbers,
|
127 |
"event": event,
|
128 |
}
|
129 |
-
|
130 |
-
response = await self.client.post("/send_message", json=data)
|
131 |
x = response.content
|
132 |
-
print(
|
133 |
-
if response.status_code
|
134 |
return response
|
135 |
else:
|
136 |
raise Exception(f"Error sending message : {x}")
|
137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
async def close(self):
|
139 |
"""Close the client session."""
|
140 |
await self.client.aclose()
|
|
|
46 |
@require_base_url
|
47 |
async def logout_user(self, phone: str) -> APIResponse:
|
48 |
"""Logout a user by phone."""
|
49 |
+
response = await self.client.post(f"/logout/{phone}", timeout=60)
|
50 |
return APIResponse(**response.json())
|
51 |
|
52 |
@require_base_url
|
|
|
67 |
@require_base_url
|
68 |
async def get_active_users(self) -> APIResponse:
|
69 |
"""Retrieve all active users."""
|
70 |
+
response = await self.client.get("/users/active", timeout=60)
|
71 |
print(response.json())
|
72 |
return APIResponse(**response.json())
|
73 |
|
|
|
76 |
"""Enable or disable a user."""
|
77 |
path = f"/users/{phone_number}/remove-session"
|
78 |
try:
|
79 |
+
response = await self.client.post(path, json=None, timeout=60)
|
80 |
return APIResponse(**response.json())
|
81 |
except:
|
82 |
pass
|
|
|
85 |
async def _disable_request(self, request: SetUserStatusRequest) -> APIResponse:
|
86 |
"""disable a user."""
|
87 |
path = f"/users/{request.phone}/disable"
|
88 |
+
response = await self.client.post(path, json=None, timeout=60)
|
89 |
|
90 |
return APIResponse(**response.json())
|
91 |
|
|
|
93 |
async def enable_user(self, request: SetUserStatusRequest) -> APIResponse:
|
94 |
"""Enable or disable a user."""
|
95 |
path = f"/users/{request.phone}/enable"
|
96 |
+
response = await self.client.post(path, json=None, timeout=60)
|
97 |
return APIResponse(**response.json())
|
98 |
|
99 |
@require_base_url
|
100 |
async def get_users_list(self) -> APIResponse:
|
101 |
"""Retrieve all users (active and inactive)."""
|
102 |
+
response = await self.client.get("/users", timeout=60)
|
103 |
return APIResponse(**response.json())
|
104 |
|
105 |
@require_base_url
|
106 |
async def get_user_stats(self, phone: Optional[str] = None) -> APIResponse:
|
107 |
"""Retrieve user statistics for a specific user or all users."""
|
108 |
url = f"/user/stats/{phone}" if phone else "/user/stats"
|
109 |
+
response = await self.client.get(url, timeout=60)
|
110 |
return APIResponse(**response.json())
|
111 |
|
112 |
@require_base_url
|
|
|
126 |
"phoneNumbers": phone_numbers,
|
127 |
"event": event,
|
128 |
}
|
129 |
+
response = await self.client.post("/send_message", json=data, timeout=60)
|
|
|
130 |
x = response.content
|
131 |
+
print(response.status_code)
|
132 |
+
if response.status_code >= 200 or response.status_code <= 300:
|
133 |
return response
|
134 |
else:
|
135 |
raise Exception(f"Error sending message : {x}")
|
136 |
|
137 |
+
@require_base_url
|
138 |
+
async def change_password(self, phone: str, new_password: str) -> APIResponse:
|
139 |
+
"""
|
140 |
+
Change the password for a user.
|
141 |
+
Args:
|
142 |
+
phone (str): The phone number of the user.
|
143 |
+
new_password (str): The new password for the user.
|
144 |
+
Returns:
|
145 |
+
APIResponse: The response from the API.
|
146 |
+
"""
|
147 |
+
path = f"/users/{phone}/change-password"
|
148 |
+
payload = {"new_password": new_password}
|
149 |
+
|
150 |
+
try:
|
151 |
+
response = await self.client.post(path, json=payload, timeout=60)
|
152 |
+
return APIResponse(**response.json())
|
153 |
+
except Exception as e:
|
154 |
+
raise Exception(f"Error changing password: {e}")
|
155 |
+
|
156 |
async def close(self):
|
157 |
"""Close the client session."""
|
158 |
await self.client.aclose()
|
App/Payments/PaymentsRoutes.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
from fastapi import APIRouter, HTTPException, status, Query, Depends
|
2 |
from typing import List
|
3 |
-
from datetime import datetime
|
4 |
from .Model import Payment
|
5 |
from App.Users.Model import User
|
6 |
from App.Plans.Model import Plan
|
@@ -13,6 +13,7 @@ from .Schema import (
|
|
13 |
PaymentListResponse,
|
14 |
)
|
15 |
from .Schema import PaymentMethod
|
|
|
16 |
from App.Users.dependencies import (
|
17 |
get_current_active_user,
|
18 |
UserType,
|
@@ -40,7 +41,7 @@ async def create_payment(request: CreatePaymentRequest, internal: bool = False):
|
|
40 |
raise HTTPException(
|
41 |
status_code=status.HTTP_404_NOT_FOUND, detail="Plan not found"
|
42 |
)
|
43 |
-
|
44 |
payment = await Payment.get_or_none(transaction_id=request.transaction_id)
|
45 |
|
46 |
if payment:
|
@@ -166,8 +167,14 @@ async def link_user_to_payment(payment_id: str, request: UpdatePaymentUserReques
|
|
166 |
|
167 |
@payment_router.get("/payments/date-range", response_model=PaymentListResponse)
|
168 |
async def get_payments_by_date_range(
|
169 |
-
start_date: datetime = Query(
|
170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
):
|
172 |
if start_date > end_date:
|
173 |
raise HTTPException(
|
|
|
1 |
from fastapi import APIRouter, HTTPException, status, Query, Depends
|
2 |
from typing import List
|
3 |
+
from datetime import datetime, timedelta
|
4 |
from .Model import Payment
|
5 |
from App.Users.Model import User
|
6 |
from App.Plans.Model import Plan
|
|
|
13 |
PaymentListResponse,
|
14 |
)
|
15 |
from .Schema import PaymentMethod
|
16 |
+
from .utils import get_current_month_range
|
17 |
from App.Users.dependencies import (
|
18 |
get_current_active_user,
|
19 |
UserType,
|
|
|
41 |
raise HTTPException(
|
42 |
status_code=status.HTTP_404_NOT_FOUND, detail="Plan not found"
|
43 |
)
|
44 |
+
print(request.transaction_id)
|
45 |
payment = await Payment.get_or_none(transaction_id=request.transaction_id)
|
46 |
|
47 |
if payment:
|
|
|
167 |
|
168 |
@payment_router.get("/payments/date-range", response_model=PaymentListResponse)
|
169 |
async def get_payments_by_date_range(
|
170 |
+
start_date: datetime = Query(
|
171 |
+
default_factory=lambda: get_current_month_range()[0],
|
172 |
+
description="Start date in ISO format",
|
173 |
+
),
|
174 |
+
end_date: datetime = Query(
|
175 |
+
default_factory=lambda: get_current_month_range()[1],
|
176 |
+
description="End date in ISO format",
|
177 |
+
),
|
178 |
):
|
179 |
if start_date > end_date:
|
180 |
raise HTTPException(
|
App/Payments/Schema.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1 |
from pydantic import BaseModel, Field
|
2 |
-
from typing import Optional, List
|
3 |
from decimal import Decimal
|
4 |
from datetime import datetime
|
|
|
|
|
|
|
5 |
|
6 |
|
7 |
class BaseResponse(BaseModel):
|
@@ -38,7 +41,8 @@ class CreatePaymentRequest(BaseModel):
|
|
38 |
..., description="Method of payment", example=PaymentMethod.CASH
|
39 |
)
|
40 |
transaction_id: Optional[str] = Field(
|
41 |
-
|
|
|
42 |
)
|
43 |
|
44 |
|
@@ -49,13 +53,13 @@ class UpdatePaymentStatusRequest(BaseModel):
|
|
49 |
|
50 |
|
51 |
class PaymentResponse(BaseModel):
|
52 |
-
id: str
|
53 |
-
user_id: Optional[str]
|
54 |
-
plan_id: Optional[str]
|
55 |
amount: Decimal
|
56 |
payment_method: str
|
57 |
status: str
|
58 |
-
transaction_id: Optional[str]
|
59 |
created_time: datetime
|
60 |
updated_time: datetime
|
61 |
|
|
|
1 |
from pydantic import BaseModel, Field
|
2 |
+
from typing import Optional, List, Union
|
3 |
from decimal import Decimal
|
4 |
from datetime import datetime
|
5 |
+
from uuid import UUID
|
6 |
+
|
7 |
+
from uuid import UUID, uuid4 # Ensure you have this import at the top
|
8 |
|
9 |
|
10 |
class BaseResponse(BaseModel):
|
|
|
41 |
..., description="Method of payment", example=PaymentMethod.CASH
|
42 |
)
|
43 |
transaction_id: Optional[str] = Field(
|
44 |
+
default_factory=lambda: str(uuid4()), # Generate a UUID and convert to string
|
45 |
+
description="Unique transaction ID (for methods like Lipa Number)",
|
46 |
)
|
47 |
|
48 |
|
|
|
53 |
|
54 |
|
55 |
class PaymentResponse(BaseModel):
|
56 |
+
id: Union[UUID, str]
|
57 |
+
user_id: Optional[Union[UUID, str]]
|
58 |
+
plan_id: Optional[Union[UUID, str]]
|
59 |
amount: Decimal
|
60 |
payment_method: str
|
61 |
status: str
|
62 |
+
transaction_id: Optional[Union[UUID, str]]
|
63 |
created_time: datetime
|
64 |
updated_time: datetime
|
65 |
|
App/Payments/utils.py
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from datetime import datetime, timedelta
|
2 |
+
import calendar
|
3 |
+
|
4 |
+
|
5 |
+
def get_current_month_range():
|
6 |
+
"""Helper function to get the first and last dates of the current month."""
|
7 |
+
now = datetime.now()
|
8 |
+
start_of_month = datetime(now.year, now.month, 1)
|
9 |
+
_, last_day = calendar.monthrange(now.year, now.month)
|
10 |
+
end_of_month = datetime(now.year, now.month, last_day, 23, 59, 59)
|
11 |
+
return start_of_month, end_of_month
|
App/Subscriptions/background_tasks.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from tortoise import Tortoise
|
2 |
+
from datetime import datetime, timedelta
|
3 |
+
from App.Subscriptions.Model import Subscription
|
4 |
+
import logging
|
5 |
+
|
6 |
+
logger = logging.getLogger(__name__)
|
7 |
+
|
8 |
+
|
9 |
+
async def check_expiring_subscriptions():
|
10 |
+
"""
|
11 |
+
Background task to check for subscriptions that are about to expire
|
12 |
+
and update their status accordingly.
|
13 |
+
"""
|
14 |
+
try:
|
15 |
+
# Get the current time
|
16 |
+
current_time = datetime.now()
|
17 |
+
|
18 |
+
# Define the threshold for expiring subscriptions (e.g., 1 hour)
|
19 |
+
threshold = current_time + timedelta(hours=1)
|
20 |
+
|
21 |
+
# Fetch subscriptions that are about to expire
|
22 |
+
expiring_subscriptions = await Subscription.filter(
|
23 |
+
active=True, expiration_time__lt=threshold
|
24 |
+
).all()
|
25 |
+
|
26 |
+
for subscription in expiring_subscriptions:
|
27 |
+
# Deactivate the subscription
|
28 |
+
subscription.active = False
|
29 |
+
await subscription.save()
|
30 |
+
logger.info(
|
31 |
+
f"Subscription {subscription.id} has been deactivated due to expiration."
|
32 |
+
)
|
33 |
+
|
34 |
+
except Exception as e:
|
35 |
+
logger.error(f"Error checking expiring subscriptions: {str(e)}")
|
App/Templates/Templates.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1 |
import os
|
2 |
from jinja2 import Environment, FileSystemLoader
|
3 |
from decimal import Decimal
|
|
|
4 |
|
5 |
-
|
|
|
6 |
|
7 |
|
8 |
class MessageTemplate:
|
@@ -26,10 +28,13 @@ class MessageTemplate:
|
|
26 |
|
27 |
def payment_confirmation_message(self, amount, expiry_date, business_name=BUSINESS):
|
28 |
"""Generates the payment confirmation message for SMS."""
|
|
|
|
|
|
|
29 |
return self.render_template(
|
30 |
"Payment.md",
|
31 |
amount=amount,
|
32 |
-
expiry_date=
|
33 |
business_name=business_name,
|
34 |
)
|
35 |
|
@@ -59,10 +64,10 @@ class MessageTemplate:
|
|
59 |
def insufficient_funds_message(
|
60 |
self, required_amount: Decimal, provided_amount: Decimal
|
61 |
) -> str:
|
62 |
-
return f"Pesa hazitoshi kwa mpango uliyochagua. Zinazohitajika: {required_amount}, Zilizotolewa: {provided_amount}. Tafadhali ongeza pesa zaidi au chagua mpango tofauti."
|
63 |
|
64 |
def balance_assigned_message(self, amount: Decimal, new_balance: Decimal) -> str:
|
65 |
-
return f"Habari! Salio la {amount} limeongezwa kwa mafanikio kwenye akaunti yako. Salio lako jipya ni {new_balance}."
|
66 |
|
67 |
def subscription_expired_message(
|
68 |
self, user_name: str, plan_name: str, business_name=BUSINESS
|
|
|
1 |
import os
|
2 |
from jinja2 import Environment, FileSystemLoader
|
3 |
from decimal import Decimal
|
4 |
+
from datetime import datetime
|
5 |
|
6 |
+
|
7 |
+
BUSINESS = "Buku Mbili Wifi"
|
8 |
|
9 |
|
10 |
class MessageTemplate:
|
|
|
28 |
|
29 |
def payment_confirmation_message(self, amount, expiry_date, business_name=BUSINESS):
|
30 |
"""Generates the payment confirmation message for SMS."""
|
31 |
+
formatted_expiry_date = expiry_date.strftime(
|
32 |
+
"%Y-%m-%d %H:%M:%S"
|
33 |
+
) # Format the date and time
|
34 |
return self.render_template(
|
35 |
"Payment.md",
|
36 |
amount=amount,
|
37 |
+
expiry_date=formatted_expiry_date,
|
38 |
business_name=business_name,
|
39 |
)
|
40 |
|
|
|
64 |
def insufficient_funds_message(
|
65 |
self, required_amount: Decimal, provided_amount: Decimal
|
66 |
) -> str:
|
67 |
+
return f"Pesa hazitoshi kwa mpango uliyochagua. Zinazohitajika: {required_amount:,.2f}, Zilizotolewa: {provided_amount:,.2f}. Tafadhali ongeza pesa zaidi au chagua mpango tofauti."
|
68 |
|
69 |
def balance_assigned_message(self, amount: Decimal, new_balance: Decimal) -> str:
|
70 |
+
return f"Habari! Salio la {amount:,.2f} limeongezwa kwa mafanikio kwenye akaunti yako. Salio lako jipya ni {new_balance:,.2f}."
|
71 |
|
72 |
def subscription_expired_message(
|
73 |
self, user_name: str, plan_name: str, business_name=BUSINESS
|
App/Users/Schema.py
CHANGED
@@ -19,6 +19,10 @@ class UserResponse(BaseModel):
|
|
19 |
account_locked: bool
|
20 |
model_config = ConfigDict(from_attributes=True)
|
21 |
|
|
|
|
|
|
|
|
|
22 |
# class Config:
|
23 |
# orm_mode = True
|
24 |
|
|
|
19 |
account_locked: bool
|
20 |
model_config = ConfigDict(from_attributes=True)
|
21 |
|
22 |
+
|
23 |
+
class UserResponseSub(UserResponse):
|
24 |
+
expiration_time: str
|
25 |
+
|
26 |
# class Config:
|
27 |
# orm_mode = True
|
28 |
|
App/Users/UserRoutes.py
CHANGED
@@ -9,6 +9,7 @@ from .Schema import (
|
|
9 |
ResetPasswordRequest,
|
10 |
BaseResponse,
|
11 |
UserResponse,
|
|
|
12 |
)
|
13 |
from .Model import User
|
14 |
from jose import jwt
|
@@ -79,7 +80,11 @@ async def login_user(request: LoginUserRequest):
|
|
79 |
if db_user and valid_password:
|
80 |
|
81 |
# Fetch active subscription if it exists
|
82 |
-
subscription =
|
|
|
|
|
|
|
|
|
83 |
|
84 |
# Handle case when no active subscription is found
|
85 |
subscription_end = (
|
@@ -196,12 +201,31 @@ async def toggle_user_status(user_id: str):
|
|
196 |
|
197 |
|
198 |
@user_router.get(
|
199 |
-
"/user/me", response_model=
|
200 |
)
|
201 |
async def get_user_details(current_user: User = Depends(get_current_active_user)):
|
202 |
"""
|
203 |
Get the current user's details and balance.
|
204 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
return UserResponse.from_orm(current_user)
|
206 |
|
207 |
|
|
|
9 |
ResetPasswordRequest,
|
10 |
BaseResponse,
|
11 |
UserResponse,
|
12 |
+
UserResponseSub,
|
13 |
)
|
14 |
from .Model import User
|
15 |
from jose import jwt
|
|
|
80 |
if db_user and valid_password:
|
81 |
|
82 |
# Fetch active subscription if it exists
|
83 |
+
subscription = (
|
84 |
+
await Subscription.filter(user=db_user, expiration_time__isnull=False)
|
85 |
+
.order_by("-expiration_time")
|
86 |
+
.first()
|
87 |
+
)
|
88 |
|
89 |
# Handle case when no active subscription is found
|
90 |
subscription_end = (
|
|
|
201 |
|
202 |
|
203 |
@user_router.get(
|
204 |
+
"/user/me", response_model=UserResponseSub, status_code=status.HTTP_200_OK
|
205 |
)
|
206 |
async def get_user_details(current_user: User = Depends(get_current_active_user)):
|
207 |
"""
|
208 |
Get the current user's details and balance.
|
209 |
"""
|
210 |
+
subscription = (
|
211 |
+
await Subscription.filter(user=current_user, expiration_time__isnull=False)
|
212 |
+
.order_by("-expiration_time")
|
213 |
+
.first()
|
214 |
+
)
|
215 |
+
|
216 |
+
data = UserResponse.from_orm(current_user).model_dump()
|
217 |
+
data["expiration_time"] = subscription.expiration_time.isoformat()
|
218 |
+
return UserResponseSub(**data)
|
219 |
+
|
220 |
+
|
221 |
+
@user_router.get(
|
222 |
+
"/user/{user_id}", response_model=UserResponse, status_code=status.HTTP_200_OK
|
223 |
+
)
|
224 |
+
async def get_user_details(user_id: str, admin: User = Depends(get_admin_user)):
|
225 |
+
"""
|
226 |
+
user's details and balance.
|
227 |
+
"""
|
228 |
+
current_user = await User.get_or_none(id=user_id)
|
229 |
return UserResponse.from_orm(current_user)
|
230 |
|
231 |
|
App/app.py
CHANGED
@@ -10,12 +10,23 @@ from .Plans.PlanRoutes import plan_router
|
|
10 |
from .Portals.PortalRoutes import portal_router
|
11 |
from .Metrics.MetricsRoutes import metrics_router
|
12 |
from .Messages.MessagesRoute import message_router
|
|
|
13 |
|
14 |
# from .Subscriptions.background_tasks import check_expiring_subscriptions
|
15 |
import asyncio, os
|
16 |
import logging
|
17 |
import subprocess
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
logging.basicConfig(level=logging.INFO)
|
20 |
|
21 |
app = FastAPI()
|
@@ -34,6 +45,9 @@ app.add_middleware(
|
|
34 |
async def startup_event():
|
35 |
await Tortoise.init(config=TORTOISE_ORM)
|
36 |
await Tortoise.generate_schemas()
|
|
|
|
|
|
|
37 |
|
38 |
|
39 |
@app.get("/")
|
|
|
10 |
from .Portals.PortalRoutes import portal_router
|
11 |
from .Metrics.MetricsRoutes import metrics_router
|
12 |
from .Messages.MessagesRoute import message_router
|
13 |
+
from .Subscriptions.background_tasks import check_expiring_subscriptions
|
14 |
|
15 |
# from .Subscriptions.background_tasks import check_expiring_subscriptions
|
16 |
import asyncio, os
|
17 |
import logging
|
18 |
import subprocess
|
19 |
|
20 |
+
|
21 |
+
async def periodic_task():
|
22 |
+
"""
|
23 |
+
Periodically run the background task to check for expiring subscriptions.
|
24 |
+
"""
|
25 |
+
while True:
|
26 |
+
await check_expiring_subscriptions()
|
27 |
+
await asyncio.sleep(100) # Run every hour (adjust the interval as needed)
|
28 |
+
|
29 |
+
|
30 |
logging.basicConfig(level=logging.INFO)
|
31 |
|
32 |
app = FastAPI()
|
|
|
45 |
async def startup_event():
|
46 |
await Tortoise.init(config=TORTOISE_ORM)
|
47 |
await Tortoise.generate_schemas()
|
48 |
+
print("check subs")
|
49 |
+
asyncio.create_task(periodic_task())
|
50 |
+
print("checked subs")
|
51 |
|
52 |
|
53 |
@app.get("/")
|