Skip to content

Commit 4860731

Browse files
authored
Merge pull request #174 from arenaxr/reprofile
Reprofile
2 parents 843c7fc + ba1c196 commit 4860731

24 files changed

Lines changed: 1209 additions & 407 deletions

users/api.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ def list_my_scenes(request, id_token: str = Form(None)):
315315
return 200, sorted(list(merged_map.values()), key=lambda x: x["name"])
316316

317317

318-
@router.api_operation(["GET", "POST", "PUT", "DELETE"], "/scenes/{path:scene_name}", response={200: SceneSchema, 201: SceneSchema, 200: MessageSchema, 400: ErrorSchema, 404: ErrorSchema})
318+
@router.api_operation(["GET", "POST", "PUT", "DELETE"], "/scenes/{path:scene_name}", response={200: SceneSchema, 201: SceneSchema, 400: ErrorSchema, 404: ErrorSchema})
319319
def scene_detail(request, scene_name: str, payload: SceneSchema = None):
320320
# check permissions model for namespace
321321
try:
@@ -382,8 +382,9 @@ def scene_detail(request, scene_name: str, payload: SceneSchema = None):
382382
return 400, {"error": "Invalid parameters"}
383383

384384
if request.method == "DELETE":
385+
name = scene.name
385386
scene.delete()
386-
return 200, {"message": "Scene was deleted successfully!"}
387+
return 200, serialize_scene(Scene(name=name))
387388

388389
return 400, {"error": "Method not allowed"}
389390

users/forms.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
from django.conf import settings
55
from django.contrib.auth.models import User
66

7-
from .models import Device, Namespace, Scene
7+
from .models import RE_NS, Device, Namespace, Scene
88
from .persistence import read_persist_scenes_by_namespace
99

1010

1111
class SocialSignupForm(_SocialSignupForm):
1212
def __init__(self, *args, **kwargs):
1313
super().__init__(*args, **kwargs)
1414

15-
if self.sociallogin and self.sociallogin.account.provider in ("google"):
15+
if self.sociallogin and self.sociallogin.account.provider in ("google",):
1616
name = self.sociallogin.account.extra_data["email"].split("@")[0]
1717
self.fields["username"].widget.attrs.update({"value": name})
1818

@@ -42,6 +42,40 @@ class UpdateStaffForm(forms.Form):
4242
class UpdateNamespaceForm(forms.Form):
4343
add = forms.CharField(label="add", required=False)
4444
edit = forms.CharField(label="edit", required=False)
45+
namespacename = forms.CharField(label="namespacename", required=False)
46+
47+
def clean_namespacename(self):
48+
import re
49+
50+
name = self.cleaned_data.get("namespacename", "").strip()
51+
if not name:
52+
return name
53+
# validate namespace regex
54+
if not re.match(RE_NS, name):
55+
raise forms.ValidationError(
56+
"Only alphanumeric, underscore, hyphen allowed."
57+
)
58+
# reject reserved words
59+
if name in settings.USERNAME_RESERVED:
60+
raise forms.ValidationError(
61+
f"Sorry, '{name}' is a reserved word."
62+
)
63+
# reject if an existing user
64+
if User.objects.filter(username=name).exists():
65+
raise forms.ValidationError(
66+
f"Sorry, '{name}' is already a user account."
67+
)
68+
# reject if namespace exists in permissions
69+
if Namespace.objects.filter(name=name).exists():
70+
raise forms.ValidationError(
71+
f"Sorry, '{name}' is an existing permissions namespace."
72+
)
73+
# reject if namespace used in persist db
74+
if len(read_persist_scenes_by_namespace([name])) > 0:
75+
raise forms.ValidationError(
76+
f"Sorry, '{name}' is a persistence namespace."
77+
)
78+
return name
4579

4680

4781
class UpdateSceneForm(forms.Form):

users/models.py

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,6 @@
2323
RE_NS_SLASH_ID, "Only alphanumeric, underscore, hyphen, period, in namespace/idname format allowed."
2424
)
2525

