Skip to content

Commit

Permalink
Added the ability to load the FAA aircraft registration DB so serial …
Browse files Browse the repository at this point in the history
…and year info can be pre-filled during aircraft creation.
  • Loading branch information
knicholson32 committed Apr 22, 2024
1 parent 2795b70 commit 3c9977c
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 45 deletions.
6 changes: 6 additions & 0 deletions prisma/migrations/20240422034748_v0_0_10/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- CreateTable
CREATE TABLE "aircraftRegistrationLookup" (
"reg" TEXT NOT NULL PRIMARY KEY,
"manufactureYear" INTEGER NOT NULL,
"serial" TEXT NOT NULL
);
16 changes: 12 additions & 4 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ model Leg {
fixes Fix[]
approaches Approach[]
route String?
route String?
forceUseBlock Boolean @default(false)
flightAwareData FlightAwareData?
Expand All @@ -253,8 +253,8 @@ model Leg {
originAirportId String? // @db.VarChar(5)
destinationAirportId String? // @db.VarChar(5)
diversionAirportId String? // @db.VarChar(5)
originAirport Airport? @relation("originAirport", fields: [originAirportId], references: [id], onDelete: Restrict)
destinationAirport Airport? @relation("destinationAirport", fields: [destinationAirportId], references: [id], onDelete: Restrict)
originAirport Airport? @relation("originAirport", fields: [originAirportId], references: [id], onDelete: Restrict)
destinationAirport Airport? @relation("destinationAirport", fields: [destinationAirportId], references: [id], onDelete: Restrict)
diversionAirport Airport? @relation("diversionAirport", fields: [diversionAirportId], references: [id], onDelete: Restrict)
startTime_utc Int?
Expand Down Expand Up @@ -452,7 +452,7 @@ model Approach {
}

model ApproachOptions {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
airportId String
type String
runway String?
Expand All @@ -463,6 +463,14 @@ model ApproachOptions {
@@map("approachesOptions")
}

model AircraftRegistrationLookup {
reg String @id
manufactureYear Int
serial String
@@map("aircraftRegistrationLookup")
}

model Position {
legId String
altitude Int
Expand Down
5 changes: 3 additions & 2 deletions src/lib/components/routeSpecific/settings/Button.svelte
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
<script lang="ts">
import { Submit } from '$lib/components/buttons';
import { Button } from '$lib/components/ui/button';
import type { API } from '$lib/types';
import Frame from './Frame.svelte';
export let name: string;
export let title: string;
export let buttonText: string = title;
export let disabled: boolean = false;
export let hoverTitle: string = '';
export let form: { success: boolean; name: string; message: string | undefined } | null = null;
export let form: API.Form.Type | null = null;
export let indent = false;
export let titleImg: string | null = null;
export let titleLink: string | null = null;
export let onClick = () => {};
</script>

<Frame {title} {hoverTitle} {indent} {titleImg} {titleLink} error={form?.success === false && form?.name === name ? form.message ?? null : null}>
<Frame {title} {hoverTitle} {indent} {titleImg} {titleLink} error={form?.ok === false && form?.name === name ? form.message ?? null : null}>
<div class="w-full xs:w-auto flex">
<button disabled={disabled} on:click={onClick} type="button" class="touch-manipulation select-none relative whitespace-nowrap transition-colors flex justify-center items-center px-3 py-2 rounded-md font-semibold shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed ring-1 ring-gray-300 dark:ring-zinc-600 text-gray-800 dark:text-gray-100 betterhover:hover:bg-gray-100 dark:betterhover:hover:bg-zinc-800 betterhover:hover:text-gray-900 dark:betterhover:hover:text-white disabled:bg-gray-50 disabled:text-gray-500 disabled:ring-gray-200 bg-white dark:bg-transparent">
{buttonText}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/routeSpecific/settings/Select.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import type { API } from '$lib/types';
import type { API } from '$lib/types';
import Frame from './Frame.svelte';
export let value: string;
Expand Down
5 changes: 3 additions & 2 deletions src/lib/components/routeSpecific/settings/Switch.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
<script lang="ts">
import { Switch } from '$lib/components/buttons';
import Frame from './Frame.svelte';
import type { API } from '$lib/types';
export let value: boolean;
export let name: string;
export let title: string;
export let disabled: boolean = false;
export let hoverTitle: string = '';
export let form: { success: boolean; name: string; message: string | undefined } | null = null;
export let form: API.Form.Type | null = null;
export let indent = false;
export let titleImg: string | null = null;
export let titleLink: string | null = null;
export let update = () => {};
</script>

<Frame {title} {hoverTitle} {indent} {titleImg} {titleLink} error={form?.success === false && form?.name === name ? form.message ?? null : null}>
<Frame {title} {hoverTitle} {indent} {titleImg} {titleLink} error={form?.ok === false && form?.name === name ? form.message ?? null : null}>
<div class="w-full xs:w-auto flex">
<Switch changed={(b) => { update(); }} type="button" forceHiddenInput={true} {disabled} {hoverTitle} valueName={name} bind:value/>
</div>
Expand Down
3 changes: 3 additions & 0 deletions src/lib/server/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const TypeNames = {
'system.lastSeenCommit': '',
// Data ----------------------------------
'data.approaches.lastSync': -1,
'data.aircraftReg.lastSync': -1,
'data.approaches.source': '',
// General -------------------------------
'general.encKey': 'UNSET',
Expand All @@ -46,6 +47,7 @@ export type ObjectType<T extends TypeName> =
T extends 'system.debug' ? number : // Integer
T extends 'system.lastSeenCommit' ? string : // String
T extends 'data.approaches.lastSync' ? number : // Integer
T extends 'data.aircraftReg.lastSync' ? number : // Integer
T extends 'data.approaches.source' ? string : // String
T extends 'general.encKey' ? string : // String
T extends 'general.aeroAPI' ? string : // String
Expand Down Expand Up @@ -91,6 +93,7 @@ export const get = async <T extends TypeName>(setting: T, settingVal?: SettingPa
case 'entry.day.current':
case 'system.debug':
case 'data.approaches.lastSync':
case 'data.aircraftReg.lastSync':
return parseInt(settingVal.value) as ObjectType<T>;

// Float Conversion --------------------------------------------------------------------------
Expand Down
13 changes: 10 additions & 3 deletions src/lib/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ namespace API {
}
}>

export type ApproachOption = Prisma.ApproachOptionsGetPayload<{}>
export type ApproachOption = Prisma.ApproachOptionsGetPayload<{}>;

export type Tour = Prisma.TourGetPayload<{}>
export type Aircraft = Prisma.AircraftGetPayload<{ include: { type: true } }>
export type Tour = Prisma.TourGetPayload<{}>;
export type Aircraft = Prisma.AircraftGetPayload<{ include: { type: true } }>;
export type Reg = Prisma.AircraftRegistrationLookupGetPayload<{}>;
}

// Export the basic response with all the available API responses
Expand All @@ -40,6 +41,7 @@ namespace API {
| Aircraft
| Approach
| TourList
| FAAReg

// Create a basic API interface that all other APIs will extend
interface API {
Expand Down Expand Up @@ -93,6 +95,11 @@ namespace API {
aircraft: Types.Aircraft
}

export interface FAAReg extends Success {
type: 'faa';
aircraft: Types.Reg
}

export interface Approach extends Success {
type: 'approach';
options: Types.ApproachOption[]
Expand Down
15 changes: 15 additions & 0 deletions src/routes/aircraft/entry/[[id]]/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ export const load = async ({ fetch, params, url }) => {
diversionPercent = currentAircraft.legs.length === 0 ? 0 : diversionPercent / currentAircraft.legs.length;
}

let lookupYear: string | null = null;
let lookupSerial: string | null = null;


if (currentAircraft !== null) {
const res = await (await fetch(`http://localhost:5173/api/aircraft/faa/${currentAircraft.registration}`)).json() as API.Response;
if (res.ok === true) {
const regInfo = res as API.FAAReg;
lookupYear = regInfo.aircraft.manufactureYear.toFixed(0);
lookupSerial = regInfo.aircraft.serial;
}
}

// Get all tail numbers (so we know if one exists)
const tails = aircrafts.map((v) => v.registration);
return {
Expand All @@ -75,6 +88,8 @@ export const load = async ({ fetch, params, url }) => {
typeOptions,
orderGroups,
avgLegLen,
lookupYear,
lookupSerial,
diversionPercent,
tails,
types,
Expand Down
42 changes: 37 additions & 5 deletions src/routes/aircraft/entry/[[id]]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import { FormManager, clearUID } from '$lib/components/entry/localStorage';
import * as Entry from '$lib/components/entry';
import MenuElement from '$lib/components/menuForm/MenuElement.svelte';
import type { API } from '$lib/types';
export let data: import('./$types').PageData;
export let form: import('./$types').ActionData;
Expand All @@ -28,21 +29,49 @@
$:{
$page.url.pathname;
data.aircraft?.id;
// if (data.aircraft?.id !== undefined) updateDefaults(data.aircraft.registration);
checkRegistrationExists();
}
// const updateDefaults = async (reg: string) => {
// const res = await (await fetch(`http://localhost:5173/api/aircraft/faa/${reg}`)).json() as API.Response;
// if (res.ok === true) {
// const regInfo = res as API.FAAReg;
// lookupYear = regInfo.aircraft.manufactureYear.toFixed(0);
// lookupSerial = regInfo.aircraft.serial;
// }
// }
let urlActiveParam: string;
let isMobileSize: boolean;
let selectedReg: string | null;
let registrationExists = false;
const checkRegistrationExists = () => {
// const lookupYear = data.lookupYear;
// const lookupSerial = data.lookupSerial;
const checkRegistrationExists = async () => {
if (selectedReg !== null && selectedReg !== undefined && selectedReg.trim().toUpperCase() !== data.aircraft?.registration && data.tails.includes(selectedReg.trim().toUpperCase())) {
registrationExists = true;
} else {
registrationExists = false;
}
if (reg !== null) {
if (selectedReg !== undefined && selectedReg !== null) {
const res = await (await fetch(`http://localhost:5173/api/aircraft/faa/${selectedReg}`)).json() as API.Response;
if (res.ok === true) {
const regInfo = res as API.FAAReg;
data.lookupYear = regInfo.aircraft.manufactureYear.toFixed(0);
data.lookupSerial = regInfo.aircraft.serial;
}
} else {
data.lookupYear = null;
data.lookupSerial = null;
}
}
}
const validate = (text: string): boolean => {
Expand All @@ -52,8 +81,9 @@
let submitting = false;
let deleting = false;
const ref = $page.url.searchParams.get('ref');
const reg = $page.url.searchParams.get('reg');
$:ref = $page.url.searchParams.get('ref');
$:reg = $page.url.searchParams.get('reg');
</script>
Expand Down Expand Up @@ -137,11 +167,13 @@
</MenuForm.FormHeader>
{/if}

{data.lookupYear}

<Section title="General" error={form !== null && form.ok === false && form.action === '?/default' && form.name === '*' ? form.message : null}>
<Entry.Select required={true} title="Type" name="type" options={data.typeOptions} placeholder={"Unset"} defaultValue={data.aircraft?.aircraftTypeId ?? null} />
<Entry.Input required={true} title="Tail Number" name="tail" placeholder="N4321J" uppercase={true} error={registrationExists ? 'Already exists' : ''} update={checkRegistrationExists} bind:value={selectedReg} defaultValue={(data.aircraft === null && reg !== null) ? reg : data.aircraft?.registration ?? null} />
<Entry.Input title="Year" name="year" placeholder="1969" validator={validate} useNumberPattern={true} defaultValue={data.aircraft?.year === null || data.aircraft?.year === undefined ? null : String(data.aircraft?.year)} />
<Entry.Input title="Serial" name="serial" placeholder="1969-0-2-22" defaultValue={data.aircraft?.serial ?? null} />
<Entry.Input title="Year" name="year" placeholder={data.lookupYear ?? 'Unknown'} validator={validate} useNumberPattern={true} defaultValue={data.aircraft?.year === null || data.aircraft?.year === undefined ? (reg === null ? null : data.lookupYear) : String(data.aircraft?.year)} />
<Entry.Input title="Serial" name="serial" placeholder={data.lookupSerial ?? 'Unknown'} defaultValue={data.aircraft?.serial ?? (reg === null ? null : data.lookupSerial)} />
</Section>
<Section title="Configuration">
<Entry.Switch title="Simulator" name="sim" defaultValue={data.aircraft?.simulator ?? false} />
Expand Down
31 changes: 31 additions & 0 deletions src/routes/api/aircraft/faa/[reg]/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { API } from '$lib/types';
import prisma from '$lib/server/prisma';
import { json } from '@sveltejs/kit';

export const GET = async ({ params }) => {

if (params.reg === undefined || params.reg === null) return API.response._400({ missingPaths: ['reg'] });

let reg = params.reg.toUpperCase().trim();
if (reg.startsWith('N') || reg.startsWith('n')) reg = reg.substring(1);

const ac = await prisma.aircraftRegistrationLookup.findUnique({ where: { reg } });
if (ac === null) return API.response._404();

try {
return new Response(JSON.stringify({
ok: true,
status: 200,
type: 'faa',
aircraft: ac
} satisfies API.FAAReg), {
status: 200,
headers: [
['Cache-Control', 'max-age=60']
]
})

} catch (e) {
return API.response.serverError(e);
}
};
Loading

0 comments on commit 3c9977c

Please sign in to comment.