Mbonea commited on
Commit
01a1238
·
1 Parent(s): c353d4b
App/Messages/MessagesRoute.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # App/Messages/Routes.py
2
+ from fastapi import APIRouter, HTTPException, Request
3
+ from .Model import Message
4
+ from .Schema import MessageCreate, MessageResponse
5
+ from uuid import UUID
6
+ from typing import List, Optional
7
+ import re
8
+ from datetime import datetime
9
+ from decimal import Decimal
10
+
11
+ message_router = APIRouter(tags=["Messages"], prefix="/messages")
12
+
13
+
14
+ @message_router.post("/sms_received", response_model=MessageResponse)
15
+ async def receive_message(request: Request):
16
+
17
+ message_raw = await request.json()
18
+ message_data = MessageCreate(**message_raw)
19
+ try:
20
+ # Extract data from the message content using regex
21
+ text = message_data.payload.message
22
+ parsed_data = parse_message_content(text)
23
+
24
+ # Create a new message record with parsed_data
25
+ new_message = await Message.create(
26
+ device_id=message_data.deviceId,
27
+ event=message_data.event,
28
+ message_id=message_data.id,
29
+ webhook_id=message_data.webhookId,
30
+ message_content=text,
31
+ phone_number=message_data.payload.phoneNumber,
32
+ received_at=message_data.payload.receivedAt,
33
+ sim_number=message_data.payload.simNumber,
34
+ parsed_data=parsed_data,
35
+ )
36
+ return MessageResponse.from_orm(new_message)
37
+ except Exception as e:
38
+ raise HTTPException(status_code=400, detail=str(e))
39
+
40
+
41
+ def parse_message_content(text: str) -> Optional[dict]:
42
+ # Regular expression to capture the data from the message
43
+ pattern = r"(\w+)\sConfirmed\.You have received Tsh([\d,]+\.\d{2}) from (\d{12}) - ([A-Z ]+) on (\d{1,2}/\d{1,2}/\d{2}) at ([\d:]+ [APM]+).*?balance is Tsh([\d,]+\.\d{2})"
44
+
45
+ matches = re.search(pattern, text)
46
+ if matches:
47
+ data = {
48
+ "transaction_id": matches.group(1),
49
+ "amount_received": parse_decimal(matches.group(2)),
50
+ "phone_number": matches.group(3),
51
+ "name": matches.group(4).strip(),
52
+ "date": parse_date(matches.group(5), matches.group(6)),
53
+ "new_balance": parse_decimal(matches.group(7)),
54
+ }
55
+ return data
56
+ else:
57
+ # Return None if the message doesn't match the expected format
58
+ return None
59
+
60
+
61
+ def parse_decimal(amount_str: str) -> float:
62
+ # Remove commas and convert to Decimal
63
+ amount_str = amount_str.replace(",", "")
64
+ return float(Decimal(amount_str))
65
+
66
+
67
+ def parse_date(date_str: str, time_str: str) -> str:
68
+ # Combine date and time strings and parse into ISO format
69
+ datetime_str = f"{date_str} {time_str}"
70
+ # Adjust the format as per the actual format in the message
71
+ try:
72
+ dt = datetime.strptime(datetime_str, "%d/%m/%y %I:%M %p")
73
+ return dt.isoformat()
74
+ except ValueError:
75
+ return datetime_str # Return as-is if parsing fails
App/Messages/Model.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # App/Messages/Model.py
2
+ from tortoise import fields, models
3
+ from uuid import uuid4
4
+ from datetime import datetime
5
+ from tortoise.models import Model
6
+
7
+
8
+ class Message(Model):
9
+ id = fields.UUIDField(pk=True, default=uuid4)
10
+ device_id = fields.CharField(max_length=100, null=True)
11
+ event = fields.CharField(max_length=100, null=True)
12
+ message_id = fields.CharField(
13
+ max_length=100, null=True
14
+ ) # Corresponds to 'id' in the JSON
15
+ webhook_id = fields.CharField(max_length=100, null=True)
16
+ message_content = fields.TextField()
17
+ phone_number = fields.CharField(max_length=20)
18
+ received_at = fields.DatetimeField()
19
+ sim_number = fields.IntField(null=True)
20
+ parsed_data = fields.JSONField(null=True) # New field for parsed data
21
+ created_time = fields.DatetimeField(auto_now_add=True)
22
+
23
+ class Meta:
24
+ table = "messages"
25
+
26
+ def __str__(self):
27
+ return f"Message from {self.phone_number} at {self.received_at}"
App/Messages/Schema.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # App/Messages/Schema.py
2
+ from pydantic import BaseModel, UUID4, Field, ConfigDict
3
+ from datetime import datetime
4
+ from typing import Optional
5
+
6
+
7
+ class PayloadSchema(BaseModel):
8
+ message: str
9
+ phoneNumber: str
10
+ receivedAt: datetime
11
+ simNumber: Optional[int]
12
+
13
+
14
+ class MessageCreate(BaseModel):
15
+ deviceId: Optional[str]
16
+ event: Optional[str]
17
+ id: Optional[str] = Field(alias="messageId") # Renaming to prevent conflict
18
+ payload: PayloadSchema
19
+ webhookId: Optional[str]
20
+
21
+
22
+ class MessageResponse(BaseModel):
23
+ id: UUID4
24
+ device_id: Optional[str]
25
+ event: Optional[str]
26
+ message_id: Optional[str]
27
+ webhook_id: Optional[str]
28
+ message_content: str
29
+ phone_number: str
30
+ received_at: datetime
31
+ sim_number: Optional[int]
32
+ parsed_data: Optional[dict] # Include parsed_data in the response
33
+ created_time: datetime
34
+ model_config = ConfigDict(from_attributes=True)
35
+
36
+ # class Config:
37
+ # orm_mode = True
38
+ # allow_population_by_field_name = True
App/app.py CHANGED
@@ -8,6 +8,7 @@ from .Payments.PaymentsRoutes import payment_router
8
  from .Plans.PlanRoutes import plan_router
