Skip to content

Commit

Permalink
Implement report evalutation
Browse files Browse the repository at this point in the history
  • Loading branch information
McSinyx committed Jan 18, 2021
1 parent 101fffd commit 06f675d
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 39 deletions.
2 changes: 2 additions & 0 deletions docs/source/meth/database/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ We will need these tables:
- ``User``
- ``Project``
- ``Task``
- ``Report``
- ``Comment``
- ``File``

Expand All @@ -20,5 +21,6 @@ Each of them is described in following sections.
user
project
task
report
comment
file
2 changes: 1 addition & 1 deletion docs/source/meth/database/project.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ It has following attributes:
Final evaluation of the project. It can be based on previous evaluation
of individual tasks.

``report`` : ``File`` or ``array`` of ``File``
``report`` : ``Report``
A file that contains the report(s) for the group project.
13 changes: 13 additions & 0 deletions docs/source/meth/database/report.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Report
======

The ``Report`` object is used for storing reports' revisions and evaluation.

``comment`` : ``string``
Evaluation comment.

``grade`` : ``integer``
Evaluated grade.

``revisions`` : ``array`` of ``string``
UUIDs of previously uploaded revisions.
10 changes: 8 additions & 2 deletions src/acanban/db.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# RethinkDB wrappers
# Copyright (C) 2020 Nguyễn Gia Phong
# Copyright (C) 2020-2021 Nguyễn Gia Phong
#
# This file is part of Acanban.
#
Expand All @@ -16,7 +16,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with Acanban. If not, see <https://www.gnu.org/licenses/>.

from typing import Any, Awaitable, Tuple
from typing import Any, Awaitable, Dict, Tuple

from quart import current_app
from rethinkdb import r
Expand Down Expand Up @@ -53,6 +53,12 @@ def __setattr__(self, name: str, value: Any) -> None:
f'{cls}.update must be used for dynamic attribute {name!r}')
super().__setattr__(name, value)

async def pluck(self, *fields: str) -> Dict[str, Any]:
"""Return the plucked fields from the wrapped object."""
query = r.table(self.table).get(self.key).pluck(*fields)
async with current_app.db_pool.connection() as conn:
return await query.run(conn)

async def update(self, **kwargs: Any) -> None:
"""Update the RethinkDB object from given keys and values.
Expand Down
71 changes: 47 additions & 24 deletions src/acanban/project.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Project pages
# Copyright (C) 2020 Nguyễn Gia Phong
# Copyright (C) 2020-2021 Nguyễn Gia Phong
# Copyright (C) 2021 Ngô Ngọc Đức Huy
#
# This file is part of Acanban.
Expand All @@ -18,7 +18,7 @@
# along with Acanban. If not, see <https://www.gnu.org/licenses/>.

from operator import itemgetter
from typing import Any, Dict, Sequence
from typing import Any, Dict, Optional, Sequence

from quart import (Blueprint, ResponseReturnValue, current_app,
redirect, render_template, request)
Expand Down Expand Up @@ -71,19 +71,22 @@ async def info_redirect(uuid: str) -> ResponseReturnValue:
return redirect(f'/p/{uuid}/info')


async def pluck(uuid: str, fields: Sequence[str] = ()) -> Dict[str, Any]:
async def pluck(uuid: str, fields: Sequence[str] = (),
projects: Optional[Sequence[str]] = None) -> Dict[str, Any]:
"""Pluck the given fields from the project, with permission check."""
query = r.table('projects').get(uuid).pluck(*fields)
async with current_app.db_pool.connection() as connection:
try:
project = await query.run(connection)
except ReqlNonExistenceError:
raise NotFound
try:
user_projects = await current_user.projects
except ReqlNonExistenceError:
raise Unauthorized
if uuid not in user_projects: raise Unauthorized

if projects is None:
try:
projects = await current_user.projects
except ReqlNonExistenceError:
raise Unauthorized
if uuid not in projects: raise Unauthorized
return project


Expand Down Expand Up @@ -111,22 +114,42 @@ async def edit(uuid: str) -> ResponseReturnValue:
return redirect(f'/p/{uuid}/info')


@blueprint.route('/<uuid>/report', methods=['GET', 'POST'])
@blueprint.route('/<uuid>/report')
@login_required
async def report(uuid: str) -> ResponseReturnValue:
"""Return the page for editting the projects' basic infomation."""
if request.method == 'POST':
await pluck(uuid) # check project's existence and permission
file = await ipfs_add()
async with current_app.db_pool.connection() as conn:
await r.table('projects').get(uuid).update(
{'reports': r.row['reports'].append(file)}).run(conn)
return redirect(request.referrer)

project = await pluck(uuid, ('id', 'name', 'reports'))
query = r.table('files').get_all(*project['reports'])
"""Return the projects' report infomation and forms."""
user = await current_user.pluck('projects', 'role')
project = await pluck(uuid, ('id', 'name', 'report'), user['projects'])
query = r.table('files').get_all(*project['report']['revisions'])
async with current_app.db_pool.connection() as conn:
reports = [report async for report in await query.run(conn)]
reports.sort(key=itemgetter('time'), reverse=True)
return await render_template('project-report.html',
project=project, reports=reports)
revisions = [file async for file in await query.run(conn)]
revisions.sort(key=itemgetter('time'), reverse=True)
return await render_template(
'project-report.html', project=project, report=project['report'],
revisions=revisions, for_student=(user['role']=='student'))


