Skip to content

Commit

Permalink
chore: with mixins
Browse files Browse the repository at this point in the history
  • Loading branch information
seankwarren committed Feb 8, 2024
1 parent f01e86d commit 3ec7cc7
Show file tree
Hide file tree
Showing 5 changed files with 372 additions and 321 deletions.
272 changes: 142 additions & 130 deletions src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,136 +7,148 @@ import lodash from "lodash";
import { Executable } from "./executable";
import { getApplicationConfig, getExecutableConfig } from "./tree";
import { ApplicationConfig, ApplicationData } from "./types";

export class Application extends NamedDefaultableHashedInMemoryEntity {
static Executable = Executable;

constructor(...args: any[]) {
const config = args[0] as ApplicationConfig;
if (!config || typeof config.name !== "string") throw new Error("Invalid application configuration object.");
const staticConfig = getApplicationConfig(config);
if (!staticConfig) throw new Error(`Application "${config.name} (${config.version}-${config.build})" is not supported.`);
super({ ...staticConfig, ...config });
}

// TODO: extract this from application-flavors "global" default config for espresso 5.4.0
static get defaultConfig() {
return {
name: "espresso",
shortName: "qe",
version: "6.3",
summary: "Quantum Espresso",
build: "Default",
};
}

static create(config: {
name: string,
version?: string,
build?: string
}) {
return this.createFromNameVersionBuild(config);
}

static createFromNameVersionBuild({
name,
version = undefined,
build = "Default"
}: {
name: string,
version?: string,
build?: string
}) {
return new Application({ name, version, build });
}

getExecutables() {
return this.executables;
}

getBuilds() {
const data = getAppData(this.prop("name")) as ApplicationData;
const { versions } = data;
const builds = ["Default"];
versions.map((v) => v.build && builds.push(v.build));
return lodash.uniq(builds);
}

getVersions() {
const data = getAppData(this.prop("name")) as ApplicationData;
const { versions } = data;
const these: string[] = versions.map((v) => v.version);
return lodash.uniq(these);
}

static getUniqueAvailableNames() {
return allApplications;
}

getExecutableByName(name?: string) {
return new Application.Executable(
getExecutableConfig({
appName: this.prop("name"),
execName: name,
}),
);
}

getExecutableByConfig(config: {name: string} | null | undefined = null) {
return config ? this.getExecutableByName(config.name) : this.defaultExecutable;
}

get defaultExecutable() {
return this.getExecutableByName();
}

// override upon inheritance
// eslint-disable-next-line class-methods-use-this
get allowedModelTypes() {
return [];
}

get summary() {
return this.prop("summary");
}

get version() {
return this.prop("version");
}

get build() {
return this.prop("build");
}

get shortName() {
return this.prop("shortName", this.prop("name"));
}

get executables() {
const tree = getAppTree(this.prop("name"));
return Object.keys(tree)
.filter((key) => {
const { supportedApplicationVersions } = tree[key];
return (
!supportedApplicationVersions ||
supportedApplicationVersions.includes(this.prop("version"))
);
})
.map((key) => {
return new Application.Executable({ ...tree[key], name: key });
});
}

get hasAdvancedComputeOptions() {
return this.prop("hasAdvancedComputeOptions");
import { Constructor } from "@exabyte-io/code.js/dist/context";

const Base = NamedDefaultableHashedInMemoryEntity;
type ApplicationBaseEntity = InstanceType<typeof Base>;

export function ApplicationMixin<
T extends Constructor<ApplicationBaseEntity> = Constructor<ApplicationBaseEntity>,
>(superclass: T) {
return class Application extends superclass {
static Executable = Executable;

constructor(...args: any[]) {
const config = args[0] as ApplicationConfig;
if (!config || typeof config.name !== "string") throw new Error("Invalid application configuration object.");
const staticConfig = getApplicationConfig(config);
if (!staticConfig) throw new Error(`Application "${config.name} (${config.version}-${config.build})" is not supported.`);
super({ ...staticConfig, ...config });
}

// TODO: extract this from application-flavors "global" default config for espresso 5.4.0
static get defaultConfig() {
return {
name: "espresso",
shortName: "qe",
version: "6.3",
summary: "Quantum Espresso",
build: "Default",
};
}

static create(config: {
name: string,
version?: string,
build?: string
}) {
return this.createFromNameVersionBuild(config);
}

static createFromNameVersionBuild({
name,
version = undefined,
build = "Default"
}: {
name: string,
version?: string,
build?: string
}) {
return new Application({ name, version, build });
}

getExecutables() {
return this.executables;
}

getBuilds() {
const data = getAppData(this.prop("name")) as ApplicationData;
const { versions } = data;
const builds = ["Default"];
versions.map((v) => v.build && builds.push(v.build));
return lodash.uniq(builds);
}

getVersions() {
const data = getAppData(this.prop("name")) as ApplicationData;
const { versions } = data;
const these: string[] = versions.map((v) => v.version);
return lodash.uniq(these);
}

static getUniqueAvailableNames() {
return allApplications;
}

getExecutableByName(name?: string) {
return new Application.Executable(
getExecutableConfig({
appName: this.prop("name"),
execName: name,
}),
);
}

getExecutableByConfig(config: {name: string} | null | undefined = null) {
return config ? this.getExecutableByName(config.name) : this.defaultExecutable;
}

get defaultExecutable() {
return this.getExecutableByName();
}

// override upon inheritance
// eslint-disable-next-line class-methods-use-this
get allowedModelTypes() {
return [];
}

get summary() {
return this.prop("summary");
}

get version() {
return this.prop("version");
}

get build() {
return this.prop("build");
}

get shortName() {
return this.prop("shortName", this.prop("name"));
}

get executables() {
const tree = getAppTree(this.prop("name"));
return Object.keys(tree)
.filter((key) => {
const { supportedApplicationVersions } = tree[key];
return (
!supportedApplicationVersions ||
supportedApplicationVersions.includes(this.prop("version"))
);
})
.map((key) => {
return new Application.Executable({ ...tree[key], name: key });
});
}

get hasAdvancedComputeOptions() {
return this.prop("hasAdvancedComputeOptions");
}

get isLicensed() {
return this.prop("isLicensed");
}

get isUsingMaterial() {
const materialUsingApplications = ["vasp", "nwchem", "espresso", "exabyteml"];
return materialUsingApplications.includes(this.name);
}
}
}

get isLicensed() {
return this.prop("isLicensed");
}
export const Application = ApplicationMixin(Base);

get isUsingMaterial() {
const materialUsingApplications = ["vasp", "nwchem", "espresso", "exabyteml"];
return materialUsingApplications.includes(this.name);
}
}
export type Application = InstanceType<typeof Application>;
86 changes: 49 additions & 37 deletions src/executable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,65 @@ import {

import { Flavor } from "./flavor";
import { FlavorData } from "./types";
import { Constructor } from "@exabyte-io/code.js/dist/context";

export class Executable extends RuntimeItemsMixin(NamedDefaultableHashedInMemoryEntity) {
static Flavor = Flavor;
const Base = RuntimeItemsMixin(NamedDefaultableHashedInMemoryEntity);
type ExecutableBaseEntity = InstanceType<typeof Base>;

// @ts-ignore
toJSON(exclude) {
return super.toJSON(["flavors"].concat(exclude));
}
export function ExecutableMixin<
T extends Constructor<ExecutableBaseEntity> = Constructor<ExecutableBaseEntity>,
>(superclass: T) {
return class Executable extends RuntimeItemsMixin(NamedDefaultableHashedInMemoryEntity) {
static Flavor = Flavor;

get flavorsTree() {
return this.prop<Record<string, FlavorData>>("flavors");
}
// @ts-ignore
toJSON(exclude) {
return super.toJSON(["flavors"].concat(exclude));
}

get flavorsTree() {
return this.prop<Record<string, FlavorData>>("flavors");
}

get flavors() {
return Object.keys(this.flavorsTree).map((key: string) => {
return Executable.Flavor.create({
...this.flavorsTree[key] as FlavorData,
name: key,
executable: this as Executable,
get flavors() {
return Object.keys(this.flavorsTree).map((key: string) => {
return Executable.Flavor.create({
...this.flavorsTree[key] as FlavorData,
name: key,
executable: this as Executable,
});
});
});
}
}

get flavorsFromTree() {
return Object.keys(this.flavorsTree).map((key: string) => {
return new Executable.Flavor({ ...this.flavorsTree[key], name: key });
});
}
get flavorsFromTree() {
return Object.keys(this.flavorsTree).map((key: string) => {
return new Executable.Flavor({ ...this.flavorsTree[key], name: key });
});
}

get defaultFlavor() {
return this.getFlavorByName();
}
get defaultFlavor() {
return this.getFlavorByName();
}

getFlavorByName(name?: string | null) {
return name ? this.getEntityByName(this.flavors, "flavor", name) as Flavor : undefined;
}
getFlavorByName(name?: string | null) {
return name ? this.getEntityByName(this.flavors, "flavor", name) as Flavor : undefined;
}

getFlavorByConfig(config?: {name: string}) {
return config ? this.getFlavorByName(config.name) : this.defaultFlavor;
}
getFlavorByConfig(config?: {name: string}) {
return config ? this.getFlavorByName(config.name) : this.defaultFlavor;
}

getFlavorsByApplicationVersion(version: string) {
const filteredFlavors = this.flavors.filter((flavor) => {
const supportedApplicationVersions = flavor.prop<string[]>("supportedApplicationVersions");
return !supportedApplicationVersions || supportedApplicationVersions.includes(version);
});
getFlavorsByApplicationVersion(version: string) {
const filteredFlavors = this.flavors.filter((flavor) => {
const supportedApplicationVersions = flavor.prop<string[]>("supportedApplicationVersions");
return !supportedApplicationVersions || supportedApplicationVersions.includes(version);
});

return filteredFlavors;
return filteredFlavors;
}
}
}

export const Executable = ExecutableMixin(Base);

export type Executable = InstanceType<typeof Executable>;
Loading

0 comments on commit 3ec7cc7

Please sign in to comment.