Skip to content

Commit 1d96109

Browse files
authored
Merge pull request #1770 from leggedrobotics/feat/1712_CRUD_mission_ops
CRUD mission ops
2 parents 09372fd + 131f8fa commit 1d96109

File tree

19 files changed

+207
-63
lines changed

19 files changed

+207
-63
lines changed

backend/src/endpoints/project/project.controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { CreateProject } from '@common/api/types/create-project.dto';
88
import { ProjectDto } from '@common/api/types/project/base-project.dto';
99
import { DeleteProjectResponseDto } from '@common/api/types/project/delete-project-response.dto';
1010
import { ProjectQueryDto } from '@common/api/types/project/project-query.dto';
11-
import { ProjectWithRequiredTags } from '@common/api/types/project/project-with-required-tags';
11+
import { ProjectWithRequiredTagsDto } from '@common/api/types/project/project-with-required-tags.dto';
1212
import { ProjectsDto } from '@common/api/types/project/projects.dto';
1313
import { ResentProjectsDto } from '@common/api/types/project/recent-projects.dto';
1414
import { RemoveTagTypeDto } from '@common/api/types/remove-tag-type.dto';
@@ -64,11 +64,11 @@ export class ProjectController {
6464
@CanReadProject()
6565
@ApiOkResponse({
6666
description: 'Returns the project',
67-
type: ProjectWithRequiredTags,
67+
type: ProjectWithRequiredTagsDto,
6868
})
6969
async getProjectById(
7070
@ParameterUID('uuid') uuid: string,
71-
): Promise<ProjectWithRequiredTags> {
71+
): Promise<ProjectWithRequiredTagsDto> {
7272
return this.projectService.findOne(uuid);
7373
}
7474

backend/src/serialization.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
import { ProjectDto } from '@common/api/types/project/base-project.dto';
2828
import { ProjectWithMissionCountDto } from '@common/api/types/project/project-with-mission-count.dto';
2929
import { ProjectWithMissionsDto } from '@common/api/types/project/project-with-missions.dto';
30+
import { ProjectWithRequiredTagsDto } from '@common/api/types/project/project-with-required-tags.dto';
3031
import { TagDto, TagTypeDto } from '@common/api/types/tags/tags.dto';
3132
import { TopicDto } from '@common/api/types/topic.dto';
3233
import { GroupMembershipDto, UserDto } from '@common/api/types/user.dto';
@@ -282,6 +283,21 @@ export const projectEntityToDtoWithRequiredTags = (
282283
};
283284
};
284285

286+
export const projectEntityToDtoWithMissionCountAndTags = (
287+
project: Project,
288+
): ProjectWithRequiredTagsDto => {
289+
if (project.creator === undefined) {
290+
throw new Error('Creator can never be undefined');
291+
}
292+
293+
return {
294+
...(projectEntityToDto(project) as ProjectWithRequiredTagsDto),
295+
creator: userEntityToDto(project.creator),
296+
missionCount: project.missionCount ?? 0,
297+
requiredTags: project.requiredTags?.map(tagTypeEntityToDto) ?? [],
298+
};
299+
};
300+
285301
export const projectEntityToDtoWithMissions = (
286302
project: Project,
287303
): ProjectWithMissionsDto => {

backend/src/services/mission.service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ export class MissionService {
165165
let query = this.missionRepository
166166
.createQueryBuilder('mission')
167167
.leftJoinAndSelect('mission.project', 'project')
168-
.leftJoinAndSelect('mission.creator', 'creator');
168+
.leftJoinAndSelect('mission.creator', 'creator')
169+
.leftJoinAndSelect('mission.tags', 'tag')
170+
.leftJoinAndSelect('tag.tagType', 'tagType');
169171

170172
query = addAccessConstraintsToMissionQuery(query, userUuid);
171173

backend/src/services/project.service.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { DefaultRightDto } from '@common/api/types/access-control/default-right.
1818
import { DefaultRights } from '@common/api/types/access-control/default-rights';
1919
import { SortOrder } from '@common/api/types/pagination';
2020
import { ProjectDto } from '@common/api/types/project/base-project.dto';
21-
import { ProjectWithRequiredTags } from '@common/api/types/project/project-with-required-tags';
21+
import { ProjectWithRequiredTagsDto } from '@common/api/types/project/project-with-required-tags.dto';
2222
import { ProjectsDto } from '@common/api/types/project/projects.dto';
2323
import { ResentProjectDto } from '@common/api/types/project/recent-projects.dto';
2424
import AccessGroup from '@common/entities/auth/accessgroup.entity';
@@ -32,7 +32,7 @@ import {
3232
import { ConfigService } from '@nestjs/config';
3333
import { AuthHeader } from '../endpoints/auth/parameter-decorator';
3434
import {
35-
projectEntityToDtoWithMissionCount,
35+
projectEntityToDtoWithMissionCountAndTags,
3636
projectEntityToDtoWithRequiredTags,
3737
} from '../serialization';
3838
import { AccessGroupConfig } from '../types/access-group-config';
@@ -80,7 +80,8 @@ export class ProjectService {
8080
): Promise<ProjectsDto> {
8181
let query = this.projectRepository
8282
.createQueryBuilder('project')
83-
.leftJoinAndSelect('project.creator', 'creator');
83+
.leftJoinAndSelect('project.creator', 'creator')
84+
.leftJoinAndSelect('project.requiredTags', 'requiredTags');
8485

8586
query = addAccessConstraintsToProjectQuery(query, userUuid);
8687

@@ -103,15 +104,15 @@ export class ProjectService {
103104

104105
return {
105106
data: projects.map((element) =>
106-
projectEntityToDtoWithMissionCount(element),
107+
projectEntityToDtoWithMissionCountAndTags(element),
107108
),
108109
count,
109110
skip,
110111
take,
111112
};
112113
}
113114

114-
async findOne(uuid: string): Promise<ProjectWithRequiredTags> {
115+
async findOne(uuid: string): Promise<ProjectWithRequiredTagsDto> {
115116
const missionPromise = this.projectRepository
116117
.createQueryBuilder('project')
117118
.where('project.uuid = :uuid', { uuid })

backend/src/services/tag.service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,11 @@ export class TagService {
291291
if (name !== '') {
292292
where.name = ILike(`%${name}%`);
293293
}
294-
if (type !== undefined && type !== DataType.ANY) {
294+
if (
295+
type !== undefined &&
296+
type !== DataType.ANY &&
297+
(type as any) !== ''
298+
) {
295299
where.datatype = type;
296300
}
297301
const [tags, count] = await this.tagTypeRepository.findAndCount({

cli/kleinkram/api/deser.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
from typing import Literal
88
from typing import NewType
99
from typing import Tuple
10+
from typing import List
1011
from uuid import UUID
1112

1213
import dateutil.parser
1314

1415
from kleinkram.errors import ParsingError
15-
from kleinkram.models import File
16+
from kleinkram.models import File, MetadataValue
1617
from kleinkram.models import FileState
1718
from kleinkram.models import Mission
1819
from kleinkram.models import Project
@@ -51,6 +52,7 @@ class MissionObjectKeys(str, Enum):
5152
DESCRIPTION = "description"
5253
CREATED_AT = "createdAt"
5354
UPDATED_AT = "updatedAt"
55+
TAGS = "tags"
5456

5557

5658
class ProjectObjectKeys(str, Enum):
@@ -59,6 +61,7 @@ class ProjectObjectKeys(str, Enum):
5961
DESCRIPTION = "description"
6062
CREATED_AT = "createdAt"
6163
UPDATED_AT = "updatedAt"
64+
REQUIRED_TAGS = "requiredTags"
6265

6366

6467
def _get_nested_info(data, key: Literal["mission", "project"]) -> Tuple[UUID, str]:
@@ -82,6 +85,19 @@ def _parse_file_state(state: str) -> FileState:
8285
except ValueError as e:
8386
raise ParsingError(f"error parsing file state: {state}") from e
8487

88+
def _parse_metadata(tags: List[Dict]) -> Dict[str, MetadataValue]:
89+
result = {}
90+
try:
91+
for tag in tags:
92+
entry = {tag.get("name"): MetadataValue(tag.get("valueAsString"), tag.get("datatype"))}
93+
result.update(entry)
94+
return result
95+
except ValueError as e:
96+
raise ParsingError(f"error parsing metadata: {e}") from e
97+
98+
def _parse_required_tags(tags: List[Dict]) -> list[str]:
99+
return list(_parse_metadata(tags).keys())
100+
85101

86102
def _parse_project(project_object: ProjectObject) -> Project:
87103
try:
@@ -90,6 +106,7 @@ def _parse_project(project_object: ProjectObject) -> Project:
90106
description = project_object[ProjectObjectKeys.DESCRIPTION]
91107
created_at = _parse_datetime(project_object[ProjectObjectKeys.CREATED_AT])
92108
updated_at = _parse_datetime(project_object[ProjectObjectKeys.UPDATED_AT])
109+
required_tags = _parse_required_tags(project_object[ProjectObjectKeys.REQUIRED_TAGS])
93110
except Exception as e:
94111
raise ParsingError(f"error parsing project: {project_object}") from e
95112
return Project(
@@ -98,6 +115,7 @@ def _parse_project(project_object: ProjectObject) -> Project:
98115
description=description,
99116
created_at=created_at,
100117
updated_at=updated_at,
118+
required_tags=required_tags,
101119
)
102120

103121

@@ -107,7 +125,7 @@ def _parse_mission(mission: MissionObject) -> Mission:
107125
name = mission[MissionObjectKeys.NAME]
108126
created_at = _parse_datetime(mission[MissionObjectKeys.CREATED_AT])
109127
updated_at = _parse_datetime(mission[MissionObjectKeys.UPDATED_AT])
110-
metadata = {} # TODO: this crap is really bad to parse
128+
metadata = _parse_metadata(mission[MissionObjectKeys.TAGS])
111129

112130
project_id, project_name = _get_nested_info(mission, PROJECT)
113131

cli/kleinkram/api/file_transfer.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -572,19 +572,20 @@ def upload_files(
572572

573573
avg_speed_bps = total_uploaded_bytes / elapsed_time if elapsed_time > 0 else 0
574574

575-
console.print(f"Upload took {elapsed_time:.2f} seconds")
576-
console.print(f"Total uploaded: {format_bytes(total_uploaded_bytes)}")
577-
console.print(f"Average speed: {format_bytes(avg_speed_bps, speed=True)}")
578-
579-
if failed_files > 0:
580-
console.print(
581-
f"\nUploaded {len(files) - failed_files - skipped_files} files, {skipped_files} skipped, {failed_files} uploads failed",
582-
style="red",
583-
)
584-
else:
585-
console.print(
586-
f"\nUploaded {len(files) - skipped_files} files, {skipped_files} skipped"
587-
)
575+
if verbose:
576+
console.print(f"Upload took {elapsed_time:.2f} seconds")
577+
console.print(f"Total uploaded: {format_bytes(total_uploaded_bytes)}")
578+
console.print(f"Average speed: {format_bytes(avg_speed_bps, speed=True)}")
579+
580+
if failed_files > 0:
581+
console.print(
582+
f"\nUploaded {len(files) - failed_files - skipped_files} files, {skipped_files} skipped, {failed_files} uploads failed",
583+
style="red",
584+
)
585+
else:
586+
console.print(
587+
f"\nUploaded {len(files) - skipped_files} files, {skipped_files} skipped"
588+
)
588589

589590

590591
def download_files(

0 commit comments

Comments
 (0)