django-restit 4.2.62__py3-none-any.whl → 4.2.64__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
account/fcm/google.py ADDED
@@ -0,0 +1,53 @@
1
+ import json
2
+ import requests
3
+ import time
4
+ from jwt import JWT, jwk_from_dict
5
+ from datetime import datetime, timedelta
6
+
7
+
8
+ def oauthLogin(service_account_info):
9
+ # Token endpoint
10
+ token_uri = service_account_info["token_uri"]
11
+
12
+ # The current time and expiration time for the assertion
13
+ issued_at_time = datetime.utcnow()
14
+ expiration_time = issued_at_time + timedelta(minutes=60)
15
+
16
+ # JWT Header
17
+ jwt_header = {
18
+ "alg": "RS256",
19
+ "typ": "JWT",
20
+ "kid": service_account_info["private_key_id"]
21
+ }
22
+
23
+ # JWT Payload
24
+ jwt_payload = {
25
+ "iss": service_account_info["client_email"],
26
+ "sub": service_account_info["client_email"],
27
+ "aud": token_uri,
28
+ "iat": int(issued_at_time.timestamp()),
29
+ "exp": int(expiration_time.timestamp()),
30
+ "scope": "https://www.googleapis.com/auth/firebase.messaging"
31
+ }
32
+
33
+ # Create a JWT
34
+ jwt_instance = JWT()
35
+ private_key = jwk_from_dict({"k": service_account_info["private_key"], "kty": "RSA"})
36
+ assertion = jwt_instance.encode(jwt_header, jwt_payload, private_key)
37
+
38
+ # Exchange the JWT for an access token
39
+ response = requests.post(token_uri, data={
40
+ "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
41
+ "assertion": assertion
42
+ })
43
+
44
+ response_data = response.json()
45
+
46
+ access_token = response_data.get("access_token")
47
+ expires_in = response_data.get("expires_in") # Seconds until the token expires
48
+
49
+ # You can now use this access_token to authenticate requests to Google APIs.
50
+ # Remember to refresh the token using a similar process once it's close to expiration.
51
+
52
+ print("Access Token:", access_token)
53
+ print("Expires In:", expires_in)
account/fcm/v1.py ADDED
@@ -0,0 +1,47 @@
1
+ from google.oauth2 import service_account
2
+ import google.auth.transport.requests
3
+ import requests
4
+ from rest import settings
5
+ from rest import log
6
+
7
+
8
+ FCM_ENDPOINT = 'https://fcm.googleapis.com/v1/projects/{}/messages:send'
9
+
10
+ logger = log.getLogger("fcm", filename="fcm.log")
11
+
12
+
13
+ def getCredentials(data):
14
+ # Load the credentials from the dictionary
15
+ credentials = service_account.Credentials.from_service_account_info(
16
+ data,
17
+ scopes=["https://www.googleapis.com/auth/firebase.messaging"])
18
+ # Use the credentials to authenticate a Requests session
19
+ auth_req = google.auth.transport.requests.Request()
20
+ credentials.refresh(auth_req)
21
+ return credentials
22
+
23
+
24
+ def sendToDevice(device, data):
25
+ return sendData(device.cm_token, data)
26
+
27
+
28
+ def sendNotification(to_token, title, body):
29
+ return postMessage(dict(token=to_token, notification=dict(title=title, body=body)))
30
+
31
+
32
+ def sendData(to_token, data, priority="high"):
33
+ return postMessage(dict(token=to_token, data=data, content_available=True, priority=priority))
34
+
35
+
36
+ def postMessage(credentials, payload):
37
+ logger.info("sending FCM", payload)
38
+ headers = {
39
+ 'Authorization': 'Bearer ' + credentials.token,
40
+ 'Content-Type': 'application/json; UTF-8',
41
+ }
42
+ body = dict(message=payload)
43
+ resp = requests.post(FCM_ENDPOINT.format(credentials.project_id), headers=headers, json=body)
44
+ logger.info("response", resp.text)
45
+ return resp
46
+
47
+
@@ -0,0 +1,47 @@
1
+ # Generated by Django 4.1.4 on 2024-04-15 13:45
2
+
3
+ from django.db import migrations, models
4
+ import django.db.models.deletion
5
+ import rest.fields
6
+ import rest.models.base
7
+ import rest.models.metadata
8
+
9
+
10
+ class Migration(migrations.Migration):
11
+
12
+ dependencies = [
13
+ ('account', '0019_group_location'),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.CreateModel(
18
+ name='CloudCredentials',
19
+ fields=[
20
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
+ ('created', models.DateTimeField(auto_now_add=True)),
22
+ ('modified', models.DateTimeField(auto_now=True)),
23
+ ('name', models.CharField(blank=True, default=None, max_length=128, null=True)),
24
+ ('uuid', models.CharField(blank=True, db_index=True, default=None, max_length=64, null=True)),
25
+ ('state', models.IntegerField(db_index=True, default=1)),
26
+ ('credentials', rest.fields.JSONField(default=None, null=True)),
27
+ ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cloud_credentials', to='account.group')),
28
+ ],
29
+ bases=(models.Model, rest.models.base.RestModel, rest.models.metadata.MetaDataModel),
30
+ ),
31
+ migrations.CreateModel(
32
+ name='CloudCredentialsMetaData',
33
+ fields=[
34
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
35
+ ('category', models.CharField(blank=True, db_index=True, default=None, max_length=32, null=True)),
36
+ ('key', models.CharField(db_index=True, max_length=80)),
37
+ ('value_format', models.CharField(max_length=16)),
38
+ ('value', models.TextField()),
39
+ ('int_value', models.IntegerField(blank=True, default=None, null=True)),
40
+ ('float_value', models.IntegerField(blank=True, default=None, null=True)),
41
+ ('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='properties', to='account.cloudcredentials')),
42
+ ],
43
+ options={
44
+ 'abstract': False,
45
+ },
46
+ ),
47
+ ]
account/models/device.py CHANGED
@@ -1,6 +1,7 @@
1
1
 
2
2
  from django.db import models
3
3
  from rest import models as rm
4
+ from rest import fields as rf
4
5
  from rest import settings
5
6
  from objict import objict
6
7
  from datetime import datetime
@@ -110,7 +111,36 @@ class MemberDeviceMetaData(rm.MetaDataBase):
110
111
  parent = models.ForeignKey(MemberDevice, related_name="properties", on_delete=models.CASCADE)
111
112
 
112
113
 
114
+ class CloudCredentials(models.Model, rm.RestModel, rm.MetaDataModel):
115
+ """
116
+ MemberDevice Model tracks personal devices associated with a user.
117
+ This can include mobile and desktop devices.
118
+ """
119
+ created = models.DateTimeField(auto_now_add=True)
120
+ modified = models.DateTimeField(auto_now=True)
121
+ group = models.ForeignKey("account.Group", related_name="cloud_credentials", on_delete=models.CASCADE)
122
+
123
+ name = models.CharField(max_length=128, blank=True, null=True, default=None)
124
+ uuid = models.CharField(db_index=True, max_length=64, blank=True, null=True, default=None)
125
+ state = models.IntegerField(db_index=True, default=1)
126
+
127
+ credentials = rf.JSONField()
128
+
129
+ def sendToDevice(self, device, message):
130
+ pass
131
+
132
+ def sendNotification(self, token, title, body):
133
+ pass
134
+
135
+
136
+ class CloudCredentialsMetaData(rm.MetaDataBase):
137
+ parent = models.ForeignKey(CloudCredentials, related_name="properties", on_delete=models.CASCADE)
138
+
139
+
113
140
  def getCloudMessanger(name):
141
+ # creds = CloudCredentials.objects.filter(uuid=name).last()
142
+ # if creds is not None:
143
+ # return creds
114
144
  if name not in CM_BACKENDS:
115
145
  if name == "fcm":
116
146
  from account import fcm
account/models/feeds.py CHANGED
@@ -39,9 +39,9 @@ class GroupFeed(FeedBase, rm.RestModel):
39
39
  VIEW_PERMS = ["manage_groups", "manage_group", "view_all_groups"]
40
40
  GRAPHS = {
41
41
  "default": {
42
- "recurse_into": ["generic__component"],
43
42
  "graphs": {
44
- "member": "basic"
43
+ "member": "basic",
44
+ "generic__component": "basic"
45
45
  }
46
46
  }
47
47
  }
auditlog/cloudwatch.py ADDED
@@ -0,0 +1,93 @@
1
+ import boto3
2
+ import datetime
3
+ import time
4
+ import json
5
+ from objict import objict
6
+ from rest import settings
7
+ from concurrent.futures import ThreadPoolExecutor
8
+
9
+
10
+ LOG_CACHE = objict()
11
+
12
+
13
+ def getClient():
14
+ if LOG_CACHE.client is None:
15
+ key = settings.AWS_KEY
16
+ secret = settings.AWS_SECRET
17
+ region = settings.AWS_REGION
18
+ LOG_CACHE.client = boto3.client("logs", aws_access_key_id=key, aws_secret_access_key=secret, region_name=region)
19
+ return LOG_CACHE.client
20
+
21
+
22
+ def log(data, log_group, log_stream):
23
+ if LOG_CACHE.pool is None:
24
+ LOG_CACHE.pool = ThreadPoolExecutor(max_workers=1)
25
+ LOG_CACHE.pool.submit(logToCloudWatch, data, log_group, log_stream)
26
+ return True
27
+
28
+
29
+ def logToCloudWatch(data, log_group, log_stream):
30
+ message = data
31
+ if isinstance(message, dict):
32
+ message = json.dumps(message)
33
+ return logBatchToCloudWatch([
34
+ dict(
35
+ timestamp=int(datetime.datetime.utcnow().timestamp() * 1000),
36
+ message=message)
37
+ ], log_group, log_stream)
38
+
39
+
40
+ def logBatchToCloudWatch(batch, log_group, log_stream):
41
+ return getClient().put_log_events(
42
+ logGroupName=log_group,
43
+ logStreamName=log_stream,
44
+ logEvents=batch
45
+ )
46
+
47
+
48
+ def getLogGroups():
49
+ response = getClient().describe_log_groups()
50
+ return response.get('logGroups', [])
51
+
52
+
53
+ def createLogStream(log_group, log_stream):
54
+ try:
55
+ getClient().create_log_stream(logGroupName=log_group, logStreamName=log_stream)
56
+ except Exception:
57
+ pass # Log stream already exists, no need to create it
58
+
59
+
60
+ def getInsights(log_group, start_time, end_time, query_string):
61
+ """
62
+ Executes a CloudWatch Logs Insights query and returns the results.
63
+
64
+ :param log_group: The name of the log group to query.
65
+ :param start_time: The start time of the query (epoch time in seconds).
66
+ :param end_time: The end time of the query (epoch time in seconds).
67
+ :param query_string: The query string to use.
68
+ :param region_name: AWS region name.
69
+ :return: The query results.
70
+ """
71
+ # Create a CloudWatch Logs client
72
+ client = getClient()
73
+
74
+ # Start the query
75
+ start_query_response = client.start_query(
76
+ logGroupName=log_group,
77
+ startTime=start_time,
78
+ endTime=end_time,
79
+ queryString=query_string,
80
+ )
81
+
82
+ query_id = start_query_response['queryId']
83
+
84
+ # Wait for the query to complete
85
+ response = None
86
+ while response is None or response['status'] == 'Running':
87
+ time.sleep(1) # Sleep to rate limit the polling
88
+ response = client.get_query_results(queryId=query_id)
89
+
90
+ if response['status'] == 'Complete':
91
+ return response['results']
92
+ else:
93
+ raise Exception(f"Query did not complete successfully. Status: {response['status']}")
auditlog/models.py CHANGED
@@ -8,6 +8,7 @@ import copy
8
8
  from rest.models import RestModel
9
9
  from rest import helpers
10
10
  from rest.log import getLogger
11
+ from auditlog import cloudwatch
11
12
 
12
13
  import traceback
13
14
 
@@ -281,7 +282,9 @@ class PersistentLog(models.Model, RestModel):
281
282
 
282
283
  @staticmethod
283
284
  def log(message, level=0, request=None, component=None,
284
- pkey=None, action=None, group=None, path=None, method=None, tid=None, no_truncate=False):
285
+ pkey=None, action=None, group=None, path=None,
286
+ method=None, tid=None, no_truncate=False,
287
+ aws_log_group=None, aws_log_stream=None):
285
288
  plog = PersistentLog.createLogFromRequest(
286
289
  request, component=component, tid=tid, pkey=pkey, action=action, group=group,
287
290
  path=path, method=method, level=level)
