This commit is contained in:
QkoSad
2023-08-08 16:02:54 +03:00
commit 0a7a469d56
315 changed files with 426907 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"plugins": ["@typescript-eslint"],
"env": {
"browser": true,
"es6": true,
"node": true
},
"rules": {
"@typescript-eslint/semi": ["error"],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/restrict-plus-operands": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{ "argsIgnorePattern": "^_" }
],
"no-case-declarations": "off"
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
}
}
@@ -0,0 +1,80 @@
import { Diagnose } from "../src/types";
const data: Diagnose[] = [
{
code: "M24.2",
name: "Disorder of ligament",
latin: "Morbositas ligamenti",
},
{
code: "M51.2",
name: "Other specified intervertebral disc displacement",
latin: "Alia dislocatio disci intervertebralis specificata",
},
{
code: "S03.5",
name: "Sprain and strain of joints and ligaments of other and unspecified parts of head",
latin:
"Distorsio et/sive distensio articulationum et/sive ligamentorum partium aliarum sive non specificatarum capitis",
},
{
code: "J10.1",
name: "Influenza with other respiratory manifestations, other influenza virus codeentified",
latin:
"Influenza cum aliis manifestationibus respiratoriis ab agente virali codeentificato",
},
{
code: "J06.9",
name: "Acute upper respiratory infection, unspecified",
latin: "Infectio acuta respiratoria superior non specificata",
},
{
code: "Z57.1",
name: "Occupational exposure to radiation",
},
{
code: "N30.0",
name: "Acute cystitis",
latin: "Cystitis acuta",
},
{
code: "H54.7",
name: "Unspecified visual loss",
latin: "Amblyopia NAS",
},
{
code: "J03.0",
name: "Streptococcal tonsillitis",
latin: "Tonsillitis (palatina) streptococcica",
},
{
code: "L60.1",
name: "Onycholysis",
latin: "Onycholysis",
},
{
code: "Z74.3",
name: "Need for continuous supervision",
},
{
code: "L20",
name: "Atopic dermatitis",
latin: "Atopic dermatitis",
},
{
code: "F43.2",
name: "Adjustment disorders",
latin: "Perturbationes adaptationis",
},
{
code: "S62.5",
name: "Fracture of thumb",
latin: "Fractura [ossis/ossium] pollicis",
},
{
code: "H35.29",
name: "Other proliferative retinopathy",
latin: "Alia retinopathia proliferativa",
},
];
export default data;
+116
View File
@@ -0,0 +1,116 @@
import { Patient, Gender } from '../src/types';
const patients: Patient[] = [
{
id: 'd2773336-f723-11e9-8f0b-362b9e155667',
name: 'John McClane',
dateOfBirth: '1986-07-09',
ssn: '090786-122X',
gender: Gender.Male,
occupation: 'New york city cop',
entries: [
{
id: 'd811e46d-70b3-4d90-b090-4535c7cf8fb1',
date: '2015-01-02',
type: 'Hospital',
specialist: 'MD House',
diagnosisCodes: ['S62.5'],
description:
"Healing time appr. 2 weeks. patient doesn't remember how he got the injury.",
discharge: {
date: '2015-01-16',
criteria: 'Thumb has healed.',
},
},
],
},
{
id: 'd2773598-f723-11e9-8f0b-362b9e155667',
name: 'Martin Riggs',
dateOfBirth: '1979-01-30',
ssn: '300179-777A',
gender: Gender.Male,
occupation: 'Cop',
entries: [
{
id: 'fcd59fa6-c4b4-4fec-ac4d-df4fe1f85f62',
date: '2019-08-05',
type: 'OccupationalHealthcare',
specialist: 'MD House',
employerName: 'HyPD',
diagnosisCodes: ['Z57.1', 'Z74.3', 'M51.2'],
description:
'Patient mistakenly found himself in a nuclear plant waste site without protection gear. Very minor radiation poisoning. ',
sickLeave: {
startDate: '2019-08-05',
endDate: '2019-08-28',
},
},
],
},
{
id: 'd27736ec-f723-11e9-8f0b-362b9e155667',
name: 'Hans Gruber',
dateOfBirth: '1970-04-25',
ssn: '250470-555L',
gender: Gender.Other,
occupation: 'Technician',
entries: [],
},
{
id: 'd2773822-f723-11e9-8f0b-362b9e155667',
name: 'Dana Scully',
dateOfBirth: '1974-01-05',
ssn: '050174-432N',
gender: Gender.Female,
occupation: 'Forensic Pathologist',
entries: [
{
id: 'b4f4eca1-2aa7-4b13-9a18-4a5535c3c8da',
date: '2019-10-20',
specialist: 'MD House',
type: 'HealthCheck',
description: 'Yearly control visit. Cholesterol levels back to normal.',
healthCheckRating: 0,
},
{
id: 'fcd59fa6-c4b4-4fec-ac4d-df4fe1f85f62',
date: '2019-09-10',
specialist: 'MD House',
type: 'OccupationalHealthcare',
employerName: 'FBI',
description: 'Prescriptions renewed.',
},
{
id: '37be178f-a432-4ba4-aac2-f86810e36a15',
date: '2018-10-05',
specialist: 'MD House',
type: 'HealthCheck',
description:
'Yearly control visit. Due to high cholesterol levels recommended to eat more vegetables.',
healthCheckRating: 1,
},
],
},
{
id: 'd2773c6e-f723-11e9-8f0b-362b9e155667',
name: 'Matti Luukkainen',
dateOfBirth: '1971-04-09',
ssn: '090471-8890',
gender: Gender.Male,
occupation: 'Digital evangelist',
entries: [
{
id: '54a8746e-34c4-4cf4-bf72-bfecd039be9a',
date: '2019-05-01',
specialist: 'Dr Byte House',
type: 'HealthCheck',
description: 'Digital overdose, very bytestatic. Otherwise healthy.',
healthCheckRating: 0,
},
],
},
];
export default patients;
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"tsc": "tsc",
"dev": "ts-node-dev ./src/index.ts",
"lint": "eslint --ext .ts ."
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/express": "^4.17.17",
"@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.60.1",
"eslint": "^8.43.0",
"ts-node-dev": "^2.0.0",
"typescript": "^5.1.5"
},
"dependencies": {
"express": "^4.18.2",
"uuid": "^9.0.0"
}
}
+39
View File
@@ -0,0 +1,39 @@
import express from "express";
import diagnoses from "../data/diagnoses";
import {
addPatient,
addEntry,
getPatient,
getAllPatients,
} from "./services/patients";
import { toNewPatient,toNewEntry } from "./utils";
const app = express();
app.use(express.json());
app.get("/api/ping", (_req, res) => {
res.send("pong");
});
app.get("/api/diagnoses", (_req, res) => {
res.json(diagnoses);
});
app.get("/api/patients", (_req, res) => {
res.json(getAllPatients());
});
app.get("/api/patients/:id", (req, res) => {
res.json(getPatient(req.params.id));
});
app.post("/api/patients/:id/entries", (req, res) => {
const newEntry = toNewEntry(req.body);
res.json(addEntry(newEntry, req.params.id));
});
app.post("/api/patients", (req, res) => {
const newPatient = toNewPatient(req.body);
res.json(addPatient(newPatient));
});
const PORT = 3001;
app.listen(PORT, () => {
console.log(`Server running at port${PORT}`);
});
@@ -0,0 +1,53 @@
import patients from "../../data/patients";
import { Patient, NewPatient, NonSensitivePatient, Entry } from "../types";
import { v1 as uuid } from "uuid";
export const getAllPatients = (): NonSensitivePatient[] =>
patients.map(({ id, name, dateOfBirth, gender, occupation, entries }) => {
return {
id,
name,
dateOfBirth,
gender,
occupation,
entries,
};
});
export const addPatient = (patient: NewPatient): Patient => {
const newPatient = { ...patient, id: uuid() };
patients.push(newPatient);
return newPatient;
};
export const getPatient = (id: string): NonSensitivePatient => {
const patient = patients.find((el) => el.id === id);
if (patient)
return {
id: patient.id,
name: patient.name,
dateOfBirth: patient.dateOfBirth,
gender: patient.gender,
occupation: patient.occupation,
entries: patient.entries,
};
throw new Error("patient not found");
};
export const addEntry = (entry:Omit<Entry,'id'>, id: string): NonSensitivePatient => {
const patient = patients.find((el) => el.id === id);
if (patient) {
const newEntry = {...entry,id:uuid()} as Entry
const entries = patient.entries.concat(newEntry);
const updatedPatient = { ...patient, entries };
return {
id: updatedPatient.id,
name: updatedPatient.name,
dateOfBirth: updatedPatient.dateOfBirth,
gender: updatedPatient.gender,
occupation: updatedPatient.occupation,
entries: updatedPatient.entries,
};
}
throw new Error("patient not found");
};
+54
View File
@@ -0,0 +1,54 @@
export interface Diagnose {
code: string;
name: string;
latin?: string;
}
interface HospitalEntry extends BaseEntry {
type: "Hospital";
diagnosisCodes: string[];
discharge: {
date: string;
criteria: string;
};
}
interface OccupationalHealthcareEntry extends BaseEntry {
type: "OccupationalHealthcare";
employerName: string;
diagnosisCodes?: string[];
sickLeave?: {
startDate: string;
endDate: string;
};
}
interface HealthCheck extends BaseEntry {
type: "HealthCheck";
employerName?: string;
healthCheckRating: number;
}
interface BaseEntry {
id: string;
date: string;
specialist: string;
description: string;
}
export type Entry = HospitalEntry | OccupationalHealthcareEntry | HealthCheck;
export interface Patient {
id: string;
dateOfBirth: string;
gender: string;
occupation: string;
name: string;
ssn: string;
entries: Entry[];
}
export type NonSensitivePatient = Omit<Patient, "ssn">;
export type NewPatient = Omit<Patient, "id">;
export enum Gender {
Male = "male",
Female = "female",
Other = "other",
}
+193
View File
@@ -0,0 +1,193 @@
import { Entry, Gender, NewPatient } from "./types";
export const toNewPatient = (object: unknown): NewPatient => {
if (!object || typeof object !== "object")
throw new Error("Incorrect or request body");
if (
"name" in object &&
"gender" in object &&
"dateOfBirth" in object &&
"occupation" in object &&
"ssn" in object
) {
const newPatient: NewPatient = {
name: parseString(object.name, "Incorrect or missing name"),
gender: parseGender(object.gender),
dateOfBirth: parseDate(object.dateOfBirth),
occupation: parseString(object.occupation, "Incorrect or missing ssn"),
ssn: parseString(object.name, "Incorrect or missing ssn"),
entries: [],
};
return newPatient;
}
throw new Error("Incorrect data some fields are missing");
};
const parseDate = (date: unknown): string => {
if (!date || !isString(date) || !isDate(date))
throw new Error("Incorrect or missing date");
return date;
};
const parseGender = (gender: unknown): Gender => {
if (!gender || !isString(gender) || !isGender(gender))
throw new Error("Incorrect or missing gender");
return gender;
};
const parseNumber = (number: unknown, message: string): number => {
if (!number || !isNumber(number)) throw new Error(message);
return number;
};
const parseString = (name: unknown, message: string): string => {
if (!name || !isString(name)) throw new Error(message);
return name;
};
const isString = (text: unknown): text is string => {
return typeof text === "string" || text instanceof String;
};
const isNumber = (number: unknown): number is number => {
return typeof number === "number" || number instanceof Number;
};
const isDate = (date: string): boolean => {
return Boolean(Date.parse(date));
};
const isGender = (gender: string): gender is Gender => {
return Object.values(Gender)
.map((v) => v.toString())
.includes(gender);
};
type UnionOmit<T, K extends string | number | symbol> = T extends unknown
? Omit<T, K>
: never;
export const toNewEntry = (object: unknown): UnionOmit<Entry, "id"> => {
if (!object || typeof object !== "object") throw new Error("incorrect body");
if (
"date" in object &&
"specialist" in object &&
"description" in object &&
"type" in object
) {
console.log(object.type)
switch (object.type) {
case "Hospital":
if (
"diagnosisCodes" in object &&
"discharge" in object &&
typeof object.discharge === "object" &&
object.discharge !== null &&
"date" in object.discharge &&
"criteria" in object.discharge
) {
let diagnosisCodes = undefined;
if (isDiagnosisCodes(object.diagnosisCodes))
diagnosisCodes = [...object.diagnosisCodes];
else throw new Error("error in diagnoses codes");
return {
date: parseString(object.date, "Incorrect date"),
specialist: parseString(object.specialist, "Incorrect specialist"),
description: parseString(
object.description,
"Incorrect description"
),
type: object.type,
discharge: {
date: parseString(object.discharge.date, "error in discharge date"),
criteria: parseString(object.discharge.criteria, "error in discharge criteria"),
},
diagnosisCodes,
};
}
throw new Error(
"diagnosisCodes or discharge missing for type Hospital"
);
case "HealthCheck":
if ("healthCheckRating" in object) {
let employerName = undefined;
if ("employerName" in object && employerName !== "undefined")
employerName = object.employerName;
return {
date: parseString(object.date, "Incorrect date"),
specialist: parseString(object.specialist, "Incorrect specialist"),
description: parseString(
object.description,
"Incorrect description"
),
type: object.type,
healthCheckRating: parseNumber(
object.healthCheckRating,
"Incorrect HealthCheckRating"
),
employerName: parseString(employerName, "Incorrect employername"),
};
}
throw new Error("healthCheckRating missing for type HealthCheck");
case "OccupationalHealthcare":
if ("employerName" in object) {
let diagnosisCodes: undefined | string[] = undefined;
let sickLeave: undefined | { startDate: string; endDate: string } =
undefined;
if (
"diagnosisCodes" in object &&
object.diagnosisCodes !== undefined
) {
diagnosisCodes = isDiagnosisCodes(object.diagnosisCodes)
? object.diagnosisCodes
: undefined;
}
if (
"sickLeave" in object &&
object.sickLeave !== undefined &&
object.sickLeave !== null &&
typeof object.sickLeave === "object" &&
"startDate" in object.sickLeave &&
"endDate" in object.sickLeave
) {
sickLeave = {
startDate: parseString(object.sickLeave.startDate, ""),
endDate: parseString(object.sickLeave.endDate, ""),
};
}
return {
date: parseString(object.date, "Incorrect date"),
specialist: parseString(object.specialist, "Incorrect specialist"),
description: parseString(
object.description,
"Incorrect description"
),
type: object.type,
...(isDiagnosisCodes(diagnosisCodes) && {diagnosisCodes}),
//diagnosisCodes,
employerName: parseString(
object.employerName,
"Incorrect employername"
),
sickLeave,
};
}
throw new Error("employerName missing in OccupationalHealthcare");
default:
throw new Error("wrong type");
}
}
throw new Error("erro");
};
const isDiagnosisCodes = (
diagnosisCodes: unknown
): diagnosisCodes is string[] => {
if (diagnosisCodes instanceof Array) {
diagnosisCodes.forEach((el) => {
if (typeof el === "string") throw new Error("");
});
return true;
}
return false;
};
+13
View File
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ES6",
"outDir": "./build/",
"module": "commonjs",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true
}
}