Add configurable levels

- Add Level table and gql type
- change level field on User
- add hook on user to update the level
pull/4/head
trivernis 5 years ago
parent 781ee274b8
commit 9328367bb5

@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- delete handler for media to delete the corresponding file - delete handler for media to delete the corresponding file
- type for create post to know if it is a media or text post (media posts are invisible until a media file is uploaded) - type for create post to know if it is a media or text post (media posts are invisible until a media file is uploaded)
- reports and mutations to report posts and create reasons to report - reports and mutations to report posts and create reasons to report
- level entity
### Removed ### Removed
@ -51,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- config behaviour to use all files that reside in the ./config directory with the .toml format - config behaviour to use all files that reside in the ./config directory with the .toml format
- default response timeout from 2 minutes to 30 seconds - default response timeout from 2 minutes to 30 seconds
- cluster api to start workers with a 2 second delay each to avoid race conditions - cluster api to start workers with a 2 second delay each to avoid race conditions
- levels to be configured in the backend
### Fixed ### Fixed
@ -60,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- style issues - style issues
- graphql schema for denyRequest using the wrong parameters - graphql schema for denyRequest using the wrong parameters
- sendRequest allowing duplicates - sendRequest allowing duplicates
- upload throwing an error when the old picture doesn't exist
## [0.9] - 2019-10-29 ## [0.9] - 2019-10-29

@ -67,12 +67,12 @@ namespace dataaccess {
models.Media, models.Media,
models.Report, models.Report,
models.ReportReason, models.ReportReason,
models.Level,
]); ]);
} catch (err) { } catch (err) {
globals.logger.error(err.message); globals.logger.error(err.message);
globals.logger.debug(err.stack); globals.logger.debug(err.stack);
} }
await databaseCleanup();
setInterval(databaseCleanup, config.get<number>("database.cleanupInterval") * 1000); setInterval(databaseCleanup, config.get<number>("database.cleanupInterval") * 1000);
} }

@ -0,0 +1,33 @@
import * as sqz from "sequelize";
import {Column, Model, NotNull, Table, Unique} from "sequelize-typescript";
/**
* A level of the ranking system
*/
@Table({underscored: true})
export class Level extends Model<Level> {
/**
* The name of the level
*/
@NotNull
@Unique
@Column({allowNull: false, type: sqz.STRING(64), unique: true})
public name: string;
/**
* The number of the level
*/
@NotNull
@Unique
@Column({allowNull: false, unique: true})
public levelNumber: number;
/**
* The required points for the level
*/
@NotNull
@Unique
@Column({allowNull: false, unique: true})
public points: number;
}

@ -18,7 +18,9 @@ export class Media extends Model<Media> {
*/ */
@BeforeDestroy @BeforeDestroy
public static async deleteMediaFile(instance: Media) { public static async deleteMediaFile(instance: Media) {
await fsx.unlink(instance.path); if (await fsx.pathExists(instance.path)) {
await fsx.unlink(instance.path);
}
} }
/** /**

@ -1,5 +1,6 @@
import * as sqz from "sequelize"; import * as sqz from "sequelize";
import { import {
BeforeUpdate,
BelongsTo, BelongsTo,
BelongsToMany, BelongsToMany,
Column, Column,
@ -24,6 +25,7 @@ import {Friendship} from "./Friendship";
import {Group} from "./Group"; import {Group} from "./Group";
import {GroupAdmin} from "./GroupAdmin"; import {GroupAdmin} from "./GroupAdmin";
import {GroupMember} from "./GroupMember"; import {GroupMember} from "./GroupMember";
import {Level} from "./Level";
import {Media} from "./Media"; import {Media} from "./Media";
import {Post} from "./Post"; import {Post} from "./Post";
import {PostVote} from "./PostVote"; import {PostVote} from "./PostVote";
@ -35,6 +37,19 @@ import {Request, RequestType} from "./Request";
@Table({underscored: true}) @Table({underscored: true})
export class User extends Model<User> { export class User extends Model<User> {
/**
* A function that is called before the user is updated.
* It assigns the corresponding level to the user
* @param instance
*/
@BeforeUpdate
public static async assignLevel(instance: User) {
const level = await Level.findOne({where: {points: {[sqz.Op.lte]: instance.rankpoints}}, order: [["levelNumber", "desc"]]}) as Level;
if (level) {
instance.$set("rLevel", level);
}
}
/** /**
* The name of the user * The name of the user
*/ */
@ -99,6 +114,13 @@ export class User extends Model<User> {
@Column({defaultValue: false, allowNull: false}) @Column({defaultValue: false, allowNull: false})
public isAdmin: boolean; public isAdmin: boolean;
/**
* The level of the user
*/
@ForeignKey(() => Level)
@Column({allowNull: true})
public levelId: number;
/** /**
* The id of the media that is the users profile picture * The id of the media that is the users profile picture
*/ */
@ -106,6 +128,12 @@ export class User extends Model<User> {
@Column({allowNull: true}) @Column({allowNull: true})
public mediaId: number; public mediaId: number;
/**
* The level of the user
*/
@BelongsTo(() => Level)
public rLevel: Level;
/** /**
* The media of the user * The media of the user
*/ */
@ -220,8 +248,8 @@ export class User extends Model<User> {
/** /**
* The level of the user which is the points divided by 100 * The level of the user which is the points divided by 100
*/ */
public get level(): number { public async level(): Promise<Level> {
return Math.ceil(this.getDataValue("rankpoints") / 100); return await this.$get("rLevel") as Level;
} }
/** /**

@ -16,3 +16,4 @@ export {BlacklistedPhrase} from "./BlacklistedPhrase";
export {Media} from "./Media"; export {Media} from "./Media";
export {Report} from "./Report"; export {Report} from "./Report";
export {ReportReason} from "./ReportReason"; export {ReportReason} from "./ReportReason";
export {Level} from "./Level";

@ -189,7 +189,7 @@ interface UserData {
points: Int! points: Int!
"the levels of the user depending on the points" "the levels of the user depending on the points"
level: Int! level: Level
} }
"represents a single user account" "represents a single user account"
@ -240,7 +240,7 @@ type User implements UserData{
eventCount: Int! eventCount: Int!
"the levels of the user depending on the points" "the levels of the user depending on the points"
level: Int! level: Level
} }
type Profile implements UserData { type Profile implements UserData {
@ -311,7 +311,7 @@ type Profile implements UserData {
points: Int! points: Int!
"the levels of the user depending on the points" "the levels of the user depending on the points"
level: Int! level: Level
"the custom settings for the frontend" "the custom settings for the frontend"
settings: String! settings: String!
@ -571,6 +571,16 @@ type ReportReason {
description: String! description: String!
} }
"A level of a user"
type Level {
"The name of the level"
name: String!
"The number of the level in the ranking"
levelNumber: String!
}
"represents the type of media" "represents the type of media"
enum MediaType { enum MediaType {
VIDEO VIDEO

Loading…
Cancel
Save