@@ -290,6 +293,8 @@ class PersistentLog(models.Model, RestModel):
290
293
 
291
294
  try:
292
295
  plog.save()
296
+ if aws_log_group and aws_log_stream:
297
+ cloudwatch.log(plog.toDict(), aws_log_group, aws_log_stream)
293
298
  except Exception:
294
299
  helpers.log_exception(plog.message)
295
300
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-restit
3
- Version: 4.2.62
3
+ Version: 4.2.64
4
4
  Summary: A Rest Framework for DJANGO
5
5
  License: MIT
6
6
  Author: Ian Starnes
@@ -1,6 +1,8 @@
1
1
  account/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  account/admin.py,sha256=8MQ1gAgjpPUC_SGCVMwd7I8fqLetqcutLiasjssEPRY,1839
3
3
  account/fcm/__init__.py,sha256=gSTChf99a5T20oKuf4Lh9UR-f3V5qI20AZxIhlPlnVs,980
4
+ account/fcm/google.py,sha256=T-TZZgrT90YY9sEtKC6rNGpwfBmVTCuFpB31vRDguu0,1710
5
+ account/fcm/v1.py,sha256=PGpSmAnq40cMao4SwDU2tv6m5PpteDWLbzvldjalRJ8,1434
4
6
  account/migrations/0001_initial.py,sha256=PhYNDTiwjyUplErBmYc34ecynLIEJL2JuC02o8GCXes,15894
