simo 2.5.2__py3-none-any.whl → 2.5.4__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.
Potentially problematic release.
This version of simo might be problematic. Click here for more details.
- simo/core/__pycache__/app_widgets.cpython-38.pyc +0 -0
- simo/core/__pycache__/base_types.cpython-38.pyc +0 -0
- simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/core/__pycache__/signal_receivers.cpython-38.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/core/app_widgets.py +19 -1
- simo/core/base_types.py +2 -0
- simo/core/controllers.py +157 -6
- simo/core/management/_hub_template/hub/supervisor.conf +4 -0
- simo/core/management/update.py +5 -3
- simo/core/signal_receivers.py +8 -5
- simo/core/tasks.py +1 -1
- simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
- simo/fleet/controllers.py +20 -119
- simo/fleet/forms.py +101 -0
- simo/generic/__pycache__/app_widgets.cpython-38.pyc +0 -0
- simo/generic/__pycache__/base_types.cpython-38.pyc +0 -0
- simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
- simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
- simo/generic/app_widgets.py +0 -18
- simo/generic/base_types.py +0 -2
- simo/generic/controllers.py +2 -262
- simo/generic/forms.py +0 -49
- simo/generic/gateways.py +8 -118
- simo/generic/scripting/__pycache__/helpers.cpython-38.pyc +0 -0
- simo/generic/scripting/example.py +67 -0
- simo/generic/scripting/helpers.py +66 -10
- simo/users/__pycache__/admin.cpython-38.pyc +0 -0
- simo/users/__pycache__/api.cpython-38.pyc +0 -0
- simo/users/__pycache__/models.cpython-38.pyc +0 -0
- simo/users/admin.py +25 -5
- simo/users/api.py +25 -11
- simo/users/migrations/0035_instanceuser_last_seen_speed_kmh_and_more.py +23 -0
- simo/users/migrations/0036_instanceuser_phone_on_charge_user_phone_on_charge.py +23 -0
- simo/users/migrations/0037_rename_last_seen_location_datetime_instanceuser_last_seen_and_more.py +53 -0
- simo/users/migrations/__pycache__/0035_instanceuser_last_seen_speed_kmh_and_more.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0036_instanceuser_phone_on_charge_user_phone_on_charge.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0037_rename_last_seen_location_datetime_instanceuser_last_seen_and_more.cpython-38.pyc +0 -0
- simo/users/models.py +12 -55
- {simo-2.5.2.dist-info → simo-2.5.4.dist-info}/METADATA +1 -1
- {simo-2.5.2.dist-info → simo-2.5.4.dist-info}/RECORD +47 -41
- {simo-2.5.2.dist-info → simo-2.5.4.dist-info}/WHEEL +1 -1
- simo/scripting.py +0 -39
- {simo-2.5.2.dist-info → simo-2.5.4.dist-info}/LICENSE.md +0 -0
- {simo-2.5.2.dist-info → simo-2.5.4.dist-info}/entry_points.txt +0 -0
- {simo-2.5.2.dist-info → simo-2.5.4.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import pytz
|
|
2
|
+
import math
|
|
1
3
|
from django.utils import timezone
|
|
2
4
|
from suntime import Sun
|
|
3
5
|
from simo.core.models import Instance
|
|
@@ -5,10 +7,12 @@ from simo.core.models import Instance
|
|
|
5
7
|
|
|
6
8
|
class LocalSun(Sun):
|
|
7
9
|
|
|
8
|
-
def __init__(self,
|
|
9
|
-
if not
|
|
10
|
+
def __init__(self, location=None):
|
|
11
|
+
if not location:
|
|
10
12
|
instance = Instance.objects.all().first()
|
|
11
|
-
|
|
13
|
+
coordinates = instance.location.split(',')
|
|
14
|
+
else:
|
|
15
|
+
coordinates = location.split(',')
|
|
12
16
|
try:
|
|
13
17
|
lat = float(coordinates[0])
|
|
14
18
|
except:
|
|
@@ -19,17 +23,69 @@ class LocalSun(Sun):
|
|
|
19
23
|
lon = 0
|
|
20
24
|
super().__init__(lat, lon)
|
|
21
25
|
|
|
22
|
-
def
|
|
23
|
-
|
|
26
|
+
def get_sunrise_time(self, localdatetime=None):
|
|
27
|
+
sunrise = super().get_sunrise_time(date=localdatetime)
|
|
28
|
+
if not localdatetime or not localdatetime.tzinfo:
|
|
29
|
+
return sunrise
|
|
30
|
+
return sunrise.astimezone(localdatetime.tzinfo)
|
|
31
|
+
|
|
32
|
+
def get_sunset_time(self, localdatetime=None):
|
|
33
|
+
sunset = super().get_sunset_time(date=localdatetime)
|
|
34
|
+
if not localdatetime or not localdatetime.tzinfo:
|
|
35
|
+
return sunset
|
|
36
|
+
return sunset.astimezone(localdatetime.tzinfo)
|
|
37
|
+
|
|
38
|
+
def _get_utc_datetime(self, localdatetime=None):
|
|
39
|
+
if not localdatetime:
|
|
40
|
+
utc_datetime = timezone.now()
|
|
41
|
+
else:
|
|
42
|
+
utc_datetime = localdatetime.astimezone(pytz.utc)
|
|
43
|
+
return utc_datetime
|
|
44
|
+
|
|
45
|
+
def is_night(self, localdatetime=None):
|
|
46
|
+
utc_datetime = self._get_utc_datetime(localdatetime)
|
|
47
|
+
if utc_datetime > self.get_sunset_time(utc_datetime):
|
|
24
48
|
return True
|
|
25
|
-
if
|
|
49
|
+
if utc_datetime < self.get_sunrise_time(utc_datetime):
|
|
26
50
|
return True
|
|
27
51
|
return False
|
|
28
52
|
|
|
29
|
-
def seconds_to_sunset(self):
|
|
30
|
-
|
|
53
|
+
def seconds_to_sunset(self, localdatetime=None):
|
|
54
|
+
utc_datetime = self._get_utc_datetime(localdatetime)
|
|
55
|
+
return (self.get_sunset_time(utc_datetime) - utc_datetime).total_seconds()
|
|
56
|
+
|
|
57
|
+
def seconds_to_sunrise(self, localdatetime=None):
|
|
58
|
+
utc_datetime = self._get_utc_datetime(localdatetime)
|
|
59
|
+
return (self.get_sunrise_time(utc_datetime) - utc_datetime).total_seconds()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def haversine_distance(location1, location2, units_of_measure='metric'):
|
|
63
|
+
# Radius of Earth in meters
|
|
64
|
+
R = 6371000
|
|
65
|
+
|
|
66
|
+
# Unpack coordinates
|
|
67
|
+
lat1, lon1 = location1.split(',')
|
|
68
|
+
lat2, lon2 = location2.split(',')
|
|
69
|
+
lat1, lon1, lat2, lon2 = float(lat1), float(lon1), float(lat2), float(lon2)
|
|
70
|
+
|
|
71
|
+
# Convert latitude and longitude from degrees to radians
|
|
72
|
+
phi1 = math.radians(lat1)
|
|
73
|
+
phi2 = math.radians(lat2)
|
|
74
|
+
delta_phi = math.radians(lat2 - lat1)
|
|
75
|
+
delta_lambda = math.radians(lon2 - lon1)
|
|
76
|
+
|
|
77
|
+
# Haversine formula
|
|
78
|
+
a = math.sin(delta_phi / 2) ** 2 + math.cos(phi1) * math.cos(
|
|
79
|
+
phi2) * math.sin(delta_lambda / 2) ** 2
|
|
80
|
+
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
|
|
31
81
|
|
|
32
|
-
|
|
33
|
-
|
|
82
|
+
# Distance in meters
|
|
83
|
+
distance_meters = R * c
|
|
34
84
|
|
|
85
|
+
# Convert to feet if 'imperial' is chosen
|
|
86
|
+
if units_of_measure == 'imperial':
|
|
87
|
+
distance = distance_meters * 3.28084 # Convert meters to feet
|
|
88
|
+
else:
|
|
89
|
+
distance = distance_meters # Keep in meters for 'metric'
|
|
35
90
|
|
|
91
|
+
return distance
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/users/admin.py
CHANGED
|
@@ -50,6 +50,20 @@ class PermissionsRoleAdmin(admin.ModelAdmin):
|
|
|
50
50
|
return fields
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
@admin.register(InstanceUser)
|
|
54
|
+
class InstanceUserAdmin(admin.ModelAdmin):
|
|
55
|
+
list_display = (
|
|
56
|
+
'user', 'role', 'is_active', 'at_home', 'last_seen', 'phone_on_charge'
|
|
57
|
+
)
|
|
58
|
+
fields = (
|
|
59
|
+
'user', 'role', 'is_active', 'at_home', 'last_seen',
|
|
60
|
+
'last_seen_location', 'last_seen_speed_kmh', 'phone_on_charge'
|
|
61
|
+
)
|
|
62
|
+
readonly_fields = (
|
|
63
|
+
'at_home', 'last_seen', 'last_seen_speed_kmh', 'phone_on_charge',
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
53
67
|
class InstanceUserInline(admin.TabularInline):
|
|
54
68
|
model = InstanceUser
|
|
55
69
|
extra = 0
|
|
@@ -69,7 +83,6 @@ class UserAdmin(OrgUserAdmin):
|
|
|
69
83
|
fields = (
|
|
70
84
|
'name', 'email', 'is_active', 'is_master',
|
|
71
85
|
'ssh_key', 'secret_key',
|
|
72
|
-
'last_seen_location',
|
|
73
86
|
)
|
|
74
87
|
readonly_fields = (
|
|
75
88
|
'name', 'email', 'avatar', 'last_action', 'is_active'
|
|
@@ -114,11 +127,18 @@ admin.site.unregister(Group)
|
|
|
114
127
|
|
|
115
128
|
|
|
116
129
|
@admin.register(UserDeviceReportLog)
|
|
117
|
-
class
|
|
130
|
+
class UserDeviceLog(admin.ModelAdmin):
|
|
118
131
|
model = UserDeviceReportLog
|
|
119
|
-
readonly_fields =
|
|
120
|
-
|
|
132
|
+
readonly_fields = (
|
|
133
|
+
'datetime', 'app_open', 'location', 'relay', 'users',
|
|
134
|
+
'speed_kmh', 'phone_on_charge'
|
|
135
|
+
)
|
|
136
|
+
list_display = (
|
|
137
|
+
'datetime', 'app_open', 'location', 'relay', 'speed_kmh',
|
|
138
|
+
'phone_on_charge', 'users'
|
|
139
|
+
)
|
|
121
140
|
fields = readonly_fields
|
|
141
|
+
list_filter = 'user_device__users',
|
|
122
142
|
|
|
123
143
|
def has_add_permission(self, request, obj=None):
|
|
124
144
|
return False
|
|
@@ -142,7 +162,7 @@ class UserDeviceAdmin(admin.ModelAdmin):
|
|
|
142
162
|
readonly_fields = (
|
|
143
163
|
'users_display', 'token', 'os', 'last_seen',
|
|
144
164
|
)
|
|
145
|
-
fields = readonly_fields + ('
|
|
165
|
+
fields = readonly_fields + ('is_primary', )
|
|
146
166
|
|
|
147
167
|
def get_queryset(self, request):
|
|
148
168
|
qs = super().get_queryset(request)
|
simo/users/api.py
CHANGED
|
@@ -8,6 +8,7 @@ from rest_framework.exceptions import ValidationError, PermissionDenied
|
|
|
8
8
|
from django.contrib.gis.geos import Point
|
|
9
9
|
from django.utils import timezone
|
|
10
10
|
from django_filters.rest_framework import DjangoFilterBackend
|
|
11
|
+
from simo.conf import dynamic_settings
|
|
11
12
|
from simo.core.api import InstanceMixin
|
|
12
13
|
from .models import (
|
|
13
14
|
User, UserDevice, UserDeviceReportLog, PermissionsRole, InstanceInvitation,
|
|
@@ -172,7 +173,7 @@ class UserDeviceReport(InstanceMixin, viewsets.GenericViewSet):
|
|
|
172
173
|
|
|
173
174
|
@action(url_path='device-report', detail=False, methods=['post'])
|
|
174
175
|
def report(self, request, *args, **kwargs):
|
|
175
|
-
|
|
176
|
+
from simo.generic.scripting.helpers import haversine_distance
|
|
176
177
|
if not request.data.get('device_token'):
|
|
177
178
|
return RESTResponse(
|
|
178
179
|
{'status': 'error', 'msg': 'device_token - not provided'},
|
|
@@ -199,12 +200,23 @@ class UserDeviceReport(InstanceMixin, viewsets.GenericViewSet):
|
|
|
199
200
|
except:
|
|
200
201
|
location = None
|
|
201
202
|
|
|
203
|
+
relay = None
|
|
204
|
+
if request.META.get('HTTP_HOST', '').endswith('.simo.io'):
|
|
205
|
+
relay = request.META.get('HTTP_HOST')
|
|
206
|
+
|
|
207
|
+
|
|
202
208
|
user_device.last_seen = timezone.now()
|
|
203
209
|
if location:
|
|
204
210
|
user_device.last_seen_location = ','.join(
|
|
205
211
|
[str(i) for i in location]
|
|
206
212
|
) if location else None
|
|
207
213
|
|
|
214
|
+
speed_mps = float(request.data.get('speed', 0))
|
|
215
|
+
if speed_mps < 0:
|
|
216
|
+
speed_mps = 0
|
|
217
|
+
speed_kmh = speed_mps * 3.6
|
|
218
|
+
|
|
219
|
+
|
|
208
220
|
if request.data.get('app_open', False):
|
|
209
221
|
user_device.is_primary = True
|
|
210
222
|
UserDevice.objects.filter(
|
|
@@ -213,23 +225,25 @@ class UserDeviceReport(InstanceMixin, viewsets.GenericViewSet):
|
|
|
213
225
|
user_device.save()
|
|
214
226
|
|
|
215
227
|
for iu in request.user.instance_roles.filter(is_active=True):
|
|
228
|
+
if location:
|
|
229
|
+
iu.at_home = haversine_distance(
|
|
230
|
+
iu.instance.location, user_device.last_seen_location
|
|
231
|
+
) < dynamic_settings['users__at_home_radius']
|
|
232
|
+
elif not relay:
|
|
233
|
+
iu.at_home = True
|
|
234
|
+
|
|
235
|
+
iu.last_seen = user_device.last_seen
|
|
216
236
|
iu.last_seen_location = user_device.last_seen_location
|
|
217
|
-
iu.
|
|
237
|
+
iu.last_seen_speed_kmh = speed_kmh
|
|
238
|
+
iu.phone_on_charge = request.data.get('is_charging', False)
|
|
218
239
|
iu.save()
|
|
219
240
|
|
|
220
|
-
request.user.last_seen_location = user_device.last_seen_location
|
|
221
|
-
request.user.last_seen_location_datetime = user_device.last_seen
|
|
222
|
-
request.user.save()
|
|
223
|
-
|
|
224
|
-
relay = None
|
|
225
|
-
if request.META.get('HTTP_HOST', '').endswith('.simo.io'):
|
|
226
|
-
relay = request.META.get('HTTP_HOST')
|
|
227
|
-
|
|
228
241
|
UserDeviceReportLog.objects.create(
|
|
229
242
|
user_device=user_device, instance=self.instance,
|
|
230
243
|
app_open=request.data.get('app_open', False),
|
|
231
244
|
location=','.join([str(i) for i in location]) if location else None,
|
|
232
|
-
relay=relay
|
|
245
|
+
relay=relay, speed_kmh=speed_kmh,
|
|
246
|
+
phone_on_charge=request.data.get('is_charging', False)
|
|
233
247
|
)
|
|
234
248
|
|
|
235
249
|
return RESTResponse({'status': 'success'})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2024-10-29 07:38
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('users', '0034_instanceuser_last_seen_location_and_more'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='instanceuser',
|
|
15
|
+
name='last_seen_speed_kmh',
|
|
16
|
+
field=models.FloatField(default=0),
|
|
17
|
+
),
|
|
18
|
+
migrations.AddField(
|
|
19
|
+
model_name='user',
|
|
20
|
+
name='last_seen_speed_kmh',
|
|
21
|
+
field=models.FloatField(default=0),
|
|
22
|
+
),
|
|
23
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2024-10-29 12:58
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('users', '0035_instanceuser_last_seen_speed_kmh_and_more'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='instanceuser',
|
|
15
|
+
name='phone_on_charge',
|
|
16
|
+
field=models.BooleanField(db_index=True, default=False),
|
|
17
|
+
),
|
|
18
|
+
migrations.AddField(
|
|
19
|
+
model_name='user',
|
|
20
|
+
name='phone_on_charge',
|
|
21
|
+
field=models.BooleanField(db_index=True, default=False),
|
|
22
|
+
),
|
|
23
|
+
]
|
simo/users/migrations/0037_rename_last_seen_location_datetime_instanceuser_last_seen_and_more.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2024-10-29 13:23
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('users', '0036_instanceuser_phone_on_charge_user_phone_on_charge'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.RenameField(
|
|
14
|
+
model_name='instanceuser',
|
|
15
|
+
old_name='last_seen_location_datetime',
|
|
16
|
+
new_name='last_seen',
|
|
17
|
+
),
|
|
18
|
+
migrations.RemoveField(
|
|
19
|
+
model_name='user',
|
|
20
|
+
name='last_seen_location',
|
|
21
|
+
),
|
|
22
|
+
migrations.RemoveField(
|
|
23
|
+
model_name='user',
|
|
24
|
+
name='last_seen_location_datetime',
|
|
25
|
+
),
|
|
26
|
+
migrations.RemoveField(
|
|
27
|
+
model_name='user',
|
|
28
|
+
name='last_seen_speed_kmh',
|
|
29
|
+
),
|
|
30
|
+
migrations.RemoveField(
|
|
31
|
+
model_name='user',
|
|
32
|
+
name='phone_on_charge',
|
|
33
|
+
),
|
|
34
|
+
migrations.RemoveField(
|
|
35
|
+
model_name='userdevice',
|
|
36
|
+
name='last_seen_location',
|
|
37
|
+
),
|
|
38
|
+
migrations.AddField(
|
|
39
|
+
model_name='userdevicereportlog',
|
|
40
|
+
name='phone_on_charge',
|
|
41
|
+
field=models.BooleanField(db_index=True, default=False),
|
|
42
|
+
),
|
|
43
|
+
migrations.AddField(
|
|
44
|
+
model_name='userdevicereportlog',
|
|
45
|
+
name='speed_kmh',
|
|
46
|
+
field=models.FloatField(default=0),
|
|
47
|
+
),
|
|
48
|
+
migrations.AlterField(
|
|
49
|
+
model_name='userdevice',
|
|
50
|
+
name='last_seen',
|
|
51
|
+
field=models.DateTimeField(auto_now=True, db_index=True),
|
|
52
|
+
),
|
|
53
|
+
]
|
simo/users/migrations/__pycache__/0035_instanceuser_last_seen_speed_kmh_and_more.cpython-38.pyc
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/users/models.py
CHANGED
|
@@ -97,17 +97,22 @@ class InstanceUser(DirtyFieldsMixin, models.Model, OnChangeMixin):
|
|
|
97
97
|
related_name='instance_users',
|
|
98
98
|
)
|
|
99
99
|
role = models.ForeignKey(PermissionsRole, on_delete=models.CASCADE)
|
|
100
|
-
at_home = models.BooleanField(default=False, db_index=True)
|
|
101
100
|
is_active = models.BooleanField(default=True, db_index=True)
|
|
101
|
+
|
|
102
|
+
at_home = models.BooleanField(default=False, db_index=True)
|
|
103
|
+
last_seen = models.DateTimeField(
|
|
104
|
+
null=True, blank=True,
|
|
105
|
+
)
|
|
102
106
|
last_seen_location = PlainLocationField(
|
|
103
107
|
zoom=7, null=True, blank=True, help_text="Sent by user mobile app"
|
|
104
108
|
)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
)
|
|
109
|
+
last_seen_speed_kmh = models.FloatField(default=0)
|
|
110
|
+
phone_on_charge = models.BooleanField(default=False, db_index=True)
|
|
108
111
|
|
|
109
112
|
objects = ActiveInstanceManager()
|
|
110
113
|
|
|
114
|
+
on_change_fields = ('last_seen',)
|
|
115
|
+
|
|
111
116
|
class Meta:
|
|
112
117
|
unique_together = 'user', 'instance'
|
|
113
118
|
|
|
@@ -175,12 +180,6 @@ class User(AbstractBaseUser, SimoAdminMixin):
|
|
|
175
180
|
help_text="Will be placed in /root/.ssh/authorized_keys "
|
|
176
181
|
"if user is active and is master of a hub."
|
|
177
182
|
)
|
|
178
|
-
last_seen_location = PlainLocationField(
|
|
179
|
-
zoom=7, null=True, blank=True, help_text="Sent by user mobile app"
|
|
180
|
-
)
|
|
181
|
-
last_seen_location_datetime = models.DateTimeField(
|
|
182
|
-
null=True, blank=True,
|
|
183
|
-
)
|
|
184
183
|
secret_key = models.CharField(
|
|
185
184
|
max_length=20, db_index=True, default=get_random_string
|
|
186
185
|
)
|
|
@@ -422,8 +421,7 @@ class UserDevice(models.Model, SimoAdminMixin):
|
|
|
422
421
|
os = models.CharField(max_length=100, db_index=True)
|
|
423
422
|
token = models.CharField(max_length=1000, db_index=True, unique=True)
|
|
424
423
|
is_primary = models.BooleanField(default=True, db_index=True)
|
|
425
|
-
last_seen = models.DateTimeField(
|
|
426
|
-
last_seen_location = PlainLocationField(zoom=7, null=True, blank=True)
|
|
424
|
+
last_seen = models.DateTimeField(auto_now=True, db_index=True)
|
|
427
425
|
|
|
428
426
|
class Meta:
|
|
429
427
|
ordering = '-last_seen',
|
|
@@ -445,54 +443,13 @@ class UserDeviceReportLog(models.Model):
|
|
|
445
443
|
help_text="Sent via remote relay if specified, otherwise it's from LAN."
|
|
446
444
|
)
|
|
447
445
|
location = PlainLocationField(zoom=7, null=True, blank=True)
|
|
446
|
+
speed_kmh = models.FloatField(default=0)
|
|
447
|
+
phone_on_charge = models.BooleanField(default=False, db_index=True)
|
|
448
448
|
|
|
449
449
|
class Meta:
|
|
450
450
|
ordering = '-datetime',
|
|
451
451
|
|
|
452
452
|
|
|
453
|
-
@receiver(post_save, sender=UserDeviceReportLog)
|
|
454
|
-
def set_user_at_home(sender, instance, created, **kwargs):
|
|
455
|
-
from simo.core.models import Instance
|
|
456
|
-
if not created:
|
|
457
|
-
return
|
|
458
|
-
|
|
459
|
-
if not instance.location and not instance.relay:
|
|
460
|
-
for item in InstanceUser.objects.filter(
|
|
461
|
-
user__in=instance.user_device.users.all()
|
|
462
|
-
):
|
|
463
|
-
item.at_home = True
|
|
464
|
-
item.save()
|
|
465
|
-
return
|
|
466
|
-
|
|
467
|
-
for hub_instance in Instance.objects.filter(is_active=True):
|
|
468
|
-
try:
|
|
469
|
-
instance_location = Point(
|
|
470
|
-
[float(hub_instance.location.split(',')[0]),
|
|
471
|
-
float(hub_instance.location.split(',')[1])],
|
|
472
|
-
srid=4326
|
|
473
|
-
)
|
|
474
|
-
except:
|
|
475
|
-
return
|
|
476
|
-
try:
|
|
477
|
-
log_location = Point(
|
|
478
|
-
[
|
|
479
|
-
float(instance.location.split(',')[0]),
|
|
480
|
-
float(instance.location.split(',')[1])
|
|
481
|
-
], srid=4326
|
|
482
|
-
)
|
|
483
|
-
except:
|
|
484
|
-
return
|
|
485
|
-
else:
|
|
486
|
-
for item in InstanceUser.objects.filter(
|
|
487
|
-
user__in=instance.user_device.users.all(),
|
|
488
|
-
instance=hub_instance
|
|
489
|
-
):
|
|
490
|
-
item.at_home = distance(
|
|
491
|
-
instance_location, log_location
|
|
492
|
-
).meters < dynamic_settings['users__at_home_radius']
|
|
493
|
-
item.save()
|
|
494
|
-
|
|
495
|
-
|
|
496
453
|
class ComponentPermission(models.Model):
|
|
497
454
|
role = models.ForeignKey(
|
|
498
455
|
PermissionsRole, on_delete=models.CASCADE,
|