simo 2.7.13__py3-none-any.whl → 2.7.14__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.

@@ -227,30 +227,42 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
227
227
 
228
228
  terminating_scripts = set()
229
229
 
230
+ def __init__(self, *args, **kwargs):
231
+ super().__init__(*args, **kwargs)
232
+ self.last_death = 0
233
+
230
234
 
231
235
  def watch_scripts(self):
232
236
  drop_current_instance()
233
237
  # observe running scripts and drop the ones that are no longer alive
234
238
  dead_scripts = False
235
239
  for id, process in list(self.running_scripts.items()):
240
+
236
241
  comp = Component.objects.filter(id=id).first()
237
- if comp.value == 'finished':
242
+ if comp and comp.value == 'finished':
238
243
  self.running_scripts.pop(id)
239
244
  continue
245
+
240
246
  if process.is_alive():
241
247
  if not comp and id not in self.terminating_scripts:
242
- # script is deleted, or instance deactivated
248
+ # script is deleted and was not properly called to stop
249
+ self.running_scripts.pop(id)
243
250
  process.kill()
244
251
  continue
245
252
  else:
246
- if id not in self.terminating_scripts:
247
- dead_scripts = True
248
- if comp:
249
- logger = get_component_logger(comp)
250
- logger.log(logging.INFO, "-------DEAD!-------")
251
- self.stop_script(comp, 'dead')
252
-
253
- if dead_scripts:
253
+ self.last_death = time.time()
254
+ self.running_scripts.pop(id, None) # no longer running for sure!
255
+ if not comp or comp.value != 'running':
256
+ continue
257
+
258
+ if id not in self.terminating_scripts: # was not intentionaly terminated
259
+ logger = get_component_logger(comp)
260
+ logger.log(logging.INFO, "-------DEAD!-------")
261
+ comp.value = 'error'
262
+ comp.save()
263
+
264
+
265
+ if self.last_death and time.time() - self.last_death < 5:
254
266
  # give 10s air before we wake these dead scripts up!
255
267
  return
256
268
 
@@ -338,23 +350,14 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
338
350
  def start_script(self, component):
339
351
  print("START SCRIPT %s" % str(component))
340
352
  if component.id in self.running_scripts:
341
- if component.value == 'finished':
353
+ if component.value in ('finished', 'error', 'stopped'):
342
354
  self.running_scripts.pop(component.id)
343
- elif component.id not in self.terminating_scripts:
355
+ elif component.id not in self.terminating_scripts \
356
+ and self.running_scripts[component.id].is_alive():
344
357
  if component.value != 'running':
345
358
  component.value = 'running'
346
359
  component.save()
347
- return
348
- else:
349
- good_to_go = False
350
- for i in range(12): # wait for 3s
351
- time.sleep(0.2)
352
- component.refresh_from_db()
353
- if component.id not in self.running_scripts:
354
- good_to_go = True
355
- break
356
- if not good_to_go:
357
- return self.stop_script(component, 'error')
360
+ return
358
361
 
