Skip to content

Commit

Permalink
Merge pull request #1317 from ScilifelabDataCentre/dev
Browse files Browse the repository at this point in the history
New release v2.2.2 - Thur 2022-11-17, 9 AM
  • Loading branch information
i-oden authored Nov 16, 2022
2 parents 986b38e + 26ef78e commit fd802ff
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 4 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,8 @@ Please add a _short_ line describing the PR you make, if the PR implements a spe
- Add link to the dds instance to the end of all emails ([#1305](https://github.com/ScilifelabDataCentre/dds_web/pull/1305))
- Troubleshooting steps added to web page ([#1309](https://github.com/ScilifelabDataCentre/dds_web/pull/1309))
- Bug: Return instead of project creator if user has been deleted ([#1311](https://github.com/ScilifelabDataCentre/dds_web/pull/1311))
- New endpoint: ProjectInfo - display project information ([#1310](https://github.com/ScilifelabDataCentre/dds_web/pull/1310))

## Sprint (2022-11-11 - 2022-11-25)

- Link to "How do I get my user account?" from the login form ([#1318](https://github.com/ScilifelabDataCentre/dds_web/pull/1318))
1 change: 1 addition & 0 deletions dds_web/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def output_json(data, code, headers=None):
api.add_resource(project.ProjectStatus, "/proj/status", endpoint="project_status")
api.add_resource(project.ProjectAccess, "/proj/access", endpoint="project_access")
api.add_resource(project.ProjectBusy, "/proj/busy", endpoint="project_busy")
api.add_resource(project.ProjectInfo, "/proj/info", endpoint="project_info")

# User management ################################################################ User management #
api.add_resource(user.RetrieveUserInfo, "/user/info", endpoint="user_info")
Expand Down
28 changes: 28 additions & 0 deletions dds_web/api/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
dbsession,
json_required,
handle_validation_errors,
handle_db_error,
)
from dds_web.errors import (
AccessDeniedError,
Expand Down Expand Up @@ -949,3 +950,30 @@ def put(self):
"ok": True,
"message": f"Project {project_id} was set to {'busy' if set_to_busy else 'not busy'}.",
}


class ProjectInfo(flask_restful.Resource):
"""Get information for a specific project."""

@auth.login_required
@logging_bind_request
@handle_db_error
def get(self):
# Get project ID, project and verify access
project_id = dds_web.utils.get_required_item(obj=flask.request.args, req="project")
project = dds_web.utils.collect_project(project_id=project_id)
dds_web.utils.verify_project_access(project=project)

# Construct a dict with info items
project_info = {
"Project ID": project.public_id,
"Created by": project.creator.name if project.creator else "Former User",
"Status": project.current_status,
"Last updated": project.date_updated,
"Size": project.size,
"Title": project.title,
"Description": project.description,
}

return_info = {"project_info": project_info}
return return_info
2 changes: 1 addition & 1 deletion dds_web/scheduled_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def set_available_to_expired():
scheduler.app.logger.error(f"Error for project '{proj}': {errors[unit][proj]} ")


@scheduler.task("cron", id="expired_to_archived", hour=0, minute=1, misfire_grace_time=3600)
@scheduler.task("cron", id="expired_to_archived", hour=1, minute=1, misfire_grace_time=3600)
# @scheduler.task("interval", id="expired_to_archived", seconds=15, misfire_grace_time=1)
def set_expired_to_archived():
"""Search for expired projects whose deadlines are past and archive them"""
Expand Down
1 change: 1 addition & 0 deletions dds_web/templates/components/login_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ <h1 style="text-align: center; font-size: x-large;">Log In</h1>
Log in
</button>
<p class="text-center mb-1 small"><a class="text-muted text-decoration-none" href="{{ url_for('auth_blueprint.request_reset_password') }}">Forgot your password?</a></p>
<p class="text-center mb-1 small"><a class="text-muted text-decoration-none" href="https://scilifelabdatacentre.github.io/dds_cli/#how-do-i-get-my-user-account">No account?</a></p>
</form>
2 changes: 1 addition & 1 deletion dds_web/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.2.0"
__version__ = "2.2.2"
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ MarkupSafe==2.0.1
marshmallow==3.14.1
marshmallow-sqlalchemy==0.27.0
packaging==21.3
Pillow==9.0.1 # required by qrcode
Pillow==9.3.0 # required by qrcode
pycparser==2.21
PyMySQL==1.0.2
PyNaCl==1.5.0
Expand Down
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class DDSEndpoint:
PROJECT_ACCESS = BASE_ENDPOINT + "/proj/access"
PROJECT_BUSY = BASE_ENDPOINT + "/proj/busy"
PROJECT_BUSY_ANY = BASE_ENDPOINT + "/proj/busy/any"
PROJECT_INFO = BASE_ENDPOINT + "/proj/info"

# Listing urls
LIST_PROJ = BASE_ENDPOINT + "/proj/list"
Expand Down
109 changes: 109 additions & 0 deletions tests/test_project_info_listing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# IMPORTS ################################################################################ IMPORTS #

# Standard library
import http
import unittest

# Own
import tests


# CONFIG ################################################################################## CONFIG #

proj_info_items = [
"Project ID",
"Created by",
"Status",
"Last updated",
"Size",
"Title",
"Description",
]
proj_query = {"project": "public_project_id"}
proj_query_restricted = {"project": "restricted_project_id"}

# TESTS #################################################################################### TESTS #


def test_list_proj_info_no_token(client):
"""Token required to list project information"""

response = client.get(tests.DDSEndpoint.PROJECT_INFO, headers=tests.DEFAULT_HEADER)
assert response.status_code == http.HTTPStatus.UNAUTHORIZED
response_json = response.json
assert response_json.get("message")
assert "No token" in response_json.get("message")


def test_list_proj_info_without_project(client):
"""Attempting to get the project information without specifying a project"""

token = tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client)
response = client.get(tests.DDSEndpoint.PROJECT_INFO, headers=token)
response_json = response.json
assert "Missing required information: 'project'" in response_json.get("message")


def test_list_proj_info_access_granted(client):
"""Researcher should be able to list project information"""

token = tests.UserAuth(tests.USER_CREDENTIALS["researchuser"]).token(client)
response = client.get(tests.DDSEndpoint.PROJECT_INFO, headers=token, query_string=proj_query)
assert response.status_code == http.HTTPStatus.OK
response_json = response.json
project_info = response_json.get("project_info")

assert "public_project_id" == project_info.get("Project ID")
# check that endpoint returns dictionary and not a list
assert isinstance(project_info, dict)


def test_list_proj_info_unit_user(client):
"""Unit user should be able to list project information"""

token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client)
response = client.get(tests.DDSEndpoint.PROJECT_INFO, headers=token, query_string=proj_query)
assert response.status_code == http.HTTPStatus.OK
response_json = response.json
project_info = response_json.get("project_info")

assert "public_project_id" == project_info.get("Project ID")
assert (
"This is a test project. You will be able to upload to but NOT download"
in project_info.get("Description")
)
assert "Size" in project_info.keys() and project_info["Size"] is not None


def test_list_proj_info_returned_items(client):
"""Returned project information should contain certain items"""

token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client)
response = client.get(tests.DDSEndpoint.PROJECT_INFO, headers=token, query_string=proj_query)
assert response.status_code == http.HTTPStatus.OK
response_json = response.json
project_info = response_json.get("project_info")

assert all(item in project_info for item in proj_info_items)


def test_list_project_info_by_researchuser_not_in_project(client):
"""Researchuser not in project should not be able to list project info"""

token = tests.UserAuth(tests.USER_CREDENTIALS["researchuser2"]).token(client)
response = client.get(tests.DDSEndpoint.PROJECT_INFO, query_string=proj_query, headers=token)
assert response.status_code == http.HTTPStatus.FORBIDDEN
response_json = response.json
assert "Project access denied" in response_json.get("message")


def test_list_proj_info_public_insufficient_credentials(client):
"""If the project access has not been granted, the project info should not be provided."""

token = tests.UserAuth(tests.USER_CREDENTIALS["researchuser"]).token(client)
response = client.get(
tests.DDSEndpoint.PROJECT_INFO, query_string=proj_query_restricted, headers=token
)
assert response.status_code == http.HTTPStatus.FORBIDDEN
response_json = response.json
assert "Project access denied" in response_json.get("message")
2 changes: 1 addition & 1 deletion tests/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@


def test_version():
assert version.__version__ == "2.2.0"
assert version.__version__ == "2.2.2"

0 comments on commit fd802ff

Please sign in to comment.