Skip to content

Commit

Permalink
Merge pull request #840 from openzim/add_is_requested
Browse files Browse the repository at this point in the history
Add is_requested property to schedule endpoints
  • Loading branch information
rgaudin authored Sep 29, 2023
2 parents a3b4b5b + 4e8b83b commit 7474c66
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 36 deletions.
6 changes: 6 additions & 0 deletions dispatcher/backend/docs/openapi_v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1722,6 +1722,7 @@ components:
- language
- name
- category
- is_requested
properties:
config:
type: object
Expand All @@ -1736,6 +1737,8 @@ components:
$ref: '#/components/schemas/Language'
name:
type: string
is_requested:
type: boolean
most_recent_task:
type: object
required:
Expand Down Expand Up @@ -1780,6 +1783,7 @@ components:
- language
- name
- tags
- is_requested
properties:
category:
$ref: '#/components/schemas/ScheduleCategory'
Expand All @@ -1792,6 +1796,8 @@ components:
$ref: '#/components/schemas/Language'
name:
$ref: '#/components/schemas/ScheduleName'
is_requested:
type: boolean
most_recent_task:
type: object
required:
Expand Down
8 changes: 8 additions & 0 deletions dispatcher/backend/src/common/schemas/orms.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,15 @@ class LanguageSchema(m.Schema):


class ScheduleLightSchema(m.Schema):
def get_is_requested(schedule: dbm.Schedule):
return schedule.count_requested_task > 0

name = mf.String()
category = mf.String()
most_recent_task = mf.Nested(MostRecentTaskSchema)
config = mf.Nested(ConfigTaskOnlySchema)
language = mf.Nested(LanguageSchema)
is_requested = mf.Function(get_is_requested)


class ScheduleFullSchema(BaseSchema):
Expand Down Expand Up @@ -180,6 +184,9 @@ def get_duration(schedule: dbm.Schedule):
] = ScheduleFullSchema.get_one_duration(duration)
return duration_res

def get_is_requested(schedule: dbm.Schedule):
return len(schedule.requested_tasks) > 0

name = auto_field()
category = auto_field()
config = auto_field()
Expand All @@ -190,3 +197,4 @@ def get_duration(schedule: dbm.Schedule):
language = mf.Function(get_language)
most_recent_task = mf.Nested(MostRecentTaskSchema)
duration = mf.Function(get_duration)
is_requested = mf.Function(get_is_requested)
13 changes: 13 additions & 0 deletions dispatcher/backend/src/routes/schedules/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ def get(self, session: so.Session):
request_args.get("name"),
)

subq = (
sa.select(
sa.func.count(dbm.RequestedTask.id).label("count_requested_task"),
dbm.RequestedTask.schedule_id,
)
.group_by(dbm.RequestedTask.schedule_id)
.subquery("requested_task_count")
)

stmt = (
sa.select(
dbm.Schedule.name,
Expand All @@ -71,8 +80,12 @@ def get(self, session: so.Session):
dbm.Task.status,
dbm.Task.updated_at,
),
sa.func.coalesce(subq.c.count_requested_task, 0).label(
"count_requested_task"
),
)
.join(dbm.Task, dbm.Schedule.most_recent_task, isouter=True)
.join(subq, subq.c.schedule_id == dbm.Schedule.id, isouter=True)
.order_by(dbm.Schedule.name)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ class TestRequestedTaskBusiness:
def headers(self, access_token):
return {"Authorization": access_token, "Content-Type": "application/json"}

@pytest.fixture(scope="module")
def requested_task(self, client, headers, schedule):
@pytest.fixture
def temp_requested_task(self, client, headers, temp_schedule):
response = client.post(
"/requested-tasks/",
headers=headers,
data=json.dumps({"schedule_names": [schedule["name"]]}),
data=json.dumps({"schedule_names": [temp_schedule["name"]]}),
)
assert response.status_code == 201
assert "requested" in response.json
Expand All @@ -28,29 +28,29 @@ def requested_task(self, client, headers, schedule):
assert response.status_code == 200

def test_requested_task_with_schedule(
self, client, headers, schedule, requested_task
self, client, headers, temp_schedule, temp_requested_task
):
url = f"/requested-tasks/{requested_task}"
url = f"/requested-tasks/{temp_requested_task}"
response = client.get(
url,
headers=headers,
)
assert response.status_code == 200
assert "schedule_name" in response.json
assert response.json["schedule_name"] == schedule["name"]
assert response.json["schedule_name"] == temp_schedule["name"]
assert "original_schedule_name" in response.json
assert response.json["original_schedule_name"] == schedule["name"]
assert response.json["original_schedule_name"] == temp_schedule["name"]