9
  from .Portals.PortalRoutes import portal_router
10
  from .Metrics.MetricsRoutes import metrics_router
 
11
 
12
  app = FastAPI()
13
 
@@ -30,6 +31,7 @@ app.include_router(payment_router)
30
  app.include_router(plan_router)
31
  app.include_router(portal_router)
32
  app.include_router(metrics_router)
 
33
 
34
 
35
  if __name__ == "__main__":
 
8
  from .Plans.PlanRoutes import plan_router
9
  from .Portals.PortalRoutes import portal_router
10
  from .Metrics.MetricsRoutes import metrics_router
11
+ from .Messages.MessagesRoute import message_router
12
 
13
  app = FastAPI()
14
 
 
31
  app.include_router(plan_router)
32
  app.include_router(portal_router)
33
  app.include_router(metrics_router)
34
+ app.include_router(message_router)
35
 
36
 
37
  if __name__ == "__main__":
App/discovery.py CHANGED
@@ -28,7 +28,7 @@ def discover_models(target_file: str, directory: str = None) -> List[str]:
28
  os.sep, "."
29
  ) # Correct dot notation
30
  module_name = module_name[:-3] # Remove '.py' extension
31
- model_modules.append(module_name)
32
 
33
  print("Discovered models:", model_modules)
34
  return model_modules
 
28
  os.sep, "."
29
  ) # Correct dot notation
30
  module_name = module_name[:-3] # Remove '.py' extension
31
+ model_modules.append("App." + module_name)
32
 
33
  print("Discovered models:", model_modules)
34
  return model_modules
App/modelInit.py CHANGED
@@ -26,13 +26,7 @@ TORTOISE_ORM = {
26
  },
27
  "apps": {
28
  "models": {
29
- "models": [
30
- "App.Payments.Model",
31
- "App.Plans.Model",
32
- "App.Portals.Model",
33
- "App.Subscriptions.Model",
34
- "App.Users.Model",
35
- ],
36
  "default_connection": "default",
37
  }
38
  },
 
26
  },
27
  "apps": {
28
  "models": {
29
+ "models": models,
 
 
 
 
 
 
30
  "default_connection": "default",
31
  }
32
  },