Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…ikone into trunk
  • Loading branch information
sasumaki committed May 22, 2019
2 parents bf0f663 + 14e995a commit 1189521
Show file tree
Hide file tree
Showing 16 changed files with 160 additions and 113 deletions.
83 changes: 83 additions & 0 deletions cypress/integration/Course_statistics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@

describe('Course Statistics tests', () => {
beforeEach(() => {
cy.server({
onAnyRequest: function (route, proxy) {
if (Cypress.config().baseUrl.includes("http://localhost:1337/")) {
proxy.xhr.setRequestHeader('uid', 'tktl')
proxy.xhr.setRequestHeader('shib-session-id', 'mock-shibboleth')
proxy.xhr.setRequestHeader('hygroupcn', 'grp-oodikone-users')
proxy.xhr.setRequestHeader('edupersonaffiliation', 'asdasd')
}
}
})
console.log(Cypress.config().baseUrl)
cy.visit(Cypress.config().baseUrl)
cy.contains("Course statistics").click()
cy.contains("Search for courses")
})

it('Searching single course having duplicate mappings shows course statistics', () => {
cy.contains("Fetch statistics").should('be.disabled')
cy.url().should('include', '/coursestatistics')
cy.contains("Search for courses")
cy.get("input[placeholder='Search by entering a course code']").type('TKT20003')
cy.contains("tr", "TKT20003").within(($row) => {
cy.get('button').click()
})
cy.contains("Fetch statistics").should('be.enabled').click()
cy.contains("Search for courses").should('not.exist')

cy.contains("Käyttöjärjestelmät")
cy.contains("TKT20003")
cy.contains("582640") // old mapped code

cy.contains(".tabular.menu a", "Table").click()
cy.contains("All")
cy.contains(".modeSelectorRow a", "Cumulative").click()
cy.contains(".modeSelectorRow a", "Student").click()
cy.contains(".modeSelectorRow a", "Grades").click()

cy.contains(".tabular.menu a", "Pass rate chart").click()
cy.get("div.modeSelectorContainer").click()
cy.contains("svg", "Pass rate chart")

cy.contains(".tabular.menu a", "Grade distribution chart").click()
cy.get("div.modeSelectorContainer").click()
cy.contains("svg", "Grades")

cy.contains("a", "New query").click()
cy.contains("Search for courses")
})

it('Searching multiple courses having duplicate mappings shows course statistics', () => {
cy.contains("Fetch statistics").should('be.disabled')
cy.url().should('include', '/coursestatistics')
cy.contains("Search for courses")
cy.get("input[placeholder='Search by entering a course code']").type('TKT')
cy.contains("tr", "TKT20003").within(($row) => {
cy.get('button').click()
})
cy.contains("tr", "TKT10002").within(($row) => {
cy.get('button').click()
})
cy.contains("Fetch statistics").should('be.enabled').click()
cy.contains("Search for courses").should('not.exist')

cy.contains('.courseNameCell', "Käyttöjärjestelmät").contains("TKT20003").click()
cy.contains('.courseNameCell', "Ohjelmoinnin perusteet").should('not.exist');
cy.contains("TKT20003")
cy.contains("582640") // old mapped code
cy.contains("Summary").click()

cy.contains('.courseNameCell', "Ohjelmoinnin perusteet").contains("TKT10002").click()
cy.contains('.courseNameCell', "Käyttöjärjestelmät").should('not.exist');
cy.contains("TKT10002")
cy.contains("581325") // old mapped code
cy.contains("Summary").click()

cy.contains("a", "New query").click()
cy.contains("Search for courses")
})

})
4 changes: 2 additions & 2 deletions cypress/integration/Population_statistics.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ describe('Population Statistics tests', () => {
cy.contains("DIGI-000A")
cy.go("back")



cy.contains("Courses of Population").parentsUntil(".ui.segment").parent().within(() => {
cy.contains("number at least").siblings().within(() => cy.get("input").clear().type("0"))
Expand Down Expand Up @@ -252,6 +252,6 @@ describe('Population Statistics tests', () => {
})
})
cy.get("button").contains("Delete for good").click({ force: true })

})
})
26 changes: 25 additions & 1 deletion cypress/integration/Studyprogramme_overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,30 @@ describe('Studyprogramme overview', () => {
cy.contains("Study Programme", { timeout: 100000 })
})

it('can search for course mappings', () => {
cy.contains("Tietojenkäsittelytieteen kandiohjelma").click()
cy.contains('Code Mapper').click()
cy.contains('tr', 'TKT20003 Käyttöjärjestelmät').get('input').type('582219')
cy.contains('tr', 'TKT20003 Käyttöjärjestelmät').get('.results').contains("Käyttöjärjestelmät (582219)")
cy.contains('tr', 'TKT20003 Käyttöjärjestelmät').contains('button', "Add")
})

