IEEE.org     |     IEEE Xplore Digital Library     |     IEEE Standards     |     IEEE Spectrum     |     More Sites

Verified Commit 32d02697 authored by Emi Simpson's avatar Emi Simpson
Browse files

Added spinners for pending sources in editor

Also adapted to the new version of the coordinator
parent 97346b38
Pipeline #254 failed with stages
in 40 seconds
......@@ -136,15 +136,15 @@ class CoordinatorConnection:
)
await self.post_job(project, desc, priority, backends)
async def jobs_for_project(
async def jobs_by_tag(
self,
project: Project,
) -> List['JobDict']:
tags: List[str],
) -> Dict[str, List['JobDict']]:
"""
Call "jobs_for_project" on the remote instance.
Call "jobs_by_tag" on the remote instance.
See
https://rit.ieee-saopen.org/mystic-coordinator/mystic_coordinator.html#jobs_for_project
https://rit.ieee-saopen.org/mystic-coordinator/mystic_coordinator.html#jobs_by_tag
for more details
This blocks until a response is recieved.
......@@ -152,9 +152,9 @@ class CoordinatorConnection:
msg = [
0, # message_type
self.msg_count, # message_id
"jobs_for_project",
"jobs_by_tag",
[
project.alphaid, # project_id
tags, # project_id
],
]
msg_packed: Optional[bytes] = msgpack.packb(msg)
......@@ -171,6 +171,31 @@ class CoordinatorConnection:
response = await self._recieve_specific_messages(matching_response).__anext__()
return response.response
async def jobs_for_project(self, project: Project) -> List['JobDict']:
"""
Return a list of jobs running for a given project
Wraps :func:`jobs_by_tag`, and assumes that jobs are tagged with the given
project's alphaid.
"""
return (await self.jobs_by_tag([project.alphaid]))[project.alphaid]
async def jobs_for_projects(self, projects: List[Project]) -> Dict[Project, List['JobDict']]:
"""
A wrapper for :func:`jobs_by_tag` that uses projects instead of tags
Assumes that jobs are tagged with the alphaid of the project that they're running
for. Results are returned mapped by project
"""
results = await self.jobs_by_tag([project.alphaid for project in projects])
return dict(
(
project,
results[project.alphaid]
)
for project in projects
)
def subscribe_to_notification(self, method_name: str) -> AsyncGenerator[List[Any], None]:
"""
Produces a generator returning any notification type messages to the given method
......@@ -289,9 +314,10 @@ class JobDict(TypedDict):
https://rit.ieee-saopen.org/mystic-coordinator/mystic_coordinator.html#jobs_for_project
for more specifics about what each feild means.
"""
tag: str
description: str
priority: int
remaining_backends: int
remaining_backends: Dict[str, List[str]]
queue_position: int
class Response(NamedTuple):
......
......@@ -656,13 +656,12 @@ class Project:
def __eq__(self, other: object) -> bool:
if isinstance(other, Project):
return self.project_id == other.project_id
elif isinstance(other, int):
return self.project_id == other
elif isinstance(other, str):
return self.alphaid == other
else:
return False
def __hash__(self) -> int:
return self.project_id
class User:
def __init__(self, user_id: int) -> None:
"""
......
......@@ -25,7 +25,7 @@ from mystic.sources import SOURCE_PROCESSORS
from flask import request, session, g
from mystic.database import MalformedId, Project, User
from mystic.config import connect_coordinator, service_provider, save_projects_json
from typing import Any, Callable, Coroutine, Dict, List, Optional, cast
from typing import Any, Callable, Coroutine, Dict, List, Optional, Tuple, cast
class ErrorTuple(Exception):
"""
......@@ -405,3 +405,30 @@ def get_pending_jobs(p: Project) -> List[JobDict]:
coordinator = await connect_coordinator()
return await coordinator.jobs_for_project(p)
return asyncio.run(get_jobs())
def get_many_pending_sources(projects: List[Project]) -> Dict[Project, List[str]]:
"""
Gets pending sources for all provided projects
Results are formatted in a dict maping each project to its pending sources. Pending
sources are represented only as the Repo URL.
This will be changed tomorrow when I switch to a multi-tag system so don't get too
attached
A pending source is a source who MAY be flagged as invalid by a pending job running
in the coordinator.
"""
async def get_jobs() -> Dict[Project, List[JobDict]]:
coordinator = await connect_coordinator()
return await coordinator.jobs_for_projects(projects)
return {
project:
[
repo_url
for job_dict in jobs
for _, urls in job_dict["remaining_backends"].items()
for repo_url in urls
]
for project, jobs in asyncio.run(get_jobs()).items()
}
......@@ -12,6 +12,7 @@
* - Sizes have been adjusted to a smaller scale
* - Arc length has been changed to 180deg
* - Spinner has been shifted slightly up
* - The icon class has been added
*/
.loader,
.loader:after {
......@@ -55,3 +56,13 @@
transform: rotate(360deg);
}
}
.loader.icon {
width: 9px;
height: 9px;
border-top: 3px solid rgba(0,0,0,0);
border-right: 3px solid var(--link-color);
border-bottom: 3px solid var(--link-color);
border-left: 3px solid var(--link-color);
animation: load8 1.4s infinite linear;
}
......@@ -169,7 +169,7 @@
<div class="loader"></div>
<span>{{job.description}}</span>
<span>
#{{job.queue_position - job.remaining_backends + 1}}
#{{job.queue_position - job.remaining_backends|length + 1}}
in queue
</span>
</div>
......
{% import "utils.html" as utils %}
{% macro editform(cursor, header, model_project) %}
{% macro editform(cursor, header, model_project, pending_sources = []) %}
{% set id = model_project.alphaid %}
{% set data_sources = model_project.get_folded_data_sources(cursor) %}
{% set owners = model_project.get_owners(cursor) %}
......@@ -55,7 +55,9 @@
{% for source in sources %}
<a href="{{source.url}}">{{source.url}}</a>
<div class="delete-and-warning-container">
{% if source.flagged %}
{% if source.url is in(pending_sources) %}
<div class="loader icon"></div>
{% elif source.flagged %}
{{utils.show_icon("alert-triangle", "problems occured while scanning", 16)}}
{% endif %}
<form class=delete method="POST">
......
......@@ -2,11 +2,12 @@
{% import "utils.html" as utils %}
{% import "projects-common.html" as project_utils %}
{% set selected = "home" %}
{% set projects = user.get_projects(cursor) %}
{% set projects_and_pending_sources = jobs.items() %}
{% block title %} Projects {% endblock %}
{% block head %}
{{super()}}
<link rel=stylesheet href={{static_versioned('styles-projects.css')}} />
<link rel=stylesheet href={{static_versioned("spinners.css")}}/>
<style>
:root {
{% if high_contrast() %}
......@@ -40,7 +41,7 @@ header {
<a href=#new-project><button class=big> + Add </button></a>
</header>
<div id=projects>
{% for project in projects %}
{% for project, pending_sources in projects_and_pending_sources %}
{% set id = project.alphaid %}
<div class=project>
<h6>
......@@ -78,7 +79,7 @@ header {
</a>
</div>
{% call utils.popup("edit-" + id) %}
{{ project_utils.editform(cursor, "Edit Project", project) }}
{{ project_utils.editform(cursor, "Edit Project", project, pending_sources) }}
{% endcall %}
</div>
{% endfor %}
......
......@@ -12,7 +12,7 @@ from flask import json, render_template, Blueprint
from typing import Any, Dict, Tuple, cast
from mystic.config import get_database, get_elastic, service_provider
from mystic.frontend import get_analytics, get_pending_jobs, try_action, ErrorTuple, get_user
from mystic.frontend import get_analytics, get_many_pending_sources, get_pending_jobs, try_action, ErrorTuple, get_user
import flask
bp = Blueprint("main", __name__, url_prefix="/")
......@@ -41,7 +41,8 @@ def main() -> str:
return render_template(
"projects.html",
login=_login_or_profile(),
user=user, cursor=cursor
user=user, cursor=cursor,
jobs=get_many_pending_sources(user.get_projects(cursor))
)
else:
return render_template(
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment