Skip to content

Commit

Permalink
Merge pull request #2 from HBreddam/Testsandvalidation
Browse files Browse the repository at this point in the history
Data can now be written and read to CSV files. The first test have been created and the first version of a solutionvalidator is ready.
  • Loading branch information
HBreddam authored Mar 18, 2020
2 parents be7a22f + 23234d2 commit 1f6734f
Show file tree
Hide file tree
Showing 22 changed files with 1,474 additions and 162 deletions.
5 changes: 5 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ authors = ["Henrik Bøgedal Breddam"]
version = "0.1.0"

[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
CSVFiles = "5d742f6a-9f54-50ce-8119-2520741973ca"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
Query = "1a8c2f83-1ff3-5112-b086-8aa67b057ba1"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
TextParse = "e0df1984-e451-5cb5-8b61-797a481e67e3"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0"

Expand Down
21 changes: 9 additions & 12 deletions src/GenerateSampleData.jl
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
using XLSX
using DataFrames
using Random
using UUIDs
using JuliaDB
using Lazy


#TODO sort treatmentplan by default
function generateTreatmentplan!(visits::IndexedTable,row::DataFrameRow,bestord::Pair{Int64,Date},columns::Dict{Symbol,String},patientID)
function generateTreatmentplan!(visits::IndexedTable,row,bestord,columns::Dict{Symbol,String},patientID)
for col in columns
if !ismissing(row[col[1]])
if row[col[1]] == 1 || rand() < row[col[1]]
push!(rows(visits),(intID=length(visits)+1,patientID=patientID,bestord=bestord,req_type=col[2]))
push!(rows(visits),(intID=length(visits)+1,patientID=patientID,bestord=bestord[1],bestord_date=bestord[2],req_type=col[2]))
end
end
end

end

"Read aggregated table data of the format found in 'test/Sample data/PatientOverview_test.xlsx'. Produced an table of patients and a table of visists"
function readPatientTable(path,sheet,columns,mastercalendar)
patientOverview = DataFrame(XLSX.readtable(path,sheet)...)
patients = JuliaDB.table((intID=Int64[],diagnosis=String[],bestord=Pair{Int64,Date}[],);pkey=[:intID])
visits = JuliaDB.table((intID=Int64[],patientID=Int64[],bestord=Pair{Int64,Date}[],req_type=String[]);pkey=[:intID])
patients = JuliaDB.table((intID=Int64[],diagnosis=String[],bestord=Int64[],bestord_date=Date[]);pkey=[:intID])
visits = JuliaDB.table((intID=Int64[],patientID=Int64[],bestord=Int64[],bestord_date=Date[],req_type=String[]);pkey=[:intID])
for row in eachrow(patientOverview)
if row.Kategori === missing
continue
Expand All @@ -29,14 +25,15 @@ function readPatientTable(path,sheet,columns,mastercalendar)
bestord = rand(mastercalendar)
intID=length(patients)+1
generateTreatmentplan!(visits,row,bestord,columns,intID)
push!(rows(patients),(intID=intID,diagnosis="test",bestord=bestord,))
push!(rows(patients),(intID=intID,diagnosis="test",bestord=bestord[1],bestord_date=bestord[2]))
end
end
patients, visits
end

function readWorkPattern(path::String,sheet::String)
resources = JuliaDB.table((intID=Int64[],id=String[],type=String[],name=String[],qualifications=Dict{String,String}[],);pkey=[:intID])
resources = JuliaDB.table((intID=Int64[],id=String[],type=String[],name=String[],);pkey=[:intID])
#resources = JuliaDB.table((intID=Int64[],id=String[],type=String[],name=String[],qualifications=Dict{String,String}[],);pkey=[:intID])
workpattern = JuliaDB.table((resourceID=Int64[],type = String[],weekdayID=Int64[],oddWeek=Bool[],timeslotID=Int64[],startTime=Time[],endTime=Time[]);pkey=[:resourceID,:weekdayID,:timeslotID])
timeslots = JuliaDB.table((resourceID=Int64[],type = String[], dayID=Int64[],timeslotID=Int64[],startTime=Time[],endTime=Time[],booked=Bool[]);pkey=[:resourceID,:dayID,:timeslotID])
readWorkPattern!(resources,timeslots,workpattern,path::String,sheet::String,)
Expand Down
14 changes: 13 additions & 1 deletion src/Hospitalplanning.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
__precompile__()

module Hospitalplanning
using JuMP
using Gurobi
Expand All @@ -7,14 +9,24 @@ using Dates
using LightGraphs
using Query
using Lazy
using CSVFiles
using XLSX
using Random
using UUIDs
using JuliaDB
using Lazy
using TextParse
using CSV
using DataFrames



include("Misc.jl")

include("Resource.jl")

include("GenerateSampleData.jl")

include("readwritedata.jl")
include("MIP/datastructures.jl")
include("MIP/SupportFunctions.jl")
include("MIP/masterproblem.jl")
Expand Down
4 changes: 2 additions & 2 deletions src/MIP/SupportFunctions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ function patientgroup(visits,Vp,id)
tempvisits = visits[Vp[id].v]

(sort(JuliaDB.select(tempvisits,:req_type)),
Dates.Month(reduce(max,tempvisits,select=:bestord)[2]),
Dates.Month(reduce(min,tempvisits,select=:bestord)[2]))
Dates.Month(reduce(max,tempvisits,select=:bestord_date)),
Dates.Month(reduce(min,tempvisits,select=:bestord_date)))
end

