... | ... |
@@ -2,41 +2,97 @@ import React, { PureComponent } from 'react'; |
2 | 2 |
import Form from './components/Form'; |
3 | 3 |
import Gantt from './components/Gantt'; |
4 | 4 |
import { normalizeData } from './ganttUtils'; |
5 |
+import algorithms from './algorithms'; |
|
6 |
+import { filter, flatten, identity, map, pluck, range, times } from 'rambda'; |
|
7 |
+import uuid from 'uuid/v4'; |
|
8 |
+import { selectAlgorithms, fastestResult, najneomeskanejsiResult } from './selectAlgorithm'; |
|
5 | 9 |
|
6 |
-const algoData = [ |
|
7 |
- [ |
|
8 |
- { startTime: 1, endTime: 3, processor: 1, name: 'UI dizajnér' }, |
|
9 |
- { startTime: 4, endTime: 6, processor: 1, name: 'UX dizajnér' }, |
|
10 |
- { startTime: 6, endTime: 10, processor: 1, name: 'UI dizajnér' }, |
|
11 |
- ], |
|
12 |
- [ |
|
13 |
- { startTime: 6, endTime: 10, processor: 2, name: 'UI dizajnér' }, |
|
14 |
- { startTime: 22, endTime: 23, processor: 2, name: 'UI dizajnér' }, |
|
15 |
- { startTime: 23, endTime: 24, processor: 2, name: 'UI dizajnér' }, |
|
16 |
- { startTime: 24, endTime: 25, processor: 2, name: 'UI dizajnér' }, |
|
17 |
- ], |
|
18 |
- [ |
|
19 |
- { startTime: 10, endTime: 16, processor: 3, name: 'UI dizajnér' }, |
|
20 |
- ], |
|
21 |
- [ |
|
22 |
- { startTime: 10, endTime: 16, processor: 4, name: 'UI dizajnér' }, |
|
23 |
- ], |
|
24 |
- [ |
|
25 |
- { startTime: 10, endTime: 16, processor: 5, name: 'UI dizajnér' }, |
|
26 |
- ], |
|
27 |
- [ |
|
28 |
- { startTime: 10, endTime: 16, processor: 6, name: 'UI dizajnér' }, |
|
29 |
- ] |
|
30 |
-]; |
|
31 |
- |
|
32 |
-const data = normalizeData(algoData); |
|
10 |
+// const algoData = [ |
|
11 |
+// [ |
|
12 |
+// { startTime: 1, endTime: 3, processor: 1, name: 'UI dizajnér' }, |
|
13 |
+// { startTime: 4, endTime: 6, processor: 1, name: 'UX dizajnér' }, |
|
14 |
+// { startTime: 6, endTime: 10, processor: 1, name: 'UI dizajnér' }, |
|
15 |
+// ] |
|
16 |
+// ]; |
|
17 |
+// |
|
18 |
+// const data = normalizeData(algoData); |
|
33 | 19 |
|
34 | 20 |
class App extends PureComponent { |
21 |
+ state = { ganttData: null }; |
|
22 |
+ |
|
23 |
+ onFormSubmit = ({ preempt, rows }) => { |
|
24 |
+ const algorithmNames = selectAlgorithms(rows, preempt); |
|
25 |
+ |
|
26 |
+ if (!algorithmNames) { |
|
27 |
+ alert('Pre zvolenú konfiguráciu sa nenašiel vhodný algoritmus'); |
|
28 |
+ return; |
|
29 |
+ } |
|
30 |
+ |
|
31 |
+ const results = map(algorithmName => { |
|
32 |
+ const algorithm = algorithms[algorithmName]; |
|
33 |
+ |
|
34 |
+ let jobTimes; |
|
35 |
+ |
|
36 |
+ if (algorithmName === 'johnson' || algorithmName === 'palmer' || algorithmName === 'grupt' || algorithmName === 'campbel') { |
|
37 |
+ jobTimes = times(i => ({ |
|
38 |
+ jobId: uuid(), |
|
39 |
+ operations: map(j => ({ t: rows[j].jobs[i].t }), range(0, rows.length)) |
|
40 |
+ }), rows[0].jobs.length); |
|
41 |
+ } else if (algorithmName === 'mcnaught' || algorithmName === 'vahy') { |
|
42 |
+ jobTimes = flatten(pluck('jobs', rows)); |
|
43 |
+ jobTimes = map(jobTime => ({ |
|
44 |
+ ...jobTime, |
|
45 |
+ jobId: jobTime.id, |
|
46 |
+ anc: map(anc => anc.split(':')[1], jobTime.anc || []) }), jobTimes); |
|
47 |
+ } else { |
|
48 |
+ jobTimes = map(job => ({ |
|
49 |
+ jobId: job.id, |
|
50 |
+ t: +job.t, |
|
51 |
+ d: +job.d, |
|
52 |
+ w: +job.w, |
|
53 |
+ name: job.name, |
|
54 |
+ anc: map(anc => anc.split(':')[1], job.anc || []) |
|
55 |
+ }), rows[0].jobs); |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+ // console.log(jobTimes); |
|
59 |
+ // return; |
|
60 |
+ |
|
61 |
+ // console.log(rows[1].jobs); |
|
62 |
+ |
|
63 |
+ const result = algorithm(jobTimes, rows.length); |
|
64 |
+ return result; |
|
65 |
+ }, algorithmNames); |
|
66 |
+ |
|
67 |
+ const validResults = filter(identity, results); |
|
68 |
+ const fastestIndex = rows.length === 1 |
|
69 |
+ ? najneomeskanejsiResult(validResults) |
|
70 |
+ : fastestResult(validResults); |
|
71 |
+ |
|
72 |
+ if (fastestIndex === -1) { |
|
73 |
+ alert('Nepodarilo sa násť rozvrh pre zvolenú konfiguráciu'); |
|
74 |
+ return; |
|
75 |
+ } |
|
76 |
+ |
|
77 |
+ // console.log(fastestIndex, validResults); |
|
78 |
+ |
|
79 |
+ const ganttData = normalizeData(validResults[fastestIndex]); |
|
80 |
+ |
|
81 |
+ console.log(validResults[fastestIndex]); |
|
82 |
+ |
|
83 |
+ this.setState({ |
|
84 |
+ ganttData |
|
85 |
+ }); |
|
86 |
+ }; |
|
87 |
+ |
|
35 | 88 |
render() { |
89 |
+ const { ganttData } = this.state; |
|
90 |
+ |
|
36 | 91 |
return ( |
37 | 92 |
<div style={{ padding: 20 }}> |
38 |
- <Form /> |
|
39 |
- <Gantt data={data} /> |
|
93 |
+ <Form onSubmit={this.onFormSubmit} /> |
|
94 |
+ {ganttData && <Gantt data={ganttData}/>} |
|
95 |
+ {/*<Gantt data={data} />*/} |
|
40 | 96 |
</div> |
41 | 97 |
); |
42 | 98 |
} |
... | ... |
@@ -1,31 +1,16 @@ |
1 | 1 |
import { |
2 | 2 |
addIndex, |
3 |
- findIndex, |
|
4 | 3 |
find, |
5 | 4 |
map, |
6 | 5 |
propEq, |
7 | 6 |
reduce, |
8 |
- sort, |
|
9 | 7 |
range, |
10 |
- concat, |
|
11 |
- filter, |
|
12 |
- reverse, |
|
13 |
- prop, |
|
14 |
- flatten, |
|
15 |
- uniq, |
|
16 |
- reject, |
|
17 |
- contains, |
|
18 |
- omit, |
|
19 |
- equals, |
|
20 |
- update, |
|
21 | 8 |
times, |
22 |
- add, |
|
23 |
- pluck, |
|
24 | 9 |
last |
25 | 10 |
} from 'rambda'; |
26 | 11 |
import type { JobId } from '../../types'; |
27 |
-import { max, min, remove, slice } from 'ramda'; |
|
28 | 12 |
import { createSchedule as johnson, updateCTime } from '../johnson'; |
13 |
+import { max } from 'ramda'; |
|
29 | 14 |
|
30 | 15 |
type JobOperations = { |
31 | 16 |
jobId: JobId, |
... | ... |
@@ -37,7 +22,26 @@ type JobOperationsWithC = { |
37 | 37 |
operations: Array<{ t: number, c: number }> |
38 | 38 |
}; |
39 | 39 |
|
40 |
+export const fillCorrupted = (jobsOperations: JobOperations[]) => { |
|
41 |
+ const maxOperationsCount = reduce((acc, { operations }) => max(operations.length, acc), 0, jobsOperations); |
|
42 |
+ |
|
43 |
+ return map( |
|
44 |
+ ({ operations, ...rest }) => { |
|
45 |
+ const fixedOperations = [...operations]; |
|
46 |
+ fixedOperations.length = maxOperationsCount; |
|
47 |
+ |
|
48 |
+ return { |
|
49 |
+ ...rest, |
|
50 |
+ operations: fixedOperations.fill({ t: 0 }, operations.length, maxOperationsCount) |
|
51 |
+ }; |
|
52 |
+ }, |
|
53 |
+ jobsOperations |
|
54 |
+ ) |
|
55 |
+}; |
|
56 |
+ |
|
40 | 57 |
export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => { |
58 |
+ jobsOperations = fillCorrupted(jobsOperations); |
|
59 |
+ |
|
41 | 60 |
const allSchedules = map(k => { |
42 | 61 |
const jobsOperationsForK = map((jobsOperation: JobOperations) => { |
43 | 62 |
const t1 = reduce((acc, i) => acc + jobsOperation.operations[i].t, 0, range(0, k)); |
... | ... |
@@ -2,6 +2,7 @@ import { map, reduce, sort, range, omit, times } from 'rambda'; |
2 | 2 |
import type { JobId } from '../../types'; |
3 | 3 |
import { updateCTime } from '../johnson'; |
4 | 4 |
import { min } from 'ramda'; |
5 |
+import { fillCorrupted } from '../campbel'; |
|
5 | 6 |
|
6 | 7 |
type JobOperations = { |
7 | 8 |
jobId: JobId, |
... | ... |
@@ -14,6 +15,8 @@ type JobOperationsWithC = { |
14 | 14 |
}; |
15 | 15 |
|
16 | 16 |
export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => { |
17 |
+ jobsOperations = fillCorrupted(jobsOperations); |
|
18 |
+ |
|
17 | 19 |
const jobsOperationsWithKoefs = map((jobsOperation: JobOperations) => { |
18 | 20 |
const ej = jobsOperation.operations[0].t < jobsOperation.operations[processorsCount - 1].t |
19 | 21 |
? 1 : -1; |
20 | 22 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,21 @@ |
0 |
+import campbel from './campbel'; |
|
1 |
+import grupt from './grupt'; |
|
2 |
+import johnson from './johnson'; |
|
3 |
+import lawler from './lawler'; |
|
4 |
+import mcnaught from './mcnaught'; |
|
5 |
+import moore from './moore'; |
|
6 |
+import palmer from './palmer'; |
|
7 |
+import smith from './smith'; |
|
8 |
+import vahy from './vahy'; |
|
9 |
+ |
|
10 |
+export default { |
|
11 |
+ campbel, |
|
12 |
+ grupt, |
|
13 |
+ johnson, |
|
14 |
+ lawler, |
|
15 |
+ mcnaught, |
|
16 |
+ moore, |
|
17 |
+ palmer, |
|
18 |
+ smith, |
|
19 |
+ vahy |
|
20 |
+}; |
|
0 | 21 |
\ No newline at end of file |
... | ... |
@@ -1,18 +1,19 @@ |
1 | 1 |
import { |
2 |
- addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
3 |
- filter, reverse, prop, flatten, uniq, reject, contains, omit, equals, update, times |
|
2 |
+ findIndex, map, reduce, sort, concat, |
|
3 |
+ filter, reverse, equals, update, times |
|
4 | 4 |
} from 'rambda'; |
5 | 5 |
import type { JobId } from '../../types'; |
6 |
-import { max, min, remove, slice } from 'ramda'; |
|
6 |
+import { remove } from 'ramda'; |
|
7 |
+import { fillCorrupted } from '../campbel'; |
|
7 | 8 |
|
8 | 9 |
type JobOperations = { |
9 | 10 |
jobId: JobId, |
10 |
- operations: Array<{ t: number }> |
|
11 |
+ operations: [{ t: number }, { t: number }] |
|
11 | 12 |
}; |
12 | 13 |
|
13 | 14 |
type JobOperationsWithC = { |
14 | 15 |
jobId: JobId, |
15 |
- operations: Array<{ t: number, c: number }> |
|
16 |
+ operations: [{ t: number, c: number }, { t: number, c: number }] |
|
16 | 17 |
}; |
17 | 18 |
|
18 | 19 |
const findMinT = (operations) => |
... | ... |
@@ -72,6 +73,8 @@ export const updateCTime = (jobsOperations: JobOperations[], processorCount: num |
72 | 72 |
}; |
73 | 73 |
|
74 | 74 |
export const createSchedule = (jobsOperations: JobOperations[]): JobOperationsWithC[] => { |
75 |
+ jobsOperations = fillCorrupted(jobsOperations); |
|
76 |
+ |
|
75 | 77 |
let I = jobsOperations; |
76 | 78 |
let L = [[], []]; |
77 | 79 |
|
... | ... |
@@ -1,26 +1,18 @@ |
1 | 1 |
import { |
2 |
- addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
3 |
- filter, reverse, prop, flatten, uniq, reject, contains, omit |
|
2 |
+ findIndex, map, propEq, reduce, |
|
3 |
+ reverse, prop, flatten, uniq, reject, contains, omit |
|
4 | 4 |
} from 'rambda'; |
5 | 5 |
import type { JobId } from '../../types'; |
6 |
-import { max, remove, slice } from 'ramda'; |
|
6 |
+import { max, remove } from 'ramda'; |
|
7 | 7 |
|
8 | 8 |
type JobTime = { |
9 | 9 |
jobId: JobId, |
10 | 10 |
t: number, |
11 | 11 |
d: number, |
12 |
- c: number, |
|
13 | 12 |
w: number, |
14 | 13 |
anc: JobId[], |
15 | 14 |
}; |
16 | 15 |
|
17 |
-const findMaxTJob = (jobTimes: JobTime[]) => |
|
18 |
- reduce( |
|
19 |
- (maxTJob: JobTime, jobTime: JobTime) => maxTJob.t >= jobTime.t ? maxTJob : jobTime, |
|
20 |
- { t: -Infinity }, |
|
21 |
- jobTimes |
|
22 |
- ); |
|
23 |
- |
|
24 | 16 |
const findMinJob = (fi: number, jobTimes: JobTime[]) => |
25 | 17 |
reduce( |
26 | 18 |
(minJob: JobTime, jobTime: JobTime) => { |
... | ... |
@@ -45,7 +37,13 @@ const updateCTime = (jobTimes: JobTime[]) => { |
45 | 45 |
return cj; |
46 | 46 |
}; |
47 | 47 |
|
48 |
+const fillCorrupted = (jobs: JobTime[]) => map( |
|
49 |
+ ({ d, w, anc, ...rest }) => ({ ...rest, d: d || 1000, w: w || 1, anc: anc || [] }), |
|
50 |
+ jobs); |
|
51 |
+ |
|
48 | 52 |
export const createSchedule = (jobs: JobTime[]) => { |
53 |
+ jobs = fillCorrupted(jobs); |
|
54 |
+ |
|
49 | 55 |
let fi = reduce((sum, job: JobTime) => sum + job.t, 0, jobs); |
50 | 56 |
let jobsD = jobs; |
51 | 57 |
let R = []; |
... | ... |
@@ -76,10 +74,13 @@ export const createSchedule = (jobs: JobTime[]) => { |
76 | 76 |
export default (jobs: JobTime[]) => { |
77 | 77 |
const schedule = createSchedule(jobs); |
78 | 78 |
|
79 |
+ if (!schedule) return null; |
|
80 |
+ |
|
79 | 81 |
const normalizedSchedule = map((jobTime: JobTime) => ({ |
80 | 82 |
processor: 1, |
81 | 83 |
startTime: jobTime.c - jobTime.t, |
82 | 84 |
endTime: jobTime.c, |
85 |
+ delayed: max(0, jobTime.c - jobTime.d) |
|
83 | 86 |
}), schedule); |
84 | 87 |
|
85 | 88 |
return [normalizedSchedule]; |
... | ... |
@@ -50,15 +50,15 @@ test('create gantt', () => { |
50 | 50 |
|
51 | 51 |
expect(template.length).toBe(1); |
52 | 52 |
expect(template[0].length).toBe(11); |
53 |
- expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6 }); |
|
54 |
- expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 14 }); |
|
55 |
- expect(template[0][2]).toEqual({ processor: 1, startTime: 14, endTime: 19 }); |
|
56 |
- expect(template[0][3]).toEqual({ processor: 1, startTime: 19, endTime: 24 }); |
|
57 |
- expect(template[0][4]).toEqual({ processor: 1, startTime: 24, endTime: 27 }); |
|
58 |
- expect(template[0][5]).toEqual({ processor: 1, startTime: 27, endTime: 36 }); |
|
59 |
- expect(template[0][6]).toEqual({ processor: 1, startTime: 36, endTime: 45 }); |
|
60 |
- expect(template[0][7]).toEqual({ processor: 1, startTime: 45, endTime: 50 }); |
|
61 |
- expect(template[0][8]).toEqual({ processor: 1, startTime: 50, endTime: 58 }); |
|
62 |
- expect(template[0][9]).toEqual({ processor: 1, startTime: 58, endTime: 63 }); |
|
63 |
- expect(template[0][10]).toEqual({ processor: 1, startTime: 63, endTime: 68 }); |
|
53 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6, delayed: 0 }); |
|
54 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 14, delayed: 3 }); |
|
55 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 14, endTime: 19, delayed: 5 }); |
|
56 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 19, endTime: 24, delayed: 15 }); |
|
57 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 24, endTime: 27, delayed: 13 }); |
|
58 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 27, endTime: 36, delayed: 17 }); |
|
59 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 36, endTime: 45, delayed: 9 }); |
|
60 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 45, endTime: 50, delayed: 11 }); |
|
61 |
+ expect(template[0][8]).toEqual({ processor: 1, startTime: 50, endTime: 58, delayed: 27 }); |
|
62 |
+ expect(template[0][9]).toEqual({ processor: 1, startTime: 58, endTime: 63, delayed: 2 }); |
|
63 |
+ expect(template[0][10]).toEqual({ processor: 1, startTime: 63, endTime: 68, delayed: 9 }); |
|
64 | 64 |
}); |
65 | 65 |
\ No newline at end of file |
... | ... |
@@ -1,9 +1,6 @@ |
1 |
-import { |
|
2 |
- addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
3 |
- filter, reverse, prop, flatten, uniq, reject, contains, omit, times |
|
4 |
-} from 'rambda'; |
|
1 |
+import { map, reduce, times } from 'rambda'; |
|
5 | 2 |
import type { JobId } from '../../types'; |
6 |
-import { groupBy, max, remove, slice } from 'ramda'; |
|
3 |
+import { groupBy, max } from 'ramda'; |
|
7 | 4 |
|
8 | 5 |
type JobTime = { |
9 | 6 |
jobId: JobId, |
... | ... |
@@ -46,8 +43,6 @@ export const createSchedule = (jobs: JobTime[], processorCount: number): JobTime |
46 | 46 |
let processorC = times(() => 0, processorCount + 1); |
47 | 47 |
let activeProcessor = 1; |
48 | 48 |
|
49 |
- let i = 0; |
|
50 |
- |
|
51 | 49 |
while (G.length) { |
52 | 50 |
if (processorC[activeProcessor] + G[0].t <= K) { |
53 | 51 |
processorC[activeProcessor] += G[0].t; |
... | ... |
@@ -65,8 +60,6 @@ export const createSchedule = (jobs: JobTime[], processorCount: number): JobTime |
65 | 65 |
|
66 | 66 |
activeProcessor++; |
67 | 67 |
} |
68 |
- |
|
69 |
- i++; |
|
70 | 68 |
} |
71 | 69 |
|
72 | 70 |
const withC = updateCTime(R); |
... | ... |
@@ -1,21 +1,13 @@ |
1 |
-import { addIndex, findIndex, find, map, propEq, reduce, sort, range, concat } from 'rambda'; |
|
1 |
+import { findIndex, find, map, propEq, reduce, sort, concat } from 'rambda'; |
|
2 | 2 |
import type { JobId } from '../../types'; |
3 |
-import { remove, slice } from 'ramda'; |
|
3 |
+import { max, remove, slice } from 'ramda'; |
|
4 | 4 |
|
5 | 5 |
type JobTime = { |
6 | 6 |
jobId: JobId, |
7 | 7 |
t: number, |
8 | 8 |
d: number, |
9 |
- c: number |
|
10 | 9 |
}; |
11 | 10 |
|
12 |
-const mapWithIndex = addIndex(map); |
|
13 |
- |
|
14 |
-// const byAsc = (a: JobTime, b: JobTime) => a.time - b.time; |
|
15 |
- |
|
16 |
-const findJobById = (jobId: JobId, jobTimes: JobTime[]): JobTime => |
|
17 |
- find(propEq('jobId', jobId), jobTimes); |
|
18 |
- |
|
19 | 11 |
const findLateJob = (jobTimes: JobTime[]) => |
20 | 12 |
find(({ c, d }: JobTime) => c > d, jobTimes); |
21 | 13 |
|
... | ... |
@@ -38,7 +30,13 @@ const updateCTime = (jobTimes: JobTime[]) => { |
38 | 38 |
return cj; |
39 | 39 |
}; |
40 | 40 |
|
41 |
+export const fillCorrupted = (jobs: JobTime[]) => map( |
|
42 |
+ ({ d, ...rest }) => ({ ...rest, d: d || 1000 }), |
|
43 |
+ jobs); |
|
44 |
+ |
|
41 | 45 |
export const createSchedule = (jobs: JobTime[]) => { |
46 |
+ jobs = fillCorrupted(jobs); |
|
47 |
+ |
|
42 | 48 |
const F = sort((a: JobTime, b: JobTime) => a.d - b.d, jobs); |
43 | 49 |
const L = []; |
44 | 50 |
|
... | ... |
@@ -70,6 +68,7 @@ export default (jobs: JobTime[]) => { |
70 | 70 |
processor: 1, |
71 | 71 |
startTime: jobTime.c - jobTime.t, |
72 | 72 |
endTime: jobTime.c, |
73 |
+ delayed: max(0, jobTime.c - jobTime.d) |
|
73 | 74 |
}), schedule); |
74 | 75 |
|
75 | 76 |
return [normalizedSchedule]; |
... | ... |
@@ -44,13 +44,13 @@ test('create gantt', () => { |
44 | 44 |
|
45 | 45 |
expect(template.length).toBe(1); |
46 | 46 |
expect(template[0].length).toBe(9); |
47 |
- expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6 }); |
|
48 |
- expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 12 }); |
|
49 |
- expect(template[0][2]).toEqual({ processor: 1, startTime: 12, endTime: 16 }); |
|
50 |
- expect(template[0][3]).toEqual({ processor: 1, startTime: 16, endTime: 21 }); |
|
51 |
- expect(template[0][4]).toEqual({ processor: 1, startTime: 21, endTime: 24 }); |
|
52 |
- expect(template[0][5]).toEqual({ processor: 1, startTime: 24, endTime: 28 }); |
|
53 |
- expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 34 }); |
|
54 |
- expect(template[0][7]).toEqual({ processor: 1, startTime: 34, endTime: 41 }); |
|
55 |
- expect(template[0][8]).toEqual({ processor: 1, startTime: 41, endTime: 49 }); |
|
47 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6, delayed: 0 }); |
|
48 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 12, delayed: 0 }); |
|
49 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 12, endTime: 16, delayed: 0 }); |
|
50 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 16, endTime: 21, delayed: 0 }); |
|
51 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 21, endTime: 24, delayed: 0 }); |
|
52 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 24, endTime: 28, delayed: 0 }); |
|
53 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 34, delayed: 0 }); |
|
54 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 34, endTime: 41, delayed: 21 }); |
|
55 |
+ expect(template[0][8]).toEqual({ processor: 1, startTime: 41, endTime: 49, delayed: 24 }); |
|
56 | 56 |
}); |
57 | 57 |
\ No newline at end of file |
... | ... |
@@ -1,9 +1,9 @@ |
1 | 1 |
import { |
2 |
- addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
3 |
- filter, reverse, prop, flatten, uniq, reject, contains, omit, equals, update, times |
|
2 |
+ map, reduce, sort, range, omit, times |
|
4 | 3 |
} from 'rambda'; |
5 | 4 |
import type { JobId } from '../../types'; |
6 | 5 |
import { updateCTime } from '../johnson'; |
6 |
+import { fillCorrupted } from '../campbel'; |
|
7 | 7 |
|
8 | 8 |
type JobOperations = { |
9 | 9 |
jobId: JobId, |
... | ... |
@@ -16,6 +16,8 @@ type JobOperationsWithC = { |
16 | 16 |
}; |
17 | 17 |
|
18 | 18 |
export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => { |
19 |
+ jobsOperations = fillCorrupted(jobsOperations); |
|
20 |
+ |
|
19 | 21 |
const jobsOperationsWithKoefs = map(jobsOperation => { |
20 | 22 |
const koef = reduce( |
21 | 23 |
(acc, i) => acc + Math.abs(processorsCount - 2 * i + 1) * jobsOperation.operations[i-1].t, |
... | ... |
@@ -1,15 +1,14 @@ |
1 | 1 |
import { |
2 |
- addIndex, findIndex, find, map, propEq, reduce, sort, range, concat, |
|
3 |
- filter, reverse |
|
2 |
+ findIndex, map, propEq, reduce, filter, reverse |
|
4 | 3 |
} from 'rambda'; |
5 | 4 |
import type { JobId } from '../../types'; |
6 |
-import { remove, slice } from 'ramda'; |
|
5 |
+import { max, remove } from 'ramda'; |
|
6 |
+import { fillCorrupted } from '../moore'; |
|
7 | 7 |
|
8 | 8 |
type JobTime = { |
9 | 9 |
jobId: JobId, |
10 | 10 |
t: number, |
11 | 11 |
d: number, |
12 |
- c: number |
|
13 | 12 |
}; |
14 | 13 |
|
15 | 14 |
const findMaxTJob = (jobTimes: JobTime[]) => |
... | ... |
@@ -32,6 +31,8 @@ const updateCTime = (jobTimes: JobTime[]) => { |
32 | 32 |
}; |
33 | 33 |
|
34 | 34 |
export const createSchedule = (jobs: JobTime[]) => { |
35 |
+ jobs = fillCorrupted(jobs); |
|
36 |
+ |
|
35 | 37 |
let fi = reduce((sum, job: JobTime) => sum + job.t, 0, jobs); |
36 | 38 |
let jobsD = jobs; |
37 | 39 |
let R = []; |
... | ... |
@@ -58,10 +59,13 @@ export const createSchedule = (jobs: JobTime[]) => { |
58 | 58 |
export default (jobs: JobTime[]) => { |
59 | 59 |
const schedule = createSchedule(jobs); |
60 | 60 |
|
61 |
+ if (!schedule) return null; |
|
62 |
+ |
|
61 | 63 |
const normalizedSchedule = map((jobTime: JobTime) => ({ |
62 | 64 |
processor: 1, |
63 | 65 |
startTime: jobTime.c - jobTime.t, |
64 | 66 |
endTime: jobTime.c, |
67 |
+ delayed: max(0, jobTime.c - jobTime.d) |
|
65 | 68 |
}), schedule); |
66 | 69 |
|
67 | 70 |
return [normalizedSchedule]; |
... | ... |
@@ -50,15 +50,15 @@ test('create gantt', () => { |
50 | 50 |
|
51 | 51 |
expect(template.length).toBe(1); |
52 | 52 |
expect(template[0].length).toBe(11); |
53 |
- expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 1 }); |
|
54 |
- expect(template[0][1]).toEqual({ processor: 1, startTime: 1, endTime: 5 }); |
|
55 |
- expect(template[0][2]).toEqual({ processor: 1, startTime: 5, endTime: 9 }); |
|
56 |
- expect(template[0][3]).toEqual({ processor: 1, startTime: 9, endTime: 15 }); |
|
57 |
- expect(template[0][4]).toEqual({ processor: 1, startTime: 15, endTime: 21 }); |
|
58 |
- expect(template[0][5]).toEqual({ processor: 1, startTime: 21, endTime: 28 }); |
|
59 |
- expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 30 }); |
|
60 |
- expect(template[0][7]).toEqual({ processor: 1, startTime: 30, endTime: 33 }); |
|
61 |
- expect(template[0][8]).toEqual({ processor: 1, startTime: 33, endTime: 40 }); |
|
62 |
- expect(template[0][9]).toEqual({ processor: 1, startTime: 40, endTime: 46 }); |
|
63 |
- expect(template[0][10]).toEqual({ processor: 1, startTime: 46, endTime: 50 }); |
|
53 |
+ expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 1, delayed: 0 }); |
|
54 |
+ expect(template[0][1]).toEqual({ processor: 1, startTime: 1, endTime: 5, delayed: 0 }); |
|
55 |
+ expect(template[0][2]).toEqual({ processor: 1, startTime: 5, endTime: 9, delayed: 0 }); |
|
56 |
+ expect(template[0][3]).toEqual({ processor: 1, startTime: 9, endTime: 15, delayed: 0 }); |
|
57 |
+ expect(template[0][4]).toEqual({ processor: 1, startTime: 15, endTime: 21, delayed: 0 }); |
|
58 |
+ expect(template[0][5]).toEqual({ processor: 1, startTime: 21, endTime: 28, delayed: 0 }); |
|
59 |
+ expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 30, delayed: 0 }); |
|
60 |
+ expect(template[0][7]).toEqual({ processor: 1, startTime: 30, endTime: 33, delayed: 0 }); |
|
61 |
+ expect(template[0][8]).toEqual({ processor: 1, startTime: 33, endTime: 40, delayed: 0 }); |
|
62 |
+ expect(template[0][9]).toEqual({ processor: 1, startTime: 40, endTime: 46, delayed: 0 }); |
|
63 |
+ expect(template[0][10]).toEqual({ processor: 1, startTime: 46, endTime: 50, delayed: 0 }); |
|
64 | 64 |
}); |
65 | 65 |
\ No newline at end of file |
... | ... |
@@ -1,30 +1,17 @@ |
1 | 1 |
import { |
2 |
- addIndex, |
|
3 |
- findIndex, |
|
4 | 2 |
find, |
5 | 3 |
map, |
6 |
- propEq, |
|
7 | 4 |
reduce, |
8 | 5 |
sort, |
9 |
- range, |
|
10 |
- concat, |
|
11 | 6 |
filter, |
12 |
- reverse, |
|
13 |
- prop, |
|
14 | 7 |
flatten, |
15 |
- uniq, |
|
16 | 8 |
reject, |
17 | 9 |
contains, |
18 | 10 |
omit, |
19 |
- equals, |
|
20 |
- update, |
|
21 |
- times, |
|
22 |
- add, |
|
23 |
- pluck, |
|
24 |
- last, forEach, all |
|
11 |
+ forEach, all |
|
25 | 12 |
} from 'rambda'; |
26 | 13 |
import type { JobId } from '../../types'; |
27 |
-import { groupBy, max, min, remove, slice } from 'ramda'; |
|
14 |
+import { groupBy } from 'ramda'; |
|
28 | 15 |
import { updateCTime } from '../mcnaught'; |
29 | 16 |
|
30 | 17 |
type JobTime = { |
... | ... |
@@ -41,7 +28,13 @@ const noSuccJobs = (jobTimes: JobTime[]) => { |
41 | 41 |
const successors = (id: JobId, jobTimes: JobTime[]) => |
42 | 42 |
filter(({ anc }) => contains(id, anc), jobTimes); |
43 | 43 |
|
44 |
+const fillCorrupted = (jobs: JobTime[]) => map( |
|
45 |
+ ({ anc, ...rest }) => ({ ...rest, anc: anc || [] }), |
|
46 |
+ jobs); |
|
47 |
+ |
|
44 | 48 |
export const createSchedule = (jobTimes: JobTime[], m: number) => { |
49 |
+ jobTimes = fillCorrupted(jobTimes); |
|
50 |
+ |
|
45 | 51 |
let T = reduce((acc, jobTime) => acc + jobTime.t, 0, jobTimes); |
46 | 52 |
|
47 | 53 |
let noSucc = noSuccJobs(jobTimes); |
... | ... |
@@ -124,6 +117,8 @@ export const createSchedule = (jobTimes: JobTime[], m: number) => { |
124 | 124 |
export default (jobTimes: JobTime[], processorsCount: number) => { |
125 | 125 |
const schedule = createSchedule(jobTimes, processorsCount); |
126 | 126 |
|
127 |
+ if (!schedule) return null; |
|
128 |
+ |
|
127 | 129 |
const grouped = Object.values(groupBy(jobTime => jobTime.processor, schedule)); |
128 | 130 |
|
129 | 131 |
return map(map(job => ({ |
... | ... |
@@ -187,6 +187,7 @@ class Box extends React.PureComponent { |
187 | 187 |
as = 'div', |
188 | 188 |
className, |
189 | 189 |
style, |
190 |
+ nativeRef, |
|
190 | 191 |
...props |
191 | 192 |
} = this.props; |
192 | 193 |
|
... | ... |
@@ -204,7 +205,7 @@ class Box extends React.PureComponent { |
204 | 204 |
className |
205 | 205 |
}; |
206 | 206 |
|
207 |
- return React.createElement(as, { ...restProps, ...styles }); |
|
207 |
+ return React.createElement(as, { ...restProps, ...styles, ref: nativeRef }); |
|
208 | 208 |
} |
209 | 209 |
} |
210 | 210 |
|
... | ... |
@@ -12,116 +12,130 @@ import PropTypes from 'proptypes'; |
12 | 12 |
import Select from 'react-select'; |
13 | 13 |
import 'react-select/dist/react-select.css'; |
14 | 14 |
import { |
15 |
- filter, find, findIndex, flatten, forEach, identity, map, prop, propEq, reject, uniq, update |
|
15 |
+ filter, find, flatten, identity, map, prop, propEq, reject, uniq, update |
|
16 | 16 |
} from 'rambda'; |
17 | 17 |
import { insert, remove } from 'ramda'; |
18 | 18 |
import theme from '../theme'; |
19 | 19 |
|
20 |
-const allJobs = { |
|
21 |
- whatever: 'Vševediaci', |
|
22 |
- |
|
23 |
- marketResearch: 'Prieskum trhu', |
|
24 |
- |
|
25 |
- designUi: 'Dizajnér – UI', |
|
26 |
- designUx: 'Dizajnér – UX', |
|
27 |
- |
|
28 |
- architect: 'Softwarový architekt', |
|
29 |
- |
|
30 |
- devFrontend: 'Developer – frontend', |
|
31 |
- devBackend: 'Developer – backend', |
|
32 |
- devDb: 'Developer – databázy', |
|
33 |
- |
|
34 |
- admin: 'Serverový administrátor', |
|
35 |
- |
|
36 |
- tester: 'Tester', |
|
37 |
- |
|
38 |
- analyst: 'Analytik', |
|
39 |
- |
|
40 |
- qa: 'Quality assurance', |
|
41 |
- |
|
42 |
- deployManager: 'Deploy' |
|
43 |
-}; |
|
44 |
- |
|
45 | 20 |
const nextWorkerName = (rows) => |
46 | 21 |
`${rows.length + 1}. pracovník`; |
47 | 22 |
|
48 | 23 |
class Form extends React.PureComponent { |
49 | 24 |
state = { |
25 |
+ allJobs: [], |
|
50 | 26 |
rows: [ |
51 | 27 |
// { |
52 | 28 |
// id: uuid(), |
53 | 29 |
// name: 'pracovnik 1', |
54 |
- // preempt: false, |
|
55 |
- // linkedTo: null, |
|
56 | 30 |
// jobs: [ |
57 |
- // { id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 }, |
|
58 |
- // { id: uuid(), name: 'backend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 } |
|
31 |
+ // { id: uuid(), name: 'frontend', t: '', d: '', w: '', anc: [] }, |
|
32 |
+ // { id: uuid(), name: 'backend', t: '', d: '', w: '', anc: [] } |
|
59 | 33 |
// ] |
60 | 34 |
// }, |
61 | 35 |
// { |
62 | 36 |
// id: uuid(), |
63 | 37 |
// name: 'pracovnik 2', |
64 |
- // preempt: false, |
|
65 |
- // linkedTo: null, |
|
66 | 38 |
// jobs: [ |
67 |
- // { id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 }, |
|
68 |
- // { id: uuid(), name: 'backend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 } |
|
39 |
+ // { id: uuid(), name: 'frontend', t: '', d: '', w: '', anc: [] }, |
|
40 |
+ // { id: uuid(), name: 'backend', t: '', d: '', w: '', anc: [] } |
|
69 | 41 |
// ] |
70 | 42 |
// } |
71 |
- ] |
|
43 |
+ ], |
|
44 |
+ preempt: false, |
|
72 | 45 |
}; |
73 | 46 |
|
74 | 47 |
onSubmit = (values) => { |
75 |
- console.log(values); |
|
48 |
+ if (!values || !values.rows.length) return; |
|
49 |
+ |
|
50 |
+ const { onSubmit } = this.props; |
|
51 |
+ onSubmit(values); |
|
76 | 52 |
}; |
77 | 53 |
|
78 |
- addWorker = (setFieldValue, rows) => { |
|
54 |
+ addWorker = (setFieldValue, rows, allJobs) => { |
|
79 | 55 |
const jobs = [{ |
80 |
- id: uuid(), name: 'whatever', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 |
|
56 |
+ id: uuid(), name: allJobs[0], t: '', d: '', w: '', anc: [] |
|
81 | 57 |
}]; |
82 | 58 |
|
83 | 59 |
const updatedRows = [...rows, { id: uuid(), name: nextWorkerName(rows), jobs } ]; |
84 | 60 |
setFieldValue('rows', updatedRows); |
85 | 61 |
}; |
86 | 62 |
|
63 |
+ addTemplateWorker = (setFieldValue, allJobs) => (e) => { |
|
64 |
+ if (e.keyCode !== undefined && e.keyCode !== 13) return; |
|
65 |
+ |
|
66 |
+ e.preventDefault(); |
|
67 |
+ |
|
68 |
+ const val = this.new.value.replace(/:/g, ''); |
|
69 |
+ const updatedJobs = [...allJobs, val ]; |
|
70 |
+ setFieldValue('allJobs', updatedJobs); |
|
71 |
+ |
|
72 |
+ this.new.value = null; |
|
73 |
+ }; |
|
74 |
+ |
|
87 | 75 |
render() { |
88 | 76 |
return ( |
89 |
- <Formik initialValues={this.state} onSubmit={this.onSubmit} render={({ setFieldValue, values: { rows } }) => ( |
|
77 |
+ <Formik initialValues={this.state} onSubmit={this.onSubmit} render={({ setFieldValue, values: { rows, allJobs } }) => ( |
|
90 | 78 |
<FormikForm> |
91 |
- <h1>Konfigurácia projektu</h1> |
|
92 |
- |
|
93 |
- {rows.length ? |
|
94 |
- <Box flexDirection="row" marginBottom={10}> |
|
95 |
- <Box flexBasis="15%" flexShrink={0}><strong>Pracovník</strong></Box> |
|
96 |
- <Box flexBasis="63%" flexShrink={0}><strong>Úlohy</strong></Box> |
|
97 |
- <Box flexBasis="12%" flexShrink={0} align="center"><strong>Prerušenie</strong></Box> |
|
98 |
- </Box> : null |
|
99 |
- } |
|
100 |
- |
|
101 |
- <FieldArray name="rows" render={arrayHelpers => ( |
|
102 |
- <React.Fragment> |
|
103 |
- {rows.map((row, i) => <Row key={i} {...row} arrayHelpers={arrayHelpers} prefix={`rows.${i}`} index={i} />)} |
|
104 |
- </React.Fragment> |
|
105 |
- )} /> |
|
106 |
- |
|
107 |
- <Box flexDirection="row" marginBottom={10}> |
|
108 |
- <Box |
|
109 |
- flexShrink={0} |
|
110 |
- onClick={() => this.addWorker(setFieldValue, rows)} |
|
111 |
- {...theme.buttons.primary} |
|
112 |
- > |
|
113 |
- + Nový pracovník |
|
114 |
- </Box> |
|
79 |
+ <h1>1/3 Vytvorenie pracovníkov</h1> |
|
80 |
+ <Box flexWrap="wrap" flexDirection="row"> |
|
81 |
+ {allJobs.map((_, key) => ( |
|
82 |
+ <Box key={key} width={300} flexDirection="row" marginBottom={10}> |
|
83 |
+ <Box as={Field} name={`allJobs.${key}`} flex={1} marginRight={5} onChange={({ target: { value } }) => setFieldValue(`allJobs.${key}`, value)} /> |
|
84 |
+ <Box cursor="pointer" onClick={() => setFieldValue(`allJobs`, remove(key, 1, allJobs))} color="red" marginRight={20}>✕</Box> |
|
85 |
+ </Box> |
|
86 |
+ ))} |
|
115 | 87 |
</Box> |
116 | 88 |
|
117 |
- <Box as="button" margin="auto" width="20rem" alignItems="center" padding={5} |
|
118 |
- {...theme.buttons.primary} backgroundColor="#C89C93"> |
|
119 |
- <Box fontSize="1rem">Cieľ:</Box> |
|
120 |
- <Box as="select" marginTop={5}> |
|
121 |
- <option>Minimalizovať počet oneskorených úloh</option> |
|
122 |
- <option>2...</option> |
|
123 |
- </Box> |
|
89 |
+ <Box width={300} flexDirection="row" marginBottom={10}> |
|
90 |
+ <Box as="input" placeholder="Nový pracovník" onKeyDown={this.addTemplateWorker(setFieldValue, allJobs)} nativeRef={c => this.new = c} marginRight={5} flex={1} /> |
|
91 |
+ <Box cursor="pointer" onClick={this.addTemplateWorker(setFieldValue, allJobs)} color="green" fontSize="1rem">+</Box> |
|
124 | 92 |
</Box> |
93 |
+ |
|
94 |
+ {allJobs.length ? ( |
|
95 |
+ <div> |
|
96 |
+ <h1>2/3 Konfigurácia projektu</h1> |
|
97 |
+ |
|
98 |
+ {rows.length ? |
|
99 |
+ <Box flexDirection="row" marginBottom={10}> |
|
100 |
+ <Box flexBasis="20%" flexShrink={0}><strong>Pracovníci</strong></Box> |
|
101 |
+ <Box flexBasis="63%" flexShrink={0}><strong>Úlohy</strong></Box> |
|
102 |
+ </Box> : null |
|
103 |
+ } |
|
104 |
+ |
|
105 |
+ <FieldArray name="rows" render={arrayHelpers => ( |
|
106 |
+ <React.Fragment> |
|
107 |
+ {rows.map((row, i) => <Row key={i} {...row} arrayHelpers={arrayHelpers} |
|
108 |
+ prefix={`rows.${i}`} index={i}/>)} |
|
109 |
+ </React.Fragment> |
|
110 |
+ )}/> |
|
111 |
+ |
|
112 |
+ <Box flexDirection="row" marginBottom={10}> |
|
113 |
+ <Box |
|
114 |
+ flexShrink={0} |
|
115 |
+ onClick={() => this.addWorker(setFieldValue, rows, allJobs)} |
|
116 |
+ {...theme.buttons.primary} |
|
117 |
+ > |
|
118 |
+ + Nový pracovník |
|
119 |
+ </Box> |
|
120 |
+ </Box> |
|
121 |
+ |
|
122 |
+ {rows.length ? |
|
123 |
+ <div> |
|
124 |
+ <Box as="label" justifyContent="center" flexDirection="row"> |
|
125 |
+ <Field type="checkbox" name="preempt"/> |
|
126 |
+ <Box as="em" color="gray" textAlign="center" fontSize="0.9em"> |
|
127 |
+ Úlohy pracovníkov možno prerušiť |
|
128 |
+ </Box> |
|
129 |
+ </Box> |
|
130 |
+ |
|
131 |
+ <Box as="button" margin="auto" marginTop={10} width="20rem" alignItems="center" |
|
132 |
+ padding={5} {...theme.buttons.primary} backgroundColor="#C89C93" fontSize="1rem"> |
|
133 |
+ Vytvoriť rozvrh |
|
134 |
+ </Box> |
|
135 |
+ </div> |
|
136 |
+ : null} |
|
137 |
+ </div> |
|
138 |
+ ) : null} |
|
125 | 139 |
</FormikForm> |
126 | 140 |
)}> |
127 | 141 |
</Formik> |
... | ... |
@@ -159,8 +173,8 @@ const SortableJob = SortableElement( |
159 | 159 |
}; |
160 | 160 |
|
161 | 161 |
render() { |
162 |
- const { myIndex, rowIndex, prefix, job, existingJobs: _existingJobs } = this.props; |
|
163 |
- const { formik: { values: { rows } } } = this.context; |
|
162 |
+ const { prefix, job, existingJobs: _existingJobs } = this.props; |
|
163 |
+ const { formik: { values: { rows, allJobs } } } = this.context; |
|
164 | 164 |
|
165 | 165 |
const existingJobs = map( |
166 | 166 |
value => { |
... | ... |
@@ -168,7 +182,7 @@ const SortableJob = SortableElement( |
168 | 168 |
const row = find(propEq('id', idRow), rows); |
169 | 169 |
const jobName = find(propEq('id', idJob), row.jobs).name; |
170 | 170 |
|
171 |
- return ({ value, label: `${row.name} – ${allJobs[jobName]}` }); |
|
171 |
+ return ({ value, label: `${row.name} – ${jobName}` }); |
|
172 | 172 |
}, |
173 | 173 |
reject( |
174 | 174 |
value => { |
... | ... |
@@ -187,55 +201,50 @@ const SortableJob = SortableElement( |
187 | 187 |
className="sortable-inactive" |
188 | 188 |
> |
189 | 189 |
<Box marginRight={10} fontSize="1.5rem" width={20}> |
190 |
- {!rows[rowIndex].linkedTo && |
|
191 | 190 |
<DragHandle/> |
192 |
- } |
|
193 | 191 |
</Box> |
194 | 192 |
|
195 | 193 |
<Box flex={1} padding={1}> |
196 | 194 |
<Box width={200} marginBottom={10} flexDirection="row"> |
197 |
- <Field component="select" name={`${prefix}.name`} disabled={rows[rowIndex].linkedTo}> |
|
198 |
- {Object.keys(allJobs).map(key => ( |
|
199 |
- <option value={key} key={key}>{allJobs[key]}</option> |
|
195 |
+ <Field component="select" name={`${prefix}.name`}> |
|
196 |
+ {allJobs.map((job, key) => ( |
|
197 |
+ <option value={job} key={key}>{job}</option> |
|
200 | 198 |
))} |
201 | 199 |
</Field> |
202 |
- {!rows[rowIndex].linkedTo && |
|
203 | 200 |
<Box cursor="pointer" onClick={this.deleteJob} marginLeft={5} color="red">✕</Box> |
204 |
- } |
|
205 | 201 |
</Box> |
206 | 202 |
|
207 | 203 |
<Box flexDirection="row" justifyContent="space-between" marginBottom={10}> |
208 | 204 |
<Box flexBasis="30%"> |
209 | 205 |
<label> |
210 | 206 |
Trvanie: <br /> |
211 |
- <Field type="number" step={1} name={`${prefix}.t`} style={{ width: '100%' }} /> |
|
207 |
+ <Field type="number" min={0} step={1} name={`${prefix}.t`} style={{ width: '100%' }} required /> |
|
212 | 208 |
</label> |
213 | 209 |
</Box> |
214 | 210 |
<Box flexBasis="30%"> |
215 | 211 |
<label> |
216 | 212 |
Deadline: <br /> |
217 |
- <Field type="number" step={1} name={`${prefix}.d`} style={{ width: '100%' }} /> |
|
213 |
+ <Field type="number" min={0} step={1} name={`${prefix}.d`} style={{ width: '100%' }} disabled={rows.length > 1} /> |
|
218 | 214 |
</label> |
219 | 215 |
</Box> |
220 | 216 |
<Box flexBasis="30%"> |
221 | 217 |
<label> |
222 |
- Váha: <br /> |
|
223 |
- <Field type="number" min={0} max={1} step={0.1} name={`${prefix}.w`} style={{ width: '100%' }} /> |
|
218 |
+ Váha: (1-10) <br /> |
|
219 |
+ <Field type="number" min={1} max={10} step={1} name={`${prefix}.w`} style={{ width: '100%' }} /> |
|
224 | 220 |
</label> |
225 | 221 |
</Box> |
226 | 222 |
</Box> |
227 | 223 |
|
228 | 224 |
<Box> |
229 |
- <Box as="label" flexDirection="row" marginBottom={5}> |
|
230 |
- <Field type="radio" name={`${prefix}.succOrAnc`} value={0} checked={job.succOrAnc == '0'} disabled={rows[rowIndex].linkedTo}/> Bez nadväznosti |
|
231 |
- </Box> |
|
225 |
+ {/*<Box as="label" flexDirection="row" marginBottom={5}>*/} |
|
226 |
+ {/*<Field type="radio" name={`${prefix}.succOrAnc`} value={0} checked={job.succOrAnc == '0'} disabled={rows[rowIndex].linkedTo}/> Bez nadväznosti*/} |
|
227 |
+ {/*</Box>*/} |
|
232 | 228 |
<Box marginBottom={5}> |
233 | 229 |
<Box as="label" flexDirection="row"> |
234 |
- <Field type="radio" name={`${prefix}.succOrAnc`} value={1} checked={job.succOrAnc == '1'} disabled={rows[rowIndex].linkedTo} /> Predchodcovia |
|
230 |
+ Predchodcovia |
|
235 | 231 |
</Box> |
236 |
- <Box flex={1} marginLeft={10} marginTop={5}> |
|
232 |
+ <Box flex={1} marginTop={5}> |
|
237 | 233 |
<Select |
238 |
- disabled={job.succOrAnc != '1' || rows[rowIndex].linkedTo} |
|
239 | 234 |
multi |
240 | 235 |
onChange={e => this.handleSelectChange('anc', e)} |
241 | 236 |
options={existingJobs} |
... | ... |
@@ -244,21 +253,6 @@ const SortableJob = SortableElement( |
244 | 244 |
/> |
245 | 245 |
</Box> |
246 | 246 |
</Box> |
247 |
- <Box> |
|
248 |
- <Box as="label" flexDirection="row"> |
|
249 |
- <Field type="radio" name={`${prefix}.succOrAnc`} value={2} checked={job.succOrAnc == '2'} disabled={rows[rowIndex].linkedTo} /> Nasledovníci |
|
250 |
- </Box> |
|
251 |
- <Box flex={1} marginLeft={10} marginTop={5}> |
|
252 |
- <Select |
|
253 |
- disabled={job.succOrAnc != '2' || rows[rowIndex].linkedTo} |
|
254 |
- multi |
|
255 |
- onChange={e => this.handleSelectChange('succ', e)} |
|
256 |
- options={existingJobs} |
|
257 |
- removeSelected |
|
258 |
- value={job.succ} |
|
259 |
- /> |
|
260 |
- </Box> |
|
261 |
- </Box> |
|
262 | 247 |
</Box> |
263 | 248 |
</Box> |
264 | 249 |
</Box> |
... | ... |
@@ -322,9 +316,9 @@ class Row extends React.PureComponent { |
322 | 322 |
|
323 | 323 |
addJob = () => { |
324 | 324 |
const { index } = this.props; |
325 |
- const { formik: { setFieldValue, values: { rows } } } = this.context; |
|
325 |
+ const { formik: { setFieldValue, values: { rows, allJobs } } } = this.context; |
|
326 | 326 |
const newJob = { |
327 |
- id: uuid(), name: 'whatever', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 |
|
327 |
+ id: uuid(), name: allJobs[0], t: '', d: '', w: '', anc: [] |
|
328 | 328 |
}; |
329 | 329 |
|
330 | 330 |
const jobs = [...rows[index].jobs, newJob]; |
... | ... |
@@ -333,27 +327,20 @@ class Row extends React.PureComponent { |
333 | 333 |
}; |
334 | 334 |
|
335 | 335 |
deleteRow = () => { |
336 |
- const { index, id } = this.props; |
|
336 |
+ const { index } = this.props; |
|
337 | 337 |
const { formik: { setFieldValue, values: { rows } } } = this.context; |
338 | 338 |
|
339 | 339 |
let newRows = remove(index, 1, rows); |
340 | 340 |
|
341 |
- const alsoDelete = filter(propEq('linkedTo', id), rows); |
|
342 |
- |
|
343 |
- forEach(row => { |
|
344 |
- const index = findIndex(propEq('id', row.id), newRows); |
|
345 |
- newRows = remove(index, 1, newRows); |
|
346 |
- }, alsoDelete); |
|
347 |
- |
|
348 | 341 |
setFieldValue('rows', newRows); |
349 | 342 |
}; |
350 | 343 |
|
351 | 344 |
duplicate = () => { |
352 |
- const { id, index, name, preempt, jobs } = this.props; |
|
345 |
+ const { index, name, preempt, jobs } = this.props; |
|
353 | 346 |
const { formik: { setFieldValue, values: { rows } } } = this.context; |
354 | 347 |
|
355 | 348 |
const jobsCopy = map(job => ({ ...job }), jobs); |
356 |
- const newRow = { id: uuid(), name: `${name} – kópia`, preempt, linkedTo: id, jobs: jobsCopy }; |
|
349 |
+ const newRow = { id: uuid(), name: `${name} – kópia`, preempt, jobs: jobsCopy }; |
|
357 | 350 |
|
358 | 351 |
const updatedRows = insert(index + 1, newRow, rows); |
359 | 352 |
|
... | ... |
@@ -361,16 +348,16 @@ class Row extends React.PureComponent { |
361 | 361 |
}; |
362 | 362 |
|
363 | 363 |
render() { |
364 |
- const { index, prefix, name, preempt, linkedTo, jobs } = this.props; |
|
364 |
+ const { index, prefix, name, jobs } = this.props; |
|
365 | 365 |
const { draggingIndex } = this.state; |
366 | 366 |
|
367 | 367 |
const existingJobs = this.findExistingJobs(); |
368 | 368 |
|
369 | 369 |
return ( |
370 | 370 |
<Box flexDirection="row" marginBottom={30} padding={10} className="row"> |
371 |
- <Box flexBasis="15%" flexShrink={0} paddingRight={10}> |
|
371 |
+ <Box flexBasis="20%" flexShrink={0} paddingRight={10} flexDirection="row"> |
|
372 | 372 |
<strong>{name}</strong> |
373 |
- <Box cursor="pointer" onClick={this.deleteRow} color="red">✕</Box> |
|
373 |
+ <Box cursor="pointer" onClick={this.deleteRow} color="red" marginLeft={5}>✕</Box> |
|
374 | 374 |
</Box> |
375 | 375 |
<Box flexBasis="63%" flexShrink={0}> |
376 | 376 |
<FieldArray render={() => ( |
... | ... |
@@ -386,22 +373,11 @@ class Row extends React.PureComponent { |
386 | 386 |
draggingIndex={draggingIndex} |
387 | 387 |
/> |
388 | 388 |
)} /> |
389 |
- {!linkedTo && |
|
390 | 389 |
<Box onClick={this.addJob} {...theme.buttons.secondary} alignSelf="flex-start"> |
391 | 390 |
+ Nová úloha |
392 | 391 |
</Box> |
393 |
- } |
|
394 |
- </Box> |
|
395 |
- <Box flexBasis="12%" flexShrink={0}> |
|
396 |
- <Box as="label" alignItems="center" paddingTop={10}> |
|
397 |
- <Field type="checkbox" name={`${prefix}.preempt`} disabled={linkedTo} /> |
|
398 |
- <Box as="em" color="gray" textAlign="center" padding={10} fontSize="0.9em"> |
|
399 |
- Úlohy pracovníka možno prerušiť |
|
400 |
- </Box> |
|
401 |
- </Box> |
|
402 | 392 |
</Box> |
403 |
- <Box alignItems="flex-end"> |
|
404 |
- {!linkedTo && |
|
393 |
+ <Box alignItems="center" flex={1}> |
|
405 | 394 |
<Box |
406 | 395 |
onClick={this.duplicate} |
407 | 396 |
{...theme.buttons.tertiary} |
... | ... |
@@ -413,7 +389,6 @@ class Row extends React.PureComponent { |
413 | 413 |
> |
414 | 414 |
<span style={{ fontSize: '1.8em', paddingRight: '0.2em' }}>⩇</span> Duplikovať |
415 | 415 |
</Box> |
416 |
- } |
|
417 | 416 |
</Box> |
418 | 417 |
</Box> |
419 | 418 |
) |
... | ... |
@@ -61,7 +61,7 @@ class Gannt extends React.PureComponent { |
61 | 61 |
|
62 | 62 |
return ( |
63 | 63 |
<div ref={measureRef}> |
64 |
- <h1>Ganttov diagram</h1> |
|
64 |
+ <h1>3/3 Ganttov diagram</h1> |
|
65 | 65 |
|
66 | 66 |
<ScatterChart width={width} height={height} data={data[0]} margin={theme.gantt.margin}> |
67 | 67 |
<CartesianGrid |
... | ... |
@@ -48,13 +48,17 @@ export const normalizeData = data => |
48 | 48 |
|
49 | 49 |
forEach(startTime => { |
50 | 50 |
if (!dataObj[startTime]) { |
51 |
- dataObj[startTime] = { startTime }; |
|
51 |
+ dataObj[startTime] = { startTime, processor: last(processorData).processor }; |
|
52 | 52 |
} |
53 | 53 |
}, rangeTime); |
54 | 54 |
|
55 | 55 |
const filledData = Object.values(dataObj); |
56 | 56 |
|
57 |
- filledData.push({ startTime: last(processorData).endTime }); |
|
57 |
+ filledData.push({ |
|
58 |
+ startTime: last(processorData).endTime, |
|
59 |
+ endTime: last(processorData).endTime + step, |
|
60 |
+ processor: last(processorData).processor |
|
61 |
+ }); |
|
58 | 62 |
|
59 | 63 |
return filledData; |
60 | 64 |
}, data); |
61 | 65 |
\ No newline at end of file |
62 | 66 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,71 @@ |
0 |
+import { find, indexOf, last, map, prop, reduce, sort } from 'rambda'; |
|
1 |
+import { max } from 'ramda'; |
|
2 |
+ |
|
3 |
+export const selectAlgorithms = (rows, preempt) => { |
|
4 |
+ const processorsCount = rows.length; |
|
5 |
+ |
|
6 |
+ if (!processorsCount) return null; |
|
7 |
+ |
|
8 |
+ if (processorsCount === 1) { |
|
9 |
+ const existD = !!find(prop('d'), rows[0].jobs); |
|
10 |
+ const existAnc = !!find(({ anc }) => Array.isArray(anc) && anc.length, rows[0].jobs); |
|
11 |
+ |
|
12 |
+ if (existAnc) { |
|
13 |
+ return ['lawler']; |
|
14 |
+ } |
|
15 |
+ |
|
16 |
+ if (existD) { |
|
17 |
+ return ['moore', 'smith']; |
|
18 |
+ } |
|
19 |
+ |
|
20 |
+ return ['moore']; |
|
21 |
+ } |
|
22 |
+ |
|
23 |
+ const existAnc = find( |
|
24 |
+ ({ jobs }) => !!find(({ anc }) => Array.isArray(anc) && anc.length, jobs), |
|
25 |
+ rows); |
|
26 |
+ |
|
27 |
+ if (existAnc) { |
|
28 |
+ return ['vahy']; |
|
29 |
+ } |
|
30 |
+ |
|
31 |
+ let multi = ['campbel', 'grupt', 'palmer']; |
|
32 |
+ |
|
33 |
+ if (processorsCount === 2) { |
|
34 |
+ multi.push('johnson'); |
|
35 |
+ } |
|
36 |
+ |
|
37 |
+ if (preempt) { |
|
38 |
+ multi.push('mcnaught'); |
|
39 |
+ } |
|
40 |
+ |
|
41 |
+ return multi; |
|
42 |
+}; |
|
43 |
+ |
|
44 |
+export const najneomeskanejsiResult = (algoResults: []) => { |
|
45 |
+ if (!algoResults.length) return -1; |
|
46 |
+ |
|
47 |
+ const delaysSum = reduce((acc, processorJobs) => { |
|
48 |
+ const delays = reduce((acc, { delayed }) => acc + delayed, 0, processorJobs); |
|
49 |
+ return acc + delays; |
|
50 |
+ }, 0); |
|
51 |
+ |
|
52 |
+ const delaysTimes = map(delaysSum, algoResults); |
|
53 |
+ const sortedDelaysTimes = sort((a, b) => a - b, delaysTimes); |
|
54 |
+ |
|
55 |
+ return indexOf(sortedDelaysTimes[0], delaysTimes); |
|
56 |
+}; |
|
57 |
+ |
|
58 |
+export const fastestResult = (algoResults: []) => { |
|
59 |
+ if (!algoResults.length) return -1; |
|
60 |
+ |
|
61 |
+ const slowestProcessorEndTime = reduce((endTime, processorJobs) => { |
|
62 |
+ const lastEndTime = last(processorJobs).endTime; |
|
63 |
+ return max(lastEndTime, endTime); |
|
64 |
+ }, 0); |
|
65 |
+ |
|
66 |
+ const endTimes = map(slowestProcessorEndTime, algoResults); |
|
67 |
+ const sortedEndTimes = sort((a, b) => a - b, endTimes); |
|
68 |
+ |
|
69 |
+ return indexOf(sortedEndTimes[0], endTimes); |
|
70 |
+}; |
|
0 | 71 |
\ No newline at end of file |
1 | 72 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,52 @@ |
0 |
+import { fastestResult, najneomeskanejsiResult } from './selectAlgorithm'; |
|
1 |
+ |
|
2 |
+test('find fastest result', () => { |
|
3 |
+ const fastest = fastestResult([ |
|
4 |
+ [ |
|
5 |
+ [ |
|
6 |
+ { processor: 1, endTime: 10 }, |
|
7 |
+ { processor: 1, endTime: 11 } |
|
8 |
+ ], |
|
9 |
+ [ |
|
10 |
+ { processor: 2, endTime: 7 }, |
|
11 |
+ { processor: 2, endTime: 8 } |
|
12 |
+ ] |
|
13 |
+ ], |
|
14 |
+ |
|
15 |
+ [ |
|
16 |
+ [ |
|
17 |
+ { processor: 1, endTime: 20 }, |
|
18 |
+ ], |
|
19 |
+ [ |
|
20 |
+ { processor: 2, endTime: 19 } |
|
21 |
+ ] |
|
22 |
+ ], |
|
23 |
+ ]); |
|
24 |
+ |
|
25 |
+ expect(fastest).toBe(0); |
|
26 |
+}); |
|
27 |
+ |
|
28 |
+test('find najneomeskanejsi result', () => { |
|
29 |
+ const best = najneomeskanejsiResult([ |
|
30 |
+ [ |
|
31 |
+ [ |
|
32 |
+ { processor: 1, endTime: 10, delayed: 10 }, |
|
33 |
+ { processor: 1, endTime: 11, delayed: 20 } |
|
34 |
+ ], |
|
35 |
+ ], |
|
36 |
+ [ |
|
37 |
+ [ |
|
38 |
+ { processor: 1, endTime: 10, delayed: 0 }, |
|
39 |
+ { processor: 1, endTime: 11, delayed: 5 } |
|
40 |
+ ], |
|
41 |
+ ], |
|
42 |
+ [ |
|
43 |
+ [ |
|
44 |
+ { processor: 1, endTime: 10, delayed: 10 }, |
|
45 |
+ { processor: 1, endTime: 11, delayed: 10 } |
|
46 |
+ ], |
|
47 |
+ ] |
|
48 |
+ ]); |
|
49 |
+ |
|
50 |
+ expect(best).toBe(1); |
|
51 |
+}); |
|
0 | 52 |
\ No newline at end of file |