aboutsummaryrefslogtreecommitdiff
path: root/app/main/routes.py
blob: 13fe3f8c4bf398a6bb9eaac08f70fe04a656e6f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# -*- coding: utf-8 -*-

import datetime
import logging

from flask import render_template, flash, url_for, redirect, request
from flask_login import current_user

from app import db
from app.decorators import login_required
from app.main import bp
from app.main.email import send_annotation_backup
from app.models import Annotation, Task
from app.utils.datasets import load_data_for_chart
from app.utils.tasks import generate_user_task

logger = logging.getLogger(__name__)

RUBRIC = """
Please mark the point(s) in the time series where an <b>abrupt change</b> in
 the behaviour of the series occurs. The goal is to define segments of the time 
 series that are separated by places where these abrupt changes occur. Recall 
 that it is also possible for there to be <i>no change points</i>.
<br>
"""


@bp.route("/")
@bp.route("/index")
def index():
    if not current_user.is_anonymous and not current_user.is_confirmed:
        return redirect(url_for("auth.not_confirmed"))
    if current_user.is_authenticated:
        user_id = current_user.id
        tasks = Task.query.filter_by(annotator_id=user_id).all()
        tasks_done = [t for t in tasks if t.done and not t.dataset.is_demo]
        tasks_todo = [
            t for t in tasks if (not t.done) and (not t.dataset.is_demo)
        ]
        return render_template(
            "index.html",
            title="Home",
            tasks_done=tasks_done,
            tasks_todo=tasks_todo,
        )
    return render_template("index.html", title="Home")


@bp.route("/annotate/<int:task_id>", methods=("GET", "POST"))
@login_required
def annotate(task_id):
    if request.method == "POST":
        # record post time
        now = datetime.datetime.utcnow()

        # get the json from the client
        annotation = request.get_json()
        if annotation["identifier"] != task_id:
            flash("Internal error: task id doesn't match.", "error")
            return redirect(url_for(task_id=task_id))

        task = Task.query.filter_by(id=task_id).first()

        # remove all previous annotations for this task
        for ann in Annotation.query.filter_by(task_id=task_id).all():
            db.session.delete(ann)
        task.done = False
        task.annotated_on = None
        db.session.commit()

        # record the annotation
        if annotation["changepoints"] is None:
            ann = Annotation(cp_index=None, task_id=task_id)
            db.session.add(ann)
            db.session.commit()
        else:
            for cp in annotation["changepoints"]:
                ann = Annotation(cp_index=cp["x"], task_id=task_id)
                db.session.add(ann)
                db.session.commit()

        # mark the task as done
        task.done = True
        task.annotated_on = now
        db.session.commit()
        flash("Your annotation has been recorded, thank you!", "success")

        # send the annotation as email to the admin for backup
        record = {
            "user_id": task.annotator_id,
            "dataset_name": task.dataset.name,
            "dataset_id": task.dataset_id,
            "task_id": task.id,
            "annotations_raw": annotation,
        }
        send_annotation_backup(record)

        # assign a new task if necessary
        task = generate_user_task(current_user)
        if task is None:
            return url_for("main.index")
        db.session.add(task)
        db.session.commit()
        flash(
            "A new dataset has been assigned for you to annotate. Thanks for your help!",
            "info",
        )
        return url_for("main.index")

    task = Task.query.filter_by(id=task_id).first()
    if task is None:
        flash("No task with id %r exists." % task_id, "error")
        return redirect(url_for("main.index"))
    if not task.annotator_id == current_user.id:
        flash(
            "No task with id %r has been assigned to you." % task_id, "error"
        )
        return redirect(url_for("main.index"))
    if task.done:
        flash("It's not possible to edit annotations at the moment.")
        return redirect(url_for("main.index"))
    data = load_data_for_chart(task.dataset.name, task.dataset.md5sum)
    if data is None:
        flash(
            "An internal error occurred loading this dataset, the admin has been notified. Please try again later. We apologise for the inconvenience."
        )
    title = f"Dataset: {task.dataset.id}"
    is_multi = len(data["chart_data"]["values"]) > 1
    return render_template(
        "annotate/index.html",
        title=title,
        identifier=task.id,
        data=data,
        rubric=RUBRIC,
        is_multi=is_multi,
    )