Skip to content

Commit 4bcf37d

Browse files
Revert "Add shortcuts preferences to User model (#10038)" (#10054)
This reverts commit 765f6eb.
1 parent 765f6eb commit 4bcf37d

File tree

7 files changed

+1
-254
lines changed

7 files changed

+1
-254
lines changed

h/migrations/versions/7c1a3e2b9f20_add_user_shortcuts_preferences_column.py

Lines changed: 0 additions & 16 deletions
This file was deleted.

h/models/user.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from typing import TYPE_CHECKING
55

66
import sqlalchemy as sa
7-
from sqlalchemy.dialects.postgresql import JSONB
87
from sqlalchemy.ext.declarative import declared_attr
98
from sqlalchemy.ext.hybrid import Comparator, hybrid_property
109
from sqlalchemy.orm import Mapped, mapped_column
@@ -232,10 +231,6 @@ def __table_args__(cls): # noqa: N805
232231
# Has the user opted-in for news etc.
233232
comms_opt_in = sa.Column(sa.Boolean, nullable=True)
234233

235-
# A JSON blob with user shortcuts preferences.
236-
# Use SQL NULL (not JSON null) when clearing the value.
237-
shortcuts_preferences = sa.Column(JSONB(none_as_null=True), nullable=True)
238-
239234
identities = sa.orm.relationship(
240235
"UserIdentity", backref="user", cascade="all, delete-orphan"
241236
)

h/services/user.py

Lines changed: 1 addition & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,10 @@
1-
from collections.abc import Mapping
2-
31
import sqlalchemy as sa
42

53
from h.models import User, UserIdentity
64
from h.util.db import on_transaction_end
75
from h.util.user import split_user
86

9-
UPDATE_PREFS_ALLOWED_KEYS = {
10-
"show_sidebar_tutorial",
11-
"shortcuts_preferences",
12-
}
13-
REPEATABLE_SHORTCUT_GROUPS = []
14-
ALLOWED_SHORTCUT_ACTIONS = {
15-
"applyUpdates",
16-
"openKeyboardShortcuts",
17-
"openSearch",
18-
"annotateSelection",
19-
"highlightSelection",
20-
"toggleHighlights",
21-
"showSelection",
22-
"hideAdder",
23-
}
7+
UPDATE_PREFS_ALLOWED_KEYS = {"show_sidebar_tutorial"}
248

259

2610
class UserNotActivated(Exception): # noqa: N818
@@ -187,61 +171,7 @@ def update_preferences(user, **kwargs):
187171
if "show_sidebar_tutorial" in kwargs: # pragma: no cover
188172
user.sidebar_tutorial_dismissed = not kwargs["show_sidebar_tutorial"]
189173

190-
if "shortcuts_preferences" in kwargs:
191-
updated = kwargs["shortcuts_preferences"]
192-
validated = _validate_shortcuts_preferences(updated)
193-
user.shortcuts_preferences = validated
194-
195174

196175
def user_service_factory(_context, request):
197176
"""Return a UserService instance for the passed context and request."""
198177
return UserService(default_authority=request.default_authority, session=request.db)
199-
200-
201-
def _validate_shortcuts_preferences(preferences):
202-
if preferences is None:
203-
return None
204-
if not isinstance(preferences, Mapping):
205-
message = "shortcuts_preferences must be a mapping"
206-
raise TypeError(message)
207-
208-
# Check for invalid keys
209-
invalid_keys = set(preferences.keys()) - ALLOWED_SHORTCUT_ACTIONS
210-
if invalid_keys:
211-
keys = ", ".join(sorted(invalid_keys))
212-
message = f"shortcuts_preferences with keys {keys} are not allowed"
213-
raise TypeError(message)
214-
215-
# Check for invalid shortcut values
216-
has_invalid_value = any(
217-
not isinstance(value, str) and value is not None
218-
for value in preferences.values()
219-
)
220-
if has_invalid_value:
221-
message = "shortcuts_preferences values must be strings or None"
222-
raise TypeError(message)
223-
224-
# Check for duplicate shortcut values
225-
actions_by_value = {}
226-
for action, value in preferences.items():
227-
actions_by_value.setdefault(value, set()).add(action)
228-
229-
for value, actions in actions_by_value.items():
230-
if value is None:
231-
continue
232-
if len(actions) <= 1:
233-
continue
234-
if _allow_duplicate_shortcuts(actions):
235-
continue
236-
actions_list = ", ".join(sorted(actions))
237-
message = (
238-
"shortcuts_preferences has duplicate shortcut values for actions "
239-
f"{actions_list}"
240-
)
241-
raise TypeError(message)
242-
243-
return preferences
244-
245-
246-
def _allow_duplicate_shortcuts(actions):
247-
return any(actions <= group for group in REPEATABLE_SHORTCUT_GROUPS)

h/session.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ def _user_preferences(user):
9595
preferences = {}
9696
if user and not user.sidebar_tutorial_dismissed:
9797
preferences["show_sidebar_tutorial"] = True
98-
if user and user.shortcuts_preferences is not None:
99-
preferences["shortcuts_preferences"] = user.shortcuts_preferences
10098
return preferences
10199

102100

tests/unit/h/models/user_test.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from datetime import datetime, timedelta
22

33
import pytest
4-
import sqlalchemy as sa
54
from sqlalchemy import exc
65
from sqlalchemy.sql.elements import BinaryExpression
76

@@ -204,21 +203,6 @@ def test_privacy_accepted_defaults_to_None(self):
204203
# nullable
205204
assert User().privacy_accepted is None
206205

207-
def test_shortcuts_preferences_defaults_to_None(self):
208-
assert User().shortcuts_preferences is None
209-
210-
def test_shortcuts_preferences_persists_none_as_sql_null(self, db_session, user):
211-
user.shortcuts_preferences = {"openSearch": "k"}
212-
db_session.flush()
213-
214-
user.shortcuts_preferences = None
215-
db_session.flush()
216-
217-
is_null = db_session.execute(
218-
sa.select(User.shortcuts_preferences.is_(None)).where(User.id == user.id)
219-
).scalar_one()
220-
assert is_null is True
221-
222206
def test_repr(self, user):
223207
assert repr(user) == f"User(id={user.id})"
224208

tests/unit/h/services/user_test.py

Lines changed: 0 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -109,116 +109,6 @@ def test_update_preferences_raises_for_unsupported_keys(self, svc, factories):
109109

110110
assert "keys baz, foo are not allowed" in str(exc.value)
111111

112-
def test_update_preferences_rejects_shortcuts_preferences_non_mapping(
113-
self, svc, factories
114-
):
115-
user = factories.User.build()
116-
117-
with pytest.raises(TypeError) as exc:
118-
svc.update_preferences(user, shortcuts_preferences="qux")
119-
120-
assert "shortcuts_preferences must be a mapping" in str(exc.value)
121-
122-
def test_update_preferences_rejects_shortcuts_preferences_invalid_keys(
123-
self, svc, factories
124-
):
125-
user = factories.User.build()
126-
127-
with pytest.raises(TypeError) as exc:
128-
svc.update_preferences(user, shortcuts_preferences={"notAllowed": "k"})
129-
130-
assert "keys notAllowed are not allowed" in str(exc.value)
131-
132-
def test_update_preferences_rejects_shortcuts_preferences_invalid_values(
133-
self, svc, factories
134-
):
135-
user = factories.User.build()
136-
137-
with pytest.raises(TypeError) as exc:
138-
svc.update_preferences(user, shortcuts_preferences={"openSearch": 1})
139-
140-
assert "values must be strings or None" in str(exc.value)
141-
142-
def test_update_preferences_rejects_shortcut_value_duplicates(self, svc, factories):
143-
user = factories.User.build()
144-
145-
with pytest.raises(TypeError) as exc:
146-
svc.update_preferences(
147-
user,
148-
shortcuts_preferences={
149-
"applyUpdates": "l",
150-
"openKeyboardShortcuts": "l",
151-
},
152-
)
153-
154-
assert "duplicate shortcut values" in str(exc.value)
155-
156-
def test_update_preferences_allows_repeatable_shortcut_duplicates(
157-
self, svc, factories, monkeypatch
158-
):
159-
user = factories.User.build()
160-
161-
monkeypatch.setattr(
162-
"h.services.user.REPEATABLE_SHORTCUT_GROUPS",
163-
[{"applyUpdates", "openKeyboardShortcuts"}],
164-
)
165-
166-
svc.update_preferences(
167-
user,
168-
shortcuts_preferences={
169-
"applyUpdates": "l",
170-
"openKeyboardShortcuts": "l",
171-
},
172-
)
173-
174-
assert user.shortcuts_preferences == {
175-
"applyUpdates": "l",
176-
"openKeyboardShortcuts": "l",
177-
}
178-
179-
def test_update_preferences_allows_none_shortcut_values(self, svc, factories):
180-
user = factories.User.build()
181-
182-
svc.update_preferences(
183-
user,
184-
shortcuts_preferences={
185-
"applyUpdates": None,
186-
"openKeyboardShortcuts": None,
187-
},
188-
)
189-
190-
assert user.shortcuts_preferences == {
191-
"applyUpdates": None,
192-
"openKeyboardShortcuts": None,
193-
}
194-
195-
def test_update_preferences_accepts_valid_shortcuts_preferences(
196-
self, svc, factories
197-
):
198-
user = factories.User.build()
199-
200-
svc.update_preferences(
201-
user,
202-
shortcuts_preferences={
203-
"openSearch": "k",
204-
"applyUpdates": "l",
205-
"openKeyboardShortcuts": "?",
206-
},
207-
)
208-
209-
assert user.shortcuts_preferences == {
210-
"openSearch": "k",
211-
"applyUpdates": "l",
212-
"openKeyboardShortcuts": "?",
213-
}
214-
215-
def test_update_preferences_allows_none_shortcuts_preferences(self, svc, factories):
216-
user = factories.User.build(shortcuts_preferences={"openSearch": "k"})
217-
218-
svc.update_preferences(user, shortcuts_preferences=None)
219-
220-
assert user.shortcuts_preferences is None
221-
222112
def test_sets_up_cache_clearing_on_transaction_end(self, patch, db_session):
223113
decorator = patch("h.services.user.on_transaction_end")
224114

tests/unit/h/session_test.py

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,6 @@ def test_authenticated_sidebar_tutorial(self, authenticated_request, dismissed):
8282
else:
8383
assert preferences["show_sidebar_tutorial"] is True
8484

85-
def test_authenticated_includes_shortcuts_preferences(self, authenticated_request):
86-
shortcuts_preferences = {"applyUpdates": "l"}
87-
authenticated_request.user.shortcuts_preferences = shortcuts_preferences
88-
89-
preferences = session.model(authenticated_request)["preferences"]
90-
91-
assert preferences["shortcuts_preferences"] == shortcuts_preferences
92-
93-
def test_authenticated_omits_shortcuts_preferences_when_none(
94-
self, authenticated_request
95-
):
96-
authenticated_request.user.shortcuts_preferences = None
97-
98-
preferences = session.model(authenticated_request)["preferences"]
99-
100-
assert "shortcuts_preferences" not in preferences
101-
10285

10386
class TestProfile:
10487
def test_userid_unauthenticated(self, unauthenticated_request):
@@ -183,23 +166,6 @@ def test_authenticated_sidebar_tutorial(self, authenticated_request, dismissed):
183166
else:
184167
assert preferences["show_sidebar_tutorial"] is True
185168

186-
def test_authenticated_includes_shortcuts_preferences(self, authenticated_request):
187-
shortcuts_preferences = {"applyUpdates": "l"}
188-
authenticated_request.user.shortcuts_preferences = shortcuts_preferences
189-
190-
preferences = session.profile(authenticated_request)["preferences"]
191-
192-
assert preferences["shortcuts_preferences"] == shortcuts_preferences
193-
194-
def test_authenticated_omits_shortcuts_preferences_when_none(
195-
self, authenticated_request
196-
):
197-
authenticated_request.user.shortcuts_preferences = None
198-
199-
preferences = session.profile(authenticated_request)["preferences"]
200-
201-
assert "shortcuts_preferences" not in preferences
202-
203169
def test_anonymous_authority(self, unauthenticated_request, authority):
204170
assert session.profile(unauthenticated_request)["authority"] == authority
205171

0 commit comments

Comments
 (0)