Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parser service #20

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions src/controller/CourseController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { NextFunction, Request, Response, Router } from "express";
import { check, ValidationError, validationResult } from 'express-validator';
import { courseType } from "../entity/Course";
import { CourseService } from "../services/CourseService";
import CallBack from "../services/FunctionStatusCode";
import Logger from "../services/Logger";
Expand All @@ -24,22 +23,12 @@ export class CourseController {
'/',
[
check('id').exists().withMessage('Field "id" is missing').isAlphanumeric().trim().escape(),
check('name').exists().withMessage('Field "name" is missing').trim().escape(),
check('type')
.exists().withMessage('Field "type" is missing')
.custom((value: String) => {
return (Object.values(courseType) as String[]).includes(value);
}).trim().escape(),
],
this.postOne);
this.router.put(
'/:id',
[
check('id').trim().escape(),
check('name').trim().escape(),
check('type').custom((value: String) => {
return (Object.values(courseType) as String[]).includes(value);
}).trim().escape(),
],
this.putOne);
this.router.delete('/:id', this.deleteOne);
Expand Down
18 changes: 1 addition & 17 deletions src/entity/Course.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { Entity, PrimaryColumn , Column, OneToMany, JoinTable } from "typeorm";
import { Entity, PrimaryColumn, OneToMany, JoinTable } from "typeorm";
import { TimeSlot } from "./TimeSlot";

export enum courseType {
'CS' = 'CS',
'TM' = 'TM',
'TSH' = 'TSH'
}

/**
* Initial class for one course
* @class Course
Expand All @@ -17,16 +11,6 @@ export class Course {
@PrimaryColumn()
id: String = '';

@Column("varchar")
name: String = '';

@Column({
type: "enum",
enum: ["CS" , "TM" , "TSH"],
default: "CS"
})
type: String = courseType.CS;

@OneToMany(() => TimeSlot, timeslots => timeslots.course)
@JoinTable()
timeslots!: TimeSlot[];
Expand Down
22 changes: 14 additions & 8 deletions src/entity/TimeSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,25 @@ export enum dayType {
export class TimeSlotCriteria {
course?: String;
type?: String;
roomNumber?: number;
roomNumber?: String;
startAt?: String;
endAt?: String;
day?: String;
frequency?: String;

// Serialize from object to class
constructor(queryPrams: any) {
if (queryPrams.course !== undefined || queryPrams.course !== null) {this.course = queryPrams.course;}
if (queryPrams.type !== undefined || queryPrams.type !== null) {this.type = queryPrams.type;}
if (queryPrams.roomNumber !== undefined || queryPrams.roomNumber !== null) {
this.roomNumber = queryPrams.roomNumber;
constructor(object: Object) {
if (object['course'] !== undefined || object['course'] !== null) {this.course = object['course'];}
if (object['type'] !== undefined || object['type'] !== null) {this['type'] = object['type'];}
if (object['roomNumber'] !== undefined || object['roomNumber'] !== null) {
this['roomNumber'] = object['roomNumber'];
}
if (object['startAt'] !== undefined || object['startAt'] !== null) {this['startAt'] = object['startAt'];}
if (object['endAt'] !== undefined || object['endAt'] !== null) {this['endAt'] = object['endAt'];}
if (object['day'] !== undefined || object['day'] !== null) {this['day'] = object['day'];}
if (object['frequency'] !== undefined || object['frequency'] !== null) {
this['frequency'] = object['frequency'];
}
if (queryPrams.startAt !== undefined || queryPrams.startAt !== null) {this.startAt = queryPrams.startAt;}
if (queryPrams.endAt !== undefined || queryPrams.endAt !== null) {this.endAt = queryPrams.endAt;}
}
}

Expand Down
124 changes: 68 additions & 56 deletions src/repository/TimeSlotRepository.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,48 @@
import {EntityRepository, Repository} from "typeorm";
import {EntityRepository, Repository, SelectQueryBuilder} from "typeorm";
import {TimeSlot, TimeSlotCriteria, timeSlotType} from "../entity/TimeSlot";

@EntityRepository(TimeSlot)
export class TimeSlotRepository extends Repository<TimeSlot> {

private addCriteria(criteria: TimeSlotCriteria, query: SelectQueryBuilder<TimeSlot>) {
const type = criteria.type;
if (type !== undefined && type !== null) {
query = query.andWhere("timeslots.type = :type", { type });
}

const roomNumber = criteria.roomNumber;
if (roomNumber !== undefined && roomNumber !== null) {
query = query.andWhere("timeslots.roomNumber = :roomNumber", { roomNumber });
}

const startAt = criteria.startAt;
if (startAt !== undefined && startAt !== null) {
query = query.andWhere("timeslots.startAt = :startAt", { startAt });
}

const endAt = criteria.endAt;
if (endAt !== undefined && endAt !== null) {
query = query.andWhere("timeslots.endAt = :endAt", { endAt });
}

const day = criteria.day;
if (day !== undefined && day !== null) {
query = query.andWhere("timeslots.day = :day", { day });
}

const frequency = criteria.frequency;
if (frequency !== undefined && frequency !== null) {
query = query.andWhere("timeslots.frequency = :frequency", { frequency });
}

return query;
}

public findById = (id: number) => {
return this.createQueryBuilder("timeSlots")
.innerJoinAndSelect("timeSlots.course", "course")
.innerJoinAndSelect("timeSlots.users", "user")
.where("timeSlots.id = :id", { id })
return this.createQueryBuilder("timeslots")
.innerJoinAndSelect("timeslots.course", "course")
.innerJoinAndSelect("timeslots.users", "user")
.where("timeslots.id = :id", { id })
.getOne()
}

Expand All @@ -29,79 +63,57 @@ export class TimeSlotRepository extends Repository<TimeSlot> {
}

public findAll = () => {
return this.createQueryBuilder("timeSlots")
.innerJoinAndSelect("timeSlots.course", "course")
return this.createQueryBuilder("timeslots")
.innerJoinAndSelect("timeslots.course", "course")
.getMany()
}

public findAllWithUsers = () => {
return this.createQueryBuilder("timeSlots")
.innerJoinAndSelect("timeSlots.course", "course")
.innerJoinAndSelect("timeSlots.users", "user")
return this.createQueryBuilder("timeslots")
.innerJoinAndSelect("timeslots.course", "course")
.innerJoinAndSelect("timeslots.users", "user")
.getMany()
}

public findByCriteria = (criteria: TimeSlotCriteria) => {
let query = this.createQueryBuilder("timeSlots");

const type = criteria.type;
if (type !== undefined && type !== null) {
query = query.andWhere("timeSlots.type = :type", { type });
}

const roomNumber = criteria.roomNumber;
if (roomNumber !== undefined && roomNumber !== null) {
query = query.andWhere("timeSlots.roomNumber = :roomNumber", { roomNumber });
}

const startAt = criteria.startAt;
if (startAt !== undefined && startAt !== null) {
query = query.andWhere("timeSlots.startAt = :startAt", { startAt });
}

const endAt = criteria.endAt;
if (endAt !== undefined && endAt !== null) {
query = query.andWhere("timeSlots.endAt = :endAt", { endAt });
}
let query = this.addCriteria(criteria, this.createQueryBuilder("timeslots"));

return query
.innerJoinAndSelect("timeSlots.course", "course")
.innerJoinAndSelect("timeslots.course", "course")
.getMany();
}

public findByCriteriaWithUsers = (criteria: TimeSlotCriteria) => {
let query = this.createQueryBuilder("timeSlots");
public findOneByCriteria = (criteria: TimeSlotCriteria) => {
let query = this.addCriteria(criteria, this.createQueryBuilder("timeslots"));

const type = criteria.type;
if (type !== undefined && type !== null) {
query = query.andWhere("timeSlots.type = :type", { type });
}
return query
.innerJoinAndSelect("timeslots.course", "course")
.getOne();
}

const roomNumber = criteria.roomNumber;
if (roomNumber !== undefined && roomNumber !== null) {
query = query.andWhere("timeSlots.roomNumber = :roomNumber", { roomNumber });
}
public findByCriteriaWithUsers = (criteria: TimeSlotCriteria) => {
let query = this.addCriteria(criteria, this.createQueryBuilder("timeslots"));

const startAt = criteria.startAt;
if (startAt !== undefined && startAt !== null) {
query = query.andWhere("timeSlots.startAt = :startAt", { startAt });
}
return query
.innerJoinAndSelect("timeslots.users", "user")
.innerJoinAndSelect("timeslots.course", "course")
.getMany();
}

const endAt = criteria.endAt;
if (endAt !== undefined && endAt !== null) {
query = query.andWhere("timeSlots.endAt = :endAt", { endAt });
}
public findOneByCriteriaWithUsers = (criteria: TimeSlotCriteria) => {
let query = this.addCriteria(criteria, this.createQueryBuilder("timeslots"));

// console.log("query: ", query.innerJoinAndSelect("timeslots.users", "user").innerJoinAndSelect("timeslots.course", "course").getQueryAndParameters())
return query
.innerJoinAndSelect("timeSlots.users", "user")
.innerJoinAndSelect("timeSlots.course", "course")
.getMany();
.innerJoinAndSelect("timeslots.users", "user")
.innerJoinAndSelect("timeslots.course", "course")
.getOne();
}

public findTimeSlotByCourse = (courseId: String) => {
return this.createQueryBuilder("timeSlots")
.innerJoinAndSelect("timeSlots.users", "user")
.innerJoinAndSelect("timeSlots.course", "course", "course.id = :courseId", { courseId })
return this.createQueryBuilder("timeslots")
.innerJoinAndSelect("timeslots.users", "user")
.innerJoinAndSelect("timeslots.course", "course", "course.id = :courseId", { courseId })
// .where("course.id = :id", { courseId })
.getMany();
}
Expand Down
116 changes: 116 additions & 0 deletions src/services/ParserService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { getCustomRepository, getRepository } from "typeorm";
import { frequencyType, TimeSlotCriteria } from "../entity/TimeSlot";
import { User } from "../entity/User";
import { CourseRepository } from "../repository/CourseRepository";
import { TimeSlotRepository } from "../repository/TimeSlotRepository";
import Logger from "./Logger";

const SME_FORMAT_STUDENT_REGEX = /([a-z0-9]{1,10})\s+([A-Z0-9]{3,5})\s+([0-9]{1,2})\s+(([A-Z0-9]{3,5}\s*)+)\$/;
const SME_FORMAT_UV_REGEX = /([A-Z0-9]{3,5})\s+([CDT])\s+([0-9]+)\s+/;
const SME_FORMAT_SLOT_REGEX = /\s*([ABC]*)\s*([A-Z]+)\.*\s*([0-9]{2}:[0-9]{2})-([0-9]{2}:[0-9]{2}),*\s*F([0-9]+),*\s*S=([A-Z0-9]*)\s*(>\(.*semaine ([ABC]) en distanciel\))*\$/;
const ROOM_NUMBER_REGEX = /([a-zA-Z]|\d)*/;
const FREQUENCY_REGEX = /[a-zA-Z\d\s\(\)]*semaine\s([A]|[B])/;
const DAY_TRANSLATION = {
'lundi': 'Monday',
'mardi': 'Tuesday',
'mercredi': 'Wednesday',
'jeudi': 'Thursday',
'vendredi': 'Friday',
'samedi': 'Saturday',
'dimanche': 'Sunday',
}

/**
* Parser Service class
*/
export class ParserService {

private timeSlotRepository: TimeSlotRepository;
private courseRepository: CourseRepository;

constructor(
_timeSlotRepository: TimeSlotRepository = getCustomRepository(TimeSlotRepository),
_courseRepository: CourseRepository = getCustomRepository(CourseRepository)) {
this.timeSlotRepository = _timeSlotRepository;
this.courseRepository = _courseRepository;
}

private fromFrenchDayToEnglishDay = (day: String) => {
return DAY_TRANSLATION[day.toLowerCase()];
}

private getFrequencyFromString = (str: string) => {
const frequencyRegex = String(str).match(FREQUENCY_REGEX);
let frequency = frequencyType.Weekly;
if (frequencyRegex !== null) {
frequency = frequencyType[frequencyRegex[1]];
if (frequency === null || frequency === undefined) {
frequency = frequencyType.Monthly;
}
}
return frequency;
}

public parseSMEMail = async (mail: Object) => {
for (const [key, value] of Object.entries(mail)) {
// TODO: use regex to find values -> convert into entities -> store in DB
}
}

public parseWebapplisResponse = async (object: Array<Object>, user: User) => {
object.forEach(async (value) => {
// Extract values from webbaplis result
const courseName = String(value['uv']).toUpperCase();
const roomNumber = String(value['room']).match(ROOM_NUMBER_REGEX);
const day = this.fromFrenchDayToEnglishDay(value['day']);
const frequency = this.getFrequencyFromString(String(value['group']));

// Check if the course is already created however we create one
let course = await this.courseRepository.findById(courseName);
if (course === undefined) {
Logger.debug("Creating a new course from the parser Service");
course = await this.courseRepository.save({
"id": courseName,
});
}

// Check if the timeslot is created however we create one
const timeSlotCriteria = new TimeSlotCriteria({
"course": courseName,
"type": value['type'],
"roomNumber": roomNumber !== null ? roomNumber[0] : '',
"startAt": value['begin'],
"endAt": value['end'],
"day": day,
"frequency": frequency
});
let timeSlot = await this.timeSlotRepository.findOneByCriteriaWithUsers(timeSlotCriteria);

if (timeSlot !== undefined) {
// TODO: Add only if he is not already present
timeSlot.users.push(user);
try {
await this.timeSlotRepository.save(timeSlot);
} catch (err) {
Logger.error("Error saving timeSlot" + err);
}
} else {
try{
Logger.debug("Creating a new time slot from the parser Service");
await this.timeSlotRepository.save({
"course": course,
"type": value['type'],
"roomNumber": roomNumber !== null ? roomNumber[0] : '',
"startAt": value['begin'],
"endAt": value['end'],
"day": day,
"frequency": frequency,
"users": [user]
});
} catch (err) {
Logger.error("Error creating timeSlot" + err);
}
}
});
}
}
Loading