Skip to content

Commit

Permalink
alembic: change jobs table names for consistency
Browse files Browse the repository at this point in the history
* tests: adapt tests to new implementation of jobs registry
* chore: fix formatting
  • Loading branch information
kpsherva committed Aug 26, 2024
1 parent 03de8c4 commit f3a4441
Show file tree
Hide file tree
Showing 21 changed files with 280 additions and 191 deletions.
2 changes: 2 additions & 0 deletions invenio_jobs/administration/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def get_api_endpoint(self, pid_value=None):
return f"/api/jobs/{pid_value}/runs"

def get_details_api_endpoint(self):
"""Compute api endpoint link for job details view."""
api_url_prefix = current_app.config["SITE_API_URL"]
slash_tpl = "/" if not self.api_endpoint.startswith("/") else ""

Expand All @@ -117,6 +118,7 @@ def get_details_api_endpoint(self):
return f"{slash_tpl}{self.api_endpoint}"

def get_context(self, **kwargs):
"""Compute admin view context."""
ctx = super().get_context(**kwargs)
ctx["request_headers"] = self.request_headers
ctx["ui_config"] = self.item_field_list
Expand Down
9 changes: 0 additions & 9 deletions invenio_jobs/administration/runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,3 @@ class RunsListView(AdminResourceListView):
display_edit = False
display_create = False
actions = None

# item_field_list = {
# "job": {"text": _("Jobs"), "order": 1, "width": 3},
# "active": {"text": _("Status"), "order": 2, "width": 2},
# "last_run_start_time": {"text": _("Last run"), "order": 3, "width": 3},
# "user": {"text": _("Started by"), "order": 4, "width": 3},
# "next_run": {"text": _("Next run"), "order": 5, "width": 3},
# }

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#

Check failure on line 1 in invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py

View workflow job for this annotation

GitHub Actions / Tests / Tests (3.9, postgresql14, opensearch2)

isort-check """Update jobs module table names""" +import sqlalchemy as sa from alembic import op -import sqlalchemy as sa from sqlalchemy.dialects import postgresql -from sqlalchemy_utils import JSONType, UUIDType, ChoiceType +from sqlalchemy_utils import ChoiceType, JSONType, UUIDType # revision identifiers, used by Alembic. revision = '1f896f6990b8'

Check failure on line 1 in invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py

View workflow job for this annotation

GitHub Actions / Tests / Tests (3.9, postgresql14, opensearch2)

pydocstyle-check /home/runner/work/invenio-jobs/invenio-jobs/invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py:8 at module level: D400: First line should end with a period (not 's')

Check failure on line 1 in invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py

View workflow job for this annotation

GitHub Actions / Tests / Tests (3.9, postgresql14, opensearch2)

Black format check --- /home/runner/work/invenio-jobs/invenio-jobs/invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py 2024-08-26 14:15:59.921880+00:00 +++ /home/runner/work/invenio-jobs/invenio-jobs/invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py 2024-08-26 14:17:38.314581+00:00 @@ -11,32 +11,34 @@ import sqlalchemy as sa from sqlalchemy.dialects import postgresql from sqlalchemy_utils import JSONType, UUIDType, ChoiceType # revision identifiers, used by Alembic. -revision = '1f896f6990b8' -down_revision = '356496a01197' +revision = "1f896f6990b8" +down_revision = "356496a01197" branch_labels = () depends_on = None def upgrade(): """Upgrade database.""" # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('fk_run_job_id_job', 'run', type_='foreignkey') - op.rename_table("job", 'jobs_job') - op.rename_table("run", 'jobs_run') + op.drop_constraint("fk_run_job_id_job", "run", type_="foreignkey") + op.rename_table("job", "jobs_job") + op.rename_table("run", "jobs_run") - op.create_foreign_key('fk_jobs_run_job_id_jobs_job', 'jobs_run', 'jobs_job', ['job_id'], ['id']) + op.create_foreign_key( + "fk_jobs_run_job_id_jobs_job", "jobs_run", "jobs_job", ["job_id"], ["id"] + ) # ### end Alembic commands ### def downgrade(): """Downgrade database.""" # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('fk_jobs_run_job_id_jobs_job', 'jobs_run', type_='foreignkey') + op.drop_constraint("fk_jobs_run_job_id_jobs_job", "jobs_run", type_="foreignkey") - op.rename_table("jobs_job", 'job') - op.rename_table("jobs_run", 'run') - op.create_foreign_key('fk_run_job_id_job', 'run', 'job', ['job_id'], ['id']) + op.rename_table("jobs_job", "job") + op.rename_table("jobs_run", "run") + op.create_foreign_key("fk_run_job_id_job", "run", "job", ["job_id"], ["id"]) # ### end Alembic commands ###