5
7
  account/migrations/0003_member_phone_number.py,sha256=auAJCfxsK-y3Veo0vwZfrIZxvwHYBjg5CpYRgCWghCQ,738
6
8
  account/migrations/0004_group_modified_alter_group_created.py,sha256=20iNFlUGtRCWUsxEqTJFRQKQ73z4REhfo-hGam8IHXY,551
@@ -19,10 +21,11 @@ account/migrations/0016_authsession_buid.py,sha256=wZdiH_87Ik3jAXYUgtafeAo9IbJq3
19
21
  account/migrations/0017_rename_requires_topt_member_requires_totp.py,sha256=GksGiF7OQDV2RihyC2OTBzSmDwzCzenThkNs6FKni4M,375
20
22
  account/migrations/0018_userpasskey.py,sha256=SdXYo4TkIeP5wLNfCza3Jq5-gKuUufzTHGBw0hFQOMY,1475
21
23
  account/migrations/0019_group_location.py,sha256=EfMB_w4qWUGDqQeNc453PFZwpjpTeoA6xr6Qgo_YAOM,601
24
+ account/migrations/0020_cloudcredentials_cloudcredentialsmetadata.py,sha256=mHwxkyDfA4ueQOt34w5ndJB4XwNTDLv79CkKgzhlz-c,2250
22
25
  account/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
