Skip to content

Commit

Permalink
Merge branch 'develop' into feature/aethel-detail
Browse files Browse the repository at this point in the history
  • Loading branch information
XanderVertegaal committed May 24, 2024
2 parents 1809e8a + e832b02 commit f0f8689
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 78 deletions.
12 changes: 12 additions & 0 deletions backend/aethel_db/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import logging
import os
from django.conf import settings
from django.apps import AppConfig

from .models import load_dataset
from parseport.logger import logger


class AethelDbConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "aethel_db"

def ready(self):
if os.path.exists(settings.DATASET_PATH):
load_dataset()
else:
logger.critical("Æthel dataset not found.")
11 changes: 6 additions & 5 deletions backend/aethel_db/management/commands/create_aethel_subset.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import pickle
from django.conf import settings
from django.core.management.base import BaseCommand, CommandParser

from parseport.logger import logger

FULL_DATASET_PATH = getattr(settings, "FULL_DATASET_PATH")
DATA_SUBSET_PATH = getattr(settings, "DATA_SUBSET_PATH")

class Command(BaseCommand):
requires_system_checks = []

help = "Creates a subset of the Aethel dataset and outputs it to a new pickle file. Retrieves the passed number of records (default = 50) from each of the three subsets included: 'train', 'dev', and 'test'."

