-
Notifications
You must be signed in to change notification settings - Fork 0
/
MenuMaker.jl
149 lines (116 loc) · 4.32 KB
/
MenuMaker.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
module MenuMaker
using PDFIO
using JuMP
using HiGHS
export makemenu
const blacklist = ["Сарделька из мяса птицы", "Сыр", "Запечённая индейка", "Сосиска из мяса птицы"]
const mincalories = 600
const maxcalories = 700
const minprotein = 35
function makemenu(filename::AbstractString)
file = pdDocOpen(filename)
pagecount = pdDocGetPageCount(file)
menu = []
for i in 1:pagecount
page = pdDocGetPage(file, i)
if i != 5
breakfast, lunch = parsepage(page)
push!(menu, (breakfast=breakfast, lunch=lunch))
else
lunch = parsepage(page, false)
push!(menu, (breakfast=nothing, lunch=lunch))
end
end
return menu
end
function parsepage(page::PDPage, includebreakfast=true)
text = sprint(pdPageExtractText, page)
text = replace(text, r"[\n ]{2,}" => ";", "," => ".")
lunch = match(r"ОБЕД;(.*);ПОЛДНИК", text)[1] |> parsefoods
if includebreakfast
breakfast = match(r"ЗАВТРАК;(.*);ОБЕД", text)[1] |> parsefoods
return calculateplan(breakfast, lunch)
else
return calculateplan(lunch)
end
end
const foodpattern = r"([^\d;]+)[; ]?([\d.]+);([\d.]+);([\d.]+);([\d.]+);\d+ руб\.;?(\d+) гр\.;|([\d.]+);([\d.]+);([\d.]+);([\d.]+)[; ]?([^\d;]+);\d+ руб\.[; ]?(\d+) гр\.;"
function parsefoods(menu)
foods = NamedTuple[]
for match in eachmatch(foodpattern, menu)
food = makefood(match.captures)
if !(food.name in blacklist)
push!(foods, food)
end
end
return foods
end
function makefood(captures::Vector)
if isnothing(captures[7])
values = map(x -> something(tryparse(Float64, x), x), captures[1:6])
size = values[6] / 100
return (
name=values[1],
calories=values[2] * size,
protein=values[3] * size,
fat=values[4] * size,
carbs=values[5] * size,
meat=ismeat(values[1])
)
else
values = map(x -> something(tryparse(Float64, x), x), captures[7:12])
size = values[6] / 100
return (
name=values[5],
calories=values[1] * size,
protein=values[2] * size,
fat=values[3] * size,
carbs=values[4] * size,
meat=ismeat(values[5])
)
end
end
const meat_words = ["котлета", "филе", "стейк", "терияки", "печень", "треска", "бифштекс"]
function ismeat(name)
lname = lowercase(name)
for word in meat_words
if occursin(word, lname)
return true
end
end
return false
end
function calculateplan(breakfast, lunch)
breakfast_range = 1:size(breakfast, 1)
lunch_range = 1:size(lunch, 1)
model = Model(HiGHS.Optimizer)
set_silent(model)
@variable(model, x[breakfast_range], Bin)
@variable(model, y[lunch_range], Bin)
breakfast_calories = sum(breakfast[i].calories * x[i] for i in breakfast_range)
lunch_calories = sum(lunch[i].calories * y[i] for i in lunch_range)
@constraint(model, breakfast_calories >= mincalories)
@constraint(model, lunch_calories >= mincalories)
@constraint(model, breakfast_calories + lunch_calories <= maxcalories * 2)
@constraint(model, sum(breakfast[i].meat * x[i] for i in breakfast_range) <= 2)
@constraint(model, sum(lunch[i].meat * y[i] for i in lunch_range) <= 2)
bprotein = sum(breakfast[i].protein * x[i] for i in breakfast_range)
lprotein = sum(lunch[i].protein * y[i] for i in lunch_range)
@constraint(model, bprotein + lprotein >= minprotein * 2)
@objective(model, Min, sum(x) + sum(y))
optimize!(model)
return breakfast[filter(i -> value(x[i]) > 0.5, 1:end)], lunch[filter(i -> value(y[i]) > 0.5, 1:end)]
end
function calculateplan(lunch)
range = 1:size(lunch, 1)
model = Model(HiGHS.Optimizer)
set_silent(model)
@variable(model, x[range], Bin)
@constraint(model, mincalories <= sum(lunch[i].calories * x[i] for i in range) <= maxcalories)
@constraint(model, sum(lunch[i].meat * x[i] for i in range) <= 2)
@constraint(model, sum(lunch[i].protein * x[i] for i in range) >= minprotein)
@objective(model, Min, sum(x))
optimize!(model)
return lunch[filter(i -> value(x[i]) > 0.5, 1:end)]
end
end