26
  account/models/__init__.py,sha256=cV_lMnT2vL_mjiYtT4hlcIHo52ocFbGSNVkOIHHLXZY,385
24
- account/models/device.py,sha256=EzkEPjpJxxt7OGDfEkRXIpoutdfSSPBGquVdKF6nJD0,4114
25
- account/models/feeds.py,sha256=JYBfAmvSnbFaFV90Di2Hu07-SLAhyBMw0pCOvJ_eXqs,2016
27
+ account/models/device.py,sha256=MVWZEYrX_4zJaqmJS_feFplfVXYyqBJ-0_Fm3B1SVg8,5226
28
+ account/models/feeds.py,sha256=vI7fG4ASY1M0Zjke24RdnfDcuWeATl_yR_25jPmT64g,2011
26
29
  account/models/group.py,sha256=4fk-RavG1wNVzfuXMaD2XDiX2DhK1hgL0-OA54Uym18,21576
27
30
  account/models/legacy.py,sha256=zYdtv4LC0ooxPVqWM-uToPwV-lYWQLorSE6p6yn1xDw,2720
28
31
  account/models/member.py,sha256=CxxhNnFCQPEw7MGc5147J8eandYqUj6HhhQ_YRyFQX4,50397
@@ -57,11 +60,12 @@ account/templates/email/simple/reset_code.html,sha256=Dln4C8jC-PI1ToS-k2VpRUjXya
57
60
  auditlog/README,sha256=q4DXhdz5CuMyuxYISHXzhlHnIkRJlojwOMchLzW2qOI,520