26-
# assign accessible model for persist collection
27-
def get_arenaobjects_collection():
28-
from .persist_db import get_persist_db
29-
return get_persist_db()['arenaobjects']
30-
31-
# arenaobjects schema reference:
32-
# https://github.com/arenaxr/arena-persist/blob/master/server.js#L28-L42
33-
# object_id: {type: String, required: true, index: true},
34-
# type: {type: String, required: true, index: true},
35-
# attributes: {type: Object, required: true, default: {}},
36-
# expireAt: {type: Date, expires: 0},
37-
# realm: {type: String, required: true, index: true},
38-
# namespace: {type: String, required: true, index: true, default: 'public'},
39-
# sceneId: {type: String, required: true, index: true},
40-
# private: {type: Boolean},
41-
# program_id: {type: String},
42-
43-
4426
class NamespaceDefault:
4527
def __init__(self, name=""):
4628
self.name = name
@@ -58,7 +40,7 @@ class Namespace(models.Model):
5840

5941
def save(self, *args, **kwargs):
6042
self.full_clean() # performs regular validation then clean()
61-
super(Namespace, self).save(*args, **kwargs)
43+
super().save(*args, **kwargs)
6244

6345
def clean(self):
6446
if self.name == "":
@@ -103,7 +85,7 @@ class Scene(models.Model):
10385

10486
def save(self, *args, **kwargs):
10587
self.full_clean() # performs regular validation then clean()
106-
super(Scene, self).save(*args, **kwargs)
88+
super().save(*args, **kwargs)
10789

10890
def clean(self):
10991
if self.name == "":
@@ -129,11 +111,11 @@ def sceneid(self):
129111
@property
130112
def is_default(self):
131113
return (
132-
self.public_read is SCENE_PUBLIC_READ_DEF
133-
and self.public_write is SCENE_PUBLIC_WRITE_DEF
134-
and self.anonymous_users is SCENE_ANON_USERS_DEF
135-
and self.video_conference is SCENE_VIDEO_CONF_DEF
136-
and self.users is SCENE_USERS_DEF
114+
self.public_read == SCENE_PUBLIC_READ_DEF
115+
and self.public_write == SCENE_PUBLIC_WRITE_DEF
116+
and self.anonymous_users == SCENE_ANON_USERS_DEF
117+
and self.video_conference == SCENE_VIDEO_CONF_DEF
118+
and self.users == SCENE_USERS_DEF
137119
and self.editors.count() == 0
138120
and self.viewers.count() == 0
139121
)
@@ -148,7 +130,7 @@ class Device(models.Model):
148130

149131
def save(self, *args, **kwargs):
150132
self.full_clean() # performs regular validation then clean()
151-
super(Device, self).save(*args, **kwargs)
133+
super().save(*args, **kwargs)
152134

153135
def clean(self):
154136
if self.name == "":

users/mqtt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def generate_arena_token(
4848
if not os.path.exists(privkeyfile):
4949
print("Error: keyfile not found")
5050
return None
51-
with open(privkeyfile) as privatefile:
51+
with open(privkeyfile, encoding="utf-8") as privatefile:
5252
private_key = privatefile.read()
5353
payload = {}
5454
payload["sub"] = username

users/mqtt_match.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,4 @@ def topic_matches_sub(sub: str, topic: str) -> bool:
9494
next(matcher.iter_match(topic))
9595
return True
9696
except StopIteration:
97-
return False
97+
return False

users/persistence.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,25 @@
77

88
from bson import ObjectId
99

10-
from .models import get_arenaobjects_collection
10+
11+
# assign accessible model for persist collection
12+
def get_arenaobjects_collection():
13+
from .persist_db import get_persist_db
14+
return get_persist_db()['arenaobjects']
15+
16+
# arenaobjects schema reference:
17+
# https://github.com/arenaxr/arena-persist/blob/master/server.js#L30-L43
18+
# object_id: {type: String, required: true, index: true},
19+
# type: {type: String, required: true, index: true},
20+
# attributes: {type: Object, required: true, default: {}},
21+
# expireAt: {type: Date, expires: 0},
22+
# realm: {type: String, required: true, index: true},
23+
# namespace: {type: String, required: true, index: true, default: 'public'},
24+
# sceneId: {type: String, required: true, index: true},
25+
# private: {type: Boolean},
26+
# program_id: {type: String},
27+
# createdAt: {type: Date}, // via timestamps: true
28+
# updatedAt: {type: Date}, // via timestamps: true
1129

1230

1331
class MongoJSONEncoder(json.JSONEncoder):
@@ -24,11 +42,12 @@ def read_persist_ns_all():
2442
"$group": {
2543
"_id": {
2644
"namespace": "$namespace",
27-
}
28-
}
45+
},
46+
"last_updated": {"$max": "$updatedAt"},
47+
"count": {"$sum": 1}
2948
}
30-
])
31-
return [doc['_id']['namespace'] for doc in arenaobjects]
49+
}])
50+
return {doc['_id']['namespace']: {'last_updated': doc.get('last_updated'), 'count': doc.get('count', 0)} for doc in arenaobjects}
3251