@blueprint.route('/<uuid>/report/upload', methods=['POST'])
@login_required
async def report_upload(uuid: str) -> ResponseReturnValue:
"""Handle report upload."""
await pluck(uuid) # check project's existence and permission
action = r.row['report']['revisions'].append(await ipfs_add())
async with current_app.db_pool.connection() as conn:
await r.table('projects').get(uuid).update(
{'report': {'revisions': action}}).run(conn)
return redirect(request.referrer)


@blueprint.route('/<uuid>/report/eval', methods=['POST'])
@login_required
async def report_eval(uuid: str) -> ResponseReturnValue:
"""Handle report evaluation."""
user = await current_user.pluck('role', 'projects')
if 'role' == 'student': raise Unauthorized
await pluck(uuid, projects=user['projects'])
form = await request.form
updated = {'grade': int(form['grade']), 'comment': form['comment']}
query = r.table('projects').get(uuid).update({'report': updated})
async with current_app.db_pool.connection() as conn: await query.run(conn)
return redirect(request.referrer)
24 changes: 18 additions & 6 deletions src/acanban/templates/project-report.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,34 @@
{% block tabcontent %}
<div class=col-wide>
<h2>Evaluation</h2>
<form action=/p/{{ project.id }}/report/eval method=POST>
<label for=grade>Grade</label>
<input {% if for_student %}readonly{% endif %}
type=text name=grade id=grade value={{ report.grade }}>
<label for=comment>Comment</label>
<textarea {% if for_student %}readonly{% endif %}
name=comment id=comment>{{ report.comment }}</textarea>
{% if not for_student %}<button>Save evaluation</button>{% endif %}
</form>

<h2>Report revisions</h2>
<form action=/p/{{ project.id }}/report method=POST
{% if for_student %}
<form action=/p/{{ project.id }}/report/upload method=POST
enctype=multipart/form-data>
<label for=file>Select report to upload</label>
<input type=file name=file id=file>
<button>Upload new report</button>
</form>
{% for report in reports %}
{% set uri = "/ipfs/" ~ report.cid %}
{% endif %}

{% for file in revisions %}
<dl>
<dt>File</dt>
<dd><a href={{ uri }}>{{ report.name }}</a></dd>
<dd><a href=/ipfs/{{ file.cid }}>{{ file.name }}</a></dd>
<dt>Uploader</dt>
<dd><a href=/u/{{ report.user }}>{{ report.user }}</a></dd>
<dd><a href=/u/{{ file.user }}>{{ file.user }}</a></dd>
<dt>Revision</dt>
<dd>{{ report.time }}</a></dd>
<dd>{{ file.time }}</a></dd>
</dl>
{% endfor %}
</div>
Expand Down
22 changes: 17 additions & 5 deletions tests/assets/rethinkdump/test/projects.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
"description": "Consectetur neque ut adipisci labore. Dolor velit porro adipisci. Consectetur sed ipsum neque porro etincidunt dolor ut. Ipsum voluptatem dolorem dolorem etincidunt. Etincidunt ut dolore velit numquam quisquam. Consectetur non non non quiquia consectetur velit velit. Velit neque ipsum aliquam ut adipisci sit. Tempora voluptatem aliquam non dolore eius quaerat.",
"id": "7fee498b-98ac-4173-b600-e94618f9ea1f",
"name": "Amet sed porro ut quisquam velit quisquam modi",
"reports": [],
"report": {
"comment": "",
"grade": -1,
"revisions": []
},
"students": [
"opheliad",
"jamesm",
Expand Down Expand Up @@ -126,9 +130,13 @@
"description": "Quaerat quaerat dolor labore. Quisquam voluptatem tempora quiquia ut. Dolore quisquam tempora adipisci. Magnam eius sit neque quiquia est ipsum. Consectetur quiquia sit eius adipisci est. Adipisci tempora aliquam voluptatem etincidunt dolorem dolor. Ut ipsum quiquia etincidunt neque adipisci porro consectetur.",
"id": "be7a04b8-650f-41dc-912b-10d225baff29",
"name": "Eius aliquam modi modi sit adipisci dolor",
"reports": [
"fbf2caaa-b69e-4d38-8a69-6d3a068b339b"
],
"report": {
"comment": "Très bien!",
"grade": 22,
"revisions": [
"fbf2caaa-b69e-4d38-8a69-6d3a068b339b"
]
},
"students": [
"milesc",
"ellar",
Expand Down Expand Up @@ -241,7 +249,11 @@
"description": "Ipsum dolorem porro eius ut. Quaerat dolore magnam sit consectetur labore eius ipsum. Quiquia dolore dolorem sit sed sit sed. Sed sit quaerat numquam adipisci. Ipsum aliquam est dolor est non numquam. Numquam porro aliquam dolorem etincidunt. Numquam etincidunt etincidunt non. Labore modi modi porro quisquam.",
"id": "ee4af55a-cff3-4ffc-96ea-24c4b9979773",
"name": "Ut eius quiquia ipsum",
"reports": [],
"report": {
"comment": "",
"grade": -1,
"revisions": []
},
"students": [
"julietc",
"alaricw",
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ skip_covered = true

[flake8]
hang-closing = true
ignore = E226, E701, E704, W503
ignore = E225, E226, E701, E704, W503

[isort]
balanced_wrapping = true
Expand Down

0 comments on commit 06f675d

Please sign in to comment.