|
@@ -1,439 +0,0 @@
|
|
|
-import fs from 'fs'
|
|
|
-import awaitFs from 'await-fs'
|
|
|
-import express from 'express'
|
|
|
-import bhttp from 'bhttp'
|
|
|
-import Excel from '../excel'
|
|
|
-import Config from '../models/config'
|
|
|
-import Player from '../models/player'
|
|
|
-import Match from '../models/match'
|
|
|
-import PlayerList from '../models/playerList'
|
|
|
-import MatchList from '../models/matchList'
|
|
|
-import moment from 'moment'
|
|
|
-import { normalize, normalizePhone } from '../helpers'
|
|
|
-
|
|
|
-// Create the router
|
|
|
-const swisstennis = express.Router()
|
|
|
-
|
|
|
-// Use this variable for the session
|
|
|
-const session = bhttp.session()
|
|
|
-
|
|
|
-// Try to fetch config data for swisstennis login
|
|
|
-const config = {}
|
|
|
-Config.find({ key: { $in: ['tournament', 'tournamentId', 'tournamentPW'] } }).exec((error, results) => {
|
|
|
- if (error) {
|
|
|
- console.log(error.toString())
|
|
|
- } else {
|
|
|
- results.forEach(result => {
|
|
|
- config[result.key] = result.value
|
|
|
- })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-async function checkLogin () {
|
|
|
- const myTournamentsPage = await session.get('https://comp.swisstennis.ch/advantage/servlet/MyTournamentList?Lang=D')
|
|
|
- const strMyTournamentsPage = myTournamentsPage.body.toString()
|
|
|
- return strMyTournamentsPage.includes('Login-Zone') ? null : strMyTournamentsPage
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-* Define the Routes
|
|
|
-*/
|
|
|
-
|
|
|
-// Login
|
|
|
-swisstennis.post('/login', async (req, res) => {
|
|
|
- try {
|
|
|
- const currentState = await checkLogin()
|
|
|
- if (currentState) {
|
|
|
- return res.json({ msg: 'Already logged in!' })
|
|
|
- }
|
|
|
-
|
|
|
- const username = req.body.username || config.tournament
|
|
|
- const password = req.body.pasword || config.tournamentPW
|
|
|
-
|
|
|
- // return, if username or password are missing
|
|
|
- if (!username || !password) {
|
|
|
- throw Error('Parameters username and password are required')
|
|
|
- }
|
|
|
-
|
|
|
- // assemble the login data
|
|
|
- const loginData = {
|
|
|
- Lang: 'D',
|
|
|
- id: username,
|
|
|
- pwd: password,
|
|
|
- Tournament: ''
|
|
|
- }
|
|
|
- const loginPage = await session.post('https://comp.swisstennis.ch/advantage/servlet/Login', loginData)
|
|
|
- const strLoginPage = loginPage.body.toString()
|
|
|
- if (strLoginPage.includes('Zugriff verweigert')) {
|
|
|
- throw Error('Access denied!')
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
- res.json({ msg: 'Logged in successfully.' })
|
|
|
-})
|
|
|
-
|
|
|
-// Overview of tournaments
|
|
|
-swisstennis.get('/tournaments', async (req, res) => {
|
|
|
- try {
|
|
|
- const tournaments = {}
|
|
|
- let match
|
|
|
- const myTournamentsPage = await checkLogin()
|
|
|
- if (!myTournamentsPage) throw Error('Not logged in!')
|
|
|
-
|
|
|
- const tournamentRegexp = /<a href=".*ProtectedDisplayTournament.*tournament=Id(\d+)">([^<]+)<\/a>/gm
|
|
|
-
|
|
|
- do {
|
|
|
- match = tournamentRegexp.exec(myTournamentsPage)
|
|
|
- if (match) {
|
|
|
- tournaments[match[1]] = match[2]
|
|
|
- }
|
|
|
- } while (match)
|
|
|
- res.json({ tournaments })
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-// Draws of a tournament
|
|
|
-swisstennis.get('/draws', async (req, res) => {
|
|
|
- try {
|
|
|
- const tournament = req.query.tournament || config.tournamentId
|
|
|
-
|
|
|
- if (!tournament) {
|
|
|
- throw Error('No tournament given.')
|
|
|
- }
|
|
|
- let match
|
|
|
- const draws = {}
|
|
|
- const tournamentPage = await session.get(`https://comp.swisstennis.ch/advantage/servlet/ProtectedDisplayTournament?Lang=D&tournament=Id${tournament}`)
|
|
|
- const strTournamentPage = tournamentPage.body.toString()
|
|
|
- if (strTournamentPage.includes('Login-Zone')) {
|
|
|
- throw Error('Not logged in.')
|
|
|
- }
|
|
|
- const drawRegexp = /<a (?:class="text" )?href=".*DisplayEvent.*eventId=(\d+).*">([^<]+)<\/a>/gm
|
|
|
-
|
|
|
- do {
|
|
|
- match = drawRegexp.exec(strTournamentPage)
|
|
|
- if (match) {
|
|
|
- draws[match[1]] = match[2]
|
|
|
- }
|
|
|
- } while (match)
|
|
|
- return res.json({ draws })
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-swisstennis.get('/playerlists', async (req, res) => {
|
|
|
- try {
|
|
|
- const playerLists = await PlayerList.find().select({_id: 1, imported: 1, file: 1, filesize: 1})
|
|
|
- return res.json(playerLists)
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-swisstennis.get('/playerlist', async (req, res) => {
|
|
|
- try {
|
|
|
- const key = req.query.playerlistId ? { _id: req.query.playerlistId } : {}
|
|
|
- const playerList = await PlayerList.findOne(key).sort({ imported: -1 }).populate('players')
|
|
|
- return res.json(playerList)
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-// Download a playerlist
|
|
|
-swisstennis.get('/playerlist/download', async (req, res) => {
|
|
|
- try {
|
|
|
- const tournament = req.query.tournament || config.tournamentId
|
|
|
-
|
|
|
- if (!tournament) {
|
|
|
- throw Error('No tournament given.')
|
|
|
- }
|
|
|
- const filename = `PlayerList-${tournament}-${moment().format('YYYYMMDDTHHmmss')}.xls`
|
|
|
- const playerListFile = fs.createWriteStream(`swisstennis_files/${filename}`)
|
|
|
- const playerList = await session.get(`https://comp.swisstennis.ch/advantage/servlet/PlayerList.xls?tournament=Id${tournament}&lang=D`, {stream: true})
|
|
|
- playerList.pipe(playerListFile)
|
|
|
- const streamDone = new Promise((resolve, reject) => {
|
|
|
- playerListFile.on('finish', resolve)
|
|
|
- playerListFile.on('error', reject)
|
|
|
- })
|
|
|
- await streamDone
|
|
|
- const stats = fs.statSync(`swisstennis_files/${filename}`)
|
|
|
- return res.json({ filename, size: stats.size, mtime: stats.mtime })
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-swisstennis.get('/playerlist/parse/:filename', async (req, res) => {
|
|
|
- try {
|
|
|
- console.log('Parsing file', req.params.filename)
|
|
|
-
|
|
|
- const filePath = `swisstennis_files/${req.params.filename}`
|
|
|
- const dateString = req.params.filename.match(/(\d{4}\d{2}\d{2}T\d{2}\d{2}\d{2})/)[1]
|
|
|
- const fileDate = moment(dateString)
|
|
|
- const stat = fs.statSync(filePath)
|
|
|
-
|
|
|
- const fileNewer = await PlayerList.find({ imported: { $gte: fileDate.toDate() } })
|
|
|
- if (fileNewer.length) {
|
|
|
- throw Error('File has to be newer.')
|
|
|
- }
|
|
|
-
|
|
|
- console.log('About to read the player list.', filePath)
|
|
|
- const worksheets = await Excel.readWorkbook(filePath)
|
|
|
- const worksheet = worksheets.Players
|
|
|
- if (worksheet[4].length !== 32 & worksheet[3][0] !== 'Konkurrenz' & worksheet[3][31] !== 'bezahlt') {
|
|
|
- throw Error(`Wrong file structure. Length: ${worksheet[4].length} (expected 32), Column A name: ${worksheet[3][0]} (expected Konkurrenz).`)
|
|
|
- }
|
|
|
-
|
|
|
- const headers = worksheet.slice(3, 4)
|
|
|
-
|
|
|
- const allPlayers = await Promise.all(worksheet.slice(4, worksheet.length).map(async data => {
|
|
|
- const filePlayer = {
|
|
|
- created: fileDate.toDate(),
|
|
|
- category: normalize(data[0]),
|
|
|
- licenseNr: normalize(data[2]),
|
|
|
- name: normalize(data[5]),
|
|
|
- firstName: normalize(data[6]),
|
|
|
- nameDP: normalize(data[24]),
|
|
|
- firstNameDP: normalize(data[25]),
|
|
|
- birthDate: data[7] || null,
|
|
|
- email: normalize(data[16]),
|
|
|
- ranking: normalize(data[17]),
|
|
|
- licenseNrDP: normalize(data[23]),
|
|
|
- phonePrivate: normalizePhone(data[13]),
|
|
|
- phoneWork: normalizePhone(data[14]),
|
|
|
- phoneMobile: normalizePhone(data[15]),
|
|
|
- birthDateDP: data[26] || null,
|
|
|
- rankingDP: normalize(data[27]),
|
|
|
- confirmed: !!data[29],
|
|
|
- paid: !!data[31],
|
|
|
- }
|
|
|
-
|
|
|
- filePlayer.gender = filePlayer.category ?
|
|
|
- filePlayer.category[0] === 'M' ? 'm' :
|
|
|
- filePlayer.category[0] === 'W' ?
|
|
|
- 'w' :
|
|
|
- null :
|
|
|
- null,
|
|
|
- filePlayer.doubles = !!filePlayer.category.match(/DM.*|[MW]D.*/)
|
|
|
- filePlayer.junior = filePlayer.birthDate ?
|
|
|
- filePlayer.birthDate.getTime() >= (new Date((new Date()).getFullYear() - 19, 11, 31, 23, 59, 59, 999)).getTime() :
|
|
|
- false
|
|
|
- filePlayer.fullName = filePlayer.doubles ?
|
|
|
- `${filePlayer.name} ${filePlayer.firstName} / ${filePlayer.nameDP} ${filePlayer.firstNameDP}` :
|
|
|
- `${filePlayer.name} ${filePlayer.firstName}`
|
|
|
- filePlayer.idString = `${filePlayer.category} % ${filePlayer.fullName}`
|
|
|
- filePlayer.phone = null
|
|
|
- const reMobile = /^\+417/
|
|
|
- if (filePlayer.phoneWork && filePlayer.phoneWork.match(reMobile)) {
|
|
|
- filePlayer.phone = filePlayer.phoneWork
|
|
|
- } else if (filePlayer.phonePrivate && filePlayer.phonePrivate.match(reMobile)) {
|
|
|
- filePlayer.phone = filePlayer.phonePrivate
|
|
|
- } else if (filePlayer.phoneMobile && filePlayer.phoneMobile.match(reMobile)) {
|
|
|
- filePlayer.phone = filePlayer.phoneMobile
|
|
|
- }
|
|
|
-
|
|
|
- const player = new Player(filePlayer)
|
|
|
- player.save()
|
|
|
- return player._id
|
|
|
- }))
|
|
|
-
|
|
|
- const playerList = new PlayerList({
|
|
|
- imported: fileDate,
|
|
|
- file: req.params.filename,
|
|
|
- fileSize: stat.size,
|
|
|
- players: allPlayers
|
|
|
- })
|
|
|
- await playerList.save()
|
|
|
-
|
|
|
- return res.json({playerList})
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-// Delete a playerlist
|
|
|
-swisstennis.delete('/playerlist/:playerlistId', async (req, res) => {
|
|
|
- try {
|
|
|
- const { playerlistId } = req.params
|
|
|
-
|
|
|
- if (!playerlistId) {
|
|
|
- throw Error('No playerlistId given.')
|
|
|
- }
|
|
|
- const playerList = await PlayerList.findOne({ _id: playerlistId })
|
|
|
- if (!playerList) {
|
|
|
- throw Error('PlayerList not found.')
|
|
|
- }
|
|
|
- playerList.remove()
|
|
|
- return res.json({ _id: playerlistId })
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-// List downloaded files
|
|
|
-swisstennis.get('/files', async (req, res) => {
|
|
|
- const tournament = req.query.tournament
|
|
|
- const dirContent = await awaitFs.readdir('swisstennis_files')
|
|
|
- const fileList = dirContent.filter(filename => {
|
|
|
- return tournament ? filename.includes(tournament) : true
|
|
|
- }).map(filename => {
|
|
|
- const stats = fs.statSync(`swisstennis_files/${filename}`)
|
|
|
- return { filename, size: stats.size, mtime: stats.mtime }
|
|
|
- })
|
|
|
- return res.json(fileList)
|
|
|
-})
|
|
|
-
|
|
|
-swisstennis.delete('/files', async (req, res) => {
|
|
|
- try {
|
|
|
- const { filename } = req.body
|
|
|
- fs.unlink(`swisstennis_files/${filename}`, (error) => {
|
|
|
- if (error) throw error
|
|
|
- res.json({ msg: `successfully deleted swisstennis_files/${filename}.` })
|
|
|
- })
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-swisstennis.get('/calendars', async (req, res) => {
|
|
|
- try {
|
|
|
- const calendars = await MatchList.find().select({_id: 1, imported: 1, file: 1, fileSize: 1})
|
|
|
- return res.json(calendars)
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-swisstennis.get('/calendar', async (req, res) => {
|
|
|
- try {
|
|
|
- const key = req.query.calendarId ? { _id: req.query.calendarId } : {}
|
|
|
- const calendar = await MatchList.findOne(key).sort({ imported: -1 }).populate('matches')
|
|
|
- await Promise.all(calendar.matches.map(async match => {
|
|
|
- await Player.populate(match, 'player1')
|
|
|
- await Player.populate(match, 'player2')
|
|
|
- }))
|
|
|
- return res.json(calendar)
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-swisstennis.get('/calendar/download', async (req, res) => {
|
|
|
- const tournament = req.query.tournament || config.tournamentId
|
|
|
-
|
|
|
- if (!tournament) {
|
|
|
- return res.json({ msg: 'No tournament given.' })
|
|
|
- }
|
|
|
- try {
|
|
|
- const filename = `Calendar-${tournament}-${moment().format('YYYYMMDDTHHmmss')}.xls`
|
|
|
- const calendarFile = fs.createWriteStream(`swisstennis_files/${filename}`)
|
|
|
- const calendar = await session.get(`https://comp.swisstennis.ch/advantage/servlet/Calendar.xls?Lang=D&tournament=Id${tournament}&Type=Match&Inp_DateRangeFilter.fromDate=04.06.2018&Inp_DateRangeFilter.toDate=16.09.2018`, {stream: true})
|
|
|
- await calendar.pipe(calendarFile)
|
|
|
- const streamDone = new Promise((resolve, reject) => {
|
|
|
- calendarFile.on('finish', resolve)
|
|
|
- calendarFile.on('error', reject)
|
|
|
- })
|
|
|
- await streamDone
|
|
|
- const stats = fs.statSync(`swisstennis_files/${filename}`)
|
|
|
- return res.json({ filename, size: stats.size, mtime: stats.mtime })
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-swisstennis.get('/calendar/parse/:filename', async (req,res) => {
|
|
|
- try {
|
|
|
- console.log('Parsing file', req.params.filename)
|
|
|
-
|
|
|
- const filePath = `swisstennis_files/${req.params.filename}`
|
|
|
- const dateElems = req.params.filename.match(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/).slice(1,7)
|
|
|
- const fileDate = new Date(`${dateElems[0]}-${dateElems[1]}-${dateElems[2]} ${dateElems[3]}:${dateElems[4]}:${dateElems[5]}`)
|
|
|
- const stat = fs.statSync(filePath)
|
|
|
-
|
|
|
- const fileNewer = await MatchList.find({ imported: { $gte: fileDate } })
|
|
|
- if (fileNewer.length) {
|
|
|
- throw Error('File has to be newer.')
|
|
|
- }
|
|
|
-
|
|
|
- console.log('About to read the calendar list.')
|
|
|
- const worksheets = await Excel.readWorkbook(`swisstennis_files/${req.params.filename}`)
|
|
|
- const worksheet = worksheets.Sheet1
|
|
|
- if (worksheet[2].length < 8 | worksheet[2].length > 9) {
|
|
|
- throw Error(`Wrong file structure.`)
|
|
|
- }
|
|
|
-
|
|
|
- const allMatches = await Promise.all(worksheet.slice(2, worksheet.length).map(async (data, key) => {
|
|
|
- const fileMatch = {
|
|
|
- created: fileDate,
|
|
|
- fileLine: key,
|
|
|
- category: normalize(data[3]),
|
|
|
- place: normalize(data[0]),
|
|
|
- date: data[1],
|
|
|
- result: normalize(data[8]),
|
|
|
- }
|
|
|
- fileMatch.doubles = !!fileMatch.category.match(/DM.*|[MW]D.*/)
|
|
|
- const player1 = await Player.findOne({idString: `${fileMatch.category} % ${normalize(data[4])}`}).sort({created: -1})
|
|
|
- const player2 = await Player.findOne({idString: `${fileMatch.category} % ${normalize(data[6])}`}).sort({created: -1})
|
|
|
- fileMatch.idString = `${fileMatch.category} % ${player1 ? player1.idString : `${key}_1`} - ${player2 ? player2.idString : `${key}_2`}`
|
|
|
- fileMatch.player1 = player1 ? player1._id : null
|
|
|
- fileMatch.player2 = player2 ? player2._id : null
|
|
|
-
|
|
|
- const match = new Match(fileMatch)
|
|
|
- await match.save()
|
|
|
- return match._id
|
|
|
- }))
|
|
|
- const matchList = new MatchList({
|
|
|
- imported: fileDate,
|
|
|
- file: req.params.filename,
|
|
|
- fileSize: stat.size,
|
|
|
- matches: allMatches
|
|
|
- })
|
|
|
- await matchList.save()
|
|
|
-
|
|
|
- return res.json({ matchList })
|
|
|
- } catch (error) {
|
|
|
- res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-// Delete a calendar
|
|
|
-swisstennis.delete('/calendar/:calendarId', async (req, res) => {
|
|
|
- try {
|
|
|
- const { calendarId } = req.params
|
|
|
-
|
|
|
- if (!calendarId) {
|
|
|
- throw Error('No calendarId given.')
|
|
|
- }
|
|
|
- const calendar = await MatchList.findOne({ _id: calendarId })
|
|
|
- if (!calendar) {
|
|
|
- throw Error('Calendar not found.')
|
|
|
- }
|
|
|
- calendar.remove()
|
|
|
- return res.json({ _id: calendarId })
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-swisstennis.get('/download/draw', async (req, res) => {
|
|
|
- try {
|
|
|
- const { draw } = req.query
|
|
|
-
|
|
|
- if (!draw) {
|
|
|
- throw Error('No draw given.')
|
|
|
- }
|
|
|
- const fileName = `DisplayDraw${draw}-${fileDate(new Date())}.xls`
|
|
|
- const drawFile = fs.createWriteStream(`swisstennis_files/${fileName}`)
|
|
|
- const drawDisplay = await session.get(`https://comp.swisstennis.ch/advantage/servlet/DisplayDraw.xls?eventId=${draw}&lang=D`, {stream: true})
|
|
|
- drawDisplay.pipe(drawFile)
|
|
|
- return res.json({ fileName })
|
|
|
- } catch (error) {
|
|
|
- return res.status(400).json({ msg: error.toString() })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-export default swisstennis
|