"Produces a IndexedTable of patient groups based on the function patientgroup()"
Expand Down
5 changes: 4 additions & 1 deletion src/MIP/model_column.jl
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,18 @@ function deadline()
pass = true
if true
pass = false
println("deadline not implemented")
end
return pass
end
function daysused(mp,plannedappointments)
pass = true
if sum(plannedappointments |> @groupby(_.patientID) |> @map(length(unique(map(x->x.dayID,_)))) |> collect) != JuMP.objective_value(mp.model)
pass = false
println("Mip model objective wrong")
else
println("Mip model objective correct")
end
pass && "Mip model objective correct"
return pass
end

Expand Down
2 changes: 1 addition & 1 deletion src/MIP/subproblem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function setup_sub!(subproblems,patientgroup::Int64,visits,resources::IndexedTab
Te(d,j,i) = Dates.value(timeslots[d,j,i].endTime)/60000000000

(bestordmin,bestordmax) = reduce((min,max),visits[V],select=:bestord)
startdate = max(minimum(mastercalendar)[2],min(startofmonth(maximum(mastercalendar)[2]-Month(months)),startofmonth(bestordmin[2])-Month(div(months,2))))
startdate = max(minimum(mastercalendar)[2],min(startofmonth(maximum(mastercalendar)[2]-Month(months)),startofmonth(mastercalendar[bestordmin])-Month(div(months,2))))
enddate = startdate + Month(months)-Day(1)
subMastercalendar = MasterCalendar(mastercalendar,startdate,enddate)
if p== 6
Expand Down
95 changes: 35 additions & 60 deletions src/Misc.jl
Original file line number Diff line number Diff line change
@@ -1,70 +1,45 @@

"""
MasterCalendar(startdate::Date,enddate::Date)
Produces a mastercalendar (Dict{Int64,Date}) of weekdays where clinic is open between the start and enddate
# Examples
```julia-repl
julia> MasterCalendar(Date("2019-01-01"),Date("2019-02-01"))
Dict{Int64,Date} with 24 entries:
18 => 2019-01-18
24 => 2019-01-24
⋮ => ⋮
```
"""
function MasterCalendar(startdate::Date,enddate::Date)
Dict((date-startdate).value+1 => date for date = filter(td -> dayofweek(td) <= 5, startdate:Day(1):enddate))
end

"""
MasterCalendar(mastercalendar::Dict{Int64,Date},startdate,enddate)
Produces a mastercalendar (Dict{Int64,Date}) of weekdays where clinic is open.
# Examples
```julia-repl
julia> mc = MasterCalendar(Date("2019-01-01"),Date("2019-02-01"))
Dict{Int64,Date} with 24 entries:
18 => 2019-01-18
24 => 2019-01-24
⋮ => ⋮
julia> MasterCalendar(mc,Date("2019-01-01"),Date("2019-01-10"))
Dict{Int64,Date} with 6 entries:
7 => 2019-01-07
9 => 2019-01-09
4 => 2019-01-04
2 => 2019-01-02
3 => 2019-01-03
8 => 2019-01-08
```
"""
function MasterCalendar(mastercalendar::Dict{Int64,Date},startdate::Date,enddate::Date)
filter(date -> startdate <date[2] < enddate,mastercalendar)
end

@enum STATUS free = 1 booked = 2
mutable struct Timeslot
date::Pair{Int64,Date}
intID::Int
startTime::Time
endTime::Time

status::STATUS

Timeslot(date::Pair{Int64,Date},intID::Int,startTime::DateTime,endTime::DateTime)= new(date,intID,Time(startTime),Time(startTime),free)
Timeslot(date::Pair{Int64,Date},intID::Int,startTime::Time,endTime::Time)= new(date,intID,startTime,endTime,free)

end

@enum WEEK Odd = 1 Even = 2
mutable struct Workday
date::Pair{Int64,Date}
timeslots::Array{Timeslot}

weekday::Int
weektype::WEEK

Workday() = new((0 =>Date(0)),[])
Workday(weekday::Int,weektype::WEEK) = new((0 =>Date(0)),[],weekday,weektype)
Workday(date::Pair{Int64,Date},timeslots::Array{Timeslot},weekday::WEEK,weektype::Int) = new(date,timeslots,weekday,weektype)
end
function addTimeslot!(workday::Workday,startTime::Time,endTime::Time)
push!(workday.timeslots,Timeslot(workday.date,length(workday.timeslots)+1,startTime,endTime))
end

function addTimeslot!(workday::Workday,startTime::String,endTime::String)
push!(workday.timeslots,Timeslot(workday.date,length(workday.timeslots)+1,Dates.Time(startTime),Dates.Time(endTime)))
end


abstract type AbstractCalendar end



struct Calendar <: AbstractCalendar
workdays::Array{Workday}

Calendar() = new([])
Calendar(workdays::Array{Workday}) = new(workdays)
end

function Base.:(+)(cal1::AbstractCalendar, cal2::AbstractCalendar)
newcal = vcat(cal1.workdays,cal2.workdays)
sort!(newcal, by= x ->(x.date,x.weekday) )
return Calendar(newcal)
end


function addWorkday!(calendar::Calendar,workday::Workday,day::Pair{Int64,Date}=(0=>Date(0)))
workday.date = day
for y in workday.timeslots
y.date = day
end
push!(calendar.workdays,workday)
sort!(calendar.workdays,by = x ->(x.date[1],x.weekday))
end
24 changes: 0 additions & 24 deletions src/Patient.jl

This file was deleted.

23 changes: 0 additions & 23 deletions src/Resource.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,3 @@ end
#
# Offperiod(id,starttime,endtime) = new(id,starttime,endtime)
# end

mutable struct Resource <: AbstractResource
intID::Int
id::String
type::String
name::String
qualifications::Dict{String,String}
workpattern::Calendar

calendar::Calendar
#offperiods::Array{Offperiod} DELETE ME

Resource(intID::Int,type::String,name::String) = new(intID,string(type, "_" , name),type,name,Dict("name" => name,"type"=>type),Calendar(),Calendar(),Offperiod[])
Resource(type::String,name::String) = new(0,string(type, "_" , name),type,name,Dict("name" => name,"type"=>type)Calendar(),Calendar(),Offperiod[])
end

function addNewResource!(resources::Array{Resource},type::String,name::String)
any(r-> r.id == string(type, "_" , name),resources) ? error("Not Unique resource id") : push!(resources,Resource(length(resources)+1,type,name))
end

function addWorkPattern(resource::Resource,oddcalendar::Calendar,evencalendar::Calendar)
resource.workpattern = oddcalendar + evencalendar
end
37 changes: 0 additions & 37 deletions src/Visit.jl

This file was deleted.

44 changes: 44 additions & 0 deletions src/readwritedata.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@


function savepatients(object,path::String)
CSVFiles.save(path, object)
end

function savetimeslots(object,path::String)
CSVFiles.save(path, object)
end


function loadtimeslots(path)
colparser = Dict(:startTime => dateformat"H:M:S",:endTime => dateformat"H:M:S")
res = loadtable(path,colparsers=colparser)|> @map((resourceID = _.resourceID, type= _.type,dayID = _.dayID,timeslotID = _.timeslotID,startTime = Time(_.startTime),endTime= Time(_.endTime),booked = _.booked == "true" )) |> collect
return table(res,pkey = [:resourceID,:dayID,:timeslotID])
end


function save(object,path::String)
CSVFiles.save(path, object)
end

function loadresources(path)
loadtable(path,indexcols= [:intID])
end

function loadpatients(path,indexcols=[])
if length(indexcols) == 0
loadtable(path,)
else
loadndsparse(path,indexcols)
end
end
function loadTimeDelta(path_TimeDelta::String)
loadndsparse(path_TimeDelta,indexcols=1:2)
end

function loadvisits(path)
loadtable(path,indexcols= [:intID])
end

function loadsolution(path)
loadtable(path;indexcols=[:visitID])
end
Binary file modified test/Sample data/PatientOverview_test.xlsx
Binary file not shown.
Loading

0 comments on commit 1f6734f

Please sign in to comment.