Skip to content

Commit

Permalink
[EDS-14] Use a preferences cookie to retain the last set response det…
Browse files Browse the repository at this point in the history
…ail (#130)

* [EDS-14] Use a preferences cookie to retain the last set response detail setting.

* [Bot] Update version to 2.1.6

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
Thomas Thorogood and github-actions[bot] authored Mar 3, 2022
1 parent 5b4b102 commit 9f92a60
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 8 deletions.
1 change: 1 addition & 0 deletions husky_directory/app_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class SessionSettings(FlaskConfigurationSettings):
cookie_name: str = Field(
"edu.uw.directory.session", env="SESSION_COOKIE_NAME", flask_config_key="_env"
)
preferences_cookie_name: str = Field("edu.uw.directory.preferences")
secret_key: SecretStr = Field(None, env="SECRET_KEY", flask_config_key="_env")
lifetime_seconds: int = Field(
600, env="PERMANENT_SESSION_LIFETIME", flask_config_key="_env"
Expand Down
36 changes: 31 additions & 5 deletions husky_directory/blueprints/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@
from logging import Logger
from typing import Optional, Union

from flask import Blueprint, Request, render_template, send_file
from flask import (
Blueprint,
Request,
Response,
make_response,
render_template,
send_file,
)
from inflection import humanize, underscore
from injector import Injector, inject, singleton
from pydantic import ValidationError
from werkzeug.exceptions import BadRequest, HTTPException
from werkzeug.local import LocalProxy

from husky_directory.app_config import ApplicationConfig
from husky_directory.models.common import PreferencesCookie
from husky_directory.models.search import (
DirectoryBaseModel,
Person,
Expand Down Expand Up @@ -56,9 +64,20 @@ def __init__(self, logger: Logger, injector: Injector):
)

@staticmethod
def index(session: LocalProxy, settings: ApplicationConfig):
context = RenderingContext(
uwnetid=session.get("uwnetid"), show_experimental=settings.show_experimental
def index(request: Request, session: LocalProxy, settings: ApplicationConfig):
preferences_cookie = request.cookies.get(
settings.session_settings.preferences_cookie_name
)
if preferences_cookie:
preferences = PreferencesCookie.parse_raw(preferences_cookie)
else:
preferences = PreferencesCookie()
context = RenderingContext.construct(
uwnetid=session.get("uwnetid"),
show_experimental=settings.show_experimental,
request_input=SearchDirectoryFormInput.construct(
render_length=preferences.result_detail
),
)
return (
render_template("views/index.html", **context.dict(exclude_none=True)),
Expand Down Expand Up @@ -144,9 +163,16 @@ def search_listing(
logger.exception(str(e))
SearchBlueprint.handle_search_exception(e, context)
finally:
return (
response: Response = make_response(
render_template(
"views/search_results.html", **context.dict(exclude_none=True)
),
context.status_code,
)
preferences = PreferencesCookie(
result_detail=context.request_input.length
).json(exclude_unset=True, exclude_none=True)
response.set_cookie(
settings.session_settings.preferences_cookie_name, value=preferences
)
return response
13 changes: 13 additions & 0 deletions husky_directory/models/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

from pydantic import BaseModel, validator

from husky_directory.models.enum import ResultDetail


class UWDepartmentRole(BaseModel):
"""Denotes that an identity has some role within the UW (e.g., a job title, or class level)."""
Expand Down Expand Up @@ -129,3 +131,14 @@ def validate_namespace(cls, v: Union[str, RecordNamespace]) -> RecordNamespace:
if not isinstance(v, RecordNamespace):
return RecordNamespace.validate(v)
return v


class PreferencesCookie(BaseModel):
class Config:
use_enum_values = True

result_detail: ResultDetail = ResultDetail.summary

@property
def result_detail_enum(self) -> ResultDetail:
return ResultDetail(self.result_detail)
2 changes: 1 addition & 1 deletion husky_directory/templates/search.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ <h3>Search by</h3>
<input type="text" class="form-control"
value="{{ search_value }}"
style="color:black; height:43px; z-index:0;" id="query"
{{ 'autofocus' if request_input is blank }}
{{ 'autofocus' if search_value is blank }}
name="query">
<span class="input-group-btn">
<button class="btn search"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "uw-husky-directory"
version = "2.1.5"
version = "2.1.6"
description = "An updated version of the UW Directory"
authors = ["Thomas Thorogood <[email protected]>"]
license = "MIT"
Expand Down
34 changes: 33 additions & 1 deletion tests/blueprints/test_search_blueprint.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import base64
import re
from datetime import datetime
from typing import cast
from unittest import mock

from flask import Response
from flask.testing import FlaskClient

import pytest
from bs4 import BeautifulSoup
from inflection import titleize
Expand All @@ -17,7 +21,9 @@

class BlueprintSearchTestBase:
@pytest.fixture(autouse=True)
def initialize(self, client, mock_people, injector, html_validator, mock_injected):
def initialize(
self, client: FlaskClient, mock_people, injector, html_validator, mock_injected
):
self.flask_client = client
self.session = injector.get(LocalProxy)
self.html_validator = html_validator
Expand Down Expand Up @@ -461,6 +467,32 @@ def test_list_people_sort(self, random_string):
assert html.index("999-9999") < html.index("888-8888")
assert html.index("888-8888") < html.index("222-2222")

def test_get_set_cookie(self):
# First, we make sure that the default is set to "summary"

def assert_is_checked(rv: Response, length: str):
with self.html_validator.validate_response(rv) as html:
assert (
"checked"
in html.find("input", attrs={"id": f"length-{length}"}).attrs
)

response = cast(Response, self.flask_client.get("/"))
assert_is_checked(response, "summary")

# Then, we make a second request and change it to "full"
response = cast(
Response,
self.flask_client.post(
"/", data={"method": "name", "query": "lovelace", "length": "full"}
),
)
assert_is_checked(response, "full")

# Now on a fresh get(), "full" should still be selected.
response = cast(Response, self.flask_client.get("/"))
assert_is_checked(response, "full")

def test_get_person_vcard(self):
"""
Tests that the blueprint returns the right result, but does not test
Expand Down

0 comments on commit 9f92a60

Please sign in to comment.