Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions web/src/app/converters/survey-data-converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,12 @@ function keys(dict?: {}): string[] {
return Object.keys(dict || {});
}

export function jobDocToModel(data: DocumentData): Job {
return jobPbToModel(toMessage(data, Pb.Job) as Pb.Job);
}

export function jobDocsToModel(data: DocumentData[]): List<Job> {
return List<Job>(
data.map(job => jobPbToModel(toMessage(job, Pb.Job) as Pb.Job))
);
return List<Job>(data.map(jobDocToModel));
}

function jobPbToModel(pb: Pb.IJob): Job {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@
</mat-card-content>
</mat-card>

<mat-card>
<div class="floating-button">
<button mat-stroked-button (click)="openCopySurveyDialog()">
<span i18n="@@app.labels.copySurvey">Copy survey</span>
</button>
</div>

<mat-card-header>
<mat-card-title i18n="@@app.cards.copySurvey.title">Copy survey</mat-card-title>
</mat-card-header>

<mat-card-content>
<div i18n="@@app.cards.copySurvey.description">Copies jobs and tasks in this survey. Data collection sites and submission data are not copied.</div>
</mat-card-content>
</mat-card>

<mat-card>
<div class="floating-button">
<button class="delete-survey-button" mat-stroked-button (click)="openDeleteSurveyDialog()">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,24 @@ export class EditDetailsComponent implements OnInit {
});
}

openCopySurveyDialog() {
this.dialog
.open(JobDialogComponent, {
data: {dialogType: DialogType.CopySurvey},
panelClass: 'small-width-dialog',
})
.afterClosed()
.subscribe(async (result: DialogData) => {
if (result?.dialogType === DialogType.CopySurvey) {
const {id: surveyId} = this.survey!;

const newSurveyId = await this.surveyService.copySurvey(surveyId);

this.navigationService.navigateToSurveyDashboard(newSurveyId);
}
});
}

