@adminforth/bulk-ai-flow 1.21.8 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build.log +2 -2
- package/custom/VisionAction.vue +771 -569
- package/custom/VisionTable.vue +97 -83
- package/custom/package-lock.json +28 -0
- package/custom/package.json +2 -1
- package/dist/custom/VisionAction.vue +771 -569
- package/dist/custom/VisionTable.vue +97 -83
- package/dist/custom/package-lock.json +28 -0
- package/dist/custom/package.json +2 -1
- package/dist/index.js +94 -9
- package/index.ts +99 -9
- package/package.json +1 -1
- package/types.ts +25 -0
package/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AdminForthPlugin, Filters } from "adminforth";
|
|
1
|
+
import { AdminForthFilterOperators, AdminForthPlugin, Filters } from "adminforth";
|
|
2
2
|
import type { IAdminForth, IHttpServer, AdminForthComponentDeclaration, AdminForthResource } from "adminforth";
|
|
3
3
|
import { suggestIfTypo } from "adminforth";
|
|
4
4
|
import type { PluginOptions } from './types.js';
|
|
@@ -192,10 +192,15 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
192
192
|
const selectedId = recordId;
|
|
193
193
|
let isError = false;
|
|
194
194
|
if (STUB_MODE) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
195
|
+
const fakeError = Math.random() < 0.005; // 0.05% chance of error
|
|
196
|
+
// await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 20000) + 1000));
|
|
197
|
+
if (fakeError) {
|
|
198
|
+
jobs.set(jobId, { status: 'failed', error: `ERROR: test error` });
|
|
199
|
+
return { ok: false, error: 'test error' };
|
|
200
|
+
} else {
|
|
201
|
+
jobs.set(jobId, { status: 'completed', result: {description: 'test description', price: 99999999, engine_power: 999} });
|
|
202
|
+
return { ok: true };
|
|
203
|
+
}
|
|
199
204
|
} else {
|
|
200
205
|
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
201
206
|
const record = await this.adminforth.resource(this.resourceConfig.resourceId).get( [Filters.EQ(primaryKeyColumn.name, selectedId)] );
|
|
@@ -601,7 +606,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
601
606
|
isFieldsForAnalizePlain: this.options.fillPlainFields ? Object.keys(this.options.fillPlainFields).length > 0 : false,
|
|
602
607
|
isImageGeneration: this.options.generateImages ? Object.keys(this.options.generateImages).length > 0 : false,
|
|
603
608
|
isAttachFiles: this.options.attachFiles ? true : false,
|
|
604
|
-
disabledWhenNoCheckboxes: true,
|
|
609
|
+
disabledWhenNoCheckboxes: this.options.recordSelector === 'filtered' ? false : true,
|
|
605
610
|
refreshRates: {
|
|
606
611
|
fillFieldsFromImages: this.options.refreshRates?.fillFieldsFromImages || 2_000,
|
|
607
612
|
fillPlainFields: this.options.refreshRates?.fillPlainFields || 1_000,
|
|
@@ -609,6 +614,9 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
609
614
|
regenerateImages: this.options.refreshRates?.regenerateImages || 5_000,
|
|
610
615
|
},
|
|
611
616
|
askConfirmationBeforeGenerating: this.options.askConfirmationBeforeGenerating || false,
|
|
617
|
+
concurrencyLimit: this.options.concurrencyLimit || 10,
|
|
618
|
+
recordSelector: this.options.recordSelector || 'checkbox',
|
|
619
|
+
askConfirmation: this.options.askConfirmation || [],
|
|
612
620
|
generationPrompts: {
|
|
613
621
|
plainFieldsPrompts: this.options.fillPlainFields || {},
|
|
614
622
|
imageFieldsPrompts: this.options.fillFieldsFromImages || {},
|
|
@@ -765,6 +773,26 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
765
773
|
});
|
|
766
774
|
|
|
767
775
|
|
|
776
|
+
server.endpoint({
|
|
777
|
+
method: 'POST',
|
|
778
|
+
path: `/plugin/${this.pluginInstanceId}/get_old_data`,
|
|
779
|
+
handler: async ({ body }) => {
|
|
780
|
+
const recordId = body.recordId;
|
|
781
|
+
if (recordId === undefined || recordId === null) {
|
|
782
|
+
return { ok: false, error: "Missing recordId" };
|
|
783
|
+
}
|
|
784
|
+
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
785
|
+
const record = await this.adminforth.resource(this.resourceConfig.resourceId)
|
|
786
|
+
.get([Filters.EQ(primaryKeyColumn.name, recordId)]);
|
|
787
|
+
if (!record) {
|
|
788
|
+
return { ok: false, error: "Record not found" };
|
|
789
|
+
}
|
|
790
|
+
record._label = this.resourceConfig.recordLabel(record);
|
|
791
|
+
return { ok: true, record };
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
|
|
768
796
|
server.endpoint({
|
|
769
797
|
method: 'POST',
|
|
770
798
|
path: `/plugin/${this.pluginInstanceId}/get_images`,
|
|
@@ -803,7 +831,11 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
803
831
|
}
|
|
804
832
|
}
|
|
805
833
|
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
834
|
+
|
|
806
835
|
const decimalFieldsArray = this.resourceConfig.columns.filter(c => c.type === 'decimal').map(c => c.name);
|
|
836
|
+
const integerFieldsArray = this.resourceConfig.columns.filter(c => c.type === 'integer').map(c => c.name);
|
|
837
|
+
const floatFieldsArray = this.resourceConfig.columns.filter(c => c.type === 'float').map(c => c.name);
|
|
838
|
+
|
|
807
839
|
const updates = selectedIds.map(async (ID, idx) => {
|
|
808
840
|
const oldRecord = await this.adminforth.resource(this.resourceConfig.resourceId).get( [Filters.EQ(primaryKeyColumn.name, ID)] );
|
|
809
841
|
for (const [key, value] of Object.entries(outputImageFields)) {
|
|
@@ -847,6 +879,21 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
847
879
|
}
|
|
848
880
|
}
|
|
849
881
|
}
|
|
882
|
+
if (integerFieldsArray.length > 0) {
|
|
883
|
+
for (const fieldName of integerFieldsArray) {
|
|
884
|
+
if (fieldsToUpdate[idx].hasOwnProperty(fieldName)) {
|
|
885
|
+
fieldsToUpdate[idx][fieldName] = parseInt(fieldsToUpdate[idx][fieldName], 10);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
if (floatFieldsArray.length > 0) {
|
|
890
|
+
for (const fieldName of floatFieldsArray) {
|
|
891
|
+
if (fieldsToUpdate[idx].hasOwnProperty(fieldName)) {
|
|
892
|
+
fieldsToUpdate[idx][fieldName] = parseFloat(fieldsToUpdate[idx][fieldName]);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
850
897
|
const newRecord = {
|
|
851
898
|
...oldRecord,
|
|
852
899
|
...fieldsToUpdate[idx]
|
|
@@ -855,11 +902,15 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
855
902
|
resource: this.resourceConfig,
|
|
856
903
|
recordId: ID,
|
|
857
904
|
oldRecord: oldRecord,
|
|
858
|
-
|
|
905
|
+
updates: newRecord,
|
|
859
906
|
adminUser: adminUser,
|
|
860
907
|
})
|
|
861
908
|
});
|
|
862
|
-
|
|
909
|
+
try {
|
|
910
|
+
await Promise.all(updates);
|
|
911
|
+
} catch (error) {
|
|
912
|
+
return { ok: false, error: `Error updating records, because of unprocesseble data for record ID ${selectedIds}` };
|
|
913
|
+
}
|
|
863
914
|
return { ok: true };
|
|
864
915
|
} else {
|
|
865
916
|
return { ok: false, error: isAllowedToSave.error };
|
|
@@ -905,7 +956,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
905
956
|
jobs.set(jobId, { status: "failed", error: "Missing action type" });
|
|
906
957
|
//return { error: "Missing action type" };
|
|
907
958
|
}
|
|
908
|
-
else if (!recordId) {
|
|
959
|
+
else if (!recordId && typeof recordId !== 'number') {
|
|
909
960
|
jobs.set(jobId, { status: "failed", error: "Missing record id" });
|
|
910
961
|
//return { error: "Missing record id" };
|
|
911
962
|
} else {
|
|
@@ -1013,5 +1064,44 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
|
|
|
1013
1064
|
}
|
|
1014
1065
|
}
|
|
1015
1066
|
});
|
|
1067
|
+
|
|
1068
|
+
server.endpoint({
|
|
1069
|
+
method: 'POST',
|
|
1070
|
+
path: `/plugin/${this.pluginInstanceId}/get_filtered_ids`,
|
|
1071
|
+
handler: async ({ body, adminUser, headers }) => {
|
|
1072
|
+
const filters = body.filters;
|
|
1073
|
+
|
|
1074
|
+
const normalizedFilters = { operator: AdminForthFilterOperators.AND, subFilters: [] };
|
|
1075
|
+
if (filters) {
|
|
1076
|
+
if (typeof filters !== 'object') {
|
|
1077
|
+
throw new Error(`Filter should be an array or an object`);
|
|
1078
|
+
}
|
|
1079
|
+
if (Array.isArray(filters)) {
|
|
1080
|
+
// if filters are an array, they will be connected with "AND" operator by default
|
|
1081
|
+
normalizedFilters.subFilters = filters;
|
|
1082
|
+
} else if (filters.field) {
|
|
1083
|
+
// assume filter is a SingleFilter
|
|
1084
|
+
normalizedFilters.subFilters = [filters];
|
|
1085
|
+
} else if (filters.subFilters) {
|
|
1086
|
+
// assume filter is a AndOr filter
|
|
1087
|
+
normalizedFilters.operator = filters.operator;
|
|
1088
|
+
normalizedFilters.subFilters = filters.subFilters;
|
|
1089
|
+
} else {
|
|
1090
|
+
// wrong filter
|
|
1091
|
+
throw new Error(`Wrong filter object value: ${JSON.stringify(filters)}`);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
const records = await this.adminforth.resource(this.resourceConfig.resourceId).list(normalizedFilters);
|
|
1096
|
+
if (!records) {
|
|
1097
|
+
return { ok: true, recordIds: [] };
|
|
1098
|
+
}
|
|
1099
|
+
const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
|
|
1100
|
+
|
|
1101
|
+
const recordIds = records.map(record => record[primaryKeyColumn.name]);
|
|
1102
|
+
|
|
1103
|
+
return { ok: true, recordIds }
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1016
1106
|
}
|
|
1017
1107
|
}
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -119,4 +119,29 @@ export interface PluginOptions {
|
|
|
119
119
|
}) => Record<string, any> | Promise<Record<string, any>>;
|
|
120
120
|
|
|
121
121
|
askConfirmationBeforeGenerating?: boolean;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Maximum number of records processed concurrently on the frontend.
|
|
125
|
+
*/
|
|
126
|
+
concurrencyLimit?: number;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Defines the way how records are selected for the action.
|
|
130
|
+
*
|
|
131
|
+
* 'checkbox' means that user will select records manually by checkboxes,
|
|
132
|
+
*
|
|
133
|
+
* 'filtered' means that action will be applied to all records matching current
|
|
134
|
+
* filters without showing any checkboxes (use with caution).
|
|
135
|
+
*
|
|
136
|
+
* Default is 'checkbox'.
|
|
137
|
+
*/
|
|
138
|
+
recordSelector?: 'checkbox' | 'filtered';
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* additional confirmation: generation of very many records is risky in terms of budget. On 1m budget it might be thousands USD,
|
|
142
|
+
* this settings allows to suspend rgeneration and allow user to review everything what was already generated so far
|
|
143
|
+
* and then suggest Resume / Stop Generation buttons. You can set it in mode where it is shown only afterFirst N records (e.g. at start) or every N records
|
|
144
|
+
*/
|
|
145
|
+
askConfirmation?: ({ afterRecords: number } | { everyRecords: number })[]
|
|
146
|
+
|
|
122
147
|
}
|