359
362
  self.running_scripts[component.id] = ScriptRunHandler(
360
363
  component.id, multiprocessing.Event(),
Binary file
Binary file
simo/core/api.py CHANGED
@@ -285,7 +285,6 @@ class ComponentViewSet(
285
285
 
286
286
  @action(detail=True, methods=['post'])
287
287
  def controller(self, request, pk=None, *args, **kwargs):
288
- start = time.time()
289
288
  component = self.get_object()
290
289
  data = request.data
291
290
  if not isinstance(request.data, dict):
simo/core/models.py CHANGED
@@ -544,7 +544,7 @@ def is_in_alarm(self):
544
544
  kwargs['update_fields'].append('last_change')
545
545
 
546
546
  modifying_fields = (
547
- 'name', 'icon', 'zone', 'category', 'config', 'meta',
547
+ 'name', 'icon', 'zone', 'category', 'config',
548
548
  'value_units', 'slaves', 'show_in_app', 'alarm_category'
549
549
  )
550
550
  if any(f in dirty_fields for f in modifying_fields):
simo/core/serializers.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import inspect
2
2
  import datetime
3
3
  import re
4
+ import json
4
5
  from django import forms
5
6
  from collections import OrderedDict
6
7
  from django.conf import settings
@@ -137,6 +138,10 @@ class ComponentManyToManyRelatedField(serializers.Field):
137
138
  def to_internal_value(self, data):
138
139
  if data == [] and self.allow_blank:
139
140
  return []
141
+ if type(data) == str:
142
+ data = json.loads(data)
143
+ if not isinstance(data, Iterable):
144
+ data = [data]
140
145
  return self.queryset.filter(pk__in=data)
141
146
 
142
147
 
@@ -165,9 +170,9 @@ class ComponentFormsetField(FormSerializer):
165
170
  self.form = formset_field.formset_cls.form
166
171
  super().__init__(*args, **kwargs)
167
172
 
168
- def get_form(self, data=None, **kwargs):
173
+ def get_form(self, data=None, files=None, **kwargs):
169
174
  form_cls = self.form
170
- instance = form_cls(data=data, **kwargs)
175
+ instance = form_cls(data=data, files=files, **kwargs)
171
176
  # Handle partial validation on the form side
172
177
  if self.partial:
173
178
  set_form_partial_validation(
@@ -435,7 +440,7 @@ class ComponentSerializer(FormSerializer):
435
440
  if controller:
436
441
  self.Meta.form = controller.config_form
437
442
 
438
- def get_form(self, data=None, instance=None, **kwargs):
443
+ def get_form(self, data=None, instance=None, files=None, **kwargs):
439
444
  if 'forms' not in self.context:
440
445
  self.context['forms'] = {}
441
446
  form_key = None
@@ -453,7 +458,7 @@ class ComponentSerializer(FormSerializer):
453
458
  else:
454
459
  controller_uid = self.instance.controller_uid
455
460
  form = self.Meta.form(
456
- data=data, request=self.context['request'],
461
+ data=data, files=files, request=self.context['request'],
457
462
  controller_uid=controller_uid, instance=instance,
458
463
  **kwargs
459
464
  )
@@ -508,7 +513,8 @@ class ComponentSerializer(FormSerializer):
508
513
  a_data = self.accomodate_formsets(form, data)
509
514
 
510
515
  form = self.get_form(
511
- data=a_data, instance=self.instance
516
+ data=a_data, files=self.context['request'].FILES,
517
+ instance=self.instance
512
518
  )
513
519
  if not form.is_valid():
514
520
  raise serializers.ValidationError(form.errors)
@@ -520,7 +526,10 @@ class ComponentSerializer(FormSerializer):
520
526
  def update(self, instance, validated_data):
521
527
  form = self.get_form(instance=instance)
522
528
  a_data = self.accomodate_formsets(form, validated_data)
523
- form = self.get_form(instance=instance, data=a_data)
529
+ form = self.get_form(
530
+ instance=instance, data=a_data,
531
+ files=self.context['request'].FILES
532
+ )
524
533
  if form.is_valid():
525
534
  instance = form.save(commit=True)
526
535
  return instance
@@ -529,7 +538,9 @@ class ComponentSerializer(FormSerializer):
529
538
  def create(self, validated_data):
530
539
  form = self.get_form()
531
540
  a_data = self.accomodate_formsets(form, validated_data)
532
- form = self.get_form(data=a_data)
541
+ form = self.get_form(
542
+ data=a_data, files=self.context['request'].FILES
543
+ )
533
544
  if form.is_valid():
534
545
  if form.controller.is_discoverable:
535
546
  form.controller.init_discovery(form.cleaned_data)
simo/generic/forms.py CHANGED
@@ -5,6 +5,7 @@ from django import forms
5
5
  from django.forms import formset_factory
6
6
  from django.db.models import Q
7
7
  from django.utils.translation import gettext_lazy as _
8
+ from django.core.files.uploadedfile import InMemoryUploadedFile
8
9
  from simo.core.forms import HiddenField, BaseComponentForm
9
10
  from simo.core.models import Icon, Component, PublicFile
10
11
  from simo.core.controllers import (
@@ -631,18 +632,17 @@ class AudioAlertConfigForm(BaseComponentForm):
631
632
  def __init__(self, *args, **kwargs):
632
633
  super().__init__(*args, **kwargs)
633
634
  self.basic_fields.extend(['sound', 'loop', 'volume', 'players'])
634
- if self.instance.id:
635
- public_file = PublicFile.objects.filter(
636
- component=self.instance
637
- ).first()
638
- if public_file:
639
- self.fields['sound'].help_text = f"Currently: {public_file.file}"
635
+ if self.instance.config.get('sound'):
636
+ self.fields['sound'].help_text = \
637
+ f"Currently: {self.instance.config['sound']}"
640
638
 
641
639
  def clean_sound(self):
642
640
  if not self.cleaned_data.get('sound'):
643
641
  if not self.instance.pk:
644
642
  raise forms.ValidationError("Please pick a sound!")
645
643
  return
644
+ if type(self.cleaned_data['sound']) != InMemoryUploadedFile:
645
+ return self.cleaned_data['sound']
646
646
  if self.cleaned_data['sound'].size > 1024 * 1024 * 50:
647
647
  raise forms.ValidationError("No more than 50Mb please.")
648
648
  temp_path = os.path.join(
@@ -672,19 +672,21 @@ class AudioAlertConfigForm(BaseComponentForm):
672
672
 
673
673
  def save(self, commit=True):
674
674
  obj = super().save(commit=commit)
675
- if commit and self.cleaned_data.get('sound') \
676
- and self.cleaned_data['sound'] != self.fields['sound'].initial:
677
- public_file = PublicFile(component=obj)
678
- public_file.file.save(
679
- self.cleaned_data['sound'].name, self.cleaned_data['sound'],
680
- save=True
681
- )
682
- org = PublicFile.objects.filter(
683
- id=self.instance.config.get('public_file_id', 0)
684
- )
685
- if org:
686
- org.delete()
687
- self.instance.config['public_file_id'] = public_file.id
688
- self.instance.config['duration'] = self.cleaned_data['sound'].duration
689
- self.instance.save()
675
+ if type(self.cleaned_data['sound']) != InMemoryUploadedFile:
676
+ return obj
677
+
678
+ public_file = PublicFile(component=obj)
679
+ public_file.file.save(
680
+ self.cleaned_data['sound'].name, self.cleaned_data['sound'],
681
+ save=True
682
+ )
683
+ org = PublicFile.objects.filter(
684
+ id=self.instance.config.get('public_file_id', 0)
685
+ )
686
+ if org:
687
+ org.delete()
688
+ self.instance.config['public_file_id'] = public_file.id
689
+ self.instance.config['duration'] = self.cleaned_data['sound'].duration
690
+ self.instance.config['sound'] = self.cleaned_data['sound'].name
691
+ self.instance.save()
690
692
  return obj
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.7.13
3
+ Version: 2.7.14
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
@@ -15,7 +15,7 @@ simo/automation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  simo/automation/app_widgets.py,sha256=gaqImMZjuMHm7nIb9a4D-Y3qipz_WhSPAHXcwGx4Uzs,199
16
16
  simo/automation/controllers.py,sha256=Ow8xG9hkdyMRZbrNPX5uZloYM9jZqa9mgCh4k6FeoJw,11492
17
17
  simo/automation/forms.py,sha256=UWnkxw8pILPK0smRPTo4SLgsZl78zOySx7JIc30Bgtk,10228
18
- simo/automation/gateways.py,sha256=dTrx4r3aoUynWQCHHXZFKkoHhKbC3-UMZIY755C-sdI,15470
18
+ simo/automation/gateways.py,sha256=irTmutbiqrRBjEAYwoeQ735JNPj1cuBWGbloCkuKyuc,15546
19
19
  simo/automation/helpers.py,sha256=iP-fxxB8HsFQy3k2CjFubu86aMqvWgmh-p24DiyOrek,4330
20
20
  simo/automation/models.py,sha256=l45FHgeKGsfpLtd1X1PVFpIjB5JI4BlvKkodpcxm6aE,927
21
21
  simo/automation/serializers.py,sha256=PjyFrjdPK1mBsgbNhyqMi9SWzcymqTa742ipy0LhAN4,1996
@@ -24,7 +24,7 @@ simo/automation/__pycache__/__init__.cpython-38.pyc,sha256=YmP0xAD-mxpQHgdTZeC64
24
24
  simo/automation/__pycache__/app_widgets.cpython-38.pyc,sha256=7DfUA9V_1MiwrOe_ta-ts8dYY8xXb9UMg2_9A3XoRcs,523
25
25
  simo/automation/__pycache__/controllers.cpython-38.pyc,sha256=CL-0Tq9B4-E36fYfWT1XEBTq1dkq1W8003f6MrBnQU0,8391
26
26
  simo/automation/__pycache__/forms.cpython-38.pyc,sha256=cpA5hA2Iz3JsPC0Dq01ki1I7S9c5DKRcXveHApI1dJo,7772
27
- simo/automation/__pycache__/gateways.cpython-38.pyc,sha256=F4zO0G15gGMSaQaB_fW13qtcBHQ3KYlDqZ4_Ch0YW70,10956
27
+ simo/automation/__pycache__/gateways.cpython-38.pyc,sha256=nHujqChMCqqxHbZezP3MisavjKDhczqzFGurO10h-lc,11113
28
28
  simo/automation/__pycache__/helpers.cpython-38.pyc,sha256=fNjSyn4Mfq7-JQx-bdsnj-rSxgu20dVJ9-5ZEMT6yiM,3627
29
29
  simo/automation/__pycache__/models.cpython-38.pyc,sha256=VeQNAygVr0AXPlF5LEYk90Tq8zuCVPfsA2r1NBjrkxU,1230
30
30
  simo/automation/__pycache__/serializers.cpython-38.pyc,sha256=9Te21FW5-Tki1a-tq3gt0rme0afBA8o-7V249b4muFk,3405
@@ -61,7 +61,7 @@ simo/backups/migrations/__pycache__/0004_alter_backup_options_alter_backuplog_op
61
61
  simo/backups/migrations/__pycache__/__init__.cpython-38.pyc,sha256=Lz1fs6V05h2AoxTOLNye0do9bEMnyuaXB_hHOjG5-HU,172
62
62
  simo/core/__init__.py,sha256=_s2TjJfQImsMrTIxqLAx9AZie1Ojmm6sCHASdl3WLGU,50
63
63
  simo/core/admin.py,sha256=j07Dy3IsW7SRRsAJ2XXGzE0MTxmfY87CEEbT22kLlS4,18550
64
- simo/core/api.py,sha256=D5FBWvhYpQeM9wVfapzRvBi_ypOtXBt9tmmCaUxpGjI,29992
64
+ simo/core/api.py,sha256=vqh4jOE4Y2LtykXVGLUiyoNdhcbiItSAzLJILlV5Y7k,29964
65
65
  simo/core/api_auth.py,sha256=vCxvczA8aWNcW0VyKs5WlC_ytlqeGP_H_hkKUNVkCwM,1247
66
66
  simo/core/api_meta.py,sha256=Bh-UOzANXl9Bg3FadFf7biM_olAr9GgjNJXu55SO6Og,5290
67
67
  simo/core/app_widgets.py,sha256=VxZzapuc-a29wBH7JzpvNF2SK1ECrgNUySId5ke1ffc,2509
@@ -80,10 +80,10 @@ simo/core/gateways.py,sha256=Y2BME6zSyeUq_e-hzEUF6gErCUCP6nFxedkLZKiLVOo,4141
80
80
  simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
81
81
  simo/core/managers.py,sha256=n-b3I4uXzfHKTeB1VMjSaMsDUxp8FegFJwnbV1IsWQ4,3019
82
82
  simo/core/middleware.py,sha256=eUFf6iP-Snx_0TE3MoXsSwqrd5IjlukqZk2GQGStRCo,3385
83
- simo/core/models.py,sha256=i7FT-y4wdvwwMbvxxDO3F9VSwLXBVcjlSFawb-V7N8w,23709
83
+ simo/core/models.py,sha256=f4f_ows94pAGfAZmFuAeYsv_1IzV60xn7jNqR9QZndk,23701
84
84
  simo/core/permissions.py,sha256=2YNRot2qoHjHKWPGOpO4PBseecctPbTlUQpepnFkCRs,3027
85
85
  simo/core/routing.py,sha256=X1_IHxyA-_Q7hw1udDoviVP4_FSBDl8GYETTC2zWTbY,499
86
- simo/core/serializers.py,sha256=b_38QV6LXsHZ7KfzX-h6N_-RjyV94ZDAyR6tok9lCbs,22664
86
+ simo/core/serializers.py,sha256=dK3PvKJ7fZbmknIx6QlP7E0C7sCRSozIW_VguflAy0A,23040
87
87
  simo/core/signal_receivers.py,sha256=5qp607PdNlRHyw88YOXu7rSznHm3upEpWLxB0lmEa0s,6527
88
88
  simo/core/socket_consumers.py,sha256=Es_NmacQGZjsncBXDTEXR2yZbRs7mf2FKOBJjbZRGac,9607
89
89
  simo/core/storage.py,sha256=_5igjaoWZAiExGWFEJMElxUw55DzJG1jqFty33xe8BE,342
@@ -94,7 +94,7 @@ simo/core/views.py,sha256=08H4Bm7KrHxB3p3ZKx1vrFR4d0DjCqMbqQosEsRWpkY,2841
94
94
  simo/core/widgets.py,sha256=J9e06C6I22F6xKic3VMgG7WeX07glAcl-4bF2Mg180A,2827
95
95
  simo/core/__pycache__/__init__.cpython-38.pyc,sha256=ZJFM_XN0RmJMULQulgA_wFiOnEtsMoedcOWnXjH-Y8o,208
96
96
  simo/core/__pycache__/admin.cpython-38.pyc,sha256=zdegA3IsH3QhT7Hm6Aj08F2sv-3h1X7VBcwN05fhVis,14155
97
- simo/core/__pycache__/api.cpython-38.pyc,sha256=JXCs5U9MPyZTdO4_j-ytpH1_Fo9-w36zs1eHXZ-czwo,22925
97
+ simo/core/__pycache__/api.cpython-38.pyc,sha256=TOa0p4h-zWzYdLpscveY4mY1NSYAVM4fDPvRkIDuSOQ,22905
98
98
  simo/core/__pycache__/api_auth.cpython-38.pyc,sha256=mi3mu5qEKio_PvfQEvr3Q6AhdPLAHxzxAxrMbAz_pKU,1712
99
99
  simo/core/__pycache__/api_meta.cpython-38.pyc,sha256=euP2EQsfTVCdvMT9NY10bK7xrz2dQouqQKZP0efN1ss,3906
100
100
  simo/core/__pycache__/app_widgets.cpython-38.pyc,sha256=oN657XMMZ6GYN9nblv7fX3kdnTEzSP9XV6PXM6Z0wl4,4358
@@ -113,10 +113,10 @@ simo/core/__pycache__/gateways.cpython-38.pyc,sha256=-_ugqnUOA1Cl6VfMqpV96n7ekVO
113
113
  simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
114
114
  simo/core/__pycache__/managers.cpython-38.pyc,sha256=6RTIxyjOgpQGtAqcUyE2vFPS09w1V5Wmd_vOV7rHRRI,3370
115
115
  simo/core/__pycache__/middleware.cpython-38.pyc,sha256=SgTLFNkKxvJ62hevSAVNZHgHdG_u2p7AZBhrj-jfFPs,2649
116
- simo/core/__pycache__/models.cpython-38.pyc,sha256=EG1RJ-kaOFupzHcrZnii6JQUuoQ1gbt6LN9xUMfHFo4,19610
116
+ simo/core/__pycache__/models.cpython-38.pyc,sha256=A4JsWsDMBaQ_U5sV5cX0c_Uox9mP5fAqn_712EjfNS4,19605
117
117
  simo/core/__pycache__/permissions.cpython-38.pyc,sha256=UdtxCTXPEbe99vgZOfRz9wfKSYvUn9hSRbpIV9CJSyI,2988
118
118
  simo/core/__pycache__/routing.cpython-38.pyc,sha256=3T3FPJ8Cn99xZCGvMyg2xjl7al-Shm9CelbSpkJtNP8,599
119
- simo/core/__pycache__/serializers.cpython-38.pyc,sha256=ciWQe19lrr5ImXByMZJI8JM47IlqmyAo1XcXfnVrU80,20018
119
+ simo/core/__pycache__/serializers.cpython-38.pyc,sha256=YYElaqwZPBt5cIysZzofRTWdOzLfuEL30ix8BFAtmEs,20235
120
120
  simo/core/__pycache__/signal_receivers.cpython-38.pyc,sha256=R_h1GHkoZXR-nrwiRJWQ9xE69JB1R_mP_fNYIBX4lrE,5165
121
121
  simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=Mr1-x-vGjBNffbz0S6AKpJCuzHJgRm8kXefv3qVVY_E,8397
122
122
  simo/core/__pycache__/storage.cpython-38.pyc,sha256=9R1Xu0FJDflfRXUPsqEgt0SpwiP7FGk7HaR8s8XRyI8,721
@@ -10386,7 +10386,7 @@ simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10386
10386
  simo/generic/app_widgets.py,sha256=y8W3jR76Hh26O9pPQyg2SophMbYIOtAWD33MPKbB8Mg,856
10387
10387
  simo/generic/base_types.py,sha256=u3SlfpNYaCwkVBwomWgso4ODzL71ay9MhiAW-bxgnDU,341
10388
10388
  simo/generic/controllers.py,sha256=gqL1Evt-8Cq8MeHG-eV8aCtQ4St_gylE52wnXwOQUWU,46285
10389
- simo/generic/forms.py,sha256=VVp0IflKabObfxWPrc4Q6oEs_Z8hEvwnmNB2gZmaCGQ,25732
10389
+ simo/generic/forms.py,sha256=1pacWsMcGTuXROUqZiMZ4bouGFAtHCBXqZPR7oouee4,25798
10390
10390
  simo/generic/gateways.py,sha256=04FKHgg3_cWjhu-bxexgRFpKRavW6_StgugFcDYwkiQ,17471
10391
10391
  simo/generic/models.py,sha256=Adq7ipWK-renxJlNW-SZnAq2oGEOwKx8EdUWaKnfcVQ,7597
10392
10392
  simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
@@ -10395,7 +10395,7 @@ simo/generic/__pycache__/__init__.cpython-38.pyc,sha256=mLu54WS9KIl-pHwVCBKpsDFI
10395
10395
  simo/generic/__pycache__/app_widgets.cpython-38.pyc,sha256=D9b13pbMlirgHmjDnQhfLIDGSVINoSouHb4SWOeCRrs,1642
10396
10396
  simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=aV5NdIuvXR-ItKpI__MwcyPZHD6Z882TFdgYkPCkr1I,493
10397
10397
  simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=jJjwKVaDYyazrRGNjUFoY74nr_jX_DEnsC9KjyxZCgc,30427
10398
- simo/generic/__pycache__/forms.cpython-38.pyc,sha256=JXaZ7iidF9GyfHY0DQSiox9rmGbEzU8c0tS9mpGNoDE,19342
10398
+ simo/generic/__pycache__/forms.cpython-38.pyc,sha256=k8lz3taXdWAg5P9jcnw66mWH51pCc4SOsg32kVEtBCg,19416
10399
10399
  simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=GIeMT51oZU2OCFD4eUDFdSRRYE0Qf14AcOr_gdUqG94,12705
10400
10400
  simo/generic/__pycache__/models.cpython-38.pyc,sha256=MZpum7syAFxuulf47K7gtUlJJ7xRD-IBUBAwUM1ZRnw,5825
10401
10401
  simo/generic/__pycache__/routing.cpython-38.pyc,sha256=xtxTUTBTdivzFyA5Wh7k-hUj1WDO_FiRq6HYXdbr9Ks,382
@@ -10618,9 +10618,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10618
10618
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10619
10619
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10620
10620
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10621
- simo-2.7.13.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10622
- simo-2.7.13.dist-info/METADATA,sha256=Dfh3VdJ-c5kB_5cRNa-EIIfN--LMdgEHt7tVD9cyDqo,1953
10623
- simo-2.7.13.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
10624
- simo-2.7.13.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
10625
- simo-2.7.13.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10626
- simo-2.7.13.dist-info/RECORD,,
10621
+ simo-2.7.14.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10622
+ simo-2.7.14.dist-info/METADATA,sha256=GsOE9EZng-thUQpofZCfJMSmxX-VmImqRPrXh8TVmh0,1953
10623
+ simo-2.7.14.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
10624
+ simo-2.7.14.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
10625
+ simo-2.7.14.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10626
+ simo-2.7.14.dist-info/RECORD,,
File without changes