ngOnDestroy() {
this.subscription.unsubscribe();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export enum DialogType {
RenameJob,
DeleteJob,
UndoJobs,
CopySurvey,
DeleteLois,
DeleteOption,
DeleteSurvey,
Expand Down Expand Up @@ -55,6 +56,12 @@ export const dialogConfigs: Record<DialogType, DialogConfig> = {
backButtonLabel: $localize`:@@app.labels.goBack:Go back`,
continueButtonLabel: $localize`:@@app.labels.continue:Continue`,
},
[DialogType.CopySurvey]: {
title: $localize`:@@app.dialogs.copySurvey.title:Copy survey`,
content: $localize`:@@app.dialogs.copySurvey.content:This survey and all its associated jobs will be copied. Data collection sites and submission data won't be included in the copy.`,
backButtonLabel: $localize`:@@app.labels.goBack:Cancel`,
continueButtonLabel: $localize`:@@app.labels.continue:Confirm`,
},
[DialogType.DeleteJob]: {
title: $localize`:@@app.dialogs.deleteJob.title:Delete job`,
content: $localize`:@@app.dialogs.deleteJob.content:This job and all of its associated data will be deleted. This operation can’t be undone. Are you sure?`,
Expand Down
59 changes: 58 additions & 1 deletion web/src/app/services/data-store/data-store.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ import {
import {
DocumentData,
FieldPath,
QueryDocumentSnapshot,
collection,
getDocs,
serverTimestamp,
} from '@angular/fire/firestore';
import {registry} from '@ground/lib/dist/message-registry';
import {GroundProtos} from '@ground/proto';
import {getDownloadURL, getStorage, ref} from 'firebase/storage';
import {List, Map} from 'immutable';
import {Observable, combineLatest, firstValueFrom, merge} from 'rxjs';
import {Observable, combineLatest, firstValueFrom} from 'rxjs';
import {map} from 'rxjs/operators';

import {FirebaseDataConverter} from 'app/converters/firebase-data-converter';
Expand All @@ -40,6 +43,7 @@ import {
} from 'app/converters/proto-model-converter';
import {submissionDocToModel} from 'app/converters/submission-data-converter';
import {
jobDocToModel,
jobDocsToModel,
surveyDocToModel,
} from 'app/converters/survey-data-converter';
Expand All @@ -66,6 +70,7 @@ const AclRole = Pb.Role;
const GeneralAccess = Pb.Survey.GeneralAccess;

const SURVEYS_COLLECTION_NAME = 'surveys';
const JOBS_COLLECTION_NAME = 'jobs';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type JsonBlob = {[field: string]: any};
Expand Down Expand Up @@ -572,6 +577,58 @@ export class DataStoreService {
return Promise.resolve(surveyId);
}

async copySurvey(surveyId: string): Promise<string> {
const newSurveyId = this.generateId();

const surveyDoc = await this.db
.collection(SURVEYS_COLLECTION_NAME)
.doc(surveyId)
.ref.get();

const survey = surveyDocToModel(surveyId, surveyDoc.data() as DocumentData);

if (survey instanceof Error) return '';

const newSurvey = surveyToDocument(newSurveyId, {
...survey,
id: newSurveyId,
title: 'Copy of ' + survey.title,
acl: survey.acl.filter(role => role === Role.SURVEY_ORGANIZER),
});

await this.db
.collection(SURVEYS_COLLECTION_NAME)
.doc(newSurveyId)
.set(newSurvey);

const jobsCollectionRef = collection(
this.db.firestore,
`${SURVEYS_COLLECTION_NAME}/${surveyId}/${JOBS_COLLECTION_NAME}`
);

const qs = await getDocs(jobsCollectionRef);

qs.forEach(async (jobDoc: QueryDocumentSnapshot<DocumentData>) => {
const newJobId = this.generateId();

const job = jobDocToModel(jobDoc.data());

const newJob = jobToDocument({
...job,
id: newJobId,
} as Job);

await this.db
.collection(
`${SURVEYS_COLLECTION_NAME}/${newSurveyId}/${JOBS_COLLECTION_NAME}`
)
.doc(newJobId)
.set(newJob);
});

return newSurveyId;
}

getImageDownloadURL(path: string) {
return getDownloadURL(ref(getStorage(), path));
}
Expand Down
5 changes: 5 additions & 0 deletions web/src/app/services/navigation/navigation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ export class NavigationService implements OnDestroy {
this.router.navigateByUrl(url);
}

navigateToSurveyDashboard(surveyId: string): void {
const url = `${SURVEY_SEGMENT}/${surveyId}`;
this.router.navigateByUrl(url);
}

/**
* Navigate to the URL with new survey id.
*/
Expand Down
4 changes: 4 additions & 0 deletions web/src/app/services/survey/survey.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ export class SurveyService {
} as Survey);
}

async copySurvey(surveyId: string): Promise<string> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests please.

return this.dataStore.copySurvey(surveyId);
}

async createSurvey(name: string, description?: string): Promise<string> {
const user = await firstValueFrom(this.authService.getUser$());
const surveyId = await this.dataStore.createSurvey(
Expand Down
5 changes: 5 additions & 0 deletions web/src/locale/messages.fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@
"app.cards.surveyDetails.title": "Nom et description de l'enquête",
"app.cards.dataSharingAgreement.title": "Accord de partage de données",
"app.labels.customTerms": "Personnaliser les conditions",
"app.labels.copySurvey": "Copy survey",
"app.cards.copySurvey.title": "Copy survey",
"app.cards.copySurvey.description": "Copies jobs and tasks in this survey. Data collection sites and submission data are not copied.",
"app.labels.deleteSurvey": "Supprimer l'enquête",
"app.cards.deleteSurvey.title": "Supprimer l'enquête",
"app.cards.deleteSurvey.description": "Supprimer définitivement l'enquête et les données associées",
Expand All @@ -119,6 +122,8 @@
"app.dialogs.unpublishedChanges.title": "Modifications non publiées",
"app.dialogs.unpublishedChanges.content": "Si vous quittez cette page, les modifications apportées à cette enquête ne seront pas enregistrées. Voulez-vous vraiment continuer ?",
"app.labels.goBack": "Retour",
"app.dialogs.copySurvey.title": "Copy survey",
"app.dialogs.copySurvey.content": "This survey and all its associated jobs will be copied. Data collection sites and submission data won't be included in the copy.",
"app.dialogs.deleteJob.title": "Supprimer la mission",
"app.dialogs.deleteJob.content": "Cette mission et toutes ses données associées seront supprimées. Cette action est irréversible. Êtes-vous sûr de vouloir continuer ?",
"app.labels.confirm": "Confirmer",
Expand Down
5 changes: 5 additions & 0 deletions web/src/locale/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@
"app.cards.surveyDetails.title": "Survey name and description",
"app.cards.dataSharingAgreement.title": "Data sharing agreement",
"app.labels.customTerms": "Custom terms",
"app.labels.copySurvey": "Copy survey",
"app.cards.copySurvey.title": "Copy survey",
"app.cards.copySurvey.description": "Copies jobs and tasks in this survey. Data collection sites and submission data are not copied.",
"app.labels.deleteSurvey": "Delete survey",
"app.cards.deleteSurvey.title": "Delete survey",
"app.cards.deleteSurvey.description": "Permanently delete survey and associated data",
Expand All @@ -119,6 +122,8 @@
"app.dialogs.unpublishedChanges.title": "Unpublished changes",
"app.dialogs.unpublishedChanges.content": "If you leave this page, changes you’ve made to this survey won’t be published. Are you sure you want to continue?",
"app.labels.goBack": "Go back",
"app.dialogs.copySurvey.title": "Copy survey",
"app.dialogs.copySurvey.content": "This survey and all its associated jobs will be copied. Data collection sites and submission data won't be included in the copy.",
"app.dialogs.deleteJob.title": "Delete job",
"app.dialogs.deleteJob.content": "This job and all of its associated data will be deleted. This operation can’t be undone. Are you sure?",
"app.labels.confirm": "Confirm",
Expand Down
5 changes: 5 additions & 0 deletions web/src/locale/messages.vi.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@
"app.cards.surveyDetails.title": "Tên và mô tả khảo sát",
"app.cards.dataSharingAgreement.title": "Thỏa thuận chia sẻ dữ liệu",
"app.labels.customTerms": "Điều khoản tùy chỉnh",
"app.labels.copySurvey": "Copy survey",
"app.cards.copySurvey.title": "Copy survey",
"app.cards.copySurvey.description": "Copies jobs and tasks in this survey. Data collection sites and submission data are not copied.",
"app.labels.deleteSurvey": "Xóa khảo sát",
"app.cards.deleteSurvey.title": "Xóa khảo sát",
"app.cards.deleteSurvey.description": "Xóa vĩnh viễn khảo sát và tất cả dữ liệu liên quan",
Expand All @@ -119,6 +122,8 @@
"app.dialogs.unpublishedChanges.title": "Thay đổi chưa được xuất bản",
"app.dialogs.unpublishedChanges.content": "Nếu bạn rời trang này, các thay đổi trong khảo sát sẽ không được lưu. Bạn có chắc muốn tiếp tục?",
"app.labels.goBack": "Quay lại",
"app.dialogs.copySurvey.title": "Copy survey",
"app.dialogs.copySurvey.content": "This survey and all its associated jobs will be copied. Data collection sites and submission data won't be included in the copy.",
"app.dialogs.deleteJob.title": "Xóa công việc",
"app.dialogs.deleteJob.content": "Công việc này và tất cả dữ liệu liên quan sẽ bị xóa vĩnh viễn. Hành động này không thể hoàn tác. Bạn có chắc không?",
"app.labels.confirm": "Xác nhận",
Expand Down
Loading