58
61
  auditlog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
62
  auditlog/admin.py,sha256=-q7fstdFjNeDFfbwdrxVqy0WGKxMpBwrsM7AyG1p80g,1006
63
+ auditlog/cloudwatch.py,sha256=R-B_ByVM3We26YnDoFYIQeWV31CUyS63QTojRAkfWa8,2805
60
64
  auditlog/decorators.py,sha256=ZoIv0fhZjxtMEV15NcKijW4xPF5UEScPna60zB3TxZo,6553
61
65
  auditlog/middleware.py,sha256=Q4bXg8rnm8y2fMnAsN6ha3Fz6TW8jIzLnvpu4H9SpWE,1537
62
66
  auditlog/migrations/0001_initial.py,sha256=X171gKQZIaTO9FGNG1yKTjGSZS0ZjZj5gvimF9-_kks,3309
63
67
  auditlog/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
- auditlog/models.py,sha256=6CyWMRNvqCYoyiXE9_qk41EJFjutwo6nfoP9KKYWiYw,16294
68
+ auditlog/models.py,sha256=skDAiuzR4chC-WNIaH2nm_VVcbnDD6ZtUxBwhk7UY8U,16517
65
69
  auditlog/periodic.py,sha256=AUhDeVsZtC47BJ-lklvYEegHoxAzj1RpIvRFSsM7g5E,363
66
70
  auditlog/rpc.py,sha256=lhme-ScqwVSKfHo3RlzMVm_C-Yx4xOxPogBEv7b3w1M,1720
67
71
  auditlog/tq.py,sha256=OgzJVspWI6FL92GEhDPtabYoP_Hd3zGNh0E297abz3Y,2415
@@ -103,7 +107,7 @@ incident/migrations/0013_rulecheck_is_required.py,sha256=cL7tOj5XGPpKd2f5BojIKfN
103
107
  incident/migrations/0014_event_group_alter_rulecheck_index.py,sha256=v3gm5k0LVoas27qUDOt7el7YtK4yjFVLeEpuFUCoXaQ,724
104
108
  incident/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
109
  incident/models/__init__.py,sha256=NMphuhb0RTMf7Ov4QkNv7iv6_I8Wtr3xQ54yjX_a31M,209
106
- incident/models/event.py,sha256=qa6BIlellU7uUQcA2c4Botd2x7JLL8b7IjSfxyi5iHk,7306
110
+ incident/models/event.py,sha256=WRNzvjo0jypdnQNBksOOyi-_0kVT4qWUrDZf0Aw_MPM,7355
107
111
  incident/models/incident.py,sha256=Jx0RnOo70BzPX4BLMVF8jB5UOYwFPKPlxWznkTeMzOU,19332
108
112
  incident/models/ossec.py,sha256=p1ptr-8lnaj1EP_VmPR58b2LmaYBGaYYKAMqhWK5yZM,2227
109
113
  incident/models/rules.py,sha256=uT5GhW6Flso287lJGphAlWwL20NRnHDAZoGrWBBQfeE,6260
@@ -364,7 +368,7 @@ pushit/utils.py,sha256=IeTCGa-164nmB1jIsK1lu1O1QzUhS3BKfuXHGjCW-ck,2121
364
368
  rest/.gitignore,sha256=TbEvWRMnAiajCTOdhiNrd9eeCAaIjRp9PRjE_VkMM5g,118
365
369
  rest/README.md,sha256=V3ETc-cJu8PZIbKr9xSe_pA4JEUpC8Dhw4bQeVCDJPw,5460
366
370
  rest/RemoteEvents.py,sha256=nL46U7AuxIrlw2JunphR1tsXyqi-ep_gD9CYGpYbNgE,72
367
- rest/__init__.py,sha256=nqUQGTs1aNMT8VVPk-Lu2qFRYD6XdwBeqh9cbJmiwxY,121
371
+ rest/__init__.py,sha256=p0-hoQXw9I4GjJGpnAZji3gbFzmQxNAIsg1J5OP5Bm8,121
368
372
  rest/arc4.py,sha256=y644IbF1ec--e4cUJ3KEYsewTCITK0gmlwa5mJruFC0,1967