it('can view course groups', () => {
cy.contains("Kasvatustieteiden kandiohjelma").click()
cy.contains('Course Groups').click()

cy.contains('tr', 'Test course group').get('i.edit').click()
cy.contains("Edit group")
cy.get('.prompt').type("Professori Pekka")
cy.contains("Add teacher").parent().contains("9000960")
cy.contains("Teachers in group").parent().contains("9000960")
cy.get("i.reply.link.icon").click()

cy.contains('tr a', 'Test course group').click()
cy.contains("Total teachers")
cy.get("i.reply.icon").click()
})

it('renders progress and productivity tables', () => {
cy.contains("Tietojenkäsittelytieteen kandiohjelma").click()
cy.get('table').should('have.length', 2)
Expand All @@ -40,5 +64,5 @@ describe('Studyprogramme overview', () => {
cy.contains('Thesis Courses').click()
cy.contains('Add thesis course').click()
cy.contains('No results')
})
})
})
35 changes: 19 additions & 16 deletions services/backend/oodikone2-backend/src/routes/population.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { updateStudents } = require('../services/doo_api_database_updater/databas
const StudyrightService = require('../services/studyrights')

// POST instead of GET because of too long params and "sensitive" data
router.post('/v2/populationstatistics/courses', async (req, res) => {
router.post('/v2/populationstatistics/courses', async (req, res) => {
try {
if (!req.body.year || !req.body.semesters || !req.body.studyRights) {
res.status(400).json({ error: 'The body should have a year, semester and study rights defined' })
Expand All @@ -32,30 +32,33 @@ router.post('/v2/populationstatistics/courses', async (req, res) => {
})

router.get('/v3/populationstatistics', async (req, res) => {
console.log(req.query.year, req.query.semesters, req.query.studyRights)
const { year, semesters, studyRights: studyRightsJSON } = req.query
try {
if (!req.query.year || !req.query.semesters || !req.query.studyRights) {
res.status(400).json({ error: 'The query should have a year, semester and study rights defined' })
if (!year || !semesters || !studyRightsJSON) {
res.status(400).json({ error: 'The query should have a year, semester and studyRights defined' })
return
}
if (!Array.isArray(req.query.studyRights)) { // studyRights should always be an array
req.query.studyRights = [req.query.studyRights]
}
req.query.studyRights = req.query.studyRights.filter(sr => sr !== 'undefined')
const roles = req.decodedToken.roles
if (!roles || !roles.map(r => r.group_code).includes('admin')) {
const elements = new Set(req.decodedToken.rights)
if (req.query.studyRights.some(code => !elements.has(code))) {
res.status(403).json([])
return
let studyRights = null
try {
studyRights = JSON.parse(studyRightsJSON)
const { roles, rights } = req.decodedToken
if (!roles || !roles.map(r => r.group_code).includes('admin')) {
if (!rights.includes(studyRights.programme)) {
res.status(403).json([])
return
}
}
} catch(e) {
console.error(e)
res.status(400).json({ error: 'The query had invalid studyRights' })
return
}

if (req.query.months == null) {
req.query.months = 12
}

const result = await Population.optimizedStatisticsOf(req.query)
const result = await Population.optimizedStatisticsOf({ ...req.query, studyRights})

if (result.error) {
console.log(result.error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ const parseQueryParams = query => {
exchangeStudents,
cancelledStudents,
nondegreeStudents,
studyRights,
studyRights: Array.isArray(studyRights) ? studyRights : Object.values(studyRights),
months,
startDate,
endDate
Expand Down
11 changes: 7 additions & 4 deletions services/backend/oodikone2-backend/src/services/studyrights.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,18 +333,21 @@ const getFilteredAssociations = async (codes) => {
console.log(codes)
const associations = await getAssociations()
associations.programmes = _.pick(associations.programmes, codes)

const degrees = []
const studyTracks = []
Object.keys(associations.programmes).forEach(k => {
Object.keys(associations.programmes[k].enrollmentStartYears).forEach(year => {
const yearData = associations.programmes[k].enrollmentStartYears[year]
yearData.degrees = _.pick(yearData.degrees, codes)
yearData.studyTracks = _.pick(yearData.studyTracks, codes)
degrees.push(...Object.keys(yearData.degrees))
studyTracks.push(...Object.keys(yearData.studyTracks))
})
})
associations.degrees = _.pick(associations.degrees, codes)
associations.degrees = _.pick(associations.degrees, degrees)
Object.keys(associations.degrees).forEach(k => {
associations.degrees[k].programmes = _.pick(associations.degrees[k].programmes, codes)
})
associations.studyTracks = _.pick(associations.studyTracks, codes)
associations.studyTracks = _.pick(associations.studyTracks, studyTracks)
Object.keys(associations.studyTracks).forEach(k => {
associations.studyTracks[k].programmes = _.pick(associations.studyTracks[k].programmes, codes)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Dropdown, Icon, Form, Segment, Button } from 'semantic-ui-react'
import { shape, func, arrayOf, string } from 'prop-types'
import { shape, func, string } from 'prop-types'
import InfoBox from '../InfoBox'
import infoTooltips from '../../common/InfoToolTips'
import { canceledStudyright } from '../../populationFilters'
Expand All @@ -12,7 +12,7 @@ class CanceledStudyright extends Component {
filter: shape({}).isRequired,
removePopulationFilter: func.isRequired,
setPopulationFilter: func.isRequired,
studyrights: arrayOf(string).isRequired
studyrights: shape({ programme: string, degree: string, studyTrack: string }).isRequired
}

state = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const SimpleExtentGraduated = (props) => {


const mapStateToProps = ({ settings, populations, populationDegreesAndProgrammes }) => {
const code = populations.query.studyRights[0]
const code = populations.query.studyRights.programme
const studyrightName = populationDegreesAndProgrammes.data.programmes[code].name
return { language: settings.language, programme: studyrightName, code }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class TransferToStudyrightFilter extends Component {
}

const mapStateToProps = (state) => {
const code = state.populations.query.studyRights[0]
const code = state.populations.query.studyRights.programme
const studyrightName = state.populationDegreesAndProgrammes.data.programmes[code].name
return ({
language: state.settings.language,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Segment, Header, Button, Form, Radio, Modal, Icon, TextArea, Input } from 'semantic-ui-react'
import { object, func, arrayOf, bool, string } from 'prop-types'
import { object, func, arrayOf, bool, shape, string } from 'prop-types'
import _ from 'lodash'
import uuidv4 from 'uuid/v4'

Expand Down Expand Up @@ -71,7 +71,7 @@ class PopulationFilters extends Component {
setComplementFilter: func.isRequired,
savePopulationFilters: func.isRequired,
setPopulationFilter: func.isRequired,
studyRights: arrayOf(string).isRequired,
studyRights: shape({ programme: string, degree: string, studyTrack: string }).isRequired,
populationFilters: object.isRequired, //eslint-disable-line
populationCourses: object.isRequired //eslint-disable-line
}
Expand Down Expand Up @@ -138,7 +138,7 @@ class PopulationFilters extends Component {
id: uuidv4(),
name: this.state.presetName,
description: this.state.presetDescription,
population: this.props.studyRights,
population: Object.values(this.props.studyRights),
filters: this.props.filters
}
this.setState({ presetName: '', presetDescription: '' })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ PopulationQueryCard.propTypes = {
query: shape({
year: oneOfType([string, number]),
semester: string,
studyRights: arrayOf(string),
studyRights: shape({ programme: string, degree: string, studyTrack: string }),
uuid: string
}).isRequired,
removeSampleFn: func.isRequired,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,14 @@ class PopulationSearchForm extends Component {
}

fetchPopulation = (query) => {
let queryCodes = []
queryCodes = [...Object.values(query.studyRights).filter(e => e != null)]
const backendQuery = { ...query, studyRights: queryCodes }
const queryCodes = Object.values(query.studyRights).filter(e => e != null)

const uuid = uuidv4()
const request = { ...backendQuery, uuid }
const request = { ...query, studyRights: queryCodes, uuid }
this.setState({ isLoading: true })
this.props.setLoading()
Promise.all([
this.props.getPopulationStatistics(request),
this.props.getPopulationStatistics({ ...query, uuid }),
this.props.getPopulationCourses(request),
this.props.getPopulationFilters(request),
this.props.getMandatoryCourses(query.studyRights.programme)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ class PopulationSearchHistory extends Component {
population={populations.data}
query={populations.query}
queryId={0}
unit={units.data.programmes[populations.query.studyRights[0]]} // Possibly deprecated
unit={units.data.programmes[populations.query.studyRights.programme]} // Possibly deprecated
units={
([
...Object.values(units.data.programmes),
...Object.values(units.data.degrees),
...Object.values(units.data.studyTracks)
]).filter(u => populations.query.studyRights.includes(u.code))
]).filter(u => Object.values(populations.query.studyRights).includes(u.code))
}
removeSampleFn={this.removePopulation}
updateStudentsFn={() => this.props.updatePopulationStudents(studentNumberList)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ const mapStateToProps = ({ settings, populations, populationCourses, populationM
showNames: settings.namesVisible,
showList: settings.studentlistVisible,
language: settings.language,
queryStudyrights: populations.query.studyRights,
queryStudyrights: Object.values(populations.query.studyRights),
mandatoryCourses: populationMandatoryCourses.data,
mandatoryPassed
}
Expand Down
Loading

0 comments on commit 1189521

Please sign in to comment.