Check failure on line 1 in invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py

View workflow job for this annotation

GitHub Actions / Tests / Tests (3.12, postgresql14, opensearch2)

isort-check """Update jobs module table names""" +import sqlalchemy as sa from alembic import op -import sqlalchemy as sa from sqlalchemy.dialects import postgresql -from sqlalchemy_utils import JSONType, UUIDType, ChoiceType +from sqlalchemy_utils import ChoiceType, JSONType, UUIDType # revision identifiers, used by Alembic. revision = '1f896f6990b8'

Check failure on line 1 in invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py

View workflow job for this annotation

GitHub Actions / Tests / Tests (3.12, postgresql14, opensearch2)

pydocstyle-check /home/runner/work/invenio-jobs/invenio-jobs/invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py:8 at module level: D400: First line should end with a period (not 's')

Check failure on line 1 in invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py

View workflow job for this annotation

GitHub Actions / Tests / Tests (3.12, postgresql14, opensearch2)

Black format check --- /home/runner/work/invenio-jobs/invenio-jobs/invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py 2024-08-26 14:15:59.675473+00:00 +++ /home/runner/work/invenio-jobs/invenio-jobs/invenio_jobs/alembic/1f896f6990b8_update_jobs_module_table_names.py 2024-08-26 14:17:49.166283+00:00 @@ -11,32 +11,34 @@ import sqlalchemy as sa from sqlalchemy.dialects import postgresql from sqlalchemy_utils import JSONType, UUIDType, ChoiceType # revision identifiers, used by Alembic. -revision = '1f896f6990b8' -down_revision = '356496a01197' +revision = "1f896f6990b8" +down_revision = "356496a01197" branch_labels = () depends_on = None def upgrade(): """Upgrade database.""" # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('fk_run_job_id_job', 'run', type_='foreignkey') - op.rename_table("job", 'jobs_job') - op.rename_table("run", 'jobs_run') + op.drop_constraint("fk_run_job_id_job", "run", type_="foreignkey") + op.rename_table("job", "jobs_job") + op.rename_table("run", "jobs_run") - op.create_foreign_key('fk_jobs_run_job_id_jobs_job', 'jobs_run', 'jobs_job', ['job_id'], ['id']) + op.create_foreign_key( + "fk_jobs_run_job_id_jobs_job", "jobs_run", "jobs_job", ["job_id"], ["id"] + ) # ### end Alembic commands ### def downgrade(): """Downgrade database.""" # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('fk_jobs_run_job_id_jobs_job', 'jobs_run', type_='foreignkey') + op.drop_constraint("fk_jobs_run_job_id_jobs_job", "jobs_run", type_="foreignkey") - op.rename_table("jobs_job", 'job') - op.rename_table("jobs_run", 'run') - op.create_foreign_key('fk_run_job_id_job', 'run', 'job', ['job_id'], ['id']) + op.rename_table("jobs_job", "job") + op.rename_table("jobs_run", "run") + op.create_foreign_key("fk_run_job_id_job", "run", "job", ["job_id"], ["id"]) # ### end Alembic commands ###
# This file is part of Invenio.
# Copyright (C) 2016-2018 CERN.
#
# Invenio is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Update jobs module table names"""

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from sqlalchemy_utils import JSONType, UUIDType, ChoiceType

# revision identifiers, used by Alembic.
revision = '1f896f6990b8'
down_revision = '356496a01197'
branch_labels = ()
depends_on = None


def upgrade():
"""Upgrade database."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('fk_run_job_id_job', 'run', type_='foreignkey')
op.rename_table("job", 'jobs_job')
op.rename_table("run", 'jobs_run')

op.create_foreign_key('fk_jobs_run_job_id_jobs_job', 'jobs_run', 'jobs_job', ['job_id'], ['id'])

# ### end Alembic commands ###


def downgrade():
"""Downgrade database."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('fk_jobs_run_job_id_jobs_job', 'jobs_run', type_='foreignkey')

