Changes in / [6901f8b:1e0d869]


Ignore:
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  
    2424  public user: ApplicationUser | null = null;
    2525  public oidcUser: User | null = null;
    26   public selfUser: SelfUserResponse | null = null;
    2726
    2827  constructor(private baseApi: BaseApiService) {
     
    3635        user?.profile.isVerified
    3736      );
    38 
    39       this.selfUserDto().subscribe((selfUser) => {
    40         if (selfUser) {
    41           this.selfUser = selfUser;
    42         }
    43       });
    4437    });
    4538  }
     
    9386        user.profile.isVerified
    9487      );
    95 
    96       if (!this.selfUser) {
    97         this.selfUserDto().subscribe((selfUser) => {
    98           if (selfUser) {
    99             this.selfUser = selfUser;
    100           }
    101         });
    102       }
    10388    });
    10489  }
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-facade.service.ts

    r6901f8b r1e0d869  
    22import { Injectable } from '@angular/core';
    33import { Action, Store } from '@ngrx/store';
    4 import { Observable, Subject } from 'rxjs';
    5 import { filter, map } from 'rxjs/operators';
     4import { Observable, Subject, throwError } from 'rxjs';
     5import { catchError, filter, map } from 'rxjs/operators';
    66
    77import {
     
    99  PreviewQuestionViewModel,
    1010  QuestionStateViewModel,
    11   SearchQuestionsQueryViewModel,
    12   VoteType
     11  SearchQuestionsQueryViewModel
    1312} from 'src/app/shared-app/models';
    14 import { AuthService } from '../services';
    1513import {
    1614  EffectStartedWorking,
     
    1816  GetPreviewQuestionsPopular,
    1917  GetQuestionState,
    20   GetSearchQuestions,
    21   SetCorrectAnswer,
    22   VoteAnswer
     18  GetSearchQuestions
    2319} from './question-state/question.actions';
    2420import { questionStateQuery } from './question-state/question.selectors';
     
    3329  effectWorking$: Observable<boolean | HttpErrorResponse>;
    3430
    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    );
    4145  }
    4246
     
    7781  }
    7882
    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 
    8783  public fetchQuestion(questionUid: string): void {
    8884    this.dispatchEffect(new GetQuestionState(questionUid));
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.actions.ts

    r6901f8b r1e0d869  
    22import { Action } from '@ngrx/store';
    33
    4 import {
    5   PreviewQuestionViewModel,
    6   QuestionStateViewModel,
    7   SearchQuestionsQueryViewModel,
    8   VoteAnswerViewModel,
    9   VoteType
    10 } from 'src/app/shared-app/models';
     4import { PreviewQuestionViewModel, QuestionStateViewModel, SearchQuestionsQueryViewModel } from 'src/app/shared-app/models';
    115
    126export enum QuestionActionTypes {
    137  GetQuestionState = '[Question] Get state',
    148  GetQuestionStateSuccess = '[Question] Get state success',
    15   SetCorrectAnswer = '[Question] Set Correct Answer',
    16   SetCorrectAnswerSuccess = '[Question] Set Correct Answer success',
    179  GetPreviewQuestionsLatest = '[Question] Get preview questions Latest',
    1810  GetPreviewQuestionsLatestSuccess = '[Question] Get preview questions Latest Success',
     
    2113  GetSearchQuestions = '[Question] Get search questions',
    2214  GetSearchQuestionsSuccess = '[Question] Get search questions Success',
    23   VoteAnswer = '[Question] Vote answer',
    24   VoteAnswerSuccess = '[Question] Vote answer Success',
    2515  EffectStartedWorking = '[Question] Effect Started Working',
    2616  EffectFinishedWorking = '[Question] Effect Finished Working',
     
    3828
    3929  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) {}
    5230}
    5331
     
    8866}
    8967
    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 
    10268export class EffectStartedWorking implements Action {
    10369  readonly type = QuestionActionTypes.EffectStartedWorking;
     
    12389  | GetPreviewQuestionsPopularSuccess
    12490  | GetSearchQuestionsSuccess
    125   | VoteAnswerSuccess
    126   | SetCorrectAnswerSuccess
    12791  | EffectStartedWorking
    12892  | EffectFinishedWorking
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.effects.ts

    r6901f8b r1e0d869  
    11import { Injectable } from '@angular/core';
    22import { act, Actions, createEffect, ofType } from '@ngrx/effects';
    3 import { catchError, filter, mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators';
     3import { catchError, filter, mergeMap, switchMap, withLatestFrom } from 'rxjs/operators';
    44import { PreviewQuestionsOrderEnum, SearchQuestionsQueryViewModel } from 'src/app/shared-app/models';
    55import { TranslateFromJsonService } from 'src/app/shared-app/services';
    66
    77import { BaseApiService } from 'src/app/shared-app/services/base-api.service';
    8 import { NotificationService } from '../../services/notification.service';
    98import { QuestionFacadeService } from '../question-facade.service';
    10 import { VoteAnswerRequest } from './question-state-request.models';
    11 import { PreviewQuestionResponse, QuestionStateResponse, VoteAnswerResponse } from './question-state-response.models';
     9import { PreviewQuestionResponse, QuestionStateResponse } from './question-state.models';
    1210import {
    1311  EffectFinishedWorking,
     
    2119  GetSearchQuestions,
    2220  GetSearchQuestionsSuccess,
    23   QuestionActionTypes,
    24   SetCorrectAnswer,
    25   SetCorrectAnswerSuccess,
    26   VoteAnswer,
    27   VoteAnswerSuccess
     21  QuestionActionTypes
    2822} from './question.actions';
    2923import { QuestionMapper } from './question.mapper';
     
    3731    private api: BaseApiService,
    3832    private translate: TranslateFromJsonService,
    39     private facade: QuestionFacadeService,
    40     private notification: NotificationService
     33    private facade: QuestionFacadeService
    4134  ) {}
    4235
     
    110103    );
    111104  });
    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   });
    139105}
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.mapper.ts

    r6901f8b r1e0d869  
    1010  QuestionStateViewModel,
    1111  StudentQuestionStateViewModel,
    12   TeamQuestionStateViewModel,
    13   VoteAnswerViewModel
     12  TeamQuestionStateViewModel
    1413} from 'src/app/shared-app/models';
    1514import { TranslateFromJsonService } from 'src/app/shared-app/services';
    16 import { PreviewQuestionResponse, QuestionStateResponse, VoteAnswerResponse } from './question-state-response.models';
     15import { PreviewQuestionResponse, QuestionStateResponse } from './question-state.models';
    1716
    1817export class QuestionMapper {
     
    5251          x.correctAnswer,
    5352          moment(x.createdOn),
    54           x.votesCount,
     53          x.upvotesCount,
    5554          answerStudent,
    5655          answerResponses
     
    114113    return questions;
    115114  }
    116 
    117   public static ToVoteAnswerViewModel(response: VoteAnswerResponse): VoteAnswerViewModel {
    118     return new VoteAnswerViewModel(response.answerUid, response.voteUid, response.voteType);
    119   }
    120115}
  • 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';
    21import { QuestionAction, QuestionActionTypes } from './question.actions';
    32import { initialState, QuestionState } from './question.state';
     
    2625        searchQuestionsQuery: action.query
    2726      };
    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     }
    9227    case QuestionActionTypes.EffectStartedWorking: {
    9328      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>
    22  <div class="vote-icons" fxLayout="column" fxLayoutAlign="center center">
    33    <mat-icon class="vote-icon" (click)="voted(VoteType.Upvote)" [inline]="true">arrow_drop_up</mat-icon>
     
    77      >check</mat-icon
    88    >
    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     >
    169  </div>
    1710</div>
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.scss

    r6901f8b r1e0d869  
    1414  cursor: pointer;
    1515}
    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  
    11import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
    2 import { VoteType } from 'src/app/shared-app/models';
    32import { ButtonType } from '../button/button.models';
     3
     4export enum VoteType {
     5  Upvote,
     6  Downvote
     7}
    48
    59@Component({
     
    1014export class VoteComponent implements OnInit {
    1115  @Input() voteCount: number | undefined;
    12   @Input() canSetCorrectAnswer: boolean | null = false;
    1316  @Input() correct = false;
    1417  @Output() voteClicked = new EventEmitter<VoteType>();
    15   @Output() setCorrectAnswer = new EventEmitter<void>();
    1618
    1719  VoteType = VoteType;
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.html

    r6901f8b r1e0d869  
    1414      <mat-card-content>
    1515        <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>
    2417          <div fxFlex="92%">
    2518            <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';
    21import { Component, OnInit } from '@angular/core';
    3 import { NotificationService } from 'src/app/core/services/notification.service';
    42import { QuestionFacadeService } from 'src/app/core/state/question-facade.service';
    53
    6 import { QuestionStateViewModel, VoteType } from 'src/app/shared-app/models';
     4import { QuestionStateViewModel } from 'src/app/shared-app/models';
    75import { ButtonType } from '../../generic/button/button.models';
    86
     
    1311})
    1412export class QuestionPreviewComponent implements OnInit {
    15   canSetCorrectAnswer = this.questionFacade.currentQuestionOwnedByCurrentUser();
    1613  question!: QuestionStateViewModel;
    1714  working = true;
    1815  ButtonType = ButtonType;
    19   constructor(private questionFacade: QuestionFacadeService, private notification: NotificationService) {}
     16  constructor(private questionFacade: QuestionFacadeService) {}
    2017
    2118  ngOnInit(): void {
     
    2421      this.working = false;
    2522    });
    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);
    4023  }
    4124}
  • src/Clients/Angular/finki-chattery/src/app/shared-app/models/question-state-enums.models.ts

    r6901f8b r1e0d869  
    33  Popular
    44}
    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

    r6901f8b r1e0d869  
    1 import { VoteType } from '.';
    2 
    31export class QuestionStateViewModel {
    42  constructor(
     
    3432    public correctAnswer: boolean,
    3533    public createdOn: moment.Moment,
    36     public votesCount: number,
     34    public upvotesCount: number,
    3735    public student: AnswerStudentQuestionStateViewModel,
    3836    public answerResponses: AnswerResponseQuestionStateViewModel[]
     
    7573  constructor(public text: string) {}
    7674}
    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  
    99}
    1010
     11export class SelfUserResponse {
     12  uid!: string;
     13}
     14
    1115export enum ApplicationUserType {
    1216  Student = 'Student',
     
    1519  Guest = 'Guest'
    1620}
    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  
    1919
    2020  public getSelfUser(): Observable<SelfUserResponse> {
    21     return this.get<SelfUserResponse>('v1/self');
     21    return this.get<SelfUserResponse>('self');
    2222  }
    2323
  • src/Clients/Angular/finki-chattery/src/assets/translations/en.json

    r6901f8b r1e0d869  
    5353  "ask-question-stepper-ask": "Ask question",
    5454  "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"
    6156}
  • src/FinkiChattery/FinkiChattery.Api/ApplicationServices/Questioning/Mapper/QuestionMapper.cs

    r6901f8b r1e0d869  
    5454                    var answerStudent = new AnswerStudentQuestionStateResponse(x.StudentDto.Id, x.StudentDto.Uid, x.StudentDto.Index, x.StudentDto.ImageUrl, x.StudentDto.Reputation);
    5555
    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);
    5757                });
    5858            }
  • src/FinkiChattery/FinkiChattery.Api/Services/RegisterServices.cs

    r6901f8b r1e0d869  
    11using FinkiChattery.Api.ApplicationServices.Authentication;
    2 using FinkiChattery.Api.ApplicationServices.Questioning.EventHandlers;
    32using FinkiChattery.Api.Services;
    43using FinkiChattery.Commands.Questioning;
     
    3231            services.AddScoped<IEventService, EventService>();
    3332            services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
    34             services.AddMediatR(typeof(AskQuestionCommand), typeof(GetQuestionStateQuery), typeof(UpdateAnswerVotesEventHandler));
     33            services.AddMediatR(typeof(AskQuestionCommand), typeof(GetQuestionStateQuery));
    3534        }
    3635
     
    5554            });
    5655            services.AddHangfireServer();
    57 
    58             services.AddScoped<IBackgroundJobClient>(provider =>
    59             {
    60                 return new BackgroundJobClient(JobStorage.Current);
    61             });
    6256        }
    6357
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/QuestioningErrorCodes.cs

    r6901f8b r1e0d869  
    99        public const string CategoriesDontExist = "CategoriesDontExist";
    1010        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";
    1911    }
    2012}
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/CategoriesUidsExist.cs

    r6901f8b r1e0d869  
    1 using FinkiChattery.Persistence.UnitOfWork;
     1using FinkiChattery.Persistence.Repositories;
     2using FinkiChattery.Persistence.UnitOfWork;
    23using FluentValidation.Validators;
    34using System;
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/QuestioningFluentValidationRules.cs

    r6901f8b r1e0d869  
    1616        }
    1717
    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 
    2318        public static IRuleBuilderOptions<T, IEnumerable<U>> ListNotNull<T, U>(this IRuleBuilder<T, IEnumerable<U>> ruleBuilder)
    2419        {
  • src/FinkiChattery/FinkiChattery.Contracts/Questioning/GetQuestionState/QuestionStateResponse.cs

    r6901f8b r1e0d869  
    8989    public class AnswerQuestionStateResponse
    9090    {
    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)
    9292        {
    9393            Id = id;
     
    9696            CorrectAnswer = correctAnswer;
    9797            CreatedOn = createdOn;
    98             VotesCount = votesCount;
     98            UpvotesCount = upvotesCount;
    9999            StudentResponse = studentResponse;
    100100            AnswerResponsesResponse = answerResponsesResponse;
     
    107107        public bool CorrectAnswer { get; }
    108108        public DateTime CreatedOn { get; }
    109         public long VotesCount { get; }
     109        public long UpvotesCount { get; }
    110110        public AnswerStudentQuestionStateResponse StudentResponse { get; }
    111111        public IEnumerable<AnswerResponseQuestionStateResponse> AnswerResponsesResponse { get; }
  • src/FinkiChattery/FinkiChattery.Database/FinkiChattery.Database.sqlproj

    r6901f8b r1e0d869  
    7575    <Folder Include="dbo\Tables\QuestionCategory" />
    7676    <Folder Include="Snapshots" />
    77     <Folder Include="dbo\Tables\Vote" />
    7877  </ItemGroup>
    7978  <ItemGroup>
     
    8281    <Build Include="dbo\Tables\StudentTeam.sql" />
    8382    <Build Include="dbo\Tables\TeacherTeam.sql" />
     83    <Build Include="dbo\Tables\Upvote.sql" />
    8484    <Build Include="dbo\Tables\User\AspNetRoleClaims.sql" />
    8585    <Build Include="dbo\Tables\User\AspNetRoles.sql" />
     
    100100    <Build Include="dbo\Tables\QuestionCategory\QuestionCategory.sql" />
    101101    <None Include="dbo\Tables\QuestionCategory\QuestionCategory.Debug.Seed.sql" />
    102     <Build Include="dbo\Tables\Vote\Vote.sql" />
    103102  </ItemGroup>
    104103  <ItemGroup>
  • src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Answer/Answer.Debug.Seed.sql

    r6901f8b r1e0d869  
    1212            (3, N'cee193c3-9d36-4ed8-81b2-15eb4ff305f1', N'Answer 3', 2, 1, 1, GETUTCDATE(), 5),
    1313            (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])
    1515    ) AS S
    1616    ON T.[ID] = S.[ID]
     
    2121                   T.[StudentFk] = S.[StudentFk],
    2222                   T.[CorrectAnswer] = S.[CorrectAnswer],
    23                    T.[VotesCount] = S.[VotesCount]
     23                   T.[UpvotesCount] = S.[UpvotesCount]
    2424    WHEN NOT MATCHED THEN
    2525        INSERT
     
    3232            [CorrectAnswer],
    3333            [CreatedOn],
    34             [VotesCount]
     34            [UpvotesCount]
    3535        )
    3636        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]);
    3838    SET IDENTITY_INSERT [dbo].[Answer] OFF
    3939END
  • src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Answer/Answer.sql

    r6901f8b r1e0d869  
    77    [CorrectAnswer] BIT              NOT NULL,
    88    [CreatedOn]     SMALLDATETIME    NOT NULL,
    9     [VotesCount] BIGINT NOT NULL DEFAULT 0,
     9    [UpvotesCount] BIGINT NOT NULL DEFAULT 0,
    1010    CONSTRAINT [PK_Answer] PRIMARY KEY CLUSTERED ([Id] ASC),
    1111    CONSTRAINT [FK_Answer_Question_QuestionFk] FOREIGN KEY ([QuestionFk]) REFERENCES [dbo].[Question] ([Id]),
  • src/FinkiChattery/FinkiChattery.Persistence/Configurations/AnswerConfig.cs

    r6901f8b r1e0d869  
    2222            builder.Property(x => x.CorrectAnswer).HasColumnName(@"CorrectAnswer").HasColumnType("bit").IsRequired();
    2323            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);
    2525
    2626            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  
    2323        public DbSet<TeacherTeam> TeacherTeams { get; set; }
    2424        public DbSet<Team> Teams { get; set; }
    25         public DbSet<Vote> Votes { get; set; }
     25        public DbSet<Upvote> Upvotes { get; set; }
    2626
    2727        protected override void OnModelCreating(ModelBuilder builder)
     
    4242            builder.ApplyConfiguration(new TeacherTeamConfig(schema));
    4343            builder.ApplyConfiguration(new TeamConfig(schema));
    44             builder.ApplyConfiguration(new VoteConfig(schema));
     44            builder.ApplyConfiguration(new UpvoteConfig(schema));
    4545        }
    4646    }
  • src/FinkiChattery/FinkiChattery.Persistence/Models/Answer.cs

    r6901f8b r1e0d869  
    66    public class Answer : BaseEntity
    77    {
    8         public Answer() : base()
    9         {
    10             CreatedOn = DateTime.UtcNow;
    11             VotesCount = 0;
    12             CorrectAnswer = false;
    13         }
    14 
    158        public string Text { get; set; }
    169
     
    2720        public DateTime CreatedOn { get; set; }
    2821
    29         public long VotesCount { get; set; }
     22        public long UpvotesCount { get; set; }
    3023
    31         public virtual ICollection<Vote> Votes { get; set; }
     24        public virtual ICollection<Upvote> Upvotes { get; set; }
    3225
    3326        public virtual ICollection<AnswerResponse> AnswerResponses { get; set; }
  • src/FinkiChattery/FinkiChattery.Persistence/Models/Moderator.cs

    r6901f8b r1e0d869  
    1 namespace FinkiChattery.Persistence.Models
     1using System;
     2using System.Collections.Generic;
     3using System.Linq;
     4using System.Text;
     5using System.Threading.Tasks;
     6
     7namespace FinkiChattery.Persistence.Models
    28{
    39    public class Moderator : BaseEntity
  • src/FinkiChattery/FinkiChattery.Persistence/Models/Student.cs

    r6901f8b r1e0d869  
    1 using System.Collections.Generic;
     1using System;
     2using System.Collections.Generic;
     3using System.Linq;
     4using System.Text;
     5using System.Threading.Tasks;
    26
    37namespace FinkiChattery.Persistence.Models
     
    1822
    1923        public virtual ICollection<Question> Questions { get; set; }
    20 
     24       
    2125        public virtual ICollection<Answer> Answers { get; set; }
    22 
     26       
    2327        public virtual ICollection<StudentTeam> StudentTeams { get; set; }
    2428    }
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IQuestionRepo.cs

    r6901f8b r1e0d869  
    1616     
    1717        Task<List<QuestionPreviewDto>> GetPreviewQuestionsPopular();
    18 
    19         Task<bool> QuestionIsOwnedByStudent(Guid questionUid, long applicationUserId);
    20 
    21         Task<Question> GetQuestionWithAnswersAndStudents(Guid questionUid);
    2218    }
    2319}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IStudentRepo.cs

    r6901f8b r1e0d869  
    11using FinkiChattery.Persistence.Models;
    2 using FinkiChattery.Persistence.Repositories.Contracts;
    32using System.Threading.Tasks;
    43
     
    87    {
    98        public Task<Student> GetStudent(long applicationUserFk);
    10         public Task<StudentSelfDto> GetStudentSelfDto(long applicationUserFk);
    119    }
    1210}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/Question/QuestionStateDto.cs

    r6901f8b r1e0d869  
    8484    public class AnswerQuestionStateDto
    8585    {
    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)
    8787        {
    8888            Id = id;
     
    9191            CorrectAnswer = correctAnswer;
    9292            CreatedOn = createdOn;
    93             VotesCount = votesCount;
     93            UpvotesCount = upvotesCount;
    9494            StudentDto = studentDto;
    9595            AnswerResponsesDto = answerResponsesDto;
     
    101101        public bool CorrectAnswer { get; }
    102102        public DateTime CreatedOn { get; }
    103         public long VotesCount { get; }
     103        public long UpvotesCount { get; }
    104104        public AnswerStudentQuestionStateDto StudentDto { get; }
    105105        public IEnumerable<AnswerResponseQuestionStateDto> AnswerResponsesDto { get; }
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/QuestionRepo.cs

    r6901f8b r1e0d869  
    8080                                                    y.CorrectAnswer,
    8181                                                    y.CreatedOn,
    82                                                     y.VotesCount,
     82                                                    y.UpvotesCount,
    8383                                                    new AnswerStudentQuestionStateDto(
    8484                                                        y.Student.Id,
     
    144144                .ToListAsync();
    145145        }
    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         }
    160146    }
    161147}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/StudentRepo.cs

    r6901f8b r1e0d869  
    11using FinkiChattery.Persistence.Context;
    22using FinkiChattery.Persistence.Models;
    3 using FinkiChattery.Persistence.Repositories.Contracts;
    43using Microsoft.EntityFrameworkCore;
    5 using System.Linq;
    64using System.Threading.Tasks;
    75
     
    1816            return await DbSet.FirstOrDefaultAsync(x => x.ApplicationUserFk == applicationUserFk);
    1917        }
    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         }
    3718    }
    3819}
  • src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Contracts/IUnitOfWork.cs

    r6901f8b r1e0d869  
    1111        IStudentRepo Students { get; }
    1212        ITeamRepo Teams { get; }
    13         IVoteRepo Votes { get; }
    14         IAnswerRepo Answers { get; }
    15         ITeacherRepo Teachers { get; }
    16         IModeratorRepo Moderators { get; }
    1713        Task<int> SaveAsync();
    1814    }
  • src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Implementations/UnitOfWork.cs

    r6901f8b r1e0d869  
    1212        private StudentRepo _students;
    1313        private TeamRepo _teams;
    14         private VoteRepo _votes;
    15         private AnswerRepo _answers;
    16         private ModeratorRepo _moderators;
    17         private TeacherRepo _teachers;
    1814
    1915        public UnitOfWork(ApplicationDbContext dbContext)
    2016        {
    2117            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             }
    4818        }
    4919
     
    10070        }
    10171
    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 
    12872        public ApplicationDbContext DbContext { get; }
    12973
Note: See TracChangeset for help on using the changeset viewer.