Changeset 6901f8b
- Timestamp:
- 11/04/21 17:01:30 (3 years ago)
- Branches:
- dev
- Children:
- caaf82d
- Parents:
- 1e0d869 (diff), b9d7ae5 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - Location:
- src
- Files:
-
- 50 added
- 2 deleted
- 37 edited
- 2 moved
Legend:
- Unmodified
- Added
- Removed
-
src/Clients/Angular/finki-chattery/src/app/core/services/auth.service.ts
r1e0d869 r6901f8b 24 24 public user: ApplicationUser | null = null; 25 25 public oidcUser: User | null = null; 26 public selfUser: SelfUserResponse | null = null; 26 27 27 28 constructor(private baseApi: BaseApiService) { … … 35 36 user?.profile.isVerified 36 37 ); 38 39 this.selfUserDto().subscribe((selfUser) => { 40 if (selfUser) { 41 this.selfUser = selfUser; 42 } 43 }); 37 44 }); 38 45 } … … 86 93 user.profile.isVerified 87 94 ); 95 96 if (!this.selfUser) { 97 this.selfUserDto().subscribe((selfUser) => { 98 if (selfUser) { 99 this.selfUser = selfUser; 100 } 101 }); 102 } 88 103 }); 89 104 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-facade.service.ts
r1e0d869 r6901f8b 2 2 import { Injectable } from '@angular/core'; 3 3 import { Action, Store } from '@ngrx/store'; 4 import { Observable, Subject , throwError} from 'rxjs';5 import { catchError,filter, map } from 'rxjs/operators';4 import { Observable, Subject } from 'rxjs'; 5 import { filter, map } from 'rxjs/operators'; 6 6 7 7 import { … … 9 9 PreviewQuestionViewModel, 10 10 QuestionStateViewModel, 11 SearchQuestionsQueryViewModel 11 SearchQuestionsQueryViewModel, 12 VoteType 12 13 } from 'src/app/shared-app/models'; 14 import { AuthService } from '../services'; 13 15 import { 14 16 EffectStartedWorking, … … 16 18 GetPreviewQuestionsPopular, 17 19 GetQuestionState, 18 GetSearchQuestions 20 GetSearchQuestions, 21 SetCorrectAnswer, 22 VoteAnswer 19 23 } from './question-state/question.actions'; 20 24 import { questionStateQuery } from './question-state/question.selectors'; … … 29 33 effectWorking$: Observable<boolean | HttpErrorResponse>; 30 34 31 constructor(private store: Store<QuestionState>) { 32 this.effectWorking$ = this.store.select(questionStateQuery.effectWorking).pipe( 33 filter((effect) => effect !== null), 34 map((effect) => { 35 if (effect instanceof HttpErrorResponse) { 36 throw effect; 37 } else { 38 return effect; 39 } 40 }), 41 catchError((err) => { 42 return throwError(err); 43 }) 44 ); 35 constructor(private store: Store<QuestionState>, private auth: AuthService) { 36 this.effectWorking$ = this.store.select(questionStateQuery.effectWorking).pipe(filter((effect) => effect !== null)); 37 } 38 39 public currentQuestionOwnedByCurrentUser(): Observable<boolean> { 40 return this.getQuestion().pipe(map((question) => this.auth.selfUser?.student?.uid === question.student.uid)); 45 41 } 46 42 … … 81 77 } 82 78 79 public setCorrectAnswer(questionUid: string, answerUid: string): void { 80 this.dispatchEffect(new SetCorrectAnswer(questionUid, answerUid)); 81 } 82 83 public voteAnswer(answerUid: string, questionUid: string, voteType: VoteType): void { 84 this.dispatchEffect(new VoteAnswer(questionUid, answerUid, voteType)); 85 } 86 83 87 public fetchQuestion(questionUid: string): void { 84 88 this.dispatchEffect(new GetQuestionState(questionUid)); -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question-state-response.models.ts
r1e0d869 r6901f8b 1 import { VoteType } from 'src/app/shared-app/models'; 2 1 3 export class QuestionStateResponse { 2 4 public uid!: string; … … 34 36 public correctAnswer!: boolean; 35 37 public createdOn!: moment.Moment; 36 public upvotesCount!: number;38 public votesCount!: number; 37 39 public studentResponse!: AnswerStudentQuestionStateResponse; 38 40 public answerResponsesResponse!: AnswerResponseQuestionStateResponse[]; … … 73 75 public text!: string; 74 76 } 77 78 export class VoteAnswerResponse { 79 public answerUid!: string; 80 public voteUid!: string; 81 public voteType!: VoteType; 82 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.actions.ts
r1e0d869 r6901f8b 2 2 import { Action } from '@ngrx/store'; 3 3 4 import { PreviewQuestionViewModel, QuestionStateViewModel, SearchQuestionsQueryViewModel } from 'src/app/shared-app/models'; 4 import { 5 PreviewQuestionViewModel, 6 QuestionStateViewModel, 7 SearchQuestionsQueryViewModel, 8 VoteAnswerViewModel, 9 VoteType 10 } from 'src/app/shared-app/models'; 5 11 6 12 export enum QuestionActionTypes { 7 13 GetQuestionState = '[Question] Get state', 8 14 GetQuestionStateSuccess = '[Question] Get state success', 15 SetCorrectAnswer = '[Question] Set Correct Answer', 16 SetCorrectAnswerSuccess = '[Question] Set Correct Answer success', 9 17 GetPreviewQuestionsLatest = '[Question] Get preview questions Latest', 10 18 GetPreviewQuestionsLatestSuccess = '[Question] Get preview questions Latest Success', … … 13 21 GetSearchQuestions = '[Question] Get search questions', 14 22 GetSearchQuestionsSuccess = '[Question] Get search questions Success', 23 VoteAnswer = '[Question] Vote answer', 24 VoteAnswerSuccess = '[Question] Vote answer Success', 15 25 EffectStartedWorking = '[Question] Effect Started Working', 16 26 EffectFinishedWorking = '[Question] Effect Finished Working', … … 28 38 29 39 constructor(public payload: QuestionStateViewModel) {} 40 } 41 42 export class SetCorrectAnswer implements Action { 43 readonly type = QuestionActionTypes.SetCorrectAnswer; 44 45 constructor(public questionUid: string, public answerUid: string) {} 46 } 47 48 export class SetCorrectAnswerSuccess implements Action { 49 readonly type = QuestionActionTypes.SetCorrectAnswerSuccess; 50 51 constructor(public payload: string) {} 30 52 } 31 53 … … 66 88 } 67 89 90 export class VoteAnswer implements Action { 91 readonly type = QuestionActionTypes.VoteAnswer; 92 93 constructor(public questionUid: string, public answerUid: string, public voteType: VoteType) {} 94 } 95 96 export class VoteAnswerSuccess implements Action { 97 readonly type = QuestionActionTypes.VoteAnswerSuccess; 98 99 constructor(public payload: VoteAnswerViewModel) {} 100 } 101 68 102 export class EffectStartedWorking implements Action { 69 103 readonly type = QuestionActionTypes.EffectStartedWorking; … … 89 123 | GetPreviewQuestionsPopularSuccess 90 124 | GetSearchQuestionsSuccess 125 | VoteAnswerSuccess 126 | SetCorrectAnswerSuccess 91 127 | EffectStartedWorking 92 128 | EffectFinishedWorking -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.effects.ts
r1e0d869 r6901f8b 1 1 import { Injectable } from '@angular/core'; 2 2 import { act, Actions, createEffect, ofType } from '@ngrx/effects'; 3 import { catchError, filter, mergeMap, switchMap, withLatestFrom } from 'rxjs/operators';3 import { catchError, filter, mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators'; 4 4 import { PreviewQuestionsOrderEnum, SearchQuestionsQueryViewModel } from 'src/app/shared-app/models'; 5 5 import { TranslateFromJsonService } from 'src/app/shared-app/services'; 6 6 7 7 import { BaseApiService } from 'src/app/shared-app/services/base-api.service'; 8 import { NotificationService } from '../../services/notification.service'; 8 9 import { QuestionFacadeService } from '../question-facade.service'; 9 import { PreviewQuestionResponse, QuestionStateResponse } from './question-state.models'; 10 import { VoteAnswerRequest } from './question-state-request.models'; 11 import { PreviewQuestionResponse, QuestionStateResponse, VoteAnswerResponse } from './question-state-response.models'; 10 12 import { 11 13 EffectFinishedWorking, … … 19 21 GetSearchQuestions, 20 22 GetSearchQuestionsSuccess, 21 QuestionActionTypes 23 QuestionActionTypes, 24 SetCorrectAnswer, 25 SetCorrectAnswerSuccess, 26 VoteAnswer, 27 VoteAnswerSuccess 22 28 } from './question.actions'; 23 29 import { QuestionMapper } from './question.mapper'; … … 31 37 private api: BaseApiService, 32 38 private translate: TranslateFromJsonService, 33 private facade: QuestionFacadeService 39 private facade: QuestionFacadeService, 40 private notification: NotificationService 34 41 ) {} 35 42 … … 103 110 ); 104 111 }); 112 113 voteAnswer$ = createEffect(() => { 114 return this.actions$.pipe( 115 ofType<VoteAnswer>(QuestionActionTypes.VoteAnswer), 116 mergeMap((action) => { 117 const body = new VoteAnswerRequest(action.voteType); 118 return this.api.post<VoteAnswerResponse>(`v1/questions/${action.questionUid}/answers/${action.answerUid}/votes`, body).pipe( 119 tap((state) => this.notification.successNotification('sucess-vote')), 120 switchMap((state) => [new VoteAnswerSuccess(QuestionMapper.ToVoteAnswerViewModel(state)), new EffectFinishedWorking()]), 121 catchError((err) => [new EffectFinishedWorkingError(err)]) 122 ); 123 }) 124 ); 125 }); 126 127 setCorrectAnswer$ = createEffect(() => { 128 return this.actions$.pipe( 129 ofType<SetCorrectAnswer>(QuestionActionTypes.SetCorrectAnswer), 130 mergeMap((action) => { 131 return this.api.put<string>(`v1/questions/${action.questionUid}/answers/${action.answerUid}/correct`).pipe( 132 tap((state) => this.notification.successNotification('success-correct-answer')), 133 switchMap((state) => [new SetCorrectAnswerSuccess(state), new EffectFinishedWorking()]), 134 catchError((err) => [new EffectFinishedWorkingError(err)]) 135 ); 136 }) 137 ); 138 }); 105 139 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.mapper.ts
r1e0d869 r6901f8b 10 10 QuestionStateViewModel, 11 11 StudentQuestionStateViewModel, 12 TeamQuestionStateViewModel 12 TeamQuestionStateViewModel, 13 VoteAnswerViewModel 13 14 } from 'src/app/shared-app/models'; 14 15 import { TranslateFromJsonService } from 'src/app/shared-app/services'; 15 import { PreviewQuestionResponse, QuestionStateResponse } from './question-state.models';16 import { PreviewQuestionResponse, QuestionStateResponse, VoteAnswerResponse } from './question-state-response.models'; 16 17 17 18 export class QuestionMapper { … … 51 52 x.correctAnswer, 52 53 moment(x.createdOn), 53 x. upvotesCount,54 x.votesCount, 54 55 answerStudent, 55 56 answerResponses … … 113 114 return questions; 114 115 } 116 117 public static ToVoteAnswerViewModel(response: VoteAnswerResponse): VoteAnswerViewModel { 118 return new VoteAnswerViewModel(response.answerUid, response.voteUid, response.voteType); 119 } 115 120 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.reducers.ts
r1e0d869 r6901f8b 1 import { AnswerQuestionStateViewModel, VoteType } from 'src/app/shared-app/models'; 1 2 import { QuestionAction, QuestionActionTypes } from './question.actions'; 2 3 import { initialState, QuestionState } from './question.state'; … … 25 26 searchQuestionsQuery: action.query 26 27 }; 28 case QuestionActionTypes.VoteAnswerSuccess: { 29 if (state.question) { 30 return { 31 ...state, 32 question: { 33 ...state.question, 34 answers: state.question.answers.map((x) => { 35 if (x.uid === action.payload.answerUid) { 36 let votesCountNew = x.votesCount; 37 38 switch (action.payload.voteType) { 39 case VoteType.Upvote: 40 votesCountNew++; 41 break; 42 case VoteType.Downvote: 43 votesCountNew--; 44 break; 45 } 46 47 return { 48 ...x, 49 votesCount: votesCountNew 50 }; 51 } 52 53 return x; 54 }) 55 } 56 }; 57 } 58 59 return { 60 ...state 61 }; 62 } 63 case QuestionActionTypes.SetCorrectAnswerSuccess: { 64 if (state.question) { 65 return { 66 ...state, 67 question: { 68 ...state.question, 69 answers: state.question.answers.map((x) => { 70 if (x.correctAnswer) { 71 return { 72 ...x, 73 correctAnswer: false 74 }; 75 } 76 if (x.uid === action.payload) { 77 return { 78 ...x, 79 correctAnswer: true 80 }; 81 } 82 return x; 83 }) 84 } 85 }; 86 } 87 88 return { 89 ...state 90 }; 91 } 27 92 case QuestionActionTypes.EffectStartedWorking: { 28 93 return { -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.html
r1e0d869 r6901f8b 1 <div >1 <div class="main-div"> 2 2 <div class="vote-icons" fxLayout="column" fxLayoutAlign="center center"> 3 3 <mat-icon class="vote-icon" (click)="voted(VoteType.Upvote)" [inline]="true">arrow_drop_up</mat-icon> … … 7 7 >check</mat-icon 8 8 > 9 <mat-icon 10 *ngIf="canSetCorrectAnswer && !correct" 11 [inline]="true" 12 class="text-center text-bold green set-correct-answer" 13 (click)="setCorrectAnswer.emit()" 14 >check</mat-icon 15 > 9 16 </div> 10 17 </div> -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.scss
r1e0d869 r6901f8b 14 14 cursor: pointer; 15 15 } 16 17 .show-on-hover { 18 display: none; 19 } 20 21 .set-correct-answer { 22 opacity: 0.3; 23 visibility: hidden; 24 } 25 26 .main-div:hover > .vote-icons > .set-correct-answer { 27 visibility: visible; 28 } 29 30 .main-div { 31 height: 100%; 32 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.ts
r1e0d869 r6901f8b 1 1 import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; 2 import { VoteType } from 'src/app/shared-app/models'; 2 3 import { ButtonType } from '../button/button.models'; 3 4 export enum VoteType {5 Upvote,6 Downvote7 }8 4 9 5 @Component({ … … 14 10 export class VoteComponent implements OnInit { 15 11 @Input() voteCount: number | undefined; 12 @Input() canSetCorrectAnswer: boolean | null = false; 16 13 @Input() correct = false; 17 14 @Output() voteClicked = new EventEmitter<VoteType>(); 15 @Output() setCorrectAnswer = new EventEmitter<void>(); 18 16 19 17 VoteType = VoteType; -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.html
r1e0d869 r6901f8b 14 14 <mat-card-content> 15 15 <div fxLayout="row wrap" fxLayoutAlign="space-around none"> 16 <app-vote [voteCount]="answer.upvotesCount" [correct]="answer.correctAnswer" fxFlex="6%"></app-vote> 16 <app-vote 17 [canSetCorrectAnswer]="canSetCorrectAnswer | async" 18 [voteCount]="answer.votesCount" 19 [correct]="answer.correctAnswer" 20 (voteClicked)="answerVoted($event, answer.uid, question.uid)" 21 (setCorrectAnswer)="setCorrectAnswer(question.uid, answer.uid)" 22 fxFlex="6%" 23 ></app-vote> 17 24 <div fxFlex="92%"> 18 25 <app-text-editor -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.ts
r1e0d869 r6901f8b 1 import { HttpErrorResponse } from '@angular/common/http'; 1 2 import { Component, OnInit } from '@angular/core'; 3 import { NotificationService } from 'src/app/core/services/notification.service'; 2 4 import { QuestionFacadeService } from 'src/app/core/state/question-facade.service'; 3 5 4 import { QuestionStateViewModel } from 'src/app/shared-app/models';6 import { QuestionStateViewModel, VoteType } from 'src/app/shared-app/models'; 5 7 import { ButtonType } from '../../generic/button/button.models'; 6 8 … … 11 13 }) 12 14 export class QuestionPreviewComponent implements OnInit { 15 canSetCorrectAnswer = this.questionFacade.currentQuestionOwnedByCurrentUser(); 13 16 question!: QuestionStateViewModel; 14 17 working = true; 15 18 ButtonType = ButtonType; 16 constructor(private questionFacade: QuestionFacadeService ) {}19 constructor(private questionFacade: QuestionFacadeService, private notification: NotificationService) {} 17 20 18 21 ngOnInit(): void { … … 21 24 this.working = false; 22 25 }); 26 27 this.questionFacade.effectWorking$.subscribe((effect) => { 28 if (effect instanceof HttpErrorResponse) { 29 this.notification.handleErrorsNotification(effect.error); 30 } 31 }); 32 } 33 34 answerVoted(voteType: VoteType, answerUid: string, questionUid: string): void { 35 this.questionFacade.voteAnswer(answerUid, questionUid, voteType); 36 } 37 38 setCorrectAnswer(questionUid: string, answerUid: string): void { 39 this.questionFacade.setCorrectAnswer(questionUid, answerUid); 23 40 } 24 41 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/models/question-state-enums.models.ts
r1e0d869 r6901f8b 3 3 Popular 4 4 } 5 6 export enum VoteType { 7 Upvote, 8 Downvote 9 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/models/question-state-view-models.models.ts
r1e0d869 r6901f8b 1 import { VoteType } from '.'; 2 1 3 export class QuestionStateViewModel { 2 4 constructor( … … 32 34 public correctAnswer: boolean, 33 35 public createdOn: moment.Moment, 34 public upvotesCount: number,36 public votesCount: number, 35 37 public student: AnswerStudentQuestionStateViewModel, 36 38 public answerResponses: AnswerResponseQuestionStateViewModel[] … … 73 75 constructor(public text: string) {} 74 76 } 77 78 export class VoteAnswerViewModel { 79 constructor(public answerUid: string, public voteUid: string, public voteType: VoteType) {} 80 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/models/user.models.ts
r1e0d869 r6901f8b 9 9 } 10 10 11 export class SelfUserResponse {12 uid!: string;13 }14 15 11 export enum ApplicationUserType { 16 12 Student = 'Student', … … 19 15 Guest = 'Guest' 20 16 } 17 18 export class SelfUserResponse { 19 public student?: StudentSelfResponse | null; 20 public teacher?: TeacherSelfResponse | null; 21 public moderator?: ModeratorSelfResponse | null; 22 } 23 24 export class StudentSelfResponse { 25 public uid!: string; 26 public applicationUserId!: number; 27 public index!: string; 28 public reputation!: number; 29 public imageUrl!: string; 30 public questions!: StudentQuestionResponse[]; 31 public teams!: StudentTeamResponse[]; 32 } 33 34 export class StudentQuestionResponse { 35 public questionUid!: string; 36 public title!: string; 37 } 38 39 export class StudentTeamResponse { 40 public teamUid!: string; 41 public name!: string; 42 } 43 44 export class ModeratorSelfResponse { 45 public uid!: string; 46 public applicationUserId!: number; 47 } 48 49 export class TeacherSelfResponse { 50 public uid!: string; 51 public applicationUserId!: number; 52 public teams!: TeacherTeamResponse[]; 53 } 54 55 export class TeacherTeamResponse { 56 public teamUid!: string; 57 public name!: string; 58 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/services/base-api.service.ts
r1e0d869 r6901f8b 19 19 20 20 public getSelfUser(): Observable<SelfUserResponse> { 21 return this.get<SelfUserResponse>(' self');21 return this.get<SelfUserResponse>('v1/self'); 22 22 } 23 23 -
src/Clients/Angular/finki-chattery/src/assets/translations/en.json
r1e0d869 r6901f8b 53 53 "ask-question-stepper-ask": "Ask question", 54 54 "ask-question-ask-button-back": "Edit question", 55 "ask-question-ask-button": "Ask question" 55 "sucess-vote": "Successfully voted answer", 56 "success-correct-answer": "Successfully set correct answer", 57 "ask-question-ask-button": "Ask question", 58 "AnswerAlreadyUpvoted": "You have already upvoted this answer", 59 "AnswerAlreadyDownvoted": "You have already downvoted this answer", 60 "StudentHasBadReputation": "You have bad reputation and can not vote" 56 61 } -
src/FinkiChattery/FinkiChattery.Api/ApplicationServices/Questioning/Mapper/QuestionMapper.cs
r1e0d869 r6901f8b 54 54 var answerStudent = new AnswerStudentQuestionStateResponse(x.StudentDto.Id, x.StudentDto.Uid, x.StudentDto.Index, x.StudentDto.ImageUrl, x.StudentDto.Reputation); 55 55 56 return new AnswerQuestionStateResponse(x.Id, x.Uid, x.Text, x.CorrectAnswer, x.CreatedOn, x. UpvotesCount, answerStudent, answerResponses);56 return new AnswerQuestionStateResponse(x.Id, x.Uid, x.Text, x.CorrectAnswer, x.CreatedOn, x.VotesCount, answerStudent, answerResponses); 57 57 }); 58 58 } -
src/FinkiChattery/FinkiChattery.Api/Services/RegisterServices.cs
r1e0d869 r6901f8b 1 1 using FinkiChattery.Api.ApplicationServices.Authentication; 2 using FinkiChattery.Api.ApplicationServices.Questioning.EventHandlers; 2 3 using FinkiChattery.Api.Services; 3 4 using FinkiChattery.Commands.Questioning; … … 31 32 services.AddScoped<IEventService, EventService>(); 32 33 services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); 33 services.AddMediatR(typeof(AskQuestionCommand), typeof(GetQuestionStateQuery) );34 services.AddMediatR(typeof(AskQuestionCommand), typeof(GetQuestionStateQuery), typeof(UpdateAnswerVotesEventHandler)); 34 35 } 35 36 … … 54 55 }); 55 56 services.AddHangfireServer(); 57 58 services.AddScoped<IBackgroundJobClient>(provider => 59 { 60 return new BackgroundJobClient(JobStorage.Current); 61 }); 56 62 } 57 63 -
src/FinkiChattery/FinkiChattery.Commands/Questioning/QuestioningErrorCodes.cs
r1e0d869 r6901f8b 9 9 public const string CategoriesDontExist = "CategoriesDontExist"; 10 10 public const string TeamDontExist = "TeamDontExist"; 11 public const string AnswerAlreadyUpvoted = "AnswerAlreadyUpvoted"; 12 public const string AnswerAlreadyDownvoted = "AnswerAlreadyDownvoted"; 13 public const string StudentHasBadReputation = "StudentHasBadReputation"; 14 public const string AnswerTextLengthInvalid = "AnswerTextLengthInvalid"; 15 public const string QuestionNotFound = "QuestionNotFound"; 16 public const string AnswerInQuestionNotFound = "AnswerInQuestionNotFound"; 17 public const string StudentDoesNotOwnQuestion = "StudentDoesNotOwnQuestion"; 18 public const string AnswerIsAlreadyMarkedAsCorrect = "AnswerIsAlreadyMarkedAsCorrect"; 11 19 } 12 20 } -
src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/CategoriesUidsExist.cs
r1e0d869 r6901f8b 1 using FinkiChattery.Persistence.Repositories; 2 using FinkiChattery.Persistence.UnitOfWork; 1 using FinkiChattery.Persistence.UnitOfWork; 3 2 using FluentValidation.Validators; 4 3 using System; -
src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/QuestioningFluentValidationRules.cs
r1e0d869 r6901f8b 16 16 } 17 17 18 public static IRuleBuilderOptions<T, string> AnswerTextValidate<T>(this IRuleBuilder<T, string> ruleBuilder) 19 { 20 return ruleBuilder.NotNull().WithMessage(QuestioningErrorCodes.CantBeNull).MaximumLength(4000).WithMessage(QuestioningErrorCodes.AnswerTextLengthInvalid); 21 } 22 18 23 public static IRuleBuilderOptions<T, IEnumerable<U>> ListNotNull<T, U>(this IRuleBuilder<T, IEnumerable<U>> ruleBuilder) 19 24 { -
src/FinkiChattery/FinkiChattery.Contracts/Questioning/GetQuestionState/QuestionStateResponse.cs
r1e0d869 r6901f8b 89 89 public class AnswerQuestionStateResponse 90 90 { 91 public AnswerQuestionStateResponse(long id, Guid uid, string text, bool correctAnswer, DateTime createdOn, long upvotesCount, AnswerStudentQuestionStateResponse studentResponse, IEnumerable<AnswerResponseQuestionStateResponse> answerResponsesResponse)91 public AnswerQuestionStateResponse(long id, Guid uid, string text, bool correctAnswer, DateTime createdOn, long votesCount, AnswerStudentQuestionStateResponse studentResponse, IEnumerable<AnswerResponseQuestionStateResponse> answerResponsesResponse) 92 92 { 93 93 Id = id; … … 96 96 CorrectAnswer = correctAnswer; 97 97 CreatedOn = createdOn; 98 UpvotesCount = upvotesCount;98 VotesCount = votesCount; 99 99 StudentResponse = studentResponse; 100 100 AnswerResponsesResponse = answerResponsesResponse; … … 107 107 public bool CorrectAnswer { get; } 108 108 public DateTime CreatedOn { get; } 109 public long UpvotesCount { get; }109 public long VotesCount { get; } 110 110 public AnswerStudentQuestionStateResponse StudentResponse { get; } 111 111 public IEnumerable<AnswerResponseQuestionStateResponse> AnswerResponsesResponse { get; } -
src/FinkiChattery/FinkiChattery.Database/FinkiChattery.Database.sqlproj
r1e0d869 r6901f8b 75 75 <Folder Include="dbo\Tables\QuestionCategory" /> 76 76 <Folder Include="Snapshots" /> 77 <Folder Include="dbo\Tables\Vote" /> 77 78 </ItemGroup> 78 79 <ItemGroup> … … 81 82 <Build Include="dbo\Tables\StudentTeam.sql" /> 82 83 <Build Include="dbo\Tables\TeacherTeam.sql" /> 83 <Build Include="dbo\Tables\Upvote.sql" />84 84 <Build Include="dbo\Tables\User\AspNetRoleClaims.sql" /> 85 85 <Build Include="dbo\Tables\User\AspNetRoles.sql" /> … … 100 100 <Build Include="dbo\Tables\QuestionCategory\QuestionCategory.sql" /> 101 101 <None Include="dbo\Tables\QuestionCategory\QuestionCategory.Debug.Seed.sql" /> 102 <Build Include="dbo\Tables\Vote\Vote.sql" /> 102 103 </ItemGroup> 103 104 <ItemGroup> -
src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Answer/Answer.Debug.Seed.sql
r1e0d869 r6901f8b 12 12 (3, N'cee193c3-9d36-4ed8-81b2-15eb4ff305f1', N'Answer 3', 2, 1, 1, GETUTCDATE(), 5), 13 13 (4, N'dee193c3-9d36-4ed8-81b2-15eb4ff305f1', N'Answer 4', 2, 1, 0, GETUTCDATE(), 5) 14 ) AS temp ([Id], [Uid], [Text], [QuestionFk], [StudentFk], [CorrectAnswer], [CreatedOn], [ UpvotesCount])14 ) AS temp ([Id], [Uid], [Text], [QuestionFk], [StudentFk], [CorrectAnswer], [CreatedOn], [VotesCount]) 15 15 ) AS S 16 16 ON T.[ID] = S.[ID] … … 21 21 T.[StudentFk] = S.[StudentFk], 22 22 T.[CorrectAnswer] = S.[CorrectAnswer], 23 T.[ UpvotesCount] = S.[UpvotesCount]23 T.[VotesCount] = S.[VotesCount] 24 24 WHEN NOT MATCHED THEN 25 25 INSERT … … 32 32 [CorrectAnswer], 33 33 [CreatedOn], 34 [ UpvotesCount]34 [VotesCount] 35 35 ) 36 36 VALUES 37 (S.[Id], S.[Uid], S.[Text], S.[QuestionFk], S.[StudentFk], S.[CorrectAnswer], S.[CreatedOn], S.[ UpvotesCount]);37 (S.[Id], S.[Uid], S.[Text], S.[QuestionFk], S.[StudentFk], S.[CorrectAnswer], S.[CreatedOn], S.[VotesCount]); 38 38 SET IDENTITY_INSERT [dbo].[Answer] OFF 39 39 END -
src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Answer/Answer.sql
r1e0d869 r6901f8b 7 7 [CorrectAnswer] BIT NOT NULL, 8 8 [CreatedOn] SMALLDATETIME NOT NULL, 9 [ UpvotesCount] BIGINT NOT NULL DEFAULT 0,9 [VotesCount] BIGINT NOT NULL DEFAULT 0, 10 10 CONSTRAINT [PK_Answer] PRIMARY KEY CLUSTERED ([Id] ASC), 11 11 CONSTRAINT [FK_Answer_Question_QuestionFk] FOREIGN KEY ([QuestionFk]) REFERENCES [dbo].[Question] ([Id]), -
src/FinkiChattery/FinkiChattery.Persistence/Configurations/AnswerConfig.cs
r1e0d869 r6901f8b 22 22 builder.Property(x => x.CorrectAnswer).HasColumnName(@"CorrectAnswer").HasColumnType("bit").IsRequired(); 23 23 builder.Property(x => x.CreatedOn).HasColumnName(@"CreatedOn").HasColumnType("smalldatetime").IsRequired(); 24 builder.Property(x => x. UpvotesCount).HasColumnName(@"UpvotesCount").HasColumnType("bigint").IsRequired().HasDefaultValue(0);24 builder.Property(x => x.VotesCount).HasColumnName(@"VotesCount").HasColumnType("bigint").IsRequired().HasDefaultValue(0); 25 25 26 26 builder.HasOne(x => x.Question).WithMany(x => x.Answers).HasForeignKey(x => x.QuestionFk).OnDelete(DeleteBehavior.Restrict); -
src/FinkiChattery/FinkiChattery.Persistence/Context/ApplicationDbContext.cs
r1e0d869 r6901f8b 23 23 public DbSet<TeacherTeam> TeacherTeams { get; set; } 24 24 public DbSet<Team> Teams { get; set; } 25 public DbSet< Upvote> Upvotes { get; set; }25 public DbSet<Vote> Votes { get; set; } 26 26 27 27 protected override void OnModelCreating(ModelBuilder builder) … … 42 42 builder.ApplyConfiguration(new TeacherTeamConfig(schema)); 43 43 builder.ApplyConfiguration(new TeamConfig(schema)); 44 builder.ApplyConfiguration(new UpvoteConfig(schema));44 builder.ApplyConfiguration(new VoteConfig(schema)); 45 45 } 46 46 } -
src/FinkiChattery/FinkiChattery.Persistence/Models/Answer.cs
r1e0d869 r6901f8b 6 6 public class Answer : BaseEntity 7 7 { 8 public Answer() : base() 9 { 10 CreatedOn = DateTime.UtcNow; 11 VotesCount = 0; 12 CorrectAnswer = false; 13 } 14 8 15 public string Text { get; set; } 9 16 … … 20 27 public DateTime CreatedOn { get; set; } 21 28 22 public long UpvotesCount { get; set; }29 public long VotesCount { get; set; } 23 30 24 public virtual ICollection< Upvote> Upvotes { get; set; }31 public virtual ICollection<Vote> Votes { get; set; } 25 32 26 33 public virtual ICollection<AnswerResponse> AnswerResponses { get; set; } -
src/FinkiChattery/FinkiChattery.Persistence/Models/Moderator.cs
r1e0d869 r6901f8b 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace FinkiChattery.Persistence.Models 1 namespace FinkiChattery.Persistence.Models 8 2 { 9 3 public class Moderator : BaseEntity -
src/FinkiChattery/FinkiChattery.Persistence/Models/Student.cs
r1e0d869 r6901f8b 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 1 using System.Collections.Generic; 6 2 7 3 namespace FinkiChattery.Persistence.Models … … 22 18 23 19 public virtual ICollection<Question> Questions { get; set; } 24 20 25 21 public virtual ICollection<Answer> Answers { get; set; } 26 22 27 23 public virtual ICollection<StudentTeam> StudentTeams { get; set; } 28 24 } -
src/FinkiChattery/FinkiChattery.Persistence/Models/Vote.cs
r1e0d869 r6901f8b 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 1 using FinkiChattery.Persistence.Helpers; 6 2 7 3 namespace FinkiChattery.Persistence.Models 8 4 { 9 public class Upvote : BaseEntity5 public class Vote : BaseEntity 10 6 { 7 public Vote() : base() 8 { 9 } 10 11 11 public long StudentFk { get; set; } 12 12 … … 16 16 17 17 public virtual Answer Answer { get; set; } 18 19 public VoteType VoteType { get; set; } 18 20 } 19 21 } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IQuestionRepo.cs
r1e0d869 r6901f8b 16 16 17 17 Task<List<QuestionPreviewDto>> GetPreviewQuestionsPopular(); 18 19 Task<bool> QuestionIsOwnedByStudent(Guid questionUid, long applicationUserId); 20 21 Task<Question> GetQuestionWithAnswersAndStudents(Guid questionUid); 18 22 } 19 23 } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IStudentRepo.cs
r1e0d869 r6901f8b 1 1 using FinkiChattery.Persistence.Models; 2 using FinkiChattery.Persistence.Repositories.Contracts; 2 3 using System.Threading.Tasks; 3 4 … … 7 8 { 8 9 public Task<Student> GetStudent(long applicationUserFk); 10 public Task<StudentSelfDto> GetStudentSelfDto(long applicationUserFk); 9 11 } 10 12 } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/Question/QuestionStateDto.cs
r1e0d869 r6901f8b 84 84 public class AnswerQuestionStateDto 85 85 { 86 public AnswerQuestionStateDto(long id, Guid uid, string text, bool correctAnswer, DateTime createdOn, long upvotesCount, AnswerStudentQuestionStateDto studentDto, IEnumerable<AnswerResponseQuestionStateDto> answerResponsesDto)86 public AnswerQuestionStateDto(long id, Guid uid, string text, bool correctAnswer, DateTime createdOn, long votesCount, AnswerStudentQuestionStateDto studentDto, IEnumerable<AnswerResponseQuestionStateDto> answerResponsesDto) 87 87 { 88 88 Id = id; … … 91 91 CorrectAnswer = correctAnswer; 92 92 CreatedOn = createdOn; 93 UpvotesCount = upvotesCount;93 VotesCount = votesCount; 94 94 StudentDto = studentDto; 95 95 AnswerResponsesDto = answerResponsesDto; … … 101 101 public bool CorrectAnswer { get; } 102 102 public DateTime CreatedOn { get; } 103 public long UpvotesCount { get; }103 public long VotesCount { get; } 104 104 public AnswerStudentQuestionStateDto StudentDto { get; } 105 105 public IEnumerable<AnswerResponseQuestionStateDto> AnswerResponsesDto { get; } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/QuestionRepo.cs
r1e0d869 r6901f8b 80 80 y.CorrectAnswer, 81 81 y.CreatedOn, 82 y. UpvotesCount,82 y.VotesCount, 83 83 new AnswerStudentQuestionStateDto( 84 84 y.Student.Id, … … 144 144 .ToListAsync(); 145 145 } 146 147 public async Task<bool> QuestionIsOwnedByStudent(Guid questionUid, long applicationUserId) 148 { 149 return await DbSet 150 .Where(x => x.Uid == questionUid && x.Student.ApplicationUserFk == applicationUserId) 151 .AnyAsync(); 152 } 153 154 public async Task<Question> GetQuestionWithAnswersAndStudents(Guid questionUid) 155 { 156 return await DbSet 157 .Include(x => x.Answers).ThenInclude(x => x.Student) 158 .FirstOrDefaultAsync(x => x.Uid == questionUid); 159 } 146 160 } 147 161 } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/StudentRepo.cs
r1e0d869 r6901f8b 1 1 using FinkiChattery.Persistence.Context; 2 2 using FinkiChattery.Persistence.Models; 3 using FinkiChattery.Persistence.Repositories.Contracts; 3 4 using Microsoft.EntityFrameworkCore; 5 using System.Linq; 4 6 using System.Threading.Tasks; 5 7 … … 16 18 return await DbSet.FirstOrDefaultAsync(x => x.ApplicationUserFk == applicationUserFk); 17 19 } 20 21 public async Task<StudentSelfDto> GetStudentSelfDto(long applicationUserFk) 22 { 23 return await DbSet 24 .AsNoTracking() 25 .Include(x => x.Questions) 26 .Include(x => x.StudentTeams).ThenInclude(x => x.Team) 27 .Where(x => x.ApplicationUserFk == applicationUserFk) 28 .Select(x => new StudentSelfDto(x.Uid, 29 x.ApplicationUserFk, 30 x.IndexNumber, 31 x.Reputation, 32 x.ImageUrl, 33 x.Questions.Select(y => new StudentQuestionDto(y.Uid, y.Title)), 34 x.StudentTeams.Select(y => new StudentTeamDto(y.Team.Uid, y.Team.Name)))) 35 .FirstOrDefaultAsync(); 36 } 18 37 } 19 38 } -
src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Contracts/IUnitOfWork.cs
r1e0d869 r6901f8b 11 11 IStudentRepo Students { get; } 12 12 ITeamRepo Teams { get; } 13 IVoteRepo Votes { get; } 14 IAnswerRepo Answers { get; } 15 ITeacherRepo Teachers { get; } 16 IModeratorRepo Moderators { get; } 13 17 Task<int> SaveAsync(); 14 18 } -
src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Implementations/UnitOfWork.cs
r1e0d869 r6901f8b 12 12 private StudentRepo _students; 13 13 private TeamRepo _teams; 14 private VoteRepo _votes; 15 private AnswerRepo _answers; 16 private ModeratorRepo _moderators; 17 private TeacherRepo _teachers; 14 18 15 19 public UnitOfWork(ApplicationDbContext dbContext) 16 20 { 17 21 DbContext = dbContext; 22 } 23 24 public IModeratorRepo Moderators 25 { 26 get 27 { 28 if (_moderators == null) 29 { 30 _moderators = new ModeratorRepo(DbContext); 31 } 32 33 return _moderators; 34 } 35 } 36 37 public ITeacherRepo Teachers 38 { 39 get 40 { 41 if (_teachers == null) 42 { 43 _teachers = new TeacherRepo(DbContext); 44 } 45 46 return _teachers; 47 } 18 48 } 19 49 … … 70 100 } 71 101 102 public IVoteRepo Votes 103 { 104 get 105 { 106 if (_votes == null) 107 { 108 _votes = new VoteRepo(DbContext); 109 } 110 111 return _votes; 112 } 113 } 114 115 public IAnswerRepo Answers 116 { 117 get 118 { 119 if (_answers == null) 120 { 121 _answers = new AnswerRepo(DbContext); 122 } 123 124 return _answers; 125 } 126 } 127 72 128 public ApplicationDbContext DbContext { get; } 73 129
Note:
See TracChangeset
for help on using the changeset viewer.