Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions backend/rest_api/job/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,5 @@
"4": "Job Termination and Kill Signals",
"5": "Software and Environment Issues",
"6": "Internal and Unknown Errors",
"7": "Brokerage Errors",
"8": "DDM Errors",
"9": "Task Buffer Errors",
"10": "PanDA Job Dispatcher Errors",
"11": "PanDA Supervisor Errors",
"12": "Transformation Errors",
}
)
37 changes: 37 additions & 0 deletions backend/rest_api/job/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,40 @@ class Meta:
app_label = "job"
db_table = f'"{settings.DB_SCHEMAS['panda']}"."error_descriptions"'
unique_together = (("component", "code"),)


class JobBaseModel(models.Model):
"""
Base model for job-related models
"""

pandaid = models.BigIntegerField(db_column="pandaid", blank=False, null=False, primary_key=True)
creationtime = models.DateTimeField(db_column="creationtime", blank=False, null=False)
computingsite = models.CharField(max_length=32, db_column="computingsite", blank=True, null=True)
username = models.CharField(max_length=64, db_column="produsername", blank=True, null=True)
jobstatus = models.CharField(max_length=16, db_column="jobstatus", blank=True, null=True)

class Meta:
abstract = True


class JobsActive4(JobBaseModel):
"""
JobsActive4 model
"""

class Meta:
managed = False
app_label = "job"
db_table = f'"{settings.DB_SCHEMAS["panda"]}"."jobsactive4"'


class JobsArchived4(JobBaseModel):
"""
JobsArchived4 model - jobs that have been archived from the active table, typically younger than 4 days.
"""

class Meta:
managed = False
app_label = "job"
db_table = f'"{settings.DB_SCHEMAS["panda"]}"."jobsarchived4"'
1 change: 1 addition & 0 deletions backend/rest_api/oauth/policies/policy_atlas.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
p,atlas-adc-pandamon,error_description,read,{},{},allow
p,atlas-adc-pandamon,error_description,write,{},{},allow
p,atlas-adc-pandamon,error_description,delete,{},{},allow
p,atlas,job,read,{},{},allow
p,atlas,task,read,{},{},allow
8 changes: 6 additions & 2 deletions backend/rest_api/oauth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@


@login_required
def redirect_after_login_view(request: HttpRequest) -> HttpResponse:
def redirect_after_login_view(request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""
Redirect user to the URL stored in the session after successful login.

Args:
request (HttpRequest): The HTTP request object.
*args: Variable length argument list.
**kwargs: Arbitrary keyword arguments.
Returns:
HttpResponse: The HTTP response object with a redirect to the frontend.
"""
Expand All @@ -62,12 +64,14 @@ def redirect_after_login_view(request: HttpRequest) -> HttpResponse:
return response


def logout_view(request: HttpRequest) -> JsonResponse:
def logout_view(request: HttpRequest, *args, **kwargs) -> JsonResponse:
"""
Logout view that logout the user, clears the session and deletes the session cookie.

Args:
request (HttpRequest): The HTTP request object.
*args: Variable length argument list.
**kwargs: Arbitrary keyword arguments.
Returns:
JsonResponse: The JSON response indicating successful logout.
"""
Expand Down
Empty file.
30 changes: 30 additions & 0 deletions backend/rest_api/search/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# Authors:
# Tatiana Korchuganova <tatiana.korchuganova@cern.ch>

"""Apps.py for the search application."""

from django.apps import AppConfig


class SearchConfig(AppConfig):
"""Django AppConfig for the Search application."""

default_auto_field = "django.db.models.BigAutoField"
name = "rest_api.search"
Empty file.
7 changes: 7 additions & 0 deletions backend/rest_api/search/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.urls import path

from .views import QuickSearchAPIView

urlpatterns = [
path("global/", QuickSearchAPIView.as_view(), name="global-search"),
]
Empty file.
47 changes: 47 additions & 0 deletions backend/rest_api/search/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from rest_api.task.models import JediTask
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView


class QuickSearchAPIView(APIView):
"""
Global search endpoint for lightweight system-wide routing.

This view serves as a fast triage system, routing queries by format (e.g., numeric IDs) to minimize database search overhead.
"""

def get(self, request, *args, **kwargs) -> Response:
"""
Handles global search queries.

Checks if the query 'q' is a numeric ID and performs fast primary key
lookups across Tasks and Datasets.

Args:
request (Request): DRF request containing query params.
*args: Variable length argument list.
**kwargs: Arbitrary keyword arguments.

Returns:
Response: A DRF Response object containing:
- A list of matching result dictionaries with fields: `title`, `type`, and `id` (on HTTP 200 OK).
- An empty list if no query parameter is provided (on HTTP 400 Bad Request).
- An error message if a string query is passed (on HTTP 501 Not Implemented).
"""
query = request.query_params.get("q", "").strip()
if not query:
return Response([], status=status.HTTP_400_BAD_REQUEST)

results = []
if query.isdigit():
target_id = int(query)

# check if the target_id exists in Jobs, Tasks, or Files
if JediTask.objects.filter(jeditaskid=target_id).exists():
results.append({"title": f"Task #{target_id}", "type": "task", "id": f"{target_id}"})

else:
return Response({"error": "String search is not implemented yet."}, status=status.HTTP_501_NOT_IMPLEMENTED)

return Response(results, status=status.HTTP_200_OK)
3 changes: 2 additions & 1 deletion backend/rest_api/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@
"rest_framework",
"rest_framework.authtoken",
# apps
"rest_api.oauth",
"rest_api.job",
"rest_api.oauth",
"rest_api.search",
"rest_api.task",
]

Expand Down
4 changes: 2 additions & 2 deletions docker/frontend/Dockerfile.frontend
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build and serve an Angular frontend application using NGINX
FROM node:20-alpine AS build
FROM node:24-alpine AS build
WORKDIR /app

# Copy package.json and package-lock.json first to leverage Docker cache
Expand All @@ -16,7 +16,7 @@ ARG APP_VERSION=0.0.0
RUN echo "{\"version\": \"${APP_VERSION}\"}" > /app/src/assets/version.json

# Build Angular app
RUN npm run build --output-path=dist --configuration=production --aot --build-optimizer --vendor-chunk --source-map=false
RUN npm run build --output-path=dist --configuration=production

# Stage 2: serve
FROM docker.io/almalinux:9
Expand Down
17 changes: 4 additions & 13 deletions frontend/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
"output": "/assets/"
}
],
"styles": ["src/styles/styles.css"],
"styles": ["src/styles/material-theme.scss", "src/styles/styles.scss"],
"stylePreprocessorOptions": {
"includePaths": ["node_modules", "src/styles"],
"sass": {
"silenceDeprecations": ["mixed-decls", "color-functions", "global-builtin", "import"]
}
Expand Down Expand Up @@ -83,19 +84,9 @@
"builder": "@angular/build:extract-i18n"
},
"test": {
"builder": "@angular/build:karma",
"builder": "@angular/build:unit-test",
"options": {
"polyfills": ["zone.js", "zone.js/testing", "@angular/localize/init"],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "src/assets"
}
],
"styles": ["src/styles/styles.css"],
"scripts": []
"tsConfig": "tsconfig.spec.json"
}
},
"lint": {
Expand Down
Loading
Loading