Skip to content

Commit f7ce413

Browse files
Merge pull request #239 from CodeForPhilly/feat/library-check-evaluation
Feat/library check evaluation
2 parents a20322b + c138773 commit f7ce413

File tree

7 files changed

+175
-51
lines changed

7 files changed

+175
-51
lines changed

builder-api/src/main/java/org/acme/controller/DecisionResource.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,7 @@ private Map<String, Object> evaluateBenefit(Benefit benefit, Map<String, Object>
136136
String dmnFilepath = storageService.getCheckDmnModelPath(checkConfig.getCheckId());
137137
EvaluationResult evaluationResult;
138138
if (isLibraryCheck(checkConfig)){
139-
140-
String evaluationUrl = checkConfig.getEvaluationUrl();
141-
evaluationResult = libraryApi.evaluateCheck(checkConfig, evaluationUrl, formData);
139+
evaluationResult = libraryApi.evaluateCheck(checkConfig, formData);
142140
} else {
143141
Map<String, Object> customFormValues = (Map<String, Object>) formData.get("custom");
144142
if (customFormValues == null) {

builder-api/src/main/java/org/acme/enums/EvaluationResult.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.acme.enums;
22

3+
import java.util.Optional;
4+
35
public enum EvaluationResult {
46
TRUE("TRUE"),
57
FALSE("FALSE"),
@@ -10,4 +12,13 @@ public enum EvaluationResult {
1012
private EvaluationResult(String label) {
1113
this.label = label;
1214
}
15+
16+
public static EvaluationResult fromStringIgnoreCase(String value) {
17+
for (EvaluationResult s : EvaluationResult.values()) {
18+
if (s.name().equalsIgnoreCase(value)) {
19+
return s;
20+
}
21+
}
22+
return EvaluationResult.UNABLE_TO_DETERMINE;
23+
}
1324
}

builder-api/src/main/java/org/acme/model/domain/EligibilityCheck.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.acme.model.domain;
22

33
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4-
import com.fasterxml.jackson.annotation.JsonProperty;
54
import com.fasterxml.jackson.databind.JsonNode;
65

76
import java.util.List;
@@ -18,7 +17,7 @@ public class EligibilityCheck {
1817
private List<ParameterDefinition> parameterDefinitions;
1918
private String ownerId;
2019
// API endpoint for evaluating library checks
21-
private String path;
20+
private String evaluationUrl;
2221
private Boolean isArchived;
2322

2423
public String getId() {
@@ -93,12 +92,12 @@ public void setInputDefinition(JsonNode inputDefinition) {
9392
this.inputDefinition = inputDefinition;
9493
}
9594

96-
public String getPath() {
97-
return path;
95+
public String getEvaluationUrl() {
96+
return evaluationUrl;
9897
}
9998

100-
public void setPath(String path) {
101-
this.path = path;
99+
public void setEvaluationUrl(String evaluationUrl) {
100+
this.evaluationUrl = evaluationUrl;
102101
}
103102

104103
public Boolean getIsArchived() {

builder-api/src/main/java/org/acme/service/LibraryApiService.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.acme.service;
22

3+
import com.fasterxml.jackson.core.JsonProcessingException;
34
import com.fasterxml.jackson.core.type.TypeReference;
45
import com.fasterxml.jackson.databind.ObjectMapper;
56
import io.quarkus.logging.Log;
@@ -12,6 +13,11 @@
1213
import org.acme.persistence.StorageService;
1314
import org.acme.persistence.FirestoreUtils;
1415

16+
import java.net.URI;
17+
import java.net.http.HttpClient;
18+
import java.net.http.HttpRequest;
19+
import java.net.http.HttpResponse;
20+
import java.util.HashMap;
1521
import java.util.List;
1622
import java.util.Map;
1723
import java.util.Optional;
@@ -70,8 +76,48 @@ public EligibilityCheck getById(String id) {
7076
return matches.getFirst();
7177
}
7278

73-
public EvaluationResult evaluateCheck(CheckConfig checkConfig, String evaluationUrl, Map<String, Object> inputs){
74-
return EvaluationResult.TRUE;
79+
public EvaluationResult evaluateCheck(CheckConfig checkConfig, Map<String, Object> inputs) throws JsonProcessingException {
80+
81+
// TODO: Check that checkConfig has required attributes and handle null values
82+
83+
Map<String, Object> data = new HashMap<>();
84+
data.put("parameters", checkConfig.getParameters());
85+
data.put("situation", inputs);
86+
ObjectMapper mapper = new ObjectMapper();
87+
String bodyJson = mapper.writeValueAsString(data);
88+
89+
HttpClient client = HttpClient.newHttpClient();
90+
91+
HttpRequest request = HttpRequest.newBuilder()
92+
.uri(URI.create("https://library-api-cnsoqyluna-uc.a.run.app" + checkConfig.getEvaluationUrl()))
93+
.header("Content-Type", "application/json")
94+
.POST(HttpRequest.BodyPublishers.ofString(bodyJson))
95+
.build();
96+
97+
try {
98+
HttpResponse<String> response =
99+
client.send(request, HttpResponse.BodyHandlers.ofString());
100+
101+
int statusCode = response.statusCode();
102+
if (statusCode != 200){
103+
Log.error("Error evaluating library check " + checkConfig.getCheckId());
104+
Log.error("Inputs and parameters that caused error:" + bodyJson);
105+
return EvaluationResult.UNABLE_TO_DETERMINE;
106+
}
107+
String body = response.body();
108+
Map<String, Object> responseBody = mapper.readValue(
109+
body,
110+
new TypeReference<Map<String, Object>>() {}
111+
);
112+
113+
// TODO: Need a safer way to validate the returned data is in the right format
114+
String result = responseBody.get("checkResult").toString();
115+
return EvaluationResult.fromStringIgnoreCase(result);
116+
}
117+
catch (Exception e){
118+
Log.error(e);
119+
return EvaluationResult.UNABLE_TO_DETERMINE;
120+
}
75121
}
76122
}
77123

builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/eligibilityCheckDetailResource.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const eligibilityCheckDetailResource = (
7171
const updatedCheck: EligibilityCheckDetail = {
7272
...eligibilityCheck,
7373
parameterDefinitions: [
74-
...eligibilityCheck.parameterDefinitions,
74+
...(eligibilityCheck.parameterDefinitions || []),
7575
parameterDef,
7676
],
7777
};

builder-frontend/src/components/project/FormEditorView.tsx

Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,34 @@
1-
import { onMount, onCleanup, createSignal, createResource, Switch, Match, For, Show } from "solid-js";
1+
import {
2+
onMount,
3+
onCleanup,
4+
createSignal,
5+
createResource,
6+
Switch,
7+
Match,
8+
For,
9+
Show,
10+
} from "solid-js";
211
import { useParams } from "@solidjs/router";
312

413
import { FormEditor } from "@bpmn-io/form-js-editor";
5-
import Drawer from '@corvu/drawer' // 'corvu/drawer'
14+
import Drawer from "@corvu/drawer"; // 'corvu/drawer'
615

716
import FilterFormComponentsModule from "./formJsExtensions/FilterFormComponentsModule";
817
import CustomFormFieldsModule from "./formJsExtensions/customFormFields";
918

1019
import { saveFormSchema } from "../../api/screener";
1120
import { fetchScreenerBenefit } from "../../api/benefit";
12-
import { extractFormPaths, extractJsonSchemaPaths } from "../../utils/formSchemaUtils";
21+
import {
22+
extractFormPaths,
23+
extractJsonSchemaPaths,
24+
} from "../../utils/formSchemaUtils";
1325
import Loading from "../Loading";
1426

1527
import type { Benefit, BenefitDetail } from "../../types";
1628

17-
1829
import "@bpmn-io/form-js/dist/assets/form-js.css";
1930
import "@bpmn-io/form-js-editor/dist/assets/form-js-editor.css";
2031

21-
2232
function FormEditorView({ project, formSchema, setFormSchema }) {
2333
const [isUnsaved, setIsUnsaved] = createSignal(false);
2434
const [isSaving, setIsSaving] = createSignal(false);
@@ -31,7 +41,7 @@ function FormEditorView({ project, formSchema, setFormSchema }) {
3141
if (!benefitDetails?.length) return [];
3242
const screenerId = params.projectId;
3343
return Promise.all(
34-
benefitDetails.map(b => fetchScreenerBenefit(screenerId, b.id))
44+
benefitDetails.map((b) => fetchScreenerBenefit(screenerId, b.id))
3545
);
3646
}
3747
);
@@ -51,8 +61,8 @@ function FormEditorView({ project, formSchema, setFormSchema }) {
5161
formEditor = new FormEditor({
5262
container,
5363
additionalModules: [
54-
FilterFormComponentsModule,
55-
CustomFormFieldsModule
64+
// FilterFormComponentsModule,
65+
CustomFormFieldsModule,
5666
],
5767
});
5868

@@ -108,7 +118,10 @@ function FormEditorView({ project, formSchema, setFormSchema }) {
108118
</div>
109119
</Match>
110120
<Match when={isSaving()}>
111-
<div onClick={handleSave} class="btn-default btn-gray cursor-not-allowed">
121+
<div
122+
onClick={handleSave}
123+
class="btn-default btn-gray cursor-not-allowed"
124+
>
112125
Saving...
113126
</div>
114127
</Match>
@@ -126,9 +139,9 @@ function FormEditorView({ project, formSchema, setFormSchema }) {
126139
);
127140
}
128141

129-
130142
const FormValidationDrawer = ({ formSchema, benefits }) => {
131-
const formOutputs = () => formSchema() ? extractFormPaths(formSchema()) : [];
143+
const formOutputs = () =>
144+
formSchema() ? extractFormPaths(formSchema()) : [];
132145

133146
// Extract expected inputs from all benefits' checks
134147
const expectedInputs = () => {
@@ -138,7 +151,7 @@ const FormValidationDrawer = ({ formSchema, benefits }) => {
138151
for (const benefit of allBenefits) {
139152
for (const check of benefit.checks || []) {
140153
const paths = extractJsonSchemaPaths(check.inputDefinition);
141-
paths.forEach(p => pathSet.add(p));
154+
paths.forEach((p) => pathSet.add(p));
142155
}
143156
}
144157
return Array.from(pathSet);
@@ -148,10 +161,10 @@ const FormValidationDrawer = ({ formSchema, benefits }) => {
148161
const formOutputSet = () => new Set(formOutputs());
149162

150163
const satisfiedInputs = () =>
151-
expectedInputs().filter(p => formOutputSet().has(p));
164+
expectedInputs().filter((p) => formOutputSet().has(p));
152165

153166
const missingInputs = () =>
154-
expectedInputs().filter(p => !formOutputSet().has(p));
167+
expectedInputs().filter((p) => !formOutputSet().has(p));
155168

156169
return (
157170
<Drawer side="right">
@@ -173,7 +186,11 @@ const FormValidationDrawer = ({ formSchema, benefits }) => {
173186
fixed inset-0 z-50
174187
data-transitioning:transition-colors data-transitioning:duration-500
175188
data-transitioning:ease-[cubic-bezier(0.32,0.72,0,1)]"
176-
style={{'background-color': `rgb(0 0 0 / ${0.5 * props.openPercentage})`}}
189+
style={{
190+
"background-color": `rgb(0 0 0 / ${
191+
0.5 * props.openPercentage
192+
})`,
193+
}}
177194
/>
178195
<Drawer.Content
179196
class="
@@ -189,8 +206,17 @@ const FormValidationDrawer = ({ formSchema, benefits }) => {
189206

190207
{/* Form Outputs Section */}
191208
<div class="mt-4 mr-10 px-4 pb-10">
192-
<h3 class="text-lg font-semibold text-gray-700 mb-2">Form Outputs</h3>
193-
<For each={formOutputs()} fallback={<p class="text-gray-500 italic text-sm">No form fields defined yet.</p>}>
209+
<h3 class="text-lg font-semibold text-gray-700 mb-2">
210+
Form Outputs
211+
</h3>
212+
<For
213+
each={formOutputs()}
214+
fallback={
215+
<p class="text-gray-500 italic text-sm">
216+
No form fields defined yet.
217+
</p>
218+
}
219+
>
194220
{(path) => (
195221
<div class="py-2 px-3 mb-2 bg-white rounded border border-gray-300 font-mono text-sm">
196222
{path}
@@ -201,8 +227,17 @@ const FormValidationDrawer = ({ formSchema, benefits }) => {
201227

202228
{/* Missing Inputs Section */}
203229
<div class="mt-4 mr-10 px-4">
204-
<h3 class="text-lg font-semibold text-red-900 mb-2">Missing Inputs</h3>
205-
<For each={missingInputs()} fallback={<p class="text-gray-500 italic text-sm">All required inputs are satisfied!</p>}>
230+
<h3 class="text-lg font-semibold text-red-900 mb-2">
231+
Missing Inputs
232+
</h3>
233+
<For
234+
each={missingInputs()}
235+
fallback={
236+
<p class="text-gray-500 italic text-sm">
237+
All required inputs are satisfied!
238+
</p>
239+
}
240+
>
206241
{(path) => (
207242
<div class="py-2 px-3 mb-2 bg-red-50 rounded border border-red-300 font-mono text-sm text-red-800">
208243
{path}
@@ -213,8 +248,17 @@ const FormValidationDrawer = ({ formSchema, benefits }) => {
213248

214249
{/* Satisfied Inputs Section */}
215250
<div class="mt-4 mr-10 px-4">
216-
<h3 class="text-lg font-semibold text-green-900 mb-2">Satisfied Inputs</h3>
217-
<For each={satisfiedInputs()} fallback={<p class="text-gray-500 italic text-sm">No inputs satisfied yet.</p>}>
251+
<h3 class="text-lg font-semibold text-green-900 mb-2">
252+
Satisfied Inputs
253+
</h3>
254+
<For
255+
each={satisfiedInputs()}
256+
fallback={
257+
<p class="text-gray-500 italic text-sm">
258+
No inputs satisfied yet.
259+
</p>
260+
}
261+
>
218262
{(path) => (
219263
<div class="py-2 px-3 mb-2 bg-green-50 rounded border border-green-300 font-mono text-sm text-green-800">
220264
{path}
@@ -227,7 +271,7 @@ const FormValidationDrawer = ({ formSchema, benefits }) => {
227271
</>
228272
)}
229273
</Drawer>
230-
)
231-
}
274+
);
275+
};
232276

233277
export default FormEditorView;

0 commit comments

Comments
 (0)