op.rename_table("jobs_job", 'job')
op.rename_table("jobs_run", 'run')
op.create_foreign_key('fk_run_job_id_job', 'run', 'job', ['job_id'], ['id'])
# ### end Alembic commands ###
17 changes: 17 additions & 0 deletions invenio_jobs/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2024 CERN.
#
# Invenio-Jobs is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Record class mock."""


class AttrDict(dict):
"""Mock record class."""

def __init__(self, *args, **kwargs):
"""Constructor."""
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
1 change: 0 additions & 1 deletion invenio_jobs/ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ def jobs(self):
return self.registry.all_registered_jobs()



def finalize_app(app):
"""Finalize app."""
rr_ext = app.extensions["invenio-records-resources"]
Expand Down
10 changes: 9 additions & 1 deletion invenio_jobs/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,25 @@
#
# Invenio-Jobs is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Jobs module."""

from functools import partial


class RegisteredTask:
"""Base class to register tasks available in the admin panel."""

arguments_schema = None
task = None
id = None
title = None
description = None

@classmethod
def factory(cls, job_cls_name, arguments_schema, id_, task, description, title, attrs=None):
def factory(
cls, job_cls_name, arguments_schema, id_, task, description, title, attrs=None
):
"""Create a new instance of a job."""
if not attrs:
attrs = {}
Expand All @@ -34,6 +41,7 @@ def factory(cls, job_cls_name, arguments_schema, id_, task, description, title,

@classmethod
def build_task_arguments(cls, job_obj, since=None, custom_args=None, **kwargs):
"""Build task arguments."""
if custom_args:
return custom_args
return {}
45 changes: 17 additions & 28 deletions invenio_jobs/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""Models."""

import enum
import json
import uuid
from copy import deepcopy
from datetime import timedelta
Expand All @@ -23,10 +24,9 @@
from sqlalchemy_utils import Timestamp
from sqlalchemy_utils.types import ChoiceType, JSONType, UUIDType
from werkzeug.utils import cached_property
from invenio_jobs.proxies import current_jobs

from invenio_jobs.proxies import current_jobs

from .utils import eval_tpl_str, walk_values

JSON = (
db.JSON()
Expand All @@ -36,11 +36,6 @@
)


class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self

def _dump_dict(model):
"""Dump a model to a dictionary."""
return {c.key: getattr(model, c.key) for c in sa.inspect(model).mapper.column_attrs}
Expand All @@ -49,14 +44,15 @@ def _dump_dict(model):
class Job(db.Model, Timestamp):
"""Job model."""

__tablename__ = "jobs_job"

id = db.Column(UUIDType, primary_key=True, default=uuid.uuid4)
active = db.Column(db.Boolean, default=True, nullable=False)
title = db.Column(db.String(255), nullable=False)
description = db.Column(db.Text)

task = db.Column(db.String(255))
default_queue = db.Column(db.String(64))
# default_args = db.Column(JSON, default=lambda: dict(), nullable=True)
schedule = db.Column(JSON, nullable=True)

@property
Expand All @@ -70,12 +66,15 @@ def last_runs(self):
"""Last run of the job."""
_runs = {}
for status in RunStatusEnum:
run = self.runs.filter_by(status=status).order_by(Run.created.desc()).first()
run = (
self.runs.filter_by(status=status).order_by(Run.created.desc()).first()
)
_runs[status.name.lower()] = run if run else {}
return _runs

@property
def default_args(self):
"""Compute default job arguments."""
return Task.get(self.task).build_task_arguments(job_obj=self)

@property
Expand Down Expand Up @@ -111,6 +110,8 @@ class RunStatusEnum(enum.Enum):
class Run(db.Model, Timestamp):
"""Run model."""

__tablename__ = "jobs_run"

id = db.Column(UUIDType, primary_key=True, default=uuid.uuid4)

job_id = db.Column(UUIDType, db.ForeignKey(Job.id))
Expand Down Expand Up @@ -161,26 +162,19 @@ def generate_args(cls, job):
execute arbitrary code, or perform harmful DB operations (e.g. delete rows).
"""
args = deepcopy(job.default_args)

# ctx = {"job": job.dump()}
# Add last runs
# last_runs = {}
# for status in RunStatusEnum:
# run = job.runs.filter_by(status=status).order_by(cls.created.desc()).first()
# last_runs[status.name.lower()] = run.dump() if run else None
# ctx["last_runs"] = last_runs
# ctx["last_run"] = job.last_run.dump() if job.last_run else None
import json
args = json.dumps(args, indent=4, sort_keys=True, default=str)
args = json.loads(args)
# walk_values(args, lambda val: eval_tpl_str(val, ctx))
return args

def dump(self):
"""Dump the run as a dictionary."""
dict_run = _dump_dict(self)
from invenio_jobs.services.schema import RegisteredTaskArgumentsSchema
serialized_args = RegisteredTaskArgumentsSchema().load({"args": dict_run["args"]})


dict_run = _dump_dict(self)
serialized_args = RegisteredTaskArgumentsSchema().load(
{"args": dict_run["args"]}
)

dict_run["args"] = serialized_args
return dict_run
Expand All @@ -207,17 +201,12 @@ def description(self):
return ""
return self._obj.__doc__.split("\n")[0]

# @cached_property
# def parameters(self):
# """Return the task's parameters."""
# TODO: Make this result more user friendly or enhance with type information
# return signature(self._obj).parameters

@classmethod
def all(cls):
"""Return all tasks."""
return current_jobs.jobs

@classmethod
def get(cls, id_):
"""Get registered task by id."""
return cls(current_jobs.registry.get(id_))
11 changes: 5 additions & 6 deletions invenio_jobs/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
# Invenio-Jobs is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Registry of jobs."""


class JobsRegistry:
"""A simple class to register jobs."""

Expand All @@ -17,9 +20,7 @@ def register(self, job_instance, job_id=None):
if job_id is None:
job_id = job_instance.id
if job_id in self._jobs:
raise RuntimeError(
f"Job with job id '{job_id}' is already registered."
)
raise RuntimeError(f"Job with job id '{job_id}' is already registered.")
self._jobs[job_id] = job_instance

def get(self, job_id):
Expand All @@ -37,10 +38,8 @@ def all_registered_jobs(self):
"""Return a list of available tasks."""
return self._jobs

def all_arguments(self):
return [task.arguments_schema for task_id, task in self._jobs.items()]

def registered_schemas(self):
"""Return all schemas registered for tasks."""
schemas = {}
for id_, registered_task in self._jobs.items():
schema = registered_task.arguments_schema
Expand Down
1 change: 0 additions & 1 deletion invenio_jobs/resources/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ class TasksResourceConfig(ResourceConfig, ConfiguratorMixin):
url_prefix = "/tasks"
routes = {"list": "", "arguments": "/<registered_task_id>/args"}


# Request handling
request_search_args = SearchRequestArgsSchema
request_body_parsers = request_body_parsers
Expand Down
7 changes: 5 additions & 2 deletions invenio_jobs/resources/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def create_url_rules(self):
routes = self.config.routes
url_rules = [
route("GET", routes["list"], self.search),
route("GET", routes["arguments"], self.read_arguments)
route("GET", routes["arguments"], self.read_arguments),
]

return url_rules
Expand All @@ -54,9 +54,12 @@ def search(self):

@request_view_args
def read_arguments(self):
"""Read arguments schema of task resource."""
identity = g.identity
registered_task_id = resource_requestctx.view_args["registered_task_id"]
arguments_schema = self.service.read_registered_task_arguments(identity, registered_task_id)
arguments_schema = self.service.read_registered_task_arguments(
identity, registered_task_id
)
return jsonify_schema(arguments_schema) if arguments_schema else {}


Expand Down
4 changes: 2 additions & 2 deletions invenio_jobs/services/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ class TasksSearchOptions(SearchOptionsBase):
sort_direction_options = {
"asc": dict(
title=_("Ascending"),
fn=partial(sorted, key=lambda t: t.name),
fn=partial(sorted, key=lambda t: t.title),
),
"desc": dict(
title=_("Descending"),
fn=partial(sorted, key=lambda t: t.name, reverse=True),
fn=partial(sorted, key=lambda t: t.title, reverse=True),
),
}
sort_options = {"name": dict(title=_("Name"), fields=["name"])}
Expand Down
7 changes: 2 additions & 5 deletions invenio_jobs/services/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@
RecordList,
)


class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
from ..api import AttrDict


class Item(RecordItem):
Expand All @@ -37,6 +33,7 @@ class JobItem(Item):

@property
def data(self):
"""Data representation of job result item."""
if self._data:
return self._data

Expand Down
Loading

0 comments on commit f3a4441

Please sign in to comment.