3352

3453
def read_persist_scenes_all():
@@ -38,18 +57,22 @@ def read_persist_scenes_all():
3857
"_id": {
3958
"namespace": "$namespace",
4059
"sceneId": "$sceneId",
41-
}
60+
},
61+
"last_updated": {"$max": "$updatedAt"},
62+
"count": {"$sum": 1}
4263
}
4364
},
4465
{
4566
"$project": {
4667
"name": {
4768
"$concat": ["$_id.namespace", "/", "$_id.sceneId"]
48-
}
69+
},
70+
"last_updated": 1,
71+
"count": 1
4972
}
5073
}
5174
])
52-
return [doc['name'] for doc in arenaobjects]
75+
return {doc['name']: {'last_updated': doc.get('last_updated'), 'count': doc.get('count', 0)} for doc in arenaobjects}
5376

5477

5578
def read_persist_scenes_by_namespace(namespaces):
@@ -64,18 +87,22 @@ def read_persist_scenes_by_namespace(namespaces):
6487
"_id": {
6588
"namespace": "$namespace",
6689
"sceneId": "$sceneId",
67-
}
90+
},
91+
"last_updated": {"$max": "$updatedAt"},
92+
"count": {"$sum": 1}
6893
}
6994
},
7095
{
7196
"$project": {
7297
"name": {
7398
"$concat": ["$_id.namespace", "/", "$_id.sceneId"]
74-
}
99+
},
100+
"last_updated": 1,
101+
"count": 1
75102
}
76103
}
77104
])
78-
return [doc['name'] for doc in arenaobjects]
105+
return {doc['name']: {'last_updated': doc.get('last_updated'), 'count': doc.get('count', 0)} for doc in arenaobjects}
79106

80107

81108
def read_persist_scene_objects(namespace, scene):

users/static/users/vendor/jquery.min.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

users/static/users/vendor/popper.min.js

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

users/templates/users/device_perm_detail.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
<div class="container-fluid">
99
<div class="row d-flex justify-content-center align-items-center min-vh-75">
1010
<div class="col-md-10 bg-white p-5">
11+
<nav aria-label="breadcrumb">
12+
<ol class="breadcrumb">
13+
<li class="breadcrumb-item"><a href="{% url 'users:user_profile' %}">Profile Dashboard</a></li>
14+
<li class="breadcrumb-item"><a href="{% url 'users:profile_devices' %}">All Devices</a></li>
15+
<li class="breadcrumb-item active" aria-current="page">Edit Device</li>
16+
</ol>
17+
</nav>
1118

1219
<h1>Edit Device Permissions</h1>
1320
{% if token %}

users/templates/users/header.html

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
<link rel="preconnect" href="https://fonts.gstatic.com">
1818
<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Slab&display=swap" rel="stylesheet">
1919

20-
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"
21-
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
20+
<script type="text/javascript" src="{% static 'users/vendor/jquery.min.js' %}"></script>
2221

2322
{% block extrahead %}
2423
{% endblock extrahead %}
@@ -135,10 +134,8 @@
135134

136135
{% endblock content %}
137136

137+
<script type="text/javascript" src="{% static 'users/vendor/popper.min.js' %}"></script>
138138
<script type="text/javascript" src="{% static 'users/vendor/arenaxr-bootstrap/bootstrap.min.js' %}"></script>
139-
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
140-
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous">
141-
</script>
142139
<script type="text/javascript" src="{% static 'users/vendor/sweetalert2.min.js' %}"></script>
143140

144141
<script>

0 commit comments

Comments
 (0)