simo 2.3.7__py3-none-any.whl → 2.4.1__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/__pycache__/settings.cpython-38.pyc +0 -0
- simo/backups/__pycache__/admin.cpython-38.pyc +0 -0
- simo/backups/__pycache__/dynamic_settings.cpython-38.pyc +0 -0
- simo/backups/__pycache__/models.cpython-38.pyc +0 -0
- simo/backups/admin.py +71 -7
- simo/backups/dynamic_settings.py +0 -7
- simo/backups/migrations/0002_backuplog_backup_level_backup_size.py +32 -0
- simo/backups/migrations/__pycache__/0002_backuplog_backup_level_backup_size.cpython-38.pyc +0 -0
- simo/backups/models.py +14 -1
- simo/backups/tasks.py +102 -26
- simo/core/__pycache__/api.cpython-38.pyc +0 -0
- simo/core/__pycache__/context.cpython-38.pyc +0 -0
- simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/core/__pycache__/models.cpython-38.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/core/__pycache__/views.cpython-38.pyc +0 -0
- simo/core/api.py +2 -7
- simo/core/controllers.py +8 -1
- simo/core/management/_hub_template/hub/supervisor.conf +4 -0
- simo/core/models.py +6 -2
- simo/core/tasks.py +8 -1
- simo/core/views.py +4 -12
- simo/settings.py +1 -0
- simo/users/__pycache__/models.cpython-38.pyc +0 -0
- simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/users/models.py +38 -16
- {simo-2.3.7.dist-info → simo-2.4.1.dist-info}/METADATA +2 -1
- {simo-2.3.7.dist-info → simo-2.4.1.dist-info}/RECORD +32 -30
- {simo-2.3.7.dist-info → simo-2.4.1.dist-info}/LICENSE.md +0 -0
- {simo-2.3.7.dist-info → simo-2.4.1.dist-info}/WHEEL +0 -0
- {simo-2.3.7.dist-info → simo-2.4.1.dist-info}/entry_points.txt +0 -0
- {simo-2.3.7.dist-info → simo-2.4.1.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/backups/admin.py
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
from django.contrib import admin
|
|
2
|
-
from .
|
|
2
|
+
from django.contrib import messages
|
|
3
|
+
from django.utils.timezone import localtime
|
|
4
|
+
from django_object_actions import DjangoObjectActions, action
|
|
5
|
+
from .models import Backup, BackupLog
|
|
3
6
|
|
|
4
7
|
|
|
5
8
|
@admin.register(Backup)
|
|
6
|
-
class BackupAdmin(admin.ModelAdmin):
|
|
7
|
-
list_display = 'datetime', 'device', '
|
|
8
|
-
fields = 'datetime', 'device', 'filepath'
|
|
9
|
-
readonly_fields = 'datetime', 'device', 'filepath'
|
|
10
|
-
list_filter = 'datetime', 'mac',
|
|
9
|
+
class BackupAdmin(DjangoObjectActions, admin.ModelAdmin):
|
|
10
|
+
list_display = 'datetime', 'device', 'level', 'size_display'
|
|
11
|
+
fields = 'datetime', 'device', 'level', 'size_display', 'filepath'
|
|
12
|
+
readonly_fields = 'datetime', 'device', 'level', 'size_display', 'filepath'
|
|
13
|
+
list_filter = 'datetime', 'mac', 'level'
|
|
14
|
+
actions = 'restore',
|
|
15
|
+
changelist_actions = ('backup',)
|
|
11
16
|
|
|
12
17
|
def has_change_permission(self, request, obj=None):
|
|
13
18
|
return False
|
|
@@ -16,4 +21,63 @@ class BackupAdmin(admin.ModelAdmin):
|
|
|
16
21
|
return False
|
|
17
22
|
|
|
18
23
|
def has_delete_permission(self, request, obj=None):
|
|
19
|
-
return False
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
def size_display(self, obj):
|
|
27
|
+
if obj.size > 1024 * 1024 * 1024 * 1024:
|
|
28
|
+
return f'{round(obj.size / (1024 * 1024 * 1024), 2)} Gb'
|
|
29
|
+
elif obj.size > 1024 * 1024 * 1024:
|
|
30
|
+
return f'{round(obj.size / (1024 * 1024), 2)} Mb'
|
|
31
|
+
elif obj.size > 1024 * 1024:
|
|
32
|
+
return f'{round(obj.size / (1024 * 1024), 2)} Kb'
|
|
33
|
+
return f'{obj.size} bytes'
|
|
34
|
+
size_display.short_description = 'size'
|
|
35
|
+
|
|
36
|
+
def restore(self, request, queryset):
|
|
37
|
+
if queryset.count() > 1:
|
|
38
|
+
messages.add_message(
|
|
39
|
+
request, messages.ERROR,
|
|
40
|
+
"Please select one snapshot."
|
|
41
|
+
)
|
|
42
|
+
return
|
|
43
|
+
from simo.backups.tasks import restore_backup
|
|
44
|
+
backup = queryset.first()
|
|
45
|
+
restore_backup.delay(backup.id)
|
|
46
|
+
messages.add_message(
|
|
47
|
+
request, messages.WARNING,
|
|
48
|
+
f"Restore command initiated. "
|
|
49
|
+
f"If things go well, your hub will reboot in ~15 minutes to the "
|
|
50
|
+
f"state it was on {localtime(backup.datetime)}. "
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@action(
|
|
54
|
+
label="Backup now!", # optional
|
|
55
|
+
description="Start backup now!" # optional
|
|
56
|
+
)
|
|
57
|
+
def backup(modeladmin, request, queryset=None):
|
|
58
|
+
from simo.backups.tasks import perform_backup
|
|
59
|
+
perform_backup.delay()
|
|
60
|
+
messages.add_message(
|
|
61
|
+
request, messages.INFO,
|
|
62
|
+
f"Backup command initiated. "
|
|
63
|
+
f"If things go well, you will see "
|
|
64
|
+
f"a new backup in here in less than 10 mins. "
|
|
65
|
+
f"Check backup logs for errors if not."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@admin.register(BackupLog)
|
|
70
|
+
class BackupLogAdmin(admin.ModelAdmin):
|
|
71
|
+
fields = 'datetime', 'level', 'msg'
|
|
72
|
+
readonly_fields = fields
|
|
73
|
+
list_fields = fields
|
|
74
|
+
list_filter = 'datetime', 'level'
|
|
75
|
+
search_fields = 'msg',
|
|
76
|
+
|
|
77
|
+
def has_delete_permission(self, request, obj=None):
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
def has_add_permission(self, request):
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
|
simo/backups/dynamic_settings.py
CHANGED
|
@@ -7,13 +7,6 @@ from dynamic_preferences.registries import global_preferences_registry
|
|
|
7
7
|
backups = Section('backups')
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
@global_preferences_registry.register
|
|
11
|
-
class LastBackupError(StringPreference):
|
|
12
|
-
section = backups
|
|
13
|
-
name = 'last_error'
|
|
14
|
-
default = ''
|
|
15
|
-
|
|
16
|
-
|
|
17
10
|
@global_preferences_registry.register
|
|
18
11
|
class LastBackupCheck(IntegerPreference):
|
|
19
12
|
section = backups
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2024-10-09 07:49
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('backups', '0001_initial'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.CreateModel(
|
|
14
|
+
name='BackupLog',
|
|
15
|
+
fields=[
|
|
16
|
+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
17
|
+
('datetime', models.DateTimeField(auto_now_add=True, db_index=True)),
|
|
18
|
+
('level', models.CharField(choices=[('info', 'INFO'), ('warning', 'WARNING'), ('error', 'ERROR')], default='info')),
|
|
19
|
+
('msg', models.TextField()),
|
|
20
|
+
],
|
|
21
|
+
),
|
|
22
|
+
migrations.AddField(
|
|
23
|
+
model_name='backup',
|
|
24
|
+
name='level',
|
|
25
|
+
field=models.IntegerField(db_index=True, default=0),
|
|
26
|
+
),
|
|
27
|
+
migrations.AddField(
|
|
28
|
+
model_name='backup',
|
|
29
|
+
name='size',
|
|
30
|
+
field=models.IntegerField(default=0),
|
|
31
|
+
),
|
|
32
|
+
]
|
|
Binary file
|
simo/backups/models.py
CHANGED
|
@@ -6,6 +6,8 @@ class Backup(models.Model):
|
|
|
6
6
|
datetime = models.DateTimeField(db_index=True)
|
|
7
7
|
mac = models.CharField(max_length=100, db_index=True)
|
|
8
8
|
filepath = models.CharField(max_length=200)
|
|
9
|
+
level = models.IntegerField(default=0, db_index=True)
|
|
10
|
+
size = models.IntegerField(default=0)
|
|
9
11
|
|
|
10
12
|
class Meta:
|
|
11
13
|
unique_together = 'datetime', 'mac'
|
|
@@ -15,4 +17,15 @@ class Backup(models.Model):
|
|
|
15
17
|
def device(self):
|
|
16
18
|
if self.mac == str(hex(uuid.getnode())):
|
|
17
19
|
return "This machine"
|
|
18
|
-
return self.mac
|
|
20
|
+
return self.mac
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BackupLog(models.Model):
|
|
24
|
+
datetime = models.DateTimeField(db_index=True, auto_now_add=True)
|
|
25
|
+
level = models.CharField(default='info', choices=(
|
|
26
|
+
('info', "INFO"), ('warning', "WARNING"), ('error', "ERROR")
|
|
27
|
+
))
|
|
28
|
+
msg = models.TextField()
|
|
29
|
+
|
|
30
|
+
class Meta:
|
|
31
|
+
ordering = 'datetime',
|
simo/backups/tasks.py
CHANGED
|
@@ -2,6 +2,7 @@ import os, subprocess, json, uuid, datetime, shutil
|
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
3
|
from celeryc import celery_app
|
|
4
4
|
from simo.conf import dynamic_settings
|
|
5
|
+
from simo.core.utils.helpers import get_random_string
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
@celery_app.task
|
|
@@ -9,7 +10,7 @@ def check_backups():
|
|
|
9
10
|
'''
|
|
10
11
|
syncs up backups on external medium to the database
|
|
11
12
|
'''
|
|
12
|
-
from .models import Backup
|
|
13
|
+
from simo.backups.models import Backup, BackupLog
|
|
13
14
|
|
|
14
15
|
try:
|
|
15
16
|
lv_group, lv_name, mountpoint = get_partitions()
|
|
@@ -35,38 +36,39 @@ def check_backups():
|
|
|
35
36
|
continue
|
|
36
37
|
for filename in os.listdir(os.path.join(hub_dir, month_folder)):
|
|
37
38
|
try:
|
|
38
|
-
day, time, back = filename.split('.')
|
|
39
|
+
day, time, level, back = filename.split('.')
|
|
39
40
|
hour, minute, second = time.split('-')
|
|
40
|
-
day, hour, minute, second = \
|
|
41
|
-
int(day), int(hour), int(minute), int(second)
|
|
41
|
+
day, hour, minute, second, level = \
|
|
42
|
+
int(day), int(hour), int(minute), int(second), int(level)
|
|
42
43
|
except:
|
|
43
44
|
continue
|
|
44
45
|
|
|
46
|
+
filepath = os.path.join(hub_dir, month_folder, filename)
|
|
47
|
+
file_stats = os.stat(filepath)
|
|
45
48
|
obj, new = Backup.objects.update_or_create(
|
|
46
49
|
datetime=datetime(
|
|
47
50
|
year, month, day, hour, minute, second,
|
|
48
51
|
tzinfo=timezone.utc
|
|
49
52
|
), mac=hub_mac, defaults={
|
|
50
|
-
'filepath':
|
|
51
|
-
|
|
52
|
-
)
|
|
53
|
+
'filepath': filepath, 'level': level,
|
|
54
|
+
'size': file_stats.st_size
|
|
53
55
|
}
|
|
54
56
|
)
|
|
55
57
|
backups_mentioned.append(obj.id)
|
|
56
58
|
|
|
57
59
|
Backup.objects.all().exclude(id__in=backups_mentioned).delete()
|
|
58
60
|
|
|
59
|
-
dynamic_settings['backups__last_check'] = datetime.now()
|
|
61
|
+
dynamic_settings['backups__last_check'] = int(datetime.now().timestamp())
|
|
60
62
|
|
|
61
63
|
|
|
62
64
|
def create_snap(lv_group, lv_name):
|
|
63
65
|
try:
|
|
64
66
|
return subprocess.check_output(
|
|
65
|
-
f'lvcreate -s -n {lv_name}-snap {lv_group}/{lv_name} -L
|
|
67
|
+
f'lvcreate -s -n {lv_name}-snap {lv_group}/{lv_name} -L 5G',
|
|
66
68
|
shell=True
|
|
67
69
|
).decode()
|
|
68
70
|
except:
|
|
69
|
-
return
|
|
71
|
+
return
|
|
70
72
|
|
|
71
73
|
|
|
72
74
|
def get_lvm_partition(lsblk_data):
|
|
@@ -93,6 +95,7 @@ def get_backup_device(lsblk_data):
|
|
|
93
95
|
|
|
94
96
|
|
|
95
97
|
def get_partitions():
|
|
98
|
+
from simo.backups.models import BackupLog
|
|
96
99
|
|
|
97
100
|
lsblk_data = json.loads(subprocess.check_output(
|
|
98
101
|
'lsblk --output NAME,HOTPLUG,MOUNTPOINT,FSTYPE,TYPE,LABEL,PARTLABEL --json',
|
|
@@ -104,7 +107,9 @@ def get_partitions():
|
|
|
104
107
|
lvm_partition = get_lvm_partition(lsblk_data)
|
|
105
108
|
if not lvm_partition:
|
|
106
109
|
print("No LVM partition!")
|
|
107
|
-
|
|
110
|
+
BackupLog.objects.create(
|
|
111
|
+
level='warning', msg="Can't backup. No LVM partition!"
|
|
112
|
+
)
|
|
108
113
|
return
|
|
109
114
|
|
|
110
115
|
try:
|
|
@@ -114,14 +119,17 @@ def get_partitions():
|
|
|
114
119
|
lv_name = name[split_at + 1:].replace('--', '-')
|
|
115
120
|
except:
|
|
116
121
|
print("Failed to identify LVM partition")
|
|
117
|
-
|
|
118
|
-
'Failed to identify LVM partition
|
|
122
|
+
BackupLog.objects.create(
|
|
123
|
+
level='warning', msg="Can't backup. Failed to identify LVM partition."
|
|
124
|
+
)
|
|
119
125
|
return
|
|
120
126
|
|
|
121
127
|
if not lv_name:
|
|
122
128
|
print("LVM was not found on this system. Abort!")
|
|
123
|
-
|
|
124
|
-
'
|
|
129
|
+
BackupLog.objects.create(
|
|
130
|
+
level='warning',
|
|
131
|
+
msg="Can't backup. Failed to identify LVM partition name."
|
|
132
|
+
)
|
|
125
133
|
return
|
|
126
134
|
|
|
127
135
|
|
|
@@ -130,8 +138,10 @@ def get_partitions():
|
|
|
130
138
|
backup_device = get_backup_device(lsblk_data)
|
|
131
139
|
|
|
132
140
|
if not backup_device:
|
|
133
|
-
|
|
134
|
-
'
|
|
141
|
+
BackupLog.objects.create(
|
|
142
|
+
level='warning',
|
|
143
|
+
msg="Can't backup. No external exFAT backup device on this machine."
|
|
144
|
+
)
|
|
135
145
|
return
|
|
136
146
|
|
|
137
147
|
if lvm_partition.get('partlabel'):
|
|
@@ -159,18 +169,21 @@ def get_partitions():
|
|
|
159
169
|
|
|
160
170
|
@celery_app.task
|
|
161
171
|
def perform_backup():
|
|
162
|
-
|
|
172
|
+
from simo.backups.models import BackupLog
|
|
163
173
|
try:
|
|
164
174
|
lv_group, lv_name, mountpoint = get_partitions()
|
|
165
175
|
except:
|
|
166
176
|
return
|
|
167
177
|
|
|
168
178
|
output = create_snap(lv_group, lv_name)
|
|
169
|
-
if not output:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
if not output or f'Logical volume "{lv_name}-snap" created' not in output:
|
|
180
|
+
try:
|
|
181
|
+
subprocess.check_output(
|
|
182
|
+
f'lvremove -f {lv_group}/{lv_name}-snap',
|
|
183
|
+
shell=True
|
|
184
|
+
)
|
|
185
|
+
except:
|
|
186
|
+
pass
|
|
174
187
|
output = create_snap(lv_group, lv_name)
|
|
175
188
|
|
|
176
189
|
if f'Logical volume "{lv_name}-snap" created' not in output:
|
|
@@ -183,7 +196,7 @@ def perform_backup():
|
|
|
183
196
|
if not os.path.exists(device_backups_path):
|
|
184
197
|
os.makedirs(device_backups_path)
|
|
185
198
|
|
|
186
|
-
now = datetime.
|
|
199
|
+
now = datetime.now()
|
|
187
200
|
level = now.day
|
|
188
201
|
month_folder = os.path.join(
|
|
189
202
|
device_backups_path, f'{now.year}-{now.month}'
|
|
@@ -213,7 +226,7 @@ def perform_backup():
|
|
|
213
226
|
level = 0
|
|
214
227
|
|
|
215
228
|
time_mark = now.strftime("%H-%M-%S")
|
|
216
|
-
backup_file = f"{month_folder}/{now.day}.{time_mark}.back"
|
|
229
|
+
backup_file = f"{month_folder}/{now.day}.{time_mark}.{level}.back"
|
|
217
230
|
snap_mapper = f"/dev/mapper/{lv_group}-{lv_name.replace('-', '--')}--snap"
|
|
218
231
|
label = f"simo {now.strftime('%Y-%m-%d')}"
|
|
219
232
|
dumpdates_file = os.path.join(month_folder, 'dumpdates')
|
|
@@ -253,6 +266,9 @@ def perform_backup():
|
|
|
253
266
|
except:
|
|
254
267
|
try:
|
|
255
268
|
os.remove(backup_file)
|
|
269
|
+
BackupLog.objects.create(
|
|
270
|
+
level='error', msg="Can't backup. Dump failed."
|
|
271
|
+
)
|
|
256
272
|
print("Dump failed!")
|
|
257
273
|
except:
|
|
258
274
|
pass
|
|
@@ -263,7 +279,67 @@ def perform_backup():
|
|
|
263
279
|
)
|
|
264
280
|
if success:
|
|
265
281
|
print("DONE!")
|
|
266
|
-
|
|
282
|
+
BackupLog.objects.create(
|
|
283
|
+
level='info', msg="Backup success!"
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@celery_app.task
|
|
288
|
+
def restore_backup(backup_id):
|
|
289
|
+
from simo.backups.models import Backup, BackupLog
|
|
290
|
+
backup = Backup.objects.get(id=backup_id)
|
|
291
|
+
|
|
292
|
+
try:
|
|
293
|
+
lv_group, lv_name, mpt = get_partitions()
|
|
294
|
+
except:
|
|
295
|
+
BackupLog.objects.create(
|
|
296
|
+
level='error',
|
|
297
|
+
msg="Can't restore. LVM group is not present on this machine."
|
|
298
|
+
)
|
|
299
|
+
return
|
|
300
|
+
|
|
301
|
+
snap_name = f'{lv_name}-{get_random_string(5)}'
|
|
302
|
+
output = create_snap(lv_group, snap_name)
|
|
303
|
+
if not output or f'Logical volume "{lv_name}-snap" created' not in output:
|
|
304
|
+
BackupLog.objects.create(
|
|
305
|
+
level='error',
|
|
306
|
+
msg="Can't restore. Can't create LVM snapshot\n\n" + output
|
|
307
|
+
)
|
|
308
|
+
return
|
|
309
|
+
|
|
310
|
+
if not os.path.exists('/var/backups/simo-main'):
|
|
311
|
+
os.makedirs('/var/backups/simo-main')
|
|
312
|
+
|
|
313
|
+
subprocess.call('umount /var/backup/simo-main', shell=True)
|
|
314
|
+
|
|
315
|
+
subprocess.call('rm -rf /var/backup/simo-main/*', shell=True)
|
|
316
|
+
|
|
317
|
+
subprocess.call(
|
|
318
|
+
f"mount /dev/mapper/{lv_group}-{snap_name.replace('-', '--')} /var/backup/simo-main",
|
|
319
|
+
shell=True, stdout=subprocess.PIPE
|
|
320
|
+
)
|
|
321
|
+
try:
|
|
322
|
+
subprocess.call(
|
|
323
|
+
f"restore -C -v -b 1024 -f {backup.filepath} -D /var/backup/simo-main",
|
|
324
|
+
shell=True
|
|
325
|
+
)
|
|
326
|
+
subprocess.call(
|
|
327
|
+
f"lvconvert --mergesnapshot {lv_group}/{snap_name}",
|
|
328
|
+
shell=True
|
|
329
|
+
)
|
|
330
|
+
except Exception as e:
|
|
331
|
+
BackupLog.objects.create(
|
|
332
|
+
level='error',
|
|
333
|
+
msg="Can't restore. \n\n" + str(e)
|
|
334
|
+
)
|
|
335
|
+
subprocess.call('umount /var/backup/simo-main', shell=True)
|
|
336
|
+
subprocess.call(
|
|
337
|
+
f"lvremove -f {lv_group}/{snap_name}", shell=True,
|
|
338
|
+
stdout=subprocess.PIPE
|
|
339
|
+
)
|
|
340
|
+
else:
|
|
341
|
+
print("All good! REBOOT!")
|
|
342
|
+
subprocess.call('reboot')
|
|
267
343
|
|
|
268
344
|
|
|
269
345
|
@celery_app.on_after_finalize.connect
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/core/api.py
CHANGED
|
@@ -262,13 +262,11 @@ class ComponentViewSet(
|
|
|
262
262
|
def controller(self, request, pk=None, *args, **kwargs):
|
|
263
263
|
start = time.time()
|
|
264
264
|
component = self.get_object()
|
|
265
|
-
print(f"Component retrieved in : {time.time() - start}s")
|
|
266
265
|
data = request.data
|
|
267
266
|
if not isinstance(request.data, dict):
|
|
268
267
|
data = data.dict()
|
|
269
268
|
request_data = restore_json(data)
|
|
270
269
|
resp = self.perform_controller_method(request_data, component)
|
|
271
|
-
print(f"Command executed in : {time.time() - start}s")
|
|
272
270
|
return resp
|
|
273
271
|
|
|
274
272
|
@action(detail=False, methods=['post'])
|
|
@@ -359,8 +357,6 @@ class ComponentHistoryViewSet(InstanceMixin, viewsets.ReadOnlyModelViewSet):
|
|
|
359
357
|
minutes=start_from.minute, seconds=start_from.second
|
|
360
358
|
)
|
|
361
359
|
|
|
362
|
-
print("START FROM: ", start_from)
|
|
363
|
-
|
|
364
360
|
return RESTResponse(
|
|
365
361
|
self.get_aggregated_data(
|
|
366
362
|
component, request.GET['interval'], start_from
|
|
@@ -645,9 +641,9 @@ class StatesViewSet(InstanceMixin, viewsets.GenericViewSet):
|
|
|
645
641
|
component_values = get_components_queryset(
|
|
646
642
|
self.instance, request.user
|
|
647
643
|
).filter(zone__instance=self.instance).values(
|
|
648
|
-
'id', '
|
|
644
|
+
'id', 'last_change', 'last_modified',
|
|
649
645
|
'arm_status', 'battery_level', 'alive', 'error_msg',
|
|
650
|
-
'meta'
|
|
646
|
+
'value', 'meta'
|
|
651
647
|
)
|
|
652
648
|
for vals in component_values:
|
|
653
649
|
vals['last_change'] = datetime.datetime.timestamp(
|
|
@@ -665,7 +661,6 @@ class StatesViewSet(InstanceMixin, viewsets.GenericViewSet):
|
|
|
665
661
|
cat['last_modified']
|
|
666
662
|
)
|
|
667
663
|
|
|
668
|
-
|
|
669
664
|
return RESTResponse({
|
|
670
665
|
'zones': Zone.objects.filter(instance=self.instance).values(
|
|
671
666
|
'id', 'name'
|
simo/core/controllers.py
CHANGED
|
@@ -539,6 +539,14 @@ class Dimmer(ControllerBase, TimerMixin, OnOffPokerMixin):
|
|
|
539
539
|
default_value = 0
|
|
540
540
|
default_value_units = '%'
|
|
541
541
|
|
|
542
|
+
def _receive_from_device(self, value, *args, **kwargs):
|
|
543
|
+
if isinstance(value, bool):
|
|
544
|
+
if value:
|
|
545
|
+
value = self.component.config.get('max', 100.0)
|
|
546
|
+
else:
|
|
547
|
+
value = self.component.config.get('min', 0)
|
|
548
|
+
return super()._receive_from_device(value, *args, **kwargs)
|
|
549
|
+
|
|
542
550
|
def _prepare_for_send(self, value):
|
|
543
551
|
if isinstance(value, bool):
|
|
544
552
|
if value:
|
|
@@ -579,7 +587,6 @@ class Dimmer(ControllerBase, TimerMixin, OnOffPokerMixin):
|
|
|
579
587
|
self.turn_on()
|
|
580
588
|
|
|
581
589
|
|
|
582
|
-
|
|
583
590
|
class DimmerPlus(ControllerBase, TimerMixin, OnOffPokerMixin):
|
|
584
591
|
name = _("Dimmer Plus")
|
|
585
592
|
base_type = 'dimmer-plus'
|
simo/core/models.py
CHANGED
|
@@ -502,7 +502,6 @@ def is_in_alarm(self):
|
|
|
502
502
|
action_type='comp_value', value=self.value
|
|
503
503
|
)
|
|
504
504
|
action_performed = True
|
|
505
|
-
self.last_change = timezone.now()
|
|
506
505
|
if 'arm_status' in dirty_fields:
|
|
507
506
|
ComponentHistory.objects.create(
|
|
508
507
|
component=self, type='security',
|
|
@@ -514,11 +513,16 @@ def is_in_alarm(self):
|
|
|
514
513
|
action_type='security', value=self.value
|
|
515
514
|
)
|
|
516
515
|
action_performed = True
|
|
517
|
-
self.last_change = timezone.now()
|
|
518
516
|
if action_performed:
|
|
519
517
|
actor.last_action = timezone.now()
|
|
520
518
|
actor.save()
|
|
521
519
|
|
|
520
|
+
if any(
|
|
521
|
+
f in dirty_fields for f in
|
|
522
|
+
['value', 'arm_status', 'battery_level', 'alive', 'meta']
|
|
523
|
+
):
|
|
524
|
+
self.last_change = timezone.now()
|
|
525
|
+
|
|
522
526
|
modifying_fields = (
|
|
523
527
|
'name', 'icon', 'zone', 'category', 'config', 'meta',
|
|
524
528
|
'value_units', 'slaves', 'show_in_app', 'alarm_category'
|
simo/core/tasks.py
CHANGED
|
@@ -14,19 +14,26 @@ from django.template.loader import render_to_string
|
|
|
14
14
|
from celeryc import celery_app
|
|
15
15
|
from django.utils import timezone
|
|
16
16
|
from actstream.models import Action
|
|
17
|
-
from easy_thumbnails.files import get_thumbnailer
|
|
18
17
|
from simo.conf import dynamic_settings
|
|
19
18
|
from simo.core.utils.helpers import get_self_ip
|
|
20
19
|
from simo.users.models import PermissionsRole, InstanceUser
|
|
21
20
|
from .models import Instance, Component, ComponentHistory, HistoryAggregate
|
|
22
21
|
|
|
23
22
|
|
|
23
|
+
@celery_app.task
|
|
24
24
|
def supervisor_restart():
|
|
25
25
|
time.sleep(2)
|
|
26
26
|
subprocess.run(['redis-cli', 'flushall'])
|
|
27
27
|
subprocess.run(['supervisorctl', 'restart', 'all'])
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
@celery_app.task
|
|
31
|
+
def hardware_reboot():
|
|
32
|
+
time.sleep(2)
|
|
33
|
+
print("Reboot system")
|
|
34
|
+
subprocess.run(['reboot'])
|
|
35
|
+
|
|
36
|
+
|
|
30
37
|
def save_config(data):
|
|
31
38
|
|
|
32
39
|
vpn_change = False
|
simo/core/views.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import time
|
|
2
|
-
import threading
|
|
3
|
-
import subprocess
|
|
4
2
|
import re
|
|
5
3
|
from django.contrib.auth.decorators import login_required
|
|
6
4
|
from django.urls import reverse
|
|
@@ -9,7 +7,7 @@ from django.http import HttpResponse, Http404, JsonResponse
|
|
|
9
7
|
from django.contrib import messages
|
|
10
8
|
from simo.conf import dynamic_settings
|
|
11
9
|
from .models import Instance
|
|
12
|
-
from .tasks import update as update_task, supervisor_restart
|
|
10
|
+
from .tasks import update as update_task, supervisor_restart, hardware_reboot
|
|
13
11
|
from .middleware import introduce_instance
|
|
14
12
|
|
|
15
13
|
|
|
@@ -21,7 +19,7 @@ def update(request):
|
|
|
21
19
|
if not request.user.is_superuser:
|
|
22
20
|
raise Http404()
|
|
23
21
|
messages.warning(request, "Hub update initiated. ")
|
|
24
|
-
|
|
22
|
+
update_task.delay()
|
|
25
23
|
if request.META.get('HTTP_REFERER'):
|
|
26
24
|
return redirect(request.META.get('HTTP_REFERER'))
|
|
27
25
|
return redirect(reverse('admin:index'))
|
|
@@ -35,7 +33,7 @@ def restart(request):
|
|
|
35
33
|
request, "Hub restart initiated. "
|
|
36
34
|
"Your hub will be out of operation for next few seconds."
|
|
37
35
|
)
|
|
38
|
-
|
|
36
|
+
supervisor_restart.delay()
|
|
39
37
|
if request.META.get('HTTP_REFERER'):
|
|
40
38
|
return redirect(request.META.get('HTTP_REFERER'))
|
|
41
39
|
return redirect(reverse('admin:index'))
|
|
@@ -50,13 +48,7 @@ def reboot(request):
|
|
|
50
48
|
request,
|
|
51
49
|
"Hub reboot initiated. Hub will be out of reach for a minute or two."
|
|
52
50
|
)
|
|
53
|
-
|
|
54
|
-
def hardware_reboot():
|
|
55
|
-
time.sleep(2)
|
|
56
|
-
print("Reboot system")
|
|
57
|
-
subprocess.run(['reboot'])
|
|
58
|
-
|
|
59
|
-
threading.Thread(target=hardware_reboot).start()
|
|
51
|
+
hardware_reboot.delay()
|
|
60
52
|
if request.META.get('HTTP_REFERER'):
|
|
61
53
|
return redirect(request.META.get('HTTP_REFERER'))
|
|
62
54
|
return redirect(reverse('admin:index'))
|
simo/settings.py
CHANGED
|
Binary file
|
|
Binary file
|
simo/users/models.py
CHANGED
|
@@ -252,10 +252,15 @@ class User(AbstractBaseUser, SimoAdminMixin):
|
|
|
252
252
|
'''Used by API serializer to get users role on a given instance.'''
|
|
253
253
|
if not self._instance:
|
|
254
254
|
return None
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
cache_key = f'user-{self.id}_instance-{self._instance.id}-role-id'
|
|
256
|
+
cached_val = cache.get(cache_key, 'expired')
|
|
257
|
+
if cached_val == 'expired':
|
|
258
|
+
for role in self.roles.all().select_related('instance'):
|
|
259
|
+
if role.instance == self._instance:
|
|
260
|
+
cached_val = role.id
|
|
261
|
+
cache.set(cache_key, role.id, 20)
|
|
262
|
+
return cached_val
|
|
263
|
+
return cached_val
|
|
259
264
|
|
|
260
265
|
@role_id.setter
|
|
261
266
|
def role_id(self, id):
|
|
@@ -290,7 +295,6 @@ class User(AbstractBaseUser, SimoAdminMixin):
|
|
|
290
295
|
)
|
|
291
296
|
])
|
|
292
297
|
cache.set(cache_key, instances, 10)
|
|
293
|
-
print("INSTANCES: ", instances)
|
|
294
298
|
return instances
|
|
295
299
|
|
|
296
300
|
@property
|
|
@@ -301,26 +305,44 @@ class User(AbstractBaseUser, SimoAdminMixin):
|
|
|
301
305
|
|
|
302
306
|
@property
|
|
303
307
|
def is_active(self):
|
|
304
|
-
if
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
308
|
+
if not self._instance:
|
|
309
|
+
cache_key = f'user-{self.id}_is_active'
|
|
310
|
+
else:
|
|
311
|
+
cache_key = f'user-{self.id}_is_active_instance-{self._instance.id}'
|
|
312
|
+
cached_value = cache.get(cache_key, 'expired')
|
|
313
|
+
if cached_value == 'expired':
|
|
314
|
+
if self.is_master and not self.instance_roles.all():
|
|
315
|
+
# Master who have no roles on any instance are in GOD mode!
|
|
316
|
+
# It can not be disabled by anybody, nor it is seen by anybody. :)
|
|
317
|
+
cached_value = True
|
|
318
|
+
elif self._instance:
|
|
319
|
+
cached_value = bool(
|
|
320
|
+
self.instance_roles.filter(
|
|
321
|
+
instance=self._instance, is_active=True
|
|
322
|
+
).first()
|
|
323
|
+
)
|
|
324
|
+
else:
|
|
325
|
+
cached_value = any(
|
|
326
|
+
[ir.is_active for ir in self.instance_roles.all()]
|
|
327
|
+
)
|
|
328
|
+
cache.set(cache_key, cached_value, 20)
|
|
329
|
+
return cached_value
|
|
315
330
|
|
|
316
331
|
|
|
317
332
|
@is_active.setter
|
|
318
333
|
def is_active(self, val):
|
|
319
334
|
if not self._instance:
|
|
320
335
|
return
|
|
336
|
+
|
|
321
337
|
self.instance_roles.filter(
|
|
322
338
|
instance=self._instance
|
|
323
339
|
).update(is_active=bool(val))
|
|
340
|
+
cache_key = f'user-{self.id}_is_active_instance-{self._instance.id}'
|
|
341
|
+
try:
|
|
342
|
+
cache.delete(cache_key)
|
|
343
|
+
except:
|
|
344
|
+
pass
|
|
345
|
+
|
|
324
346
|
rebuild_authorized_keys()
|
|
325
347
|
|
|
326
348
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: simo
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Summary: Smart Home on Steroids!
|
|
5
5
|
Author-email: Simanas Venčkauskas <simanas@simo.io>
|
|
6
6
|
Project-URL: Homepage, https://simo.io
|
|
@@ -49,4 +49,5 @@ Requires-Dist: django-markdownify ==0.9.5
|
|
|
49
49
|
Requires-Dist: django-activity-stream ==2.0.0
|
|
50
50
|
Requires-Dist: gunicorn ==23.0.0
|
|
51
51
|
Requires-Dist: python-crontab ==3.2.0
|
|
52
|
+
Requires-Dist: django-object-actions ==4.3.0
|
|
52
53
|
|
|
@@ -3,31 +3,33 @@ simo/asgi.py,sha256=L8CUVZLM32IMzWDZ4IShdDN-m69t7oxAUeHods4-xNM,822
|
|
|
3
3
|
simo/celeryc.py,sha256=eab7_e9rw0c__DCeoUFUh_tjAGVlulxVrk75BaJf57Q,1512
|
|
4
4
|
simo/conf.py,sha256=H2BhXAV8MEDVXF8AbkaLSfR4ULd-9_bS4bnhE5sE5fg,112
|
|
5
5
|
simo/scripting.py,sha256=PVIkGsiMDWj4CNTbOM3rq7pJ6ruavuns-ZMU7VudLa4,923
|
|
6
|
-
simo/settings.py,sha256=
|
|
6
|
+
simo/settings.py,sha256=BcirAAKau1KiJ5qKTD0ni8hTk3jHAASBcUCBNZPGNCk,6962
|
|
7
7
|
simo/urls.py,sha256=fRmAsNQ_pzFloimLmxNeDcR6hHRJ3rOoZ3kGy8zOQ_A,2402
|
|
8
8
|
simo/__pycache__/__init__.cpython-38.pyc,sha256=j81de0BqHMr6bs0C7cuYrXl7HwtK_vv8hDEtAdSwDJc,153
|
|
9
9
|
simo/__pycache__/asgi.cpython-38.pyc,sha256=5W_YSKOIrRd6NQQuJDuA3Yuj688GzirXVVOyLe8wJIQ,845
|
|
10
10
|
simo/__pycache__/celeryc.cpython-38.pyc,sha256=eSRoaKwfYlxVaxAiwqpQ2ndEcx7W-VpZtbxRFSV8UYg,1653
|
|
11
11
|
simo/__pycache__/conf.cpython-38.pyc,sha256=MYP2yk3ULxiYwZsZR6tCLjKnU-z03A3avzQzIn66y3k,273
|
|
12
|
-
simo/__pycache__/settings.cpython-38.pyc,sha256=
|
|
12
|
+
simo/__pycache__/settings.cpython-38.pyc,sha256=Mi9iweMgAZ16ppKmiK0eFSDYsrY6XRHFpXXHK8is-Pc,6110
|
|
13
13
|
simo/__pycache__/urls.cpython-38.pyc,sha256=u0x6EqT8S1YfDOSPgbI8Kf-RDlveY9OV-EDXMYKAQ7w,2125
|
|
14
14
|
simo/__pycache__/wsgi.cpython-38.pyc,sha256=TpRxO7VM_ql31hbKphVdanydC5RI1nHB4l0QA2pdWxo,322
|
|
15
15
|
simo/backups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
simo/backups/admin.py,sha256=
|
|
17
|
-
simo/backups/dynamic_settings.py,sha256=
|
|
18
|
-
simo/backups/models.py,sha256=
|
|
19
|
-
simo/backups/tasks.py,sha256=
|
|
16
|
+
simo/backups/admin.py,sha256=2079VRAih91_F4k2yIi8068Po2QtTTfEqI2r0yQM7Zs,2797
|
|
17
|
+
simo/backups/dynamic_settings.py,sha256=Q52RLa3UQsmAhqkwR16cM6pbBnIbXqmVQ2oIUP2ZVD0,416
|
|
18
|
+
simo/backups/models.py,sha256=XNaGprjSS3XKHMu36PZj11kjT4AtO0R5LDWfRGY7H-k,875
|
|
19
|
+
simo/backups/tasks.py,sha256=7A5sJtY-ratqf9QSv4wuhe66uQijW6YVRw_1n5579eA,11041
|
|
20
20
|
simo/backups/__pycache__/__init__.cpython-38.pyc,sha256=vzOf-JIMeZ6P85FyvTpYev3mscFosUy-SJTshcQbOHU,161
|
|
21
|
-
simo/backups/__pycache__/admin.cpython-38.pyc,sha256=
|
|
22
|
-
simo/backups/__pycache__/dynamic_settings.cpython-38.pyc,sha256=
|
|
23
|
-
simo/backups/__pycache__/models.cpython-38.pyc,sha256=
|
|
21
|
+
simo/backups/__pycache__/admin.cpython-38.pyc,sha256=eUSpoTgvrQ2Dfq87G6ljvij9KUKZ6jibSuPko0-7jW8,3172
|
|
22
|
+
simo/backups/__pycache__/dynamic_settings.cpython-38.pyc,sha256=51gJFjn_XqQBRoHeubo6ppb9pNuFQKI5hAR0ms9flE8,731
|
|
23
|
+
simo/backups/__pycache__/models.cpython-38.pyc,sha256=1c0gqn5zP9OfQfqDLXbXej7v1n5mBMFjWDewxrPPHEg,1476
|
|
24
24
|
simo/backups/migrations/0001_initial.py,sha256=0LzCusTUyYf61ksiepdnqXIuYYNTNd_djh_Wa484HA0,770
|
|
25
|
+
simo/backups/migrations/0002_backuplog_backup_level_backup_size.py,sha256=w9T9MQWuecy91OZE1fMExriwPuXA8HMPKsPwXhmC8_k,1023
|
|
25
26
|
simo/backups/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
27
|
simo/backups/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=jumm0xjwRhiYPOAXi4o-AGpcRi-GVOj5bSJkOTYtT7w,834
|
|
28
|
+
simo/backups/migrations/__pycache__/0002_backuplog_backup_level_backup_size.cpython-38.pyc,sha256=moD04sv6Fc6crxZwTQHMtJ18iFG2O_zrqbMBzskPhgw,1024
|
|
27
29
|
simo/backups/migrations/__pycache__/__init__.cpython-38.pyc,sha256=Lz1fs6V05h2AoxTOLNye0do9bEMnyuaXB_hHOjG5-HU,172
|
|
28
30
|
simo/core/__init__.py,sha256=_s2TjJfQImsMrTIxqLAx9AZie1Ojmm6sCHASdl3WLGU,50
|
|
29
31
|
simo/core/admin.py,sha256=hoJ0OhfWL9T0d6JCY6_Gm3GR-nrgtDR2UQM67rMsjiE,18141
|
|
30
|
-
simo/core/api.py,sha256=
|
|
32
|
+
simo/core/api.py,sha256=BU-Lkt7m5sj5nzCqCi6uDU__xzBU97MwugL7FghI-es,27775
|
|
31
33
|
simo/core/api_auth.py,sha256=vCxvczA8aWNcW0VyKs5WlC_ytlqeGP_H_hkKUNVkCwM,1247
|
|
32
34
|
simo/core/api_meta.py,sha256=EaiY-dCADP__9MvLpoHvhjytFT92IrxPZDv95xgqasU,4955
|
|
33
35
|
simo/core/app_widgets.py,sha256=4Lh9FDzdkfh_mccJMe09dyRTT3Uqf9VXwbkurJ9E9oQ,2115
|
|
@@ -36,7 +38,7 @@ simo/core/auto_urls.py,sha256=nNXEgLAAAQAhRWQDA9AbDtw-zcPKmu_pufJaSa8g818,1102
|
|
|
36
38
|
simo/core/autocomplete_views.py,sha256=JT5LA2_Wtr60XYSAIqaXFKFYPjrmkEf6yunXD9y2zco,4022
|
|
37
39
|
simo/core/base_types.py,sha256=qVh6MrXZEfN7bFOyFftC7u0yyz0PkvpsjllLBc6SCp4,616
|
|
38
40
|
simo/core/context.py,sha256=snfPIGcZQTrx8iiZc5PI91A0dRQH6y5kH4uG_lfhU6Q,1486
|
|
39
|
-
simo/core/controllers.py,sha256=
|
|
41
|
+
simo/core/controllers.py,sha256=KB-nzDR60x5EPwtfraIRE_CoXQXpnvjqcLFw6nqcOtQ,29930
|
|
40
42
|
simo/core/dynamic_settings.py,sha256=bUs58XEZOCIEhg1TigR3LmYggli13KMryBZ9pC7ugAQ,1872
|
|
41
43
|
simo/core/events.py,sha256=LvtonJGNyCb6HLozs4EG0WZItnDwNdtnGQ4vTcnKvUs,4438
|
|
42
44
|
simo/core/filters.py,sha256=ghtOZcrwNAkIyF5_G9Sn73NkiI71mXv0NhwCk4IyMIM,411
|
|
@@ -46,21 +48,21 @@ simo/core/gateways.py,sha256=m0eS3XjVe34Dge6xtoCq16kFWCKJcdQrT0JW0REqoq8,3715
|
|
|
46
48
|
simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
|
|
47
49
|
simo/core/managers.py,sha256=n-b3I4uXzfHKTeB1VMjSaMsDUxp8FegFJwnbV1IsWQ4,3019
|
|
48
50
|
simo/core/middleware.py,sha256=1xKsaN0Ro5Fb2slB9FnlQdlDEdO4_kUGdnhA8rccBsA,2730
|
|
49
|
-
simo/core/models.py,sha256=
|
|
51
|
+
simo/core/models.py,sha256=5UwGbe2UYj15yM-P6QCIO1HErEdzpNF3nHJnInBGdpM,21666
|
|
50
52
|
simo/core/permissions.py,sha256=v0iJM4LOeYoEfMiw3OLPYio272G1aUEAg_z9Wd1q5m0,2993
|
|
51
53
|
simo/core/routing.py,sha256=X1_IHxyA-_Q7hw1udDoviVP4_FSBDl8GYETTC2zWTbY,499
|
|
52
54
|
simo/core/serializers.py,sha256=1hcCnLnItw8T3jLWivKBjSAt7kwqjcOYE0vaepw55Z0,20966
|
|
53
55
|
simo/core/signal_receivers.py,sha256=9-qFCCeSLcMFEMg6QUtKOVgUsoNoqhzGoI98nuNSTEo,6228
|
|
54
56
|
simo/core/socket_consumers.py,sha256=n7VE2Fvqt4iEAYLTRbTPOcI-7tszMAADu7gimBxB-Fg,9635
|
|
55
57
|
simo/core/storage.py,sha256=_5igjaoWZAiExGWFEJMElxUw55DzJG1jqFty33xe8BE,342
|
|
56
|
-
simo/core/tasks.py,sha256
|
|
58
|
+
simo/core/tasks.py,sha256=-swS6mJIJhAbdgKgPkGwYNn03-E9MKM_SO18mI7sml0,14433
|
|
57
59
|
simo/core/todos.py,sha256=eYVXfLGiapkxKK57XuviSNe3WsUYyIWZ0hgQJk7ThKo,665
|
|
58
60
|
simo/core/types.py,sha256=WJEq48mIbFi_5Alt4wxWMGXxNxUTXqfQU5koH7wqHHI,1108
|
|
59
|
-
simo/core/views.py,sha256=
|
|
61
|
+
simo/core/views.py,sha256=3SRZr00fyLQf8ja3U-9eekKt-ld5TvU1WQqUWprXfQ4,2390
|
|
60
62
|
simo/core/widgets.py,sha256=J9e06C6I22F6xKic3VMgG7WeX07glAcl-4bF2Mg180A,2827
|
|
61
63
|
simo/core/__pycache__/__init__.cpython-38.pyc,sha256=ZJFM_XN0RmJMULQulgA_wFiOnEtsMoedcOWnXjH-Y8o,208
|
|
62
64
|
simo/core/__pycache__/admin.cpython-38.pyc,sha256=c5Q8YfIiLk25yVCxQvNsH-lwuOwfQa88X6xDYjOBogU,13428
|
|
63
|
-
simo/core/__pycache__/api.cpython-38.pyc,sha256=
|
|
65
|
+
simo/core/__pycache__/api.cpython-38.pyc,sha256=w4uvlv0sZtY-xlVJ4UNLC60EyP8fRFGqUwKNc9XcrrE,21694
|
|
64
66
|
simo/core/__pycache__/api_auth.cpython-38.pyc,sha256=6M9Cl_ha4y_Vf8Rv4GMYL8dcBCmp0KzYi6jn3SQTgys,1712
|
|
65
67
|
simo/core/__pycache__/api_meta.cpython-38.pyc,sha256=VYx5ZeDyNBI4B_CBEIhV5B3GnLsMOx9s3rNZTSMODco,3703
|
|
66
68
|
simo/core/__pycache__/app_widgets.cpython-38.pyc,sha256=vUCEAYqppjgRZYMs6pTuSxWWuZxreLygPuPBGw044dQ,3643
|
|
@@ -68,8 +70,8 @@ simo/core/__pycache__/apps.cpython-38.pyc,sha256=JL0BEqgXcSQvMlcK48PBpPfyDEkPMdO
|
|
|
68
70
|
simo/core/__pycache__/auto_urls.cpython-38.pyc,sha256=Tyf8PYHq5YqSwTp25Joy-eura_Fm86fpX9zKLSklhvo,872
|
|
69
71
|
simo/core/__pycache__/autocomplete_views.cpython-38.pyc,sha256=hJ6JILI1LqrAtpQMvxnLvljGdW1v1gpvBsD79vFkZ58,3972
|
|
70
72
|
simo/core/__pycache__/base_types.cpython-38.pyc,sha256=hmq22vvGyCmhbYyuV6bFAOOSIupspgW5yq_VzqWd-vY,759
|
|
71
|
-
simo/core/__pycache__/context.cpython-38.pyc,sha256=
|
|
72
|
-
simo/core/__pycache__/controllers.cpython-38.pyc,sha256=
|
|
73
|
+
simo/core/__pycache__/context.cpython-38.pyc,sha256=ck1FcBljLB4__5F6poS2tEEn8IDDgK7pU3FcXDPc_mI,1329
|
|
74
|
+
simo/core/__pycache__/controllers.cpython-38.pyc,sha256=EOs8vHYOjzV9-zrOLr6FS49fywErGkegPteuKt2FqGU,26860
|
|
73
75
|
simo/core/__pycache__/dynamic_settings.cpython-38.pyc,sha256=wGpnscX1DxFpRl54MQURhjz2aD3NJohSzw9JCFnzh2Y,2384
|
|
74
76
|
simo/core/__pycache__/events.cpython-38.pyc,sha256=A1Axx-qftd1r7st7wkO3DkvTdt9-RkcJe5KJhpzJVk8,5109
|
|
75
77
|
simo/core/__pycache__/filters.cpython-38.pyc,sha256=VIMADCBiYhziIyRmxAyUDJluZvuZmiC4bNYWTRsGSao,721
|
|
@@ -79,16 +81,16 @@ simo/core/__pycache__/gateways.cpython-38.pyc,sha256=D1ooHL-iSpQrxnD8uAl4xWFJmm-
|
|
|
79
81
|
simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
|
|
80
82
|
simo/core/__pycache__/managers.cpython-38.pyc,sha256=6RTIxyjOgpQGtAqcUyE2vFPS09w1V5Wmd_vOV7rHRRI,3370
|
|
81
83
|
simo/core/__pycache__/middleware.cpython-38.pyc,sha256=OH2wvHHavuS4YYCgyvJd4GTrTCEV577CQ-ZBrTFuy6Y,2408
|
|
82
|
-
simo/core/__pycache__/models.cpython-38.pyc,sha256=
|
|
84
|
+
simo/core/__pycache__/models.cpython-38.pyc,sha256=h8vUx9VV721RwNmMMjKc8OY3qva4h-Ydc7Gr60un_tM,18017
|
|
83
85
|
simo/core/__pycache__/permissions.cpython-38.pyc,sha256=fH4iyqd9DdzRLEu2b621-FeM-napR0M7hzBUTHo9Q3g,2972
|
|
84
86
|
simo/core/__pycache__/routing.cpython-38.pyc,sha256=3T3FPJ8Cn99xZCGvMyg2xjl7al-Shm9CelbSpkJtNP8,599
|
|
85
87
|
simo/core/__pycache__/serializers.cpython-38.pyc,sha256=xsGMcx9ISHmvY_PzWqosUdkWRBNqYKOtVkFDYNEHc1A,19479
|
|
86
88
|
simo/core/__pycache__/signal_receivers.cpython-38.pyc,sha256=3Bt9S47DR_ZFS3O-crElFgLLXPIYyDgPIc2ibwEkaic,4904
|
|
87
89
|
simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=NJUr7nRyHFvmAumxxWpsod5wzVVZM99rCEuJs1utHA4,8432
|
|
88
90
|
simo/core/__pycache__/storage.cpython-38.pyc,sha256=9R1Xu0FJDflfRXUPsqEgt0SpwiP7FGk7HaR8s8XRyI8,721
|
|
89
|
-
simo/core/__pycache__/tasks.cpython-38.pyc,sha256=
|
|
91
|
+
simo/core/__pycache__/tasks.cpython-38.pyc,sha256=t2WCeMMfyXe9pdJVU-QP6g8pOP1iXYljjQl20AF2Kew,9968
|
|
90
92
|
simo/core/__pycache__/todos.cpython-38.pyc,sha256=lOqGZ58siHM3isoJV4r7sg8igrfE9fFd-jSfeBa0AQI,253
|
|
91
|
-
simo/core/__pycache__/views.cpython-38.pyc,sha256=
|
|
93
|
+
simo/core/__pycache__/views.cpython-38.pyc,sha256=ySH8l-clhUdpkIVm9aMQOlt1VoZoJgNrqoE_sEiYMR8,2495
|
|
92
94
|
simo/core/__pycache__/widgets.cpython-38.pyc,sha256=sR0ZeHCHrhnNDBJuRrxp3zUsfBp0xrtF0xrK2TkQv1o,3520
|
|
93
95
|
simo/core/db_backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
96
|
simo/core/db_backend/base.py,sha256=wY5jsZ8hQpwot3o-7JNtDe33xy-vlfMLXa011htDojI,601
|
|
@@ -147,7 +149,7 @@ simo/core/management/_hub_template/hub/celeryc.py,sha256=3ksDXftIZKJ4Cq9WNKJERdZ
|
|
|
147
149
|
simo/core/management/_hub_template/hub/manage.py,sha256=PNNlw3EVeIJDgkG0l-klqoxsKWfTYWG9jzRG0upmAaI,620
|
|
148
150
|
simo/core/management/_hub_template/hub/nginx.conf,sha256=40hvXL42MeiqqkLURNcDQsRudv1dNFLJnvb2-Y3RCkk,2394
|
|
149
151
|
simo/core/management/_hub_template/hub/settings.py,sha256=4QhvhbtLRxHvAntwqG_qeAAtpDUqKvN4jzw9u3vqff8,361
|
|
150
|
-
simo/core/management/_hub_template/hub/supervisor.conf,sha256=
|
|
152
|
+
simo/core/management/_hub_template/hub/supervisor.conf,sha256=MOsMo8QjBKUl5EUWG4GrA6BjLc4s7pU17xAKY2uGjZU,1938
|
|
151
153
|
simo/core/management/_hub_template/hub/urls.py,sha256=Ydm-1BkYAzWeEF-MKSDIFf-7aE4qNLPm48-SA51XgJQ,25
|
|
152
154
|
simo/core/management/_hub_template/hub/wsgi.py,sha256=Lo-huLHnMDTxSmMBOodVFMWBls9poddrV2KRzXU0xGo,280
|
|
153
155
|
simo/core/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -10416,7 +10418,7 @@ simo/users/auto_urls.py,sha256=lcJvteBsbHQMJieZpDz-63tDYejLApqsW3CUnDakd7k,272
|
|
|
10416
10418
|
simo/users/dynamic_settings.py,sha256=sEIsi4yJw3kH46Jq_aOkSuK7QTfQACGUE-lkyBogCaM,570
|
|
10417
10419
|
simo/users/managers.py,sha256=M_51bk9z4jn8e2Ci3pJfIqbf6cRNqfQNSOAg0vPl6Vo,175
|
|
10418
10420
|
simo/users/middleware.py,sha256=GMCrnWSc_2qCleyQIkfQGdL-pU-UTEcSg1wPvIKZ9uk,1210
|
|
10419
|
-
simo/users/models.py,sha256=
|
|
10421
|
+
simo/users/models.py,sha256=mYZHclBNGLUWqFQpUyplncXQrsPYjmlxZ0Jl6Fn_Dhk,20278
|
|
10420
10422
|
simo/users/permissions.py,sha256=IwtYS8yQdupWbYKR9VimSRDV3qCJ2jXP57Lyjpb2EQM,242
|
|
10421
10423
|
simo/users/serializers.py,sha256=a4R408ZgWVbF7OFw4bBfN33Wnn8ljqS8iFcsqmllkWU,2552
|
|
10422
10424
|
simo/users/sso_urls.py,sha256=gQOaPvGMYFD0NCVSwyoWO-mTEHe5j9sbzV_RK7kdvp0,251
|
|
@@ -10433,9 +10435,9 @@ simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=K-3sz2h-cEitoflSmZk1t0eUg
|
|
|
10433
10435
|
simo/users/__pycache__/dynamic_settings.cpython-38.pyc,sha256=6F8JBjZkHykySnmZjNEzjS0ijbmPdcp9yUAZ5kqq_Fo,864
|
|
10434
10436
|
simo/users/__pycache__/managers.cpython-38.pyc,sha256=C5-diljm874RAFMTkZdcfzPhkHzlUGPAhz2gTvqkDy8,604
|
|
10435
10437
|
simo/users/__pycache__/middleware.cpython-38.pyc,sha256=Tj4nVEAvxEW3xA63fBRiJWRJpz_M848ZOqbHioc_IPE,1149
|
|
10436
|
-
simo/users/__pycache__/models.cpython-38.pyc,sha256=
|
|
10438
|
+
simo/users/__pycache__/models.cpython-38.pyc,sha256=tOiUcbFxH2AKUT8X8lygDYxETjpAUR7C8vD7gqB1Nz8,18523
|
|
10437
10439
|
simo/users/__pycache__/permissions.cpython-38.pyc,sha256=ez5NxoL_JUeeH6GsKhvFreuA3FCBgGf9floSypdXUtM,633
|
|
10438
|
-
simo/users/__pycache__/serializers.cpython-38.pyc,sha256=
|
|
10440
|
+
simo/users/__pycache__/serializers.cpython-38.pyc,sha256=AkMwibZBo52rsWOwhAIc9ig1Ck1-fQAEkboe6wnh7yE,3477
|
|
10439
10441
|
simo/users/__pycache__/sso_urls.cpython-38.pyc,sha256=uAwDozpOmrhUald-8tOHANILXkH7-TI8fNYXOtPkSY8,402
|
|
10440
10442
|
simo/users/__pycache__/sso_views.cpython-38.pyc,sha256=sHEoxLOac3U3Epmhm197huFnW_J3gGCDZSji57itijU,3969
|
|
10441
10443
|
simo/users/__pycache__/tasks.cpython-38.pyc,sha256=XLMKt3suT7BlcXrJZoH9ZIhhtBuqyiW4lsOB9IbBkko,1225
|
|
@@ -10517,9 +10519,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
|
|
|
10517
10519
|
simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10518
10520
|
simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10519
10521
|
simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10520
|
-
simo-2.
|
|
10521
|
-
simo-2.
|
|
10522
|
-
simo-2.
|
|
10523
|
-
simo-2.
|
|
10524
|
-
simo-2.
|
|
10525
|
-
simo-2.
|
|
10522
|
+
simo-2.4.1.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
|
|
10523
|
+
simo-2.4.1.dist-info/METADATA,sha256=dkUhhxK0C6qOP_J2jRgkD2ltsJnLcWq2ZTsXtSRIeHg,1923
|
|
10524
|
+
simo-2.4.1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
10525
|
+
simo-2.4.1.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
|
|
10526
|
+
simo-2.4.1.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
|
|
10527
|
+
simo-2.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|