369
373
  rest/cache.py,sha256=1Qg0rkaCJCaVP0-l5hZg2CIblTdeBSlj_0fP6vlKUpU,83
370
374
  rest/crypto/__init__.py,sha256=Tl0U11rgj1eBYqd6OXJ2_XSdNLumW_JkBZnaJqI6Ldw,72
@@ -445,17 +449,17 @@ taskqueue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
445
449
  taskqueue/admin.py,sha256=E6zXoToS_ea3MdoGjZzF1JiepWFtDSoZUQdan8H-pXI,208
446
450
  taskqueue/migrations/0001_initial.py,sha256=JwYib8CK5ftSXlfxKZUcKEEVsXktNB5q3h-2tu9inGk,4738
447
451
  taskqueue/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
448
- taskqueue/models.py,sha256=tCFAKSr8JybUJV4gijg3wUCwQMJiHUsRBgHH2CWonfY,22119
452
+ taskqueue/models.py,sha256=N3_9jWHPGzs6UxlAzpjst6qPhvIs1RELTy1cMXMeXsA,22407
449
453
  taskqueue/periodic.py,sha256=2i0271khrCow3hDmlNEcoAZnesBVl40jd7MIim2Cxs4,3543
450
454
  taskqueue/rpc.py,sha256=If5E9D9AR2RqW4lHRaDuD9L9b9ZfL_PaBQ6iX91ehvU,5736
451
455
  taskqueue/tq.py,sha256=PzSoDrawYcqZylruEgsK95gcJ4J_VhdM6rxg9V6_X8E,942
452
456
  taskqueue/transports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
453
457
  taskqueue/transports/email.py,sha256=H4GaomiyCck5R_AOcsrMawCl-_Bp_Zg-uWto9t1Xcoo,623
454
- taskqueue/transports/http.py,sha256=kRBjHiwTmk5b4TMdZWt1EKKcjfSlX8DFmtmJaRGZQFw,681
458
+ taskqueue/transports/http.py,sha256=AzliUnw_LuyO2zZZOoUAJGFcTV-Gxt1iE3hCVnIiyGQ,839
455
459
  taskqueue/transports/s3.py,sha256=fMosL893u1iQdo6Y1djwb7KEoNo6TTsDPJl13OJdJP8,1913
456
460
  taskqueue/transports/sftp.py,sha256=jT1_krjTHA7DCAukD85aGYRCg9m0cEH9EWzOC-wJGdk,1891
457
461
  taskqueue/transports/sms.py,sha256=H1-LIGEMfbUNqJD9amRcsvKUSwtz9yBj1QNfB7EHjHE,142
458
- taskqueue/worker.py,sha256=u7H-FLkz7vpBoDaab9hpw4RduvG1ZTdtlw3PSHRBmUw,15737
462
+ taskqueue/worker.py,sha256=wzp44fk6LX94MdrXLp_IJmWgutLCBKqoobk433OiLqw,15822
459
463
  telephony/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
460
464
  telephony/admin.py,sha256=iOdsBfFFbBisdqKSZ36bIrh_z5sU0Wx_PkaFi8wd1iA,243
461
465
  telephony/decorators.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -497,7 +501,7 @@ ws4redis/servers/uwsgi.py,sha256=VyhoCI1DnVFqBiJYHoxqn5Idlf6uJPHvfBKgkjs34mo,172
497
501
  ws4redis/settings.py,sha256=K0yBiLUuY81iDM4Yr-k8hbvjn5VVHu5zQhmMK8Dtz0s,1536
498
502
  ws4redis/utf8validator.py,sha256=S0OlfjeGRP75aO6CzZsF4oTjRQAgR17OWE9rgZdMBZA,5122
499
503
  ws4redis/websocket.py,sha256=R0TUyPsoVRD7Y_oU7w2I6NL4fPwiz5Vl94-fUkZgLHA,14848
500
- django_restit-4.2.62.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
501
- django_restit-4.2.62.dist-info/METADATA,sha256=XPJeA3gzUze_WPJT54LMV1INVHI_ru-82Nrsu82Wc30,7594
502
- django_restit-4.2.62.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
503
- django_restit-4.2.62.dist-info/RECORD,,
504
+ django_restit-4.2.64.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
505
+ django_restit-4.2.64.dist-info/METADATA,sha256=L_HZYV8PSsU8TdNKCTl2M7ZJ2U_JJEM9ImYZ99X6Ams,7594
506
+ django_restit-4.2.64.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
507
+ django_restit-4.2.64.dist-info/RECORD,,
incident/models/event.py CHANGED
@@ -121,6 +121,8 @@ class Event(JSONMetaData, rm.RestModel):
121
121
 
122
122
  def set_description(self, value):
123
123
  # trun desc to 84
124
+ if value is None:
125
+ value = ""
124
126
  if len(value) > 84:
125
127
  value = value[:80] + "..."
126
128
  self.description = value
rest/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  from .uberdict import UberDict # noqa: F401
2
2
  from .settings_helper import settings # noqa: F401
3
3
 
4
- __version__ = "4.2.62"
4
+ __version__ = "4.2.64"
taskqueue/models.py CHANGED
@@ -226,23 +226,25 @@ class Task(models.Model, RestModel):
226
226
  self.state = TASK_STATE_COMPLETED
227
227
  self.save()
228
228
 
229
- def failed(self, reason=None):
229
+ def failed(self, reason=None, category="taskqueue_errors"):
230
230
  if reason and len(reason) > 250:
231
231
  reason = reason[:250]
232
232
  self.reason = reason
233
233
  self.state = TASK_STATE_FAILED
234
- self.notifyError()
234
+ self.notifyError(category=category, reason=reason)
235
235
  self.save()
236
236
 
237
- def notifyError(self):
238
- handler = f"{self.model}{self.fname}"
237
+ def notifyError(self, category="taskqueue_errors", reason=None):
238
+ handler = f"{self.model}.{self.fname}"
239
239
  subject = f"TaskQueue - {handler}"
240
+ if reason is None:
241
+ reason = self.reason
240
242
 
241
- msg = f"{handler}<br>\n{self.reason}"
243
+ msg = f"{handler}<br>\n{reason}"
242
244
  metadata = {
243
245
  "server": settings.get("HOSTNAME", "unknown"),
244
246
  "task": self.pk,
245
- "reason": self.reason,
247
+ "reason": reason,
246
248
  "app": self.model,
247
249
  "fname": self.fname,
248
250
  "channel": self.channel,
@@ -253,6 +255,9 @@ class Task(models.Model, RestModel):
253
255
  if self.data:
254
256
  if self.data.url:
255
257
  metadata["url"] = self.data.url
258
+ from urllib.parse import urlparse
259
+ purl = urlparse(self.data.url)
260
+ metadata["host"] = purl.netloc
256
261
  msg = f"{msg}<br>\n{self.data.url}"
257
262
  if self.data.log_component:
258
263
  metadata["component"] = self.data.log_component
@@ -261,7 +266,7 @@ class Task(models.Model, RestModel):
261
266
  try:
262
267
  import incident
263
268
  incident.event_now(
264
- "taskqueue_errors", description=subject, details=msg,
269
+ category, description=subject, details=msg,
265
270
  level=3, metadata=metadata)
266
271
  except Exception as err:
267
272
  self.log(str(err), kind="error")
@@ -15,9 +15,13 @@ def REQUEST(task):
15
15
  data=task.data.data, raw_response=True,
16
16
  verify=False, timeout=REQ_TIMEOUT)
17
17
  task.log(resp.text, kind="response")
18
- return resp.status_code == 200
18
+ if resp.status_code == 200:
19
+ return True
20
+ task.reason = f"invalid response of {resp.status_code}"
19
21
  except requests.Timeout:
22
+ task.reason = "request timed out"
20
23
  task.log("request timed out", kind="error")
21
24
  except Exception as err:
25
+ task.reason = str(err)
22
26
  task.log_exception(err)
23
27
  return False
taskqueue/worker.py CHANGED
@@ -198,6 +198,8 @@ class WorkManager(object):
198
198
  task.completed()
199
199
  elif task.attempts < task.max_attempts:
200
200
  # -1 will auto calculate retry with back off
201
+ # lets report the issue
202
+ task.notifyError(reason=task.reason)
201
203
  task.retry_later(from_now_secs=-1)
202
204
  else:
203
205
  task.failed("max attempts")