def add_arguments(self, parser: CommandParser) -> None:
parser.add_argument('src', help="Path to dataset (pickle format)")
parser.add_argument('dst', help="Path to subset output")
parser.add_argument(
"--number-of-records",
"-n",
Expand All @@ -32,7 +33,7 @@ def handle(self, *args, **options):
subset_size = options["number-of-records"]

logger.info(f"Creating a subset of the Aethel dataset with size {subset_size}...")
with open(FULL_DATASET_PATH, "rb") as f:
with open(options['src'], "rb") as f:
version, (train, dev, test) = pickle.load(f)
logger.info("Full pickle loaded!")

Expand All @@ -52,7 +53,7 @@ def handle(self, *args, **options):
new_dev = dev[:clamped]

logger.info("Writing smaller dataset to new pickle file...")
with open(DATA_SUBSET_PATH, "wb") as f:
with open(options['dst'], "wb") as f:
pickle_contents = version, (new_train, new_dev, new_test)
pickle.dump(pickle_contents, f)

Expand Down
13 changes: 11 additions & 2 deletions backend/aethel_db/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
from django.db import models
from typing import Optional

# Create your models here.
from django.conf import settings

from aethel import ProofBank

dataset: Optional[ProofBank] = None


def load_dataset():
global dataset
dataset = ProofBank.load_data(settings.DATASET_PATH)
17 changes: 9 additions & 8 deletions backend/aethel_db/views.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
from dataclasses import asdict, dataclass, field
from enum import Enum
from typing import List, Optional

from django.http import HttpRequest, JsonResponse
from django.conf import settings
from rest_framework import status
from rest_framework.views import APIView
from aethel import ProofBank
from aethel.frontend import LexicalItem
from spindle.utils import serialize_phrases_with_infix_notation
from parseport.logger import logger
from aethel_db.search import search, in_lemma, in_word
from aethel.frontend import Sample

DATASET_PATH = getattr(settings, "DATASET_PATH")
try:
dataset = ProofBank.load_data(DATASET_PATH)
except FileNotFoundError:
logger.critical(f"Æthel dataset not found.")
from aethel.frontend import LexicalItem

from .models import dataset
from .search import search, in_lemma, in_word


def aethel_status():
return dataset is not None


@dataclass
Expand Down
6 changes: 3 additions & 3 deletions backend/parseport/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
SPINDLE_URL = f"http://pp-spindle:32768/"
LATEX_SERVICE_URL = f"http://pp-latex:32769/"

DATA_SUBSET_PATH = "./aethel_db/data/aethel_subset.pickle"
FULL_DATASET_PATH = "./aethel.pickle"
data_subset_path = "./aethel_db/data/aethel_subset.pickle"
full_dataset_path = "./aethel.pickle"

DATASET_PATH = DATA_SUBSET_PATH if DEBUG else FULL_DATASET_PATH
DATASET_PATH = data_subset_path if DEBUG else full_dataset_path
3 changes: 3 additions & 0 deletions backend/parseport/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from rest_framework import routers

from spindle.views import SpindleView
from .views import StatusView


api_router = routers.DefaultRouter() # register viewsets with this router

Expand All @@ -30,6 +32,7 @@
path("api-auth", RedirectView.as_view(url="/api-auth/", permanent=True)),
path("admin/", admin.site.urls),
path("api/", include(api_router.urls)),
path("api/status/", StatusView.as_view(), name="status"),
path("api/spindle/<str:mode>", SpindleView.as_view(), name="spindle"),
path("api/aethel/", include("aethel_db.urls")),
path(
Expand Down
13 changes: 13 additions & 0 deletions backend/parseport/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from rest_framework.views import APIView
from rest_framework.response import Response

from aethel_db.views import aethel_status
from spindle.views import spindle_status


class StatusView(APIView):
def get(self, request):
return Response(dict(
aethel=aethel_status(),
spindle=spindle_status()
))
14 changes: 14 additions & 0 deletions backend/spindle/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,17 @@ def proof_response(self, parsed: ParserResponse) -> JsonResponse:
return SpindleResponse(
proof=serial_proof_to_json(serialize_proof(parsed.proof))
).json_response()


def spindle_status():
try:
r = http.request(
method="GET",
url=settings.SPINDLE_URL + '/status/',
headers={"Content-Type": "application/json"},
timeout=1,
retries=False
)
return r.status < 400
except Exception:
return False
61 changes: 34 additions & 27 deletions frontend/src/app/aethel/aethel.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,41 @@ <h2 class="title is-2" i18n>Æthel</h2>
start.
</p>

<form class="form" [formGroup]="form" (ngSubmit)="submit()">
<div class="field">
<label for="aethel-input" class="label">Search:</label>
<div class="aethel-input-container">
<input
id="aethel-input"
class="input"
[class.is-danger]="form.touched && form.invalid"
type="text"
formControlName="aethelInput"
placeholder="Enter a word or lemma..."
placeholder-i18n
/>
<button
type="submit"
class="button is-primary"
[class.is-loading]="loading$ | async"
>
<span i18n>Search</span>
</button>
@if (status$ | async) {
<form class="form" [formGroup]="form" (ngSubmit)="submit()">
<div class="field">
<label for="aethel-input" class="label">Search:</label>
<div class="aethel-input-container">
<input
id="aethel-input"
class="input"
[class.is-danger]="form.touched && form.invalid"
type="text"
formControlName="aethelInput"
placeholder="Enter a word or lemma..."
placeholder-i18n
/>
<button
type="submit"
class="button is-primary"
[class.is-loading]="loading$ | async"
>
<span i18n>Search</span>
</button>
</div>
@if (form.touched && form.invalid) {
<p class="is-size-7 has-text-danger" i18n>
Please enter at least three characters.
</p>
}
</div>
@if (form.touched && form.invalid) {
<p class="is-size-7 has-text-danger" i18n>
Please enter at least three characters.
</p>
}
</div>
</form>
</form>
}
@else {
<p class="notification" i18n>
The Æthel dataset is temporarily unavailable.
</p>
}

@if (submitted | async) {
<div class="table-container">
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/app/aethel/aethel.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { AethelListReturnItem } from "../shared/types";
import { AethelApiService } from "../shared/services/aethel-api.service";
import { distinctUntilChanged, map } from "rxjs";
import { Subject, distinctUntilChanged, map } from "rxjs";
import {
faChevronDown,
faChevronRight,
} from "@fortawesome/free-solid-svg-icons";
import { ActivatedRoute, Router } from "@angular/router";
import { isNonNull } from "../shared/operators/IsNonNull";
import { StatusService } from "../shared/services/status.service";

@Component({
selector: "pp-aethel",
Expand All @@ -31,14 +32,19 @@ export class AethelComponent implements OnInit {
chevronDown: faChevronDown,
};

status$ = new Subject();

constructor(
private apiService: AethelApiService,
private destroyRef: DestroyRef,
private router: Router,
private route: ActivatedRoute,
private statusService: StatusService
) {}

ngOnInit(): void {
this.statusService.get().subscribe(status => this.status$.next(status.aethel));

this.apiService.output$
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((response) => {
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/app/shared/services/status.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { environment } from "src/environments/environment";

interface Status {
aethel: boolean;
spindle: boolean;
}

@Injectable({
providedIn: "root"
})
export class StatusService{
constructor(private http: HttpClient) {}

get(): Observable<Status> {
return this.http.get<Status>(`${environment.apiUrl}status/`);
}
}
73 changes: 41 additions & 32 deletions frontend/src/app/spindle/spindle.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,50 @@ <h2 class="title is-2" i18n>Spindle</h2>
<a [routerLink]="['/refs']">References</a>. The notations used are
explained under <a [routerLink]="['notation']">Notation</a>.
</p>
<form class="form">
<div class="field">
<label for="spindle-input" class="label">Input:</label>
<input
id="spindle-input"
class="input"
[class.is-danger]="
spindleInput.touched && spindleInput.invalid
"
type="text"
[formControl]="spindleInput"
(keydown.enter)="$event.preventDefault(); parse()"
placeholder="Enter a sentence..."
placeholder-i18n
/>
<p
*ngIf="spindleInput.touched && spindleInput.invalid"
class="help is-danger"
i18n
>
This field is required.
</p>
</div>
</form>
@if (spindleReady$ | async) {
<form class="form">
<div class="field">
<label for="spindle-input" class="label">Input:</label>
<input
id="spindle-input"
class="input"
[class.is-danger]="
spindleInput.touched && spindleInput.invalid
"
type="text"
[formControl]="spindleInput"
(keydown.enter)="$event.preventDefault(); parse()"
placeholder="Enter a sentence..."
placeholder-i18n
/>
<p
*ngIf="spindleInput.touched && spindleInput.invalid"
class="help is-danger"
i18n
>
This field is required.
</p>
</div>
</form>
}
</div>
</div>

<div class="button-container buttons mb-3">
<pp-export-button
i18n-buttonText
buttonText="Parse"
[isLoading]="(loading$ | async) === 'term-table'"
(click)="parse()"
></pp-export-button>
</div>
@if (spindleReady$ | async) {
<div class="button-container buttons mb-3">
<pp-export-button
i18n-buttonText
buttonText="Parse"
[isLoading]="(loading$ | async) === 'term-table'"
(click)="parse()"
></pp-export-button>
</div>
}
@else {
<p class="notification" i18n>
Spindle is temporarily unavailable.
</p>
}

<div *ngIf="term && lexicalPhrases">
<h4 i18n>Term:</h4>
Expand Down
Loading

0 comments on commit f0f8689

Please sign in to comment.