Changes in / [6901f8b:1e0d869]
- Location:
- src
- Files:
-
- 4 added
- 52 deleted
- 37 edited
Legend:
- Unmodified
- Added
- Removed
-
src/Clients/Angular/finki-chattery/src/app/core/services/auth.service.ts
r6901f8b r1e0d869 24 24 public user: ApplicationUser | null = null; 25 25 public oidcUser: User | null = null; 26 public selfUser: SelfUserResponse | null = null;27 26 28 27 constructor(private baseApi: BaseApiService) { … … 36 35 user?.profile.isVerified 37 36 ); 38 39 this.selfUserDto().subscribe((selfUser) => {40 if (selfUser) {41 this.selfUser = selfUser;42 }43 });44 37 }); 45 38 } … … 93 86 user.profile.isVerified 94 87 ); 95 96 if (!this.selfUser) {97 this.selfUserDto().subscribe((selfUser) => {98 if (selfUser) {99 this.selfUser = selfUser;100 }101 });102 }103 88 }); 104 89 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-facade.service.ts
r6901f8b r1e0d869 2 2 import { Injectable } from '@angular/core'; 3 3 import { Action, Store } from '@ngrx/store'; 4 import { Observable, Subject } from 'rxjs';5 import { filter, map } from 'rxjs/operators';4 import { Observable, Subject, throwError } from 'rxjs'; 5 import { catchError, filter, map } from 'rxjs/operators'; 6 6 7 7 import { … … 9 9 PreviewQuestionViewModel, 10 10 QuestionStateViewModel, 11 SearchQuestionsQueryViewModel, 12 VoteType 11 SearchQuestionsQueryViewModel 13 12 } from 'src/app/shared-app/models'; 14 import { AuthService } from '../services';15 13 import { 16 14 EffectStartedWorking, … … 18 16 GetPreviewQuestionsPopular, 19 17 GetQuestionState, 20 GetSearchQuestions, 21 SetCorrectAnswer, 22 VoteAnswer 18 GetSearchQuestions 23 19 } from './question-state/question.actions'; 24 20 import { questionStateQuery } from './question-state/question.selectors'; … … 33 29 effectWorking$: Observable<boolean | HttpErrorResponse>; 34 30 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)); 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 ); 41 45 } 42 46 … … 77 81 } 78 82 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 87 83 public fetchQuestion(questionUid: string): void { 88 84 this.dispatchEffect(new GetQuestionState(questionUid)); -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.actions.ts
r6901f8b r1e0d869 2 2 import { Action } from '@ngrx/store'; 3 3 4 import { 5 PreviewQuestionViewModel, 6 QuestionStateViewModel, 7 SearchQuestionsQueryViewModel, 8 VoteAnswerViewModel, 9 VoteType 10 } from 'src/app/shared-app/models'; 4 import { PreviewQuestionViewModel, QuestionStateViewModel, SearchQuestionsQueryViewModel } from 'src/app/shared-app/models'; 11 5 12 6 export enum QuestionActionTypes { 13 7 GetQuestionState = '[Question] Get state', 14 8 GetQuestionStateSuccess = '[Question] Get state success', 15 SetCorrectAnswer = '[Question] Set Correct Answer',16 SetCorrectAnswerSuccess = '[Question] Set Correct Answer success',17 9 GetPreviewQuestionsLatest = '[Question] Get preview questions Latest', 18 10 GetPreviewQuestionsLatestSuccess = '[Question] Get preview questions Latest Success', … … 21 13 GetSearchQuestions = '[Question] Get search questions', 22 14 GetSearchQuestionsSuccess = '[Question] Get search questions Success', 23 VoteAnswer = '[Question] Vote answer',24 VoteAnswerSuccess = '[Question] Vote answer Success',25 15 EffectStartedWorking = '[Question] Effect Started Working', 26 16 EffectFinishedWorking = '[Question] Effect Finished Working', … … 38 28 39 29 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) {}52 30 } 53 31 … … 88 66 } 89 67 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 102 68 export class EffectStartedWorking implements Action { 103 69 readonly type = QuestionActionTypes.EffectStartedWorking; … … 123 89 | GetPreviewQuestionsPopularSuccess 124 90 | GetSearchQuestionsSuccess 125 | VoteAnswerSuccess126 | SetCorrectAnswerSuccess127 91 | EffectStartedWorking 128 92 | EffectFinishedWorking -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.effects.ts
r6901f8b r1e0d869 1 1 import { Injectable } from '@angular/core'; 2 2 import { act, Actions, createEffect, ofType } from '@ngrx/effects'; 3 import { catchError, filter, mergeMap, switchMap, tap,withLatestFrom } from 'rxjs/operators';3 import { catchError, filter, mergeMap, switchMap, 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';9 8 import { QuestionFacadeService } from '../question-facade.service'; 10 import { VoteAnswerRequest } from './question-state-request.models'; 11 import { PreviewQuestionResponse, QuestionStateResponse, VoteAnswerResponse } from './question-state-response.models'; 9 import { PreviewQuestionResponse, QuestionStateResponse } from './question-state.models'; 12 10 import { 13 11 EffectFinishedWorking, … … 21 19 GetSearchQuestions, 22 20 GetSearchQuestionsSuccess, 23 QuestionActionTypes, 24 SetCorrectAnswer, 25 SetCorrectAnswerSuccess, 26 VoteAnswer, 27 VoteAnswerSuccess 21 QuestionActionTypes 28 22 } from './question.actions'; 29 23 import { QuestionMapper } from './question.mapper'; … … 37 31 private api: BaseApiService, 38 32 private translate: TranslateFromJsonService, 39 private facade: QuestionFacadeService, 40 private notification: NotificationService 33 private facade: QuestionFacadeService 41 34 ) {} 42 35 … … 110 103 ); 111 104 }); 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 });139 105 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.mapper.ts
r6901f8b r1e0d869 10 10 QuestionStateViewModel, 11 11 StudentQuestionStateViewModel, 12 TeamQuestionStateViewModel, 13 VoteAnswerViewModel 12 TeamQuestionStateViewModel 14 13 } from 'src/app/shared-app/models'; 15 14 import { TranslateFromJsonService } from 'src/app/shared-app/services'; 16 import { PreviewQuestionResponse, QuestionStateResponse , VoteAnswerResponse } from './question-state-response.models';15 import { PreviewQuestionResponse, QuestionStateResponse } from './question-state.models'; 17 16 18 17 export class QuestionMapper { … … 52 51 x.correctAnswer, 53 52 moment(x.createdOn), 54 x. votesCount,53 x.upvotesCount, 55 54 answerStudent, 56 55 answerResponses … … 114 113 return questions; 115 114 } 116 117 public static ToVoteAnswerViewModel(response: VoteAnswerResponse): VoteAnswerViewModel {118 return new VoteAnswerViewModel(response.answerUid, response.voteUid, response.voteType);119 }120 115 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.reducers.ts
r6901f8b r1e0d869 1 import { AnswerQuestionStateViewModel, VoteType } from 'src/app/shared-app/models';2 1 import { QuestionAction, QuestionActionTypes } from './question.actions'; 3 2 import { initialState, QuestionState } from './question.state'; … … 26 25 searchQuestionsQuery: action.query 27 26 }; 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: votesCountNew50 };51 }52 53 return x;54 })55 }56 };57 }58 59 return {60 ...state61 };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: false74 };75 }76 if (x.uid === action.payload) {77 return {78 ...x,79 correctAnswer: true80 };81 }82 return x;83 })84 }85 };86 }87 88 return {89 ...state90 };91 }92 27 case QuestionActionTypes.EffectStartedWorking: { 93 28 return { -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.html
r6901f8b r1e0d869 1 <div class="main-div">1 <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-icon10 *ngIf="canSetCorrectAnswer && !correct"11 [inline]="true"12 class="text-center text-bold green set-correct-answer"13 (click)="setCorrectAnswer.emit()"14 >check</mat-icon15 >16 9 </div> 17 10 </div> -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.scss
r6901f8b r1e0d869 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
r6901f8b r1e0d869 1 1 import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; 2 import { VoteType } from 'src/app/shared-app/models';3 2 import { ButtonType } from '../button/button.models'; 3 4 export enum VoteType { 5 Upvote, 6 Downvote 7 } 4 8 5 9 @Component({ … … 10 14 export class VoteComponent implements OnInit { 11 15 @Input() voteCount: number | undefined; 12 @Input() canSetCorrectAnswer: boolean | null = false;13 16 @Input() correct = false; 14 17 @Output() voteClicked = new EventEmitter<VoteType>(); 15 @Output() setCorrectAnswer = new EventEmitter<void>();16 18 17 19 VoteType = VoteType; -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.html
r6901f8b r1e0d869 14 14 <mat-card-content> 15 15 <div fxLayout="row wrap" fxLayoutAlign="space-around none"> 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> 16 <app-vote [voteCount]="answer.upvotesCount" [correct]="answer.correctAnswer" fxFlex="6%"></app-vote> 24 17 <div fxFlex="92%"> 25 18 <app-text-editor -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.ts
r6901f8b r1e0d869 1 import { HttpErrorResponse } from '@angular/common/http';2 1 import { Component, OnInit } from '@angular/core'; 3 import { NotificationService } from 'src/app/core/services/notification.service';4 2 import { QuestionFacadeService } from 'src/app/core/state/question-facade.service'; 5 3 6 import { QuestionStateViewModel , VoteType} from 'src/app/shared-app/models';4 import { QuestionStateViewModel } from 'src/app/shared-app/models'; 7 5 import { ButtonType } from '../../generic/button/button.models'; 8 6 … … 13 11 }) 14 12 export class QuestionPreviewComponent implements OnInit { 15 canSetCorrectAnswer = this.questionFacade.currentQuestionOwnedByCurrentUser();16 13 question!: QuestionStateViewModel; 17 14 working = true; 18 15 ButtonType = ButtonType; 19 constructor(private questionFacade: QuestionFacadeService , private notification: NotificationService) {}16 constructor(private questionFacade: QuestionFacadeService) {} 20 17 21 18 ngOnInit(): void { … … 24 21 this.working = false; 25 22 }); 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);40 23 } 41 24 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/models/question-state-enums.models.ts
r6901f8b r1e0d869 3 3 Popular 4 4 } 5 6 export enum VoteType {7 Upvote,8 Downvote9 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/models/question-state-view-models.models.ts
r6901f8b r1e0d869 1 import { VoteType } from '.';2 3 1 export class QuestionStateViewModel { 4 2 constructor( … … 34 32 public correctAnswer: boolean, 35 33 public createdOn: moment.Moment, 36 public votesCount: number,34 public upvotesCount: number, 37 35 public student: AnswerStudentQuestionStateViewModel, 38 36 public answerResponses: AnswerResponseQuestionStateViewModel[] … … 75 73 constructor(public text: string) {} 76 74 } 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
r6901f8b r1e0d869 9 9 } 10 10 11 export class SelfUserResponse { 12 uid!: string; 13 } 14 11 15 export enum ApplicationUserType { 12 16 Student = 'Student', … … 15 19 Guest = 'Guest' 16 20 } 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
r6901f8b r1e0d869 19 19 20 20 public getSelfUser(): Observable<SelfUserResponse> { 21 return this.get<SelfUserResponse>(' v1/self');21 return this.get<SelfUserResponse>('self'); 22 22 } 23 23 -
src/Clients/Angular/finki-chattery/src/assets/translations/en.json
r6901f8b r1e0d869 53 53 "ask-question-stepper-ask": "Ask question", 54 54 "ask-question-ask-button-back": "Edit 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" 55 "ask-question-ask-button": "Ask question" 61 56 } -
src/FinkiChattery/FinkiChattery.Api/ApplicationServices/Questioning/Mapper/QuestionMapper.cs
r6901f8b r1e0d869 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. VotesCount, answerStudent, answerResponses);56 return new AnswerQuestionStateResponse(x.Id, x.Uid, x.Text, x.CorrectAnswer, x.CreatedOn, x.UpvotesCount, answerStudent, answerResponses); 57 57 }); 58 58 } -
src/FinkiChattery/FinkiChattery.Api/Services/RegisterServices.cs
r6901f8b r1e0d869 1 1 using FinkiChattery.Api.ApplicationServices.Authentication; 2 using FinkiChattery.Api.ApplicationServices.Questioning.EventHandlers;3 2 using FinkiChattery.Api.Services; 4 3 using FinkiChattery.Commands.Questioning; … … 32 31 services.AddScoped<IEventService, EventService>(); 33 32 services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); 34 services.AddMediatR(typeof(AskQuestionCommand), typeof(GetQuestionStateQuery) , typeof(UpdateAnswerVotesEventHandler));33 services.AddMediatR(typeof(AskQuestionCommand), typeof(GetQuestionStateQuery)); 35 34 } 36 35 … … 55 54 }); 56 55 services.AddHangfireServer(); 57 58 services.AddScoped<IBackgroundJobClient>(provider =>59 {60 return new BackgroundJobClient(JobStorage.Current);61 });62 56 } 63 57 -
src/FinkiChattery/FinkiChattery.Commands/Questioning/QuestioningErrorCodes.cs
r6901f8b r1e0d869 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";19 11 } 20 12 } -
src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/CategoriesUidsExist.cs
r6901f8b r1e0d869 1 using FinkiChattery.Persistence.UnitOfWork; 1 using FinkiChattery.Persistence.Repositories; 2 using FinkiChattery.Persistence.UnitOfWork; 2 3 using FluentValidation.Validators; 3 4 using System; -
src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/QuestioningFluentValidationRules.cs
r6901f8b r1e0d869 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 23 18 public static IRuleBuilderOptions<T, IEnumerable<U>> ListNotNull<T, U>(this IRuleBuilder<T, IEnumerable<U>> ruleBuilder) 24 19 { -
src/FinkiChattery/FinkiChattery.Contracts/Questioning/GetQuestionState/QuestionStateResponse.cs
r6901f8b r1e0d869 89 89 public class AnswerQuestionStateResponse 90 90 { 91 public AnswerQuestionStateResponse(long id, Guid uid, string text, bool correctAnswer, DateTime createdOn, long votesCount, AnswerStudentQuestionStateResponse studentResponse, IEnumerable<AnswerResponseQuestionStateResponse> answerResponsesResponse)91 public AnswerQuestionStateResponse(long id, Guid uid, string text, bool correctAnswer, DateTime createdOn, long upvotesCount, AnswerStudentQuestionStateResponse studentResponse, IEnumerable<AnswerResponseQuestionStateResponse> answerResponsesResponse) 92 92 { 93 93 Id = id; … … 96 96 CorrectAnswer = correctAnswer; 97 97 CreatedOn = createdOn; 98 VotesCount =votesCount;98 UpvotesCount = upvotesCount; 99 99 StudentResponse = studentResponse; 100 100 AnswerResponsesResponse = answerResponsesResponse; … … 107 107 public bool CorrectAnswer { get; } 108 108 public DateTime CreatedOn { get; } 109 public long VotesCount { get; }109 public long UpvotesCount { get; } 110 110 public AnswerStudentQuestionStateResponse StudentResponse { get; } 111 111 public IEnumerable<AnswerResponseQuestionStateResponse> AnswerResponsesResponse { get; } -
src/FinkiChattery/FinkiChattery.Database/FinkiChattery.Database.sqlproj
r6901f8b r1e0d869 75 75 <Folder Include="dbo\Tables\QuestionCategory" /> 76 76 <Folder Include="Snapshots" /> 77 <Folder Include="dbo\Tables\Vote" />78 77 </ItemGroup> 79 78 <ItemGroup> … … 82 81 <Build Include="dbo\Tables\StudentTeam.sql" /> 83 82 <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" />103 102 </ItemGroup> 104 103 <ItemGroup> -
src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Answer/Answer.Debug.Seed.sql
r6901f8b r1e0d869 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], [ VotesCount])14 ) AS temp ([Id], [Uid], [Text], [QuestionFk], [StudentFk], [CorrectAnswer], [CreatedOn], [UpvotesCount]) 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.[ VotesCount] = S.[VotesCount]23 T.[UpvotesCount] = S.[UpvotesCount] 24 24 WHEN NOT MATCHED THEN 25 25 INSERT … … 32 32 [CorrectAnswer], 33 33 [CreatedOn], 34 [ VotesCount]34 [UpvotesCount] 35 35 ) 36 36 VALUES 37 (S.[Id], S.[Uid], S.[Text], S.[QuestionFk], S.[StudentFk], S.[CorrectAnswer], S.[CreatedOn], S.[ VotesCount]);37 (S.[Id], S.[Uid], S.[Text], S.[QuestionFk], S.[StudentFk], S.[CorrectAnswer], S.[CreatedOn], S.[UpvotesCount]); 38 38 SET IDENTITY_INSERT [dbo].[Answer] OFF 39 39 END -
src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Answer/Answer.sql
r6901f8b r1e0d869 7 7 [CorrectAnswer] BIT NOT NULL, 8 8 [CreatedOn] SMALLDATETIME NOT NULL, 9 [ VotesCount] BIGINT NOT NULL DEFAULT 0,9 [UpvotesCount] 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
r6901f8b r1e0d869 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. VotesCount).HasColumnName(@"VotesCount").HasColumnType("bigint").IsRequired().HasDefaultValue(0);24 builder.Property(x => x.UpvotesCount).HasColumnName(@"UpvotesCount").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
r6901f8b r1e0d869 23 23 public DbSet<TeacherTeam> TeacherTeams { get; set; } 24 24 public DbSet<Team> Teams { get; set; } 25 public DbSet< Vote> Votes { get; set; }25 public DbSet<Upvote> Upvotes { 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 VoteConfig(schema));44 builder.ApplyConfiguration(new UpvoteConfig(schema)); 45 45 } 46 46 } -
src/FinkiChattery/FinkiChattery.Persistence/Models/Answer.cs
r6901f8b r1e0d869 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 15 8 public string Text { get; set; } 16 9 … … 27 20 public DateTime CreatedOn { get; set; } 28 21 29 public long VotesCount { get; set; }22 public long UpvotesCount { get; set; } 30 23 31 public virtual ICollection< Vote> Votes { get; set; }24 public virtual ICollection<Upvote> Upvotes { get; set; } 32 25 33 26 public virtual ICollection<AnswerResponse> AnswerResponses { get; set; } -
src/FinkiChattery/FinkiChattery.Persistence/Models/Moderator.cs
r6901f8b r1e0d869 1 namespace FinkiChattery.Persistence.Models 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 2 8 { 3 9 public class Moderator : BaseEntity -
src/FinkiChattery/FinkiChattery.Persistence/Models/Student.cs
r6901f8b r1e0d869 1 using System.Collections.Generic; 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 2 6 3 7 namespace FinkiChattery.Persistence.Models … … 18 22 19 23 public virtual ICollection<Question> Questions { get; set; } 20 24 21 25 public virtual ICollection<Answer> Answers { get; set; } 22 26 23 27 public virtual ICollection<StudentTeam> StudentTeams { get; set; } 24 28 } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IQuestionRepo.cs
r6901f8b r1e0d869 16 16 17 17 Task<List<QuestionPreviewDto>> GetPreviewQuestionsPopular(); 18 19 Task<bool> QuestionIsOwnedByStudent(Guid questionUid, long applicationUserId);20 21 Task<Question> GetQuestionWithAnswersAndStudents(Guid questionUid);22 18 } 23 19 } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IStudentRepo.cs
r6901f8b r1e0d869 1 1 using FinkiChattery.Persistence.Models; 2 using FinkiChattery.Persistence.Repositories.Contracts;3 2 using System.Threading.Tasks; 4 3 … … 8 7 { 9 8 public Task<Student> GetStudent(long applicationUserFk); 10 public Task<StudentSelfDto> GetStudentSelfDto(long applicationUserFk);11 9 } 12 10 } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/Question/QuestionStateDto.cs
r6901f8b r1e0d869 84 84 public class AnswerQuestionStateDto 85 85 { 86 public AnswerQuestionStateDto(long id, Guid uid, string text, bool correctAnswer, DateTime createdOn, long votesCount, AnswerStudentQuestionStateDto studentDto, IEnumerable<AnswerResponseQuestionStateDto> answerResponsesDto)86 public AnswerQuestionStateDto(long id, Guid uid, string text, bool correctAnswer, DateTime createdOn, long upvotesCount, AnswerStudentQuestionStateDto studentDto, IEnumerable<AnswerResponseQuestionStateDto> answerResponsesDto) 87 87 { 88 88 Id = id; … … 91 91 CorrectAnswer = correctAnswer; 92 92 CreatedOn = createdOn; 93 VotesCount =votesCount;93 UpvotesCount = upvotesCount; 94 94 StudentDto = studentDto; 95 95 AnswerResponsesDto = answerResponsesDto; … … 101 101 public bool CorrectAnswer { get; } 102 102 public DateTime CreatedOn { get; } 103 public long VotesCount { get; }103 public long UpvotesCount { get; } 104 104 public AnswerStudentQuestionStateDto StudentDto { get; } 105 105 public IEnumerable<AnswerResponseQuestionStateDto> AnswerResponsesDto { get; } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/QuestionRepo.cs
r6901f8b r1e0d869 80 80 y.CorrectAnswer, 81 81 y.CreatedOn, 82 y. VotesCount,82 y.UpvotesCount, 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 DbSet150 .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 DbSet157 .Include(x => x.Answers).ThenInclude(x => x.Student)158 .FirstOrDefaultAsync(x => x.Uid == questionUid);159 }160 146 } 161 147 } -
src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/StudentRepo.cs
r6901f8b r1e0d869 1 1 using FinkiChattery.Persistence.Context; 2 2 using FinkiChattery.Persistence.Models; 3 using FinkiChattery.Persistence.Repositories.Contracts;4 3 using Microsoft.EntityFrameworkCore; 5 using System.Linq;6 4 using System.Threading.Tasks; 7 5 … … 18 16 return await DbSet.FirstOrDefaultAsync(x => x.ApplicationUserFk == applicationUserFk); 19 17 } 20 21 public async Task<StudentSelfDto> GetStudentSelfDto(long applicationUserFk)22 {23 return await DbSet24 .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 }37 18 } 38 19 } -
src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Contracts/IUnitOfWork.cs
r6901f8b r1e0d869 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; }17 13 Task<int> SaveAsync(); 18 14 } -
src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Implementations/UnitOfWork.cs
r6901f8b r1e0d869 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;18 14 19 15 public UnitOfWork(ApplicationDbContext dbContext) 20 16 { 21 17 DbContext = dbContext; 22 }23 24 public IModeratorRepo Moderators25 {26 get27 {28 if (_moderators == null)29 {30 _moderators = new ModeratorRepo(DbContext);31 }32 33 return _moderators;34 }35 }36 37 public ITeacherRepo Teachers38 {39 get40 {41 if (_teachers == null)42 {43 _teachers = new TeacherRepo(DbContext);44 }45 46 return _teachers;47 }48 18 } 49 19 … … 100 70 } 101 71 102 public IVoteRepo Votes103 {104 get105 {106 if (_votes == null)107 {108 _votes = new VoteRepo(DbContext);109 }110 111 return _votes;112 }113 }114 115 public IAnswerRepo Answers116 {117 get118 {119 if (_answers == null)120 {121 _answers = new AnswerRepo(DbContext);122 }123 124 return _answers;125 }126 }127 128 72 public ApplicationDbContext DbContext { get; } 129 73
Note:
See TracChangeset
for help on using the changeset viewer.