def test_requested_task_without_schedule(
self, client, headers, schedule, requested_task
self, client, headers, temp_schedule, temp_requested_task
):
url = f"/schedules/{schedule['name']}"
url = f"/schedules/{temp_schedule['name']}"
response = client.delete(
url,
headers=headers,
)
assert response.status_code == 204
url = f"/requested-tasks/{requested_task}"
url = f"/requested-tasks/{temp_requested_task}"
response = client.get(
url,
headers=headers,
Expand All @@ -59,4 +59,56 @@ def test_requested_task_without_schedule(
assert "schedule_name" in response.json
assert response.json["schedule_name"] == "none"
assert "original_schedule_name" in response.json
assert response.json["original_schedule_name"] == schedule["name"]
assert response.json["original_schedule_name"] == temp_schedule["name"]

def test_schedule_is_not_requested(self, client, headers, temp_schedule):
# check in GET /schedules/{schedule_name}
url = f"/schedules/{temp_schedule['name']}"
response = client.get(
url,
headers=headers,
)
assert response.status_code == 200
assert "is_requested" in response.json
assert response.json["is_requested"] is False

# check in GET /schedules
url = "/schedules/"
response = client.get(
url,
headers=headers,
)
assert response.status_code == 200
assert "items" in response.json
for item in response.json["items"]:
if item["name"] != temp_schedule["name"]:
continue
assert "is_requested" in item
assert item["is_requested"] is False

def test_schedule_is_requested(
self, client, headers, temp_schedule, temp_requested_task
):
# check in GET /schedules/{schedule_name}
url = f"/schedules/{temp_schedule['name']}"
response = client.get(
url,
headers=headers,
)
assert response.status_code == 200
assert "is_requested" in response.json
assert response.json["is_requested"] is True

# check in GET /schedules
url = "/schedules/"
response = client.get(
url,
headers=headers,
)
assert response.status_code == 200
assert "items" in response.json
for item in response.json["items"]:
if item["name"] != temp_schedule["name"]:
continue
assert "is_requested" in item
assert item["is_requested"] is True
18 changes: 8 additions & 10 deletions dispatcher/backend/src/tests/integration/routes/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,22 +103,20 @@ def temp_schedule(make_schedule):
NB: associated tasks and requested tasks are also deleted
"""
schedule = make_schedule(name="periodic_sched_test")
schedule_id = schedule["_id"]
yield schedule
with Session.begin() as session:
schedule_obj = dbm.Schedule.get(session, schedule["name"])
schedule_obj.most_recent_task = None
session.flush()
schedule_obj = dbm.Schedule.get(session, schedule["name"], run_checks=False)
if schedule_obj:
schedule_obj.most_recent_task = None
session.flush()
session.execute(
sa.delete(dbm.RequestedTask).where(
dbm.RequestedTask.schedule_id == schedule["_id"]
dbm.RequestedTask.schedule_id == schedule_id
)
)
session.execute(
sa.delete(dbm.Task).where(dbm.Task.schedule_id == schedule["_id"])
)
session.execute(
sa.delete(dbm.Schedule).where(dbm.Schedule.id == schedule["_id"])
)
session.execute(sa.delete(dbm.Task).where(dbm.Task.schedule_id == schedule_id))
session.execute(sa.delete(dbm.Schedule).where(dbm.Schedule.id == schedule_id))


@pytest.fixture(scope="module")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ def test_list_schedules_no_param(self, client, schedules):
assert isinstance(item["language"]["code"], str)
assert isinstance(item["language"]["name_en"], str)
assert isinstance(item["language"]["name_native"], str)
assert isinstance(item["is_requested"], bool)

@pytest.mark.parametrize(
"skip, limit, expected",
Expand Down Expand Up @@ -238,6 +239,9 @@ def test_create_schedule_ok(
assert "most_recent_task" in response_json
response_json.pop("duration", None)
response_json.pop("most_recent_task", None)
assert "is_requested" in response_json
assert response_json["is_requested"] is False
response_json.pop("is_requested")

assert response_json == document

Expand Down Expand Up @@ -367,6 +371,9 @@ def test_get_schedule_with_name(self, client, schedule):
response_json.pop("duration", None)
response_json.pop("most_recent_task", None)
schedule.pop("_id")
assert "is_requested" in response_json
assert response_json["is_requested"] is False
response_json.pop("is_requested")

assert response_json == schedule

Expand Down
16 changes: 1 addition & 15 deletions dispatcher/frontend-ui/src/components/SchedulesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
<td>{{ schedule.category }}</td>
<td>{{ schedule.language.name_en }}</td>
<td>{{ schedule.config.task_name }}</td>
<td><font-awesome-icon v-if="has_requested_task(schedule.name)" icon="check"/></td>
<td><font-awesome-icon v-if="schedule.is_requested" icon="check"/></td>
<td v-if="schedule.most_recent_task">
<code :class="statusClass(schedule.most_recent_task.status)">{{ schedule.most_recent_task.status }}</code>
</td>
Expand Down Expand Up @@ -115,7 +115,6 @@
error: null, // API originated error message
meta: {}, // API query metadata (count, skip, limit)
schedules: [], // list of schedules returned by the API
requested_tasks: [], // list of schedule_names with requested tasks
block_url_updates: false,
};
},
Expand Down Expand Up @@ -186,7 +185,6 @@
selectedTags() { return this.selectedTagsOptions.map((x) => x.value); },
},
methods: {
has_requested_task(schedule_name) { return this.requested_tasks.indexOf(schedule_name) != -1;},
clearSelection() {
this.selectedName = "";
this.selectedCategoriesOptions = [];
Expand Down Expand Up @@ -277,18 +275,6 @@
parent.schedules = [];
parent.meta = response.data.meta;
parent.schedules = response.data.items;

parent.queryAPI('get', '/requested-tasks/', {params: {
limit: parent.selectedLimit,
'schedule_name': parent.selection}})
.then(function (response) {
parent.requested_tasks = response.data.items.map((item) => {
return item["schedule_name"];
});
})
.catch(function () {
parent.requested_tasks = [];
})
})
.catch(function (error) {
parent.schedules = [];
Expand Down

0 comments on commit 7474c66

Please sign in to comment.