mirror of
https://github.com/vee1e/bulk-questionnaire-upload.git
synced 2026-09-01 09:50:06 +00:00
feat: enhance form upload component and parsing logic
- Refactored methods to extract form details such as title, language, version, questions count, and options count. - Updated XLSFormParser to use default form IDs and improved data conversion from tempData.json. - Adjusted the getFormById method to return a more flexible response type.
This commit is contained in:
parent
86a45d02da
commit
94a35c90d6
4 changed files with 245 additions and 31 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -22,3 +22,4 @@ dist/
|
||||||
project-plans/
|
project-plans/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.cursorrules
|
.cursorrules
|
||||||
|
tempData.json
|
||||||
|
|
|
||||||
|
|
@ -1187,7 +1187,7 @@ class XLSFormParser:
|
||||||
# References
|
# References
|
||||||
'mainFormId': None,
|
'mainFormId': None,
|
||||||
'filterFormId': None,
|
'filterFormId': None,
|
||||||
'copiedFormId': int(form.get('id', 125)), # Use current form ID as copied from
|
'copiedFormId': 125, # Default copied form ID
|
||||||
|
|
||||||
# Arrays
|
# Arrays
|
||||||
'filterDataBy': ['GEOGRAPHY'],
|
'filterDataBy': ['GEOGRAPHY'],
|
||||||
|
|
@ -1442,7 +1442,7 @@ class XLSFormParser:
|
||||||
form_definition = {
|
form_definition = {
|
||||||
"_id": f"ObjectId(\"{self._generate_object_id()}\")",
|
"_id": f"ObjectId(\"{self._generate_object_id()}\")",
|
||||||
"formUiniqueId": f"ObjectId(\"{form.get('id', self._generate_object_id())}\")",
|
"formUiniqueId": f"ObjectId(\"{form.get('id', self._generate_object_id())}\")",
|
||||||
"formId": int(form.get('id', 848)),
|
"formId": 848, # Default form ID
|
||||||
"parentResponseId": None,
|
"parentResponseId": None,
|
||||||
"groupResponseId": None,
|
"groupResponseId": None,
|
||||||
"transactionId": str(uuid.uuid4()),
|
"transactionId": str(uuid.uuid4()),
|
||||||
|
|
|
||||||
|
|
@ -194,21 +194,21 @@ import { FormPreviewService } from '../../services/form-preview.service';
|
||||||
<div class="schema-summary">
|
<div class="schema-summary">
|
||||||
<div class="schema-item">
|
<div class="schema-item">
|
||||||
<strong>Title:</strong>
|
<strong>Title:</strong>
|
||||||
<span class="title-text" [title]="parsedResults[fileName].title?.default || 'N/A'">
|
<span class="title-text" [title]="getFormTitle(parsedResults[fileName])">
|
||||||
{{parsedResults[fileName].title?.default || 'N/A'}}
|
{{getFormTitle(parsedResults[fileName])}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="schema-item">
|
<div class="schema-item">
|
||||||
<strong>Language:</strong> {{parsedResults[fileName].language}}
|
<strong>Language:</strong> {{getFormLanguage(parsedResults[fileName])}}
|
||||||
</div>
|
</div>
|
||||||
<div class="schema-item">
|
<div class="schema-item">
|
||||||
<strong>Questions:</strong> {{parsedResults[fileName].metadata?.questions_count || 0}}
|
<strong>Questions:</strong> {{getFormQuestionsCount(parsedResults[fileName])}}
|
||||||
</div>
|
</div>
|
||||||
<div class="schema-item">
|
<div class="schema-item">
|
||||||
<strong>Options:</strong> {{parsedResults[fileName].metadata?.options_count || 0}}
|
<strong>Options:</strong> {{getFormOptionsCount(parsedResults[fileName])}}
|
||||||
</div>
|
</div>
|
||||||
<div class="schema-item">
|
<div class="schema-item">
|
||||||
<strong>Parse Time:</strong> {{parsedResults[fileName].metadata?.parse_time?.toFixed(3) || 0}}s
|
<strong>Version:</strong> {{getFormVersion(parsedResults[fileName])}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="schema-actions">
|
<div class="schema-actions">
|
||||||
|
|
@ -347,50 +347,50 @@ import { FormPreviewService } from '../../services/form-preview.service';
|
||||||
<div class="schema-info-grid">
|
<div class="schema-info-grid">
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<strong>Title:</strong>
|
<strong>Title:</strong>
|
||||||
<span>{{selectedSchema?.title?.default || 'N/A'}}</span>
|
<span>{{getFormTitle(selectedSchema)}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<strong>Language:</strong>
|
<strong>Language:</strong>
|
||||||
<span>{{selectedSchema?.language || 'N/A'}}</span>
|
<span>{{getFormLanguage(selectedSchema)}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<strong>Version:</strong>
|
<strong>Version:</strong>
|
||||||
<span>{{selectedSchema?.version || 'N/A'}}</span>
|
<span>{{getFormVersion(selectedSchema)}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<strong>Questions:</strong>
|
<strong>Questions:</strong>
|
||||||
<span>{{selectedSchema?.metadata?.questions_count || 0}}</span>
|
<span>{{getFormQuestionsCount(selectedSchema)}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<strong>Options:</strong>
|
<strong>Options:</strong>
|
||||||
<span>{{selectedSchema?.metadata?.options_count || 0}}</span>
|
<span>{{getFormOptionsCount(selectedSchema)}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<strong>Parse Time:</strong>
|
<strong>Form ID:</strong>
|
||||||
<span>{{selectedSchema?.metadata?.parse_time?.toFixed(3) || 0}}s</span>
|
<span>{{getFormId(selectedSchema)}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="schema-section" *ngIf="selectedSchema?.groups && selectedSchema?.groups.length > 0">
|
<div class="schema-section" *ngIf="getFormQuestions(selectedSchema).length > 0">
|
||||||
<h4>
|
<h4>
|
||||||
<mat-icon class="section-icon">quiz</mat-icon>
|
<mat-icon class="section-icon">quiz</mat-icon>
|
||||||
Questions Structure
|
Questions Structure
|
||||||
</h4>
|
</h4>
|
||||||
<div class="questions-container">
|
<div class="questions-container">
|
||||||
<div *ngFor="let group of selectedSchema.groups" class="question-group">
|
<div class="question-group">
|
||||||
<h5 class="group-title">{{group.label?.default || 'Default Group'}}</h5>
|
<h5 class="group-title">Form Questions</h5>
|
||||||
<div *ngFor="let question of group.questions" class="question-item">
|
<div *ngFor="let question of getFormQuestions(selectedSchema)" class="question-item">
|
||||||
<div class="question-header">
|
<div class="question-header">
|
||||||
<span class="question-name">#{{question.name}}</span>
|
<span class="question-name">#{{question.order}}</span>
|
||||||
<span class="question-type">{{question.type}}</span>
|
<span class="question-type">Type {{question.input_type}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="question-label">{{question.label?.default}}</div>
|
<div class="question-label">{{question.title}}</div>
|
||||||
<div *ngIf="question.choices && question.choices.length > 0" class="question-choices">
|
<div *ngIf="question.answer_option && question.answer_option.length > 0" class="question-choices">
|
||||||
<div class="choice-header">Choices:</div>
|
<div class="choice-header">Options:</div>
|
||||||
<div *ngFor="let choice of question.choices" class="choice-item">
|
<div *ngFor="let option of question.answer_option" class="choice-item">
|
||||||
<span class="choice-name">{{choice.name}}</span>
|
<span class="choice-name">{{option._id}}</span>
|
||||||
<span class="choice-label">{{choice.label?.default}}</span>
|
<span class="choice-label">{{option.name}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1962,8 +1962,10 @@ export class UploadComponent implements OnInit, OnChanges {
|
||||||
this.loadingFormId = form.id;
|
this.loadingFormId = form.id;
|
||||||
this.formPreviewService.setLoadingFormId(form.id);
|
this.formPreviewService.setLoadingFormId(form.id);
|
||||||
this.formService.getFormById(form.id).subscribe({
|
this.formService.getFormById(form.id).subscribe({
|
||||||
next: (response: FormDetails) => {
|
next: (response: any) => {
|
||||||
this.formPreviewService.setSelectedFormDetails(response);
|
// Convert tempData.json format to FormDetails format
|
||||||
|
const formDetails = this.convertTempDataToFormDetails(response, form);
|
||||||
|
this.formPreviewService.setSelectedFormDetails(formDetails);
|
||||||
this.loadingFormId = null;
|
this.loadingFormId = null;
|
||||||
},
|
},
|
||||||
error: (error: any) => {
|
error: (error: any) => {
|
||||||
|
|
@ -2335,6 +2337,217 @@ export class UploadComponent implements OnInit, OnChanges {
|
||||||
return Object.keys(this.parsedResults);
|
return Object.keys(this.parsedResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper methods to extract data from tempData.json format
|
||||||
|
getFormTitle(schema: any): string {
|
||||||
|
if (!schema) return 'N/A';
|
||||||
|
|
||||||
|
// Handle tempData.json format (array)
|
||||||
|
if (Array.isArray(schema) && schema.length > 0) {
|
||||||
|
const formDef = schema[0];
|
||||||
|
if (formDef.language && Array.isArray(formDef.language) && formDef.language.length > 0) {
|
||||||
|
return formDef.language[0].title || 'N/A';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle old format (fallback)
|
||||||
|
if (schema.title?.default) {
|
||||||
|
return schema.title.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormLanguage(schema: any): string {
|
||||||
|
if (!schema) return 'N/A';
|
||||||
|
|
||||||
|
// Handle tempData.json format (array)
|
||||||
|
if (Array.isArray(schema) && schema.length > 0) {
|
||||||
|
const formDef = schema[0];
|
||||||
|
|
||||||
|
// First try the string language field
|
||||||
|
if (typeof formDef.language === 'string') {
|
||||||
|
return formDef.language;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If language is an array, get from the first language config
|
||||||
|
if (Array.isArray(formDef.language) && formDef.language.length > 0) {
|
||||||
|
return formDef.language[0].lng || 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle old format (fallback)
|
||||||
|
return schema.language || 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormVersion(schema: any): string {
|
||||||
|
if (!schema) return 'N/A';
|
||||||
|
|
||||||
|
// Handle tempData.json format (array)
|
||||||
|
if (Array.isArray(schema) && schema.length > 0) {
|
||||||
|
const formDef = schema[0];
|
||||||
|
return formDef.version || 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle old format (fallback)
|
||||||
|
return schema.version || 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormQuestionsCount(schema: any): number {
|
||||||
|
if (!schema) return 0;
|
||||||
|
|
||||||
|
// Handle tempData.json format (array)
|
||||||
|
if (Array.isArray(schema) && schema.length > 0) {
|
||||||
|
const formDef = schema[0];
|
||||||
|
if (formDef.question && Array.isArray(formDef.question)) {
|
||||||
|
return formDef.question.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle old format (fallback)
|
||||||
|
if (schema.metadata?.questions_count) {
|
||||||
|
return schema.metadata.questions_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormOptionsCount(schema: any): number {
|
||||||
|
if (!schema) return 0;
|
||||||
|
|
||||||
|
// Handle tempData.json format (array)
|
||||||
|
if (Array.isArray(schema) && schema.length > 0) {
|
||||||
|
const formDef = schema[0];
|
||||||
|
if (formDef.language && Array.isArray(formDef.language) && formDef.language.length > 0) {
|
||||||
|
const langConfig = formDef.language[0];
|
||||||
|
if (langConfig.question && Array.isArray(langConfig.question)) {
|
||||||
|
let optionsCount = 0;
|
||||||
|
langConfig.question.forEach((q: any) => {
|
||||||
|
if (q.answer_option && Array.isArray(q.answer_option)) {
|
||||||
|
optionsCount += q.answer_option.length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return optionsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle old format (fallback)
|
||||||
|
if (schema.metadata?.options_count) {
|
||||||
|
return schema.metadata.options_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormId(schema: any): string {
|
||||||
|
if (!schema) return 'N/A';
|
||||||
|
|
||||||
|
// Handle tempData.json format (array)
|
||||||
|
if (Array.isArray(schema) && schema.length > 0) {
|
||||||
|
const formDef = schema[0];
|
||||||
|
return formDef.formId?.toString() || 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle old format (fallback)
|
||||||
|
return schema.id || 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormQuestions(schema: any): any[] {
|
||||||
|
if (!schema) return [];
|
||||||
|
|
||||||
|
// Handle tempData.json format (array)
|
||||||
|
if (Array.isArray(schema) && schema.length > 0) {
|
||||||
|
const formDef = schema[0];
|
||||||
|
if (formDef.language && Array.isArray(formDef.language) && formDef.language.length > 0) {
|
||||||
|
const langConfig = formDef.language[0];
|
||||||
|
if (langConfig.question && Array.isArray(langConfig.question)) {
|
||||||
|
return langConfig.question;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle old format (fallback)
|
||||||
|
if (schema.groups && Array.isArray(schema.groups)) {
|
||||||
|
let allQuestions: any[] = [];
|
||||||
|
schema.groups.forEach((group: any) => {
|
||||||
|
if (group.questions && Array.isArray(group.questions)) {
|
||||||
|
allQuestions = allQuestions.concat(group.questions);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return allQuestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
convertTempDataToFormDetails(tempDataResponse: any, originalForm: FormData): any {
|
||||||
|
if (!Array.isArray(tempDataResponse) || tempDataResponse.length === 0) {
|
||||||
|
// Fallback to basic structure
|
||||||
|
return {
|
||||||
|
form: originalForm,
|
||||||
|
questions: [],
|
||||||
|
options: [],
|
||||||
|
questions_count: 0,
|
||||||
|
options_count: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const formDef = tempDataResponse[0];
|
||||||
|
|
||||||
|
// Extract questions from the detailed definition
|
||||||
|
const questions: any[] = [];
|
||||||
|
const options: any[] = [];
|
||||||
|
|
||||||
|
if (formDef.language && Array.isArray(formDef.language) && formDef.language.length > 0) {
|
||||||
|
const langConfig = formDef.language[0];
|
||||||
|
if (langConfig.question && Array.isArray(langConfig.question)) {
|
||||||
|
langConfig.question.forEach((q: any, index: number) => {
|
||||||
|
// Convert to expected question format
|
||||||
|
questions.push({
|
||||||
|
id: q._id || `q_${index}`,
|
||||||
|
form_id: originalForm.id,
|
||||||
|
order: parseInt(q.order) || index + 1,
|
||||||
|
title: q.title || 'Untitled Question',
|
||||||
|
view_sequence: parseInt(q.viewSequence) || index + 1,
|
||||||
|
input_type: parseInt(q.input_type) || 1,
|
||||||
|
created_at: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extract options
|
||||||
|
if (q.answer_option && Array.isArray(q.answer_option)) {
|
||||||
|
q.answer_option.forEach((opt: any) => {
|
||||||
|
options.push({
|
||||||
|
id: opt._id || `opt_${opt._id}`,
|
||||||
|
form_id: originalForm.id,
|
||||||
|
order: parseInt(q.order) || index + 1,
|
||||||
|
option_id: parseInt(opt._id) || 1,
|
||||||
|
label: opt.name || 'Untitled Option',
|
||||||
|
created_at: new Date().toISOString()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create FormDetails structure
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
id: originalForm.id,
|
||||||
|
title: this.getFormTitle(tempDataResponse),
|
||||||
|
language: this.getFormLanguage(tempDataResponse),
|
||||||
|
version: this.getFormVersion(tempDataResponse),
|
||||||
|
created_at: originalForm.created_at
|
||||||
|
},
|
||||||
|
questions: questions,
|
||||||
|
options: options,
|
||||||
|
questions_count: questions.length,
|
||||||
|
options_count: options.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
closeSchemaModal(): void {
|
closeSchemaModal(): void {
|
||||||
this.showSchemaModal = false;
|
this.showSchemaModal = false;
|
||||||
this.selectedSchema = null;
|
this.selectedSchema = null;
|
||||||
|
|
|
||||||
|
|
@ -102,8 +102,8 @@ export class FormService {
|
||||||
return this.http.get<FormsResponse>(`${this.apiUrl}/forms`);
|
return this.http.get<FormsResponse>(`${this.apiUrl}/forms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormById(formId: string): Observable<FormDetails> {
|
getFormById(formId: string): Observable<any> {
|
||||||
return this.http.get<FormDetails>(`${this.apiUrl}/forms/${formId}`);
|
return this.http.get<any>(`${this.apiUrl}/forms/${formId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteForm(formId: string): Observable<{ message: string }> {
|
deleteForm(formId: string): Observable<{ message: string }> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue