Skip to content
Merged
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
52 changes: 17 additions & 35 deletions src/puzzle/entities/puzzle-submission.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,32 @@ import {
ManyToOne,
Column,
CreateDateColumn,
JoinColumn,
Unique,
} from 'typeorm';
import { Puzzle } from './puzzle.entity';
import { ApiProperty } from '@nestjs/swagger';
import { User } from 'src/users/user.entity';
import { Puzzle } from 'src/puzzle/entities/puzzle.entity';

@Entity('puzzle_submission')
@Entity('puzzle_submissions')
@Unique(['user', 'puzzle'])
export class PuzzleSubmission {
@PrimaryGeneratedColumn()
@ApiProperty()
id: number;
@PrimaryGeneratedColumn('uuid')
id: string;

@Column({ name: 'puzzle_id' })
@ApiProperty()
puzzleId: number;
@ManyToOne(() => User, { eager: true })
user: User;

@ManyToOne(() => Puzzle, { eager: true })
@JoinColumn({ name: 'puzzle_id' })
@ApiProperty({ type: () => Puzzle })
puzzle: Puzzle;

@Column({ name: 'user_id' })
@ApiProperty()
userId: string;

@ManyToOne(() => User, { eager: true })
@JoinColumn({ name: 'user_id' })
@ApiProperty({ type: () => User })
user: User;
@Column({ default: false })
isCorrect: boolean;

@Column({ type: 'jsonb' })
@ApiProperty({
type: 'object',
description: 'Submission data like code or answers',
additionalProperties: true
})
attemptData: Record<string, any>;
@Column({ nullable: true })
selectedAnswer?: string;

@Column()
@ApiProperty({ description: 'Whether the submission passed or not' })
result: boolean;
@Column({ default: false })
skipped: boolean;

@CreateDateColumn({ name: 'submitted_at' })
@ApiProperty({ type: String, format: 'date-time' })
submittedAt: Date;
}

@CreateDateColumn()
createdAt: Date;
}
1 change: 1 addition & 0 deletions src/puzzle/enums/puzzle-type.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum PuzzleType {
LOGIC = 'logic',
CODING = 'coding',
BLOCKCHAIN = 'blockchain',
MATH = 'math',
}
76 changes: 76 additions & 0 deletions src/puzzle/providers/puzzle-progress.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
@Injectable()
export class PuzzleProgressProvider {
constructor(
@InjectRepository(PuzzleSubmission)
private submissionRepo: Repository<PuzzleSubmission>,

@InjectRepository(Puzzle)
private puzzleRepo: Repository<Puzzle>,

@InjectRepository(User)
private userRepo: Repository<User>,
) {}


async submitPuzzleAnswer(dto: SubmitPuzzleDto): Promise<PuzzleSubmission> {
const { userId, puzzleId, selectedAnswer, skipped } = dto;

const user = await this.userRepo.findOne({ where: { id: userId } });
if (!user) throw new NotFoundException('User not found');

const puzzle = await this.puzzleRepo.findOne({ where: { id: puzzleId } });
if (!puzzle) throw new NotFoundException('Puzzle not found');

const existing = await this.submissionRepo.findOne({
where: { user: { id: userId }, puzzle: { id: puzzleId } },
});

if (existing) {
throw new BadRequestException('Puzzle already submitted');
}

const isCorrect = !skipped && selectedAnswer === puzzle.solution;

const submission = this.submissionRepo.create({
user,
puzzle,
selectedAnswer,
skipped,
isCorrect,
});

return await this.submissionRepo.save(submission);
}

/**
* Get user's puzzle progress by category
*/
async getProgressByCategory(userId: string): Promise<
Record<PuzzleCategory, { completed: number; total: number }>
> {
const allPuzzles = await this.puzzleRepo.find({
where: { isPublished: true },
});

const completed = await this.submissionRepo.find({
where: { user: { id: userId }, isCorrect: true },
relations: ['puzzle'],
});

const progressMap: Record<string, { completed: number; total: number }> = {};

for (const puzzle of allPuzzles) {
const key = puzzle.category;
progressMap[key] = progressMap[key] || { completed: 0, total: 0 };
progressMap[key].total += 1;
}

for (const submission of completed) {
const key = submission.puzzle.category;
if (progressMap[key]) {
progressMap[key].completed += 1;
}
}

return progressMap;
}}
5 changes: 5 additions & 0 deletions src/puzzle/puzzle.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { User } from '../users/user.entity';
import { SubmitPuzzleDto } from './dto/puzzle.dto';
import { PuzzleSubmissionDto } from '../gamification/dto/puzzle-submission.dto';
import { PuzzleType } from './enums/puzzle-type.enum';
import { PuzzleProgressProvider } from './providers/puzzle-progress.provider';

@Injectable()
export class PuzzleService {
Expand All @@ -24,6 +25,8 @@ export class PuzzleService {
@InjectRepository(User)
private readonly userRepository: Repository<User>,
private readonly eventEmitter: EventEmitter2,
private readonly puzzleProgressProvider: PuzzleProgressProvider,

) {}

async submitPuzzleSolution(
Expand Down Expand Up @@ -196,4 +199,6 @@ export class PuzzleService {
where: { userId },
});
}


}
Loading