def new_user(get, guts): if get: template = get_template("new_user.html") return TemplateResponse(template, {"form": NewUserForm()}) else: if not guts.user.is_superuser: return ForbiddenResponse( "Only administrators may create new accounts.") f = NewUserForm(guts.parameters) if f.is_valid(): frm = settings.EMAIL_FROM current_site = Site.objects.get_current() subject = "New %s Account Created" % current_site.name base = "https://%s" % current_site.domain cleaned = f.cleaned_data password = User.objects.make_random_password() if cleaned["username"]: username = cleaned["username"] else: ## peel off the part before the @ ## and then translate non-alpha-numeric characters to underscores username = re.sub(r'\W', '_', cleaned["email"].split("@", 1)[0]) u = User.objects.create_user(username, cleaned["email"], password) u.first_name = cleaned["first_name"] u.last_name = cleaned["last_name"] u.save() send_mail( subject, """An account has been created for you on the %s system. Login information: Username: %s Password: %s Login to change your password: %s/user_management/ If you have any problems logging in, please email us for assistance at: %s """ % (current_site.name, username, password, base, frm), frm, [cleaned['email']], ) u.save() template = get_template("new_user_created.html") return TemplateResponse(template, { 'username': username, 'password': password }) else: template = get_template("new_user.html") return TemplateResponse("new_user.html", {'form': NewUserForm()})
def track_page_visit(get, guts): if get: form = PageTrackForm() return TemplateResponse(page_track_template, {"form": form}) else: if guts.user.is_superuser: form = PageTrackForm(guts.parameters) if form.is_valid(): url = form.cleaned_data["url"] view, view_args, view_kwargs = resolve(urlparse(url).path) print(repr(form.cleaned_data), file=sys.stderr) pt = PageTrack( user=form.cleaned_data["user"], view_name=view.__name__, view_args=repr(view_args), view_kwargs=repr(view_kwargs), focus_time=form.cleaned_data["focus_time"], ) if "blur_time" in form.cleaned_data: pt.blur_time = form.cleaned_data["blur_time"] pt.full_clean() pt.save() new_form = PageTrackForm() return TemplateResponse(page_track_template, { "form": new_form, "pt": pt }) else: return TemplateResponse(page_track_template, {"form": form}) else: return ForbiddenResponse("Only superusers may use this form.")
def timesheet(guts): """Generate a TimeChart object from the given spec parameters and then translate it into a viewable format.""" if guts.user.is_staff: template = get_template("timesheet.html") if all([ p in guts.parameters for p in ("resolution", "periods", "ending_date") ]): form = TimesheetForm(guts.parameters) else: form = TimesheetForm() if form.is_bound and form.is_valid(): resolution = form.cleaned_data["resolution"] periods = form.cleaned_data["periods"] spec = TimeBlockSpec(resolution, periods, form.cleaned_data["ending_date"]) users = set(form.cleaned_data["users"]) for group in form.cleaned_data["groups"]: for user in group.user_set.all(): users.add(user) chart = TimeChart(spec, users) rows = [] for (uid, bins_by_block) in six.iteritems(chart.report): user = User.objects.get(pk=uid) cells = [] for block in spec: if block in bins_by_block: cells.append(bins_by_block[block].as_dict()) else: cells.append({"data_sets": [], "work_time": 0}) rows.append({"user": user.username, "cells": cells}) headers = [str(block) for block in spec] return TemplateResponse( template, { "form": form, "headers": headers, "rows": sorted(rows, key=lambda r: r["user"]), "resolution": resolution, "periods": periods, }, ) else: return TemplateResponse(template, {"form": form}) else: return ForbiddenResponse( "The timesheet pages are only accessible to staff.")
def home(guts): """Manage the display of the homepage. Currently returns a count for the number of resolvable tasks. Template data should include the counts of tasks the user can annotate or merge. """ site_messages = Announcement.objects.filter(enabled=True) respondable_tasks = Task.objects.can_annotate(guts.user) resolvable_tasks = Task.objects.can_merge(guts.user) recent_responses = Response.objects.filter( user=guts.user).order_by('-end_time')[0:5] recent_results = Result.objects.filter( user=guts.user).order_by('-end_time')[0:5] reviews = Review.objects.filter(complete=False, response__user=guts.user) if "visitable_pages" not in guts.session: guts.session["visitable_pages"] = visitable(guts.user) template = get_template("home.html") return TemplateResponse( template, { 'respondable_tasks': respondable_tasks, 'respondable_task_count': respondable_tasks.count(), 'resolvable_tasks': resolvable_tasks, 'resolvable_task_count': resolvable_tasks.count(), 'recent_responses': recent_responses, 'recent_results': recent_results, 'reviews': reviews, "pages": guts.session["visitable_pages"], "messages": site_messages, }, )
def one_group(guts, group_id): """Summarize information about one group.""" if guts.user.is_superuser: group = get_object_or_404(Group, pk=group_id) users = [ u.username for u in group.user_set.order_by("username") if u.is_active ] emails = [ u.email for u in group.user_set.order_by("username") if u.is_active ] annotates = [{ "title": p.title, "id": p.id } for p in group.annotator_for.order_by("title")] merges = [{ "title": p.title, "id": p.id } for p in group.merger_for.order_by("title")] template = get_template("group.html") return TemplateResponse( template, { "id": group_id, "name": group.name, "users": users, "annotates": annotates, "merges": merges, "emails": emails, }, ) else: return ForbiddenResponse("Only administrators can see this page.")
def task_adhoc_review(get, guts): if get: response_id = guts.parameters['response'] response = Response.objects.get(pk=response_id) reviews = Review.objects.filter(response=response) if reviews.count() == 1: review = reviews[0] else: review = Review(response=response, comment="") task = response.task result = task.result # Tagger or merger can both view, as well as super-user if ((response.user != guts.user) and not (guts.user.is_superuser) and (result.user != guts.user)): return ForbiddenResponse( "You are not authorized to see this review.") project_type = get_project_type(task.project) task = project_type.cast(task) try: template = get_template(review.review_template) except NotImplementedError: template = task.template() template_data = task.template_data(review=review) return TemplateResponse(template, template_data) else: return ViewResponse('main:next-review')
def task_review(get, guts, review_id): """Present an interface to request the user to review a given task.""" review = get_object_or_404(Review, pk=review_id) response = review.response ## NOTE: an earlier version of this code would throw a Review.DoesNotExist exception ## if the Review object with the right id and user had complete=True. I don't see ## the harm in re-reviewing so I am not checking for that here. if (response.user != guts.user) and not (guts.user.is_superuser or get): return ForbiddenResponse("You are not authorized to see this review.") if get: task = response.task project_type = get_project_type(task.project) task = project_type.cast(task) try: template = get_template(review.review_template) except NotImplementedError: template = task.template() template_data = task.template_data(review=review) return TemplateResponse(template, template_data) else: review.complete = True review.full_clean() review.save() return ViewResponse('main:next-review')
def all_projects_brief(guts): """Summarize the active projects, whose tags match the filters in the query parameter, more succinctly.""" def extended_dict(project): d = project.as_dict() d["priority_display"] = project.get_priority_display() d["remaining_to_tag"] = project.task_set.filter( completed=False).count() d["remaining_to_merge"] = project.task_set.filter( completed=True, result__isnull=True).count() d["merged"] = project.task_set.filter(completed=True, result__isnull=False).count() return d if guts.user.is_superuser: filter_tags = guts.parameters.getlist("filter") qs = projects_query_set(filter_tags) data = { "project_list": [extended_dict(p) for p in qs], "available_tags": [tag for tag in ProjectTag.objects.all()], "selected_tags": filter_tags, } template = get_template("brief-overview.html") return TemplateResponse(template, data) else: return ForbiddenResponse("Only administrators can see this page.")
def wip_review(get, guts): if guts.user.is_superuser: wips = WorkInProgress.objects.all() elif Project.objects.filter(admin=guts.user).count(): wips = WorkInProgress.objects.filter(task__project__admin=guts.user) else: return ForbiddenResponse( "Only project administrators and superusers may see this page.") if get: wip_list = [{ "id": wip.id, "user": wip.user.username, "task_id": wip.task.id, "task_url": wip.task.get_absolute_url(), "project_id": wip.task.project.id, "project_name": wip.task.project.title, "project_url": wip.task.project.get_absolute_url(), "start_time": wip.start_time, } for wip in wips.order_by("-start_time")] template_data = {"wips": wip_list} template = get_template("wip-review.html") return TemplateResponse(template, template_data) else: try: wips_to_delete = [ wips.get(pk=pk) for pk in guts.parameters.getlist("wips_to_delete") ] for wip in wips_to_delete: wip.delete() return ViewResponse('main:wip-list') except WorkInProgress.DoesNotExist: return ForbiddenResponse("You can only delete a project " "that you are an admin for.")
def project_upload(get, guts, project): """Take an upload, and queue it for running through the task processor. # TODO: Testing # TODO: Return something better than the current Upload complete response. (template) # TODO: fix allowing user to select project form a dropdown (project is in the URL). # TODO: only allow certain users to upload projects """ ptype = get_project_type(project) project = ptype.cast(project) if get: template = get_template("project/upload_form.html") return TemplateResponse(template, {"form": UploadForm()}) else: action = guts.parameters["action"] if action == "Upload": pu = ProjectUpload(project=project) item = UploadForm(guts.parameters, guts.files, instance=pu) if item.is_valid(): item.save() if not hasattr(settings, "PROCESS_INLINE") or settings.PROCESS_INLINE: project.handle_input(pu) if project.auto_review: project.add_auto_reviews() pu.complete = True pu.save() message = ( "Upload complete to project %s, tasks processed" % project.id) else: message = "Upload complete, queued as %s" % pu.id guts.log_info(message) return DefaultResponse(message) else: ## re-present the page with input errors marked template = get_template("project/upload_form.html") return TemplateResponse(template, {"form": item}) elif action == "Empty": ## NOTE: if subclasses have customized the delete() method, ## this bulk-delete will not call it. Task.objects.filter(project=project).delete() return ViewResponse('main:view-project', project.id) else: return ErrorResponse("Bad form", "Action parameter %s not understood" % action)
def project_stats(guts, project): user_response_counts = (Response.objects.filter( task__project=project).values("user__username").annotate(Count("id"))) template = get_template("project/stats.html") return TemplateResponse(template, { 'user_response_counts': user_response_counts, 'project': project })
def abandon_wip(get, guts): """A view to abandon a WIP. When GETting this page, the user sees the \"are you sure?\" page. When POSTing, the first WIP that the user has is deleted. """ if get: wips = WorkInProgress.objects.filter(user=guts.user) template = get_template("abandon_wip.html") return TemplateResponse(template, {'wips': wips}) else: wips = WorkInProgress.objects.filter(user=guts.user) if wips.count(): wip = wips[0] wip.delete() return ViewResponse('main:home') else: template = get_template("abandon_wip.html") return TemplateResponse(template, {"wips": wips})
def all_users(guts): """Summarize information about all users.""" if guts.user.is_superuser: users = [{ "name": u.username, "is_superuser": u.is_superuser, "annotated": u.response_set.count(), "merged": u.result_set.count(), } for u in User.objects.order_by("username") if u.is_active] template = get_template("users.html") return TemplateResponse(template, {"users": users}) else: return ForbiddenResponse("Only administrators can see this page.")
def all_projects(guts): """Summarize the status of the active projects whose tags match the filters in the query parameter.""" if guts.user.is_superuser: qs = projects_query_set(guts.parameters.getlist("filter")) cache = dict([(g.id, [u.username for u in g.user_set.all() if u.is_active]) for g in Group.objects.all()]) result = {"project_list": [project_info(p, cache) for p in qs]} template = get_template("overview.html") return TemplateResponse(template, result) else: return ForbiddenResponse("Only administrators can see this page.")
def project_agreement(guts, project): ptype = get_project_type(project) if not hasattr(ptype, 'test_agreement'): return DefaultResponse("Project doesn't support testing agreement.") same_counts = {} tasks = project.task_set.filter(completed=True).all() for t in tasks: responses = t.response_set.all() same = ptype.test_agreement(responses) same_counts[same] = same_counts.setdefault(same, 0) + 1 template = get_template('project/agreement.html') print(same_counts) ## TODO: is this leftover debugging code? return TemplateResponse(template, { 'counts': same_counts, 'task_count': tasks.count() })
def all_groups(guts): """Summarize information about all the groups in the database.""" if guts.user.is_superuser: groups_info = [{ "id": g.id, "name": g.name, "users": [ u.username for u in g.user_set.order_by("username") if u.is_active ], } for g in Group.objects.order_by("name")] template = get_template("groups.html") return TemplateResponse(template, {"groups": groups_info}) else: return ForbiddenResponse("Only administrators can see this page.")
def recent_results(guts, username): """Show a list of recent results.""" skip = int(guts.parameters.get("skip", 0)) user = get_object_or_404(User, username=username) def contextlet(result): ptype = get_project_type(result.task.project) return { "result": result, "result_summary": ptype.cast(result).summary(), "project_type": result.task.project.type, "responses": [ { "response": response, "response_summary": ptype.cast(response).summary(), "match": ptype.cast(result).summary() == ptype.cast(response).summary(), } for response in result.task.response_set.all() ], } if guts.user.is_superuser or guts.user == user: recent_results = Result.objects.filter(user=user).order_by("-end_time")[ skip : skip + 50 ] skips = {"current": "%s - %s" % (skip + 1, skip + len(recent_results))} if skip >= 50: skips['forward'] = (skip - 50) or "0" if len(recent_results) == 50: skips['backward'] = skip + 50 template = get_template("recent_results.html") template_context = { "results": [contextlet(result) for result in recent_results], "skips": skips, "username": username, } return TemplateResponse(template, template_context) else: return ForbiddenResponse( "Only the user %s, or an administrator, may see this page." % username )
def recent_responses(guts, username): """Show a list of recent responses""" skip = int(guts.parameters.get("skip", 0)) user = get_object_or_404(User, username=username) if guts.user.is_superuser or guts.user == user: recent_responses = Response.objects.filter( user=user, task__result__id__isnull=False ).order_by("-task__result__end_time")[skip : skip + 50] responses = [] for response in recent_responses: res = {'response': response} res['project_type'] = response.task.project.type ptype = get_project_type(response.task.project) res['task_summary'] = ptype.cast(response.task).summary() res['response_summary'] = ptype.cast(response).summary() res['result_summary'] = ptype.cast(response.task.result).summary() if ( res["response_summary"] is not None and res["result_summary"] is not None ): res['match'] = res['response_summary'] == res['result_summary'] responses.append(res) template = get_template("recent_responses.html") skips = {} skips['current'] = "%s - %s" % (skip + 1, skip + len(responses)) if skip >= 50: skips['forward'] = (skip - 50) or "0" if len(responses) == 50: skips['backward'] = skip + 50 return TemplateResponse( template, {'responses': responses, 'skips': skips, 'username': user.username}, ) else: return ForbiddenResponse( "Only the user %s, or an administrator, may see this page." % username )
def change_password(get, guts): message = "" if get: f = ChangePassForm() else: f = ChangePassForm(guts.parameters) if f.is_valid(): cleaned = f.cleaned_data if (guts.user.check_password(cleaned['old_pass']) and cleaned['pass1'] == cleaned['pass2']): guts.user.set_password(cleaned['pass1']) guts.user.save() return ViewResponse('main:home') elif not guts.user.check_password(cleaned['old_pass']): message = "You did not enter your current password correctly" else: message = "Passwords didn't match." template = get_template("change_password.html") return TemplateResponse(template, { 'form': f, 'user': guts.user, 'message': message })
def one_user(guts, username): """Summarize information about one user.""" user = get_object_or_404(User, username=username) if guts.user.is_superuser or guts.user == user: groups = [{"name": g.name, "id": g.id} for g in user.groups.all()] recent_responses = [ r.task.id for r in Response.objects.filter( user=user).order_by("-end_time")[0:20] ] recent_results = [ r.task.id for r in Result.objects.filter( user=user).order_by("-end_time")[0:20] ] recent_reviews = [ r.id for r in Review.objects.filter( response__user=user).order_by("-creation_time")[0:20] ] template = get_template("user.html") return TemplateResponse( template, { "name": username, "is_superuser": user.is_superuser, "groups": groups, "respondable_task_count": Task.objects.can_annotate(user).count(), "resolvable_task_count": Task.objects.can_merge(user).count(), "recent_responses": recent_responses, "recent_results": recent_results, "recent_reviews": recent_reviews, }, ) else: return ForbiddenResponse( "Only the user %s, or an administrator, may see this page." % username)
def task_view(get, guts, task_id): """View a given task ID or submit a response to it; in either case, this should dispatch appropriately based on the task's type.""" import main.views.base task = Task.objects.get(pk=task_id) project_type = type_list[task.project.type] task = project_type.cast(task) try: wip = WorkInProgress.objects.get(task=task, user=guts.user) except WorkInProgress.DoesNotExist: if task.project.auto_review: try: ## Tasks in auto-review projects have a kludgey workflow: ## the task is responsible for noticing that it is in an ## auto-review project and adjusting the output of template() ## and template_data accordingly. ## When GETting the page, if a Response object already exists, then ## the output should be appropriate for displaying a review. auto_review = AutoReview.objects.get(task=task, user=guts.user) if get: ## If the Task subclass has not been retrofitted to handle ## auto-review projects, then this line will throw an exception ## "TypeError: ... unexpected keyword argument 'auto_review_user'" return TemplateResponse( task.template(), task.template_data(auto_review_user=guts.user)) else: ## NOTE: does the downcasting of task screw this query up? response_query = Response.objects.filter(user=guts.user, task=task) if not response_query.exists(): ## No response? Ask the task to make one! kwargs = { "user": guts.user, "task": task, "start_time": auto_review.start_time, } task.handle_response(guts, **kwargs) ## (Note that this code path does NOT update the task's ## completed_assignments field, because auto-review projects ## do not have a limit on how many responses they can have.) ## ## And then GET this page again, so the user can see the review. return ViewResponse('main:view-task', task_id) else: ## The user must have clicked the "I read this review" button. auto_review.end_time = timezone.now() auto_review.full_clean() auto_review.save() return ViewResponse('main:next-task') except AutoReview.DoesNotExist: ## fall through to the case below, since an admin is allowed to ## look at someone else's auto-review pass ## an admin can look at any task, but not even an admin ## can submit a response or result to a task without getting a WIP first if not (task.viewable_by(guts.user) and get): return ForbiddenResponse("You are not allowed to view %s." % six.text_type(task)) if get: return TemplateResponse(task.template(), task.template_data()) else: ## TODO: if we successfully make handle_response a method of the task, ## then we don't have to pass the task in kwargs kwargs = { "user": guts.user, "task": task, "start_time": wip.start_time } try: task.handle_response(guts, **kwargs) if task.completed: if 'review_user' in guts.parameters: users = guts.parameters.getlist('review_user') for user in users: user_obj = User.objects.get(pk=user) comment = guts.parameters.get("comment_%s" % user, "") rev = Review( response=task.response_set.get(user=user_obj), comment=comment, ) rev.full_clean() rev.save() else: task.completed_assignments = task.completed_assignments + 1 task.full_clean() task.save() wip.delete() if 'stop_working' in guts.parameters: return ViewResponse('main:home') else: return ViewResponse('main:next-task') except MultiValueDictKeyError: ## translate the MultiValueDict into a list of (key, list) pairs params = guts.parameters.lists() exc_type, exc_value, exc_traceback = sys.exc_info() tb_info = traceback.extract_tb(exc_traceback) template = get_template("parameter-error-in-task.html") context = { "task": str(task), "params": params, "exc_value": exc_value, "traceback": tb_info, } guts.log_error("Bad form? " + repr(context)) return TemplateResponse(template, context, status=500)
def one_project(guts, project_id): """Summarize information about a single project.""" project = get_object_or_404(Project, pk=project_id) if guts.user.is_superuser or guts.user == project.admin: ## Generate assignment counts. When a project has 5000 or more tasks, ## the join-group-and-count operation using the Django QuerySet API ## is painfully slow, so we are dropping back to raw SQL here. query = """SELECT completed_assignments, COUNT(t.id) AS howmany FROM main_task AS t LEFT JOIN main_result AS r ON (r.task_id = t.id) WHERE project_id = %s AND (r.id IS NULL OR NOT completed) GROUP BY completed_assignments""" cursor = connection.cursor() cursor.execute(query, [project.id]) assignments = [{ "completed_assignments": completed_assignments, "howmany": howmany } for completed_assignments, howmany in cursor.fetchall()] tasks = project.task_set needs_merging = tasks.filter(completed=True, result__isnull=True) if needs_merging.count(): assignments.append({ 'completed_assignments': 'Needs Merging', 'howmany': needs_merging.count(), }) finished = tasks.filter(result__isnull=False) if finished.count(): assignments.append({ 'completed_assignments': 'Finished', 'howmany': finished.count() }) # Project overview info pi = project_info(project) # Uploaded files uploads = ProjectUpload.objects.filter(project=project) # Task info MAXIMUM_TASKS_TO_LINK = 1500 if tasks.count() <= MAXIMUM_TASKS_TO_LINK: ti = [{ "id": t.id, "url": t.get_absolute_url(), "completed": t.completed, "merged": t.merged, } for t in tasks.order_by("id")] show_tasks = True else: ti = None show_tasks = False template = get_template("project.html") return TemplateResponse( template, { "project": pi, "show_tasks": show_tasks, "tasks": ti, 'assignments': assignments, 'uploads': uploads, }, ) else: return ForbiddenResponse( "Only project owners or administrators may see this page.")
def about(guts): """Manage the display of the homepage""" template = get_template("about.html") return TemplateResponse(template, {})