Browse Source

Introduce a basic reminder system

Neal Wilson 8 years ago
parent
commit
1c727d23b3

+ 190
- 0
config/Reports/reminders.xml View File

@@ -0,0 +1,190 @@
1
+<window id="remindersreport">
2
+	<reporttitle>Production:->Reminders</reporttitle>
3
+    <layout type="vertical">
4
+        <webview id="report" />
5
+    </layout>
6
+    <menu name="File">
7
+        <item id="print" shortcut="Ctrl+P">Print</item>
8
+    </menu>
9
+    <program>
10
+        <![CDATA[
11
+            var window = this;
12
+            this.windowTitle = "Typica - Reminders";
13
+            var report = findChildObject(this, 'report');
14
+            var printMenu = findChildObject(this, 'print');
15
+            printMenu.triggered.connect(function() {
16
+                report.print();
17
+            });
18
+            var e = new Array();
19
+            function populateReminderData() {
20
+                e = new Array();
21
+                var query = new QSqlQuery;
22
+                query.exec("SELECT id, reminder FROM reminders");
23
+                while(query.next())
24
+                {
25
+                    var reminder = JSON.parse(query.value(1));
26
+                    reminder.dbid = query.value(0);
27
+                    var start_time = "" + reminder.start_year + "-" + reminder.start_month + "-" + reminder.start_day + " " + reminder.start_time;
28
+                    if(reminder.condition == "PRODUCTIONWEIGHT")
29
+                    {
30
+                        var convert = 1;
31
+                        var unittext = " Lb";
32
+                        if(reminder.unit == "KG")
33
+                        {
34
+                            convert = 2.2;
35
+                            unittext = " Kg";
36
+                        }
37
+                        var dq = new QSqlQuery;
38
+                        dq.prepare("SELECT sum(roasted_quantity)/:conversion FROM roasting_log WHERE time > :since");
39
+                        dq.bind(":conversion", convert);
40
+                        dq.bind(":since", start_time);
41
+                        dq.exec();
42
+                        dq.next();
43
+                        var proportion;
44
+                        var remain;
45
+                        if(reminder.value == 0 || (reminder.value < Number(dq.value(0))))
46
+                        {
47
+                            proportion = 1;
48
+                            remain = 0;
49
+                        }
50
+                        else
51
+                        {
52
+                            proportion = Number(dq.value(0)) / reminder.value;
53
+                            remain = (reminder.value - Number(dq.value(0))).toFixed(0);
54
+                        }
55
+                        reminder.completion = proportion;
56
+                        reminder.detail = remain + unittext;
57
+                        dq = dq.invalidate();
58
+                    }
59
+                    else if(reminder.condition == "DAYS")
60
+                    {
61
+                        var dq = new QSqlQuery;
62
+                        dq.prepare("SELECT 'now'::date - :since::date");
63
+                        dq.bind(":since", start_time);
64
+                        dq.exec();
65
+                        dq.next();
66
+                        var proportion;
67
+                        var remain;
68
+                        if(reminder.value == 0 || (reminder.value < Number(dq.value(0))))
69
+                        {
70
+                            proportion = 1;
71
+                            remain = 0;
72
+                        }
73
+                        else
74
+                        {
75
+                            proportion = Number(dq.value(0)) / reminder.value;
76
+                            remain = reminder.value - Number(dq.value(0));
77
+                        }
78
+                        reminder.completion = proportion;
79
+                        reminder.detail = remain + " Days";
80
+                        dq = dq.invalidate();
81
+                    }
82
+                    else if(reminder.condition == "PRODUCTIONBATCHES")
83
+                    {
84
+                        var dq = new QSqlQuery;
85
+                        dq.prepare("SELECT count(1) FROM roasting_log WHERE time > :since");
86
+                        dq.bind(":since", start_time);
87
+                        dq.exec();
88
+                        dq.next();
89
+                        var proportion;
90
+                        var remain;
91
+                        if(reminder.value == 0 || (reminder.value < Number(dq.value(0))))
92
+                        {
93
+                            proportion = 1;
94
+                            remain = 0;
95
+                        }
96
+                        else
97
+                        {
98
+                            proportion = Number(dq.value(0)) / reminder.value;
99
+                            remain = reminder.value - Number(dq.value(0));
100
+                        }
101
+                        reminder.completion = proportion;
102
+                        reminder.detail = remain + " Batches";
103
+                        dq = dq.invalidate();
104
+                    }
105
+                    else if(reminder.condition == "PRODUCTIONHOURS")
106
+                    {
107
+                        var dq = new QSqlQuery;
108
+                        dq.prepare("SELECT extract(epoch FROM (SELECT sum(duration) FROM roasting_log WHERE time > :since) / 3600)");
109
+                        dq.bind(":since", start_time);
110
+                        dq.exec();
111
+                        dq.next();
112
+                        var proportion;
113
+                        var remain;
114
+                        if(reminder.value == 0 || (reminder.value < Number(dq.value(0))))
115
+                        {
116
+                            proportion = 1;
117
+                            remain = 0;
118
+                        }
119
+                        else
120
+                        {
121
+                            proportion = Number(dq.value(0)) / reminder.value;
122
+                            remain = reminder.value - Number(dq.value(0));
123
+                        }
124
+                        reminder.completion = proportion;
125
+                        reminder.detail = remain.toFixed(1) + " Hours";
126
+                        dq = dq.invalidate();
127
+                    }
128
+                    e[reminder.dbid] = reminder;
129
+                }
130
+                query = query.invalidate();
131
+            }
132
+            function refresh() {
133
+                populateReminderData();
134
+                var passedData = e.filter(function(n){return n.hasOwnProperty("completion")}).sort(function(a,b){return b.completion-a.completion});
135
+                var buffer = new QBuffer;
136
+                buffer.open(3);
137
+                var output = new XmlWriter(buffer);
138
+                output.writeStartDocument("1.0");
139
+                output.writeDTD('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg.dtd">');
140
+                output.writeStartElement("html");
141
+                output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
142
+                output.writeStartElement("head");
143
+                output.writeTextElement("title", "Reminders");
144
+                output.writeEndElement();
145
+                output.writeStartElement("body");
146
+                output.writeTextElement("h1", "Reminders");
147
+                output.writeStartElement("a");
148
+                output.writeAttribute("href", "typica://script/0");
149
+                output.writeStartElement("div");
150
+                output.writeAttribute("class", "reminder");
151
+                output.writeStartElement("div");
152
+                output.writeAttribute("class", "nocolor");
153
+                output.writeEndElement();
154
+                output.writeStartElement("span");
155
+                output.writeAttribute("class", "title");
156
+                output.writeCDATA("New Reminder");
157
+                output.writeEndElement();
158
+                output.writeEndElement();
159
+                output.writeEndElement();
160
+                output.writeTextElement("script", "e = " + JSON.stringify(passedData));
161
+                var styleFile = new QFile(QSettings.value("config") + "/Scripts/reminders.css");
162
+                styleFile.open(1);
163
+                output.writeTextElement("style", styleFile.readToString());
164
+                styleFile.close();
165
+                output.writeStartElement("script");
166
+                scriptFile = new QFile(QSettings.value("config") + "/Scripts/reminders.js");
167
+                scriptFile.open(1);
168
+                output.writeCDATA(scriptFile.readToString());
169
+                scriptFile.close();
170
+                output.writeEndElement();
171
+                output.writeEndElement();
172
+                output.writeEndDocument();
173
+                report.setContent(buffer);
174
+                buffer.close();
175
+            }
176
+            refresh();
177
+            var doRefresh = function() {
178
+                refresh();
179
+            }
180
+            report.scriptLinkClicked.connect(function(url) {
181
+                var reminder = createWindow("editreminder");
182
+                if(url != "0")
183
+                {
184
+                    reminder.loadData(e[url]);
185
+                }
186
+                reminder.setRefreshFunction(doRefresh);
187
+            });
188
+        ]]>
189
+    </program>
190
+</window>

+ 68
- 0
config/Scripts/reminders.css View File

@@ -0,0 +1,68 @@
1
+body{
2
+    background-color: #EDE3C0;
3
+}
4
+.reminder{
5
+    background-color: #EEEEEE;
6
+    display: block;
7
+    margin-left: 10px;
8
+    margin-right: 10px;
9
+    margin-top: 10px;
10
+    max-width: 300px;
11
+    padding-right: 5px;
12
+    padding-bottom: 5px;
13
+}
14
+.green{
15
+    width: 150px;
16
+    background-color: #00FF00;
17
+    height: 10px;
18
+    display: block;
19
+}
20
+.yellow{
21
+    width: 150px;
22
+    background-color: #FFFF00;
23
+    height: 10px;
24
+    display: block;
25
+}
26
+.orange{
27
+    width: 150px;
28
+    background-color: #FFA500;
29
+    height: 10px;
30
+    display: block;
31
+}
32
+.red{
33
+    width: 150px;
34
+    background-color: #FF0000;
35
+    height: 10px;
36
+    display: block;
37
+}
38
+.nocolor{
39
+    height: 10px;
40
+    display: block;
41
+}
42
+a{
43
+    text-decoration: none;
44
+    color: #000000;
45
+    display: block;
46
+    max-width: 300px;
47
+}
48
+.reminder span{
49
+    margin-left: 5px;
50
+    display: block;
51
+}
52
+.progress{
53
+    float: left;
54
+    color: #708090;
55
+    font-size: 80%
56
+}
57
+.detail{
58
+    float: right;
59
+    color: #708090;
60
+    font-size: 80%
61
+}
62
+.clearfix{
63
+    clear: both;
64
+}
65
+.title{
66
+    margin-bottom: 5px;
67
+    margin-top: 3px;
68
+}

+ 23
- 0
config/Scripts/reminders.js View File

@@ -0,0 +1,23 @@
1
+e.forEach(function(item) {
2
+    var element = document.createElement("a");
3
+    element.href="typica://script/" + item.dbid
4
+    var colordiv;
5
+    if(item.completion >= 1)
6
+    {
7
+        colordiv = '<div class="red"></div>';
8
+    }
9
+    else
10
+    {
11
+        colordiv = '<div class="orange"></div>';
12
+    }
13
+    if(item.completion < 0.6)
14
+    {
15
+        colordiv = '<div class="yellow"></div>';
16
+    }
17
+    if(item.completion < 0.3)
18
+    {
19
+        colordiv = '<div class="green"></div>';
20
+    }
21
+    element.innerHTML = '<div class="reminder">' + colordiv + '<span class="title">' + item.title + '</span><span class="progress">' + Math.floor(item.completion * 100) + '%</span><span class="detail">' + item.detail + '</span><div class="clearfix"></div></div>';
22
+    document.body.appendChild(element);
23
+})

+ 206
- 0
config/Windows/editreminder.xml View File

@@ -0,0 +1,206 @@
1
+<window id="editreminder">
2
+    <layout type="vertical">
3
+        <layout type="horizontal">
4
+            <label>Name:</label>
5
+            <line id="name" />
6
+        </layout>
7
+        <label>Description:</label>
8
+        <textarea id="description" />
9
+        <layout type="horizontal">
10
+            <label>Since:</label>
11
+            <calendar time="true" id="since" />
12
+            <button name="Now" id="now" type="push" />
13
+        </layout>
14
+        <layout type="horizontal">
15
+            <label>Condition:</label>
16
+            <sqldrop id="condition" />
17
+        </layout>
18
+        <layout type="stack" id="conditionvars">
19
+            <page>
20
+                <layout type="horizontal">
21
+                    <line id="productionweight" validator="numeric" />
22
+                    <sqldrop id="productionunit" />
23
+                </layout>
24
+            </page>
25
+            <page>
26
+                <layout type="horizontal">
27
+                    <line id="days" validator="numeric" />
28
+                </layout>
29
+            </page>
30
+            <page>
31
+                <layout type="horizontal">
32
+                    <line id="batches" validator="numeric" />
33
+                </layout>
34
+            </page>
35
+            <page>
36
+                <layout type="horizontal">
37
+                    <line id="roastingtime" validator="numeric" />
38
+                </layout>
39
+            </page>
40
+        </layout>
41
+        <layout type="horizontal">
42
+            <button name="Delete" id="delete" type="push" />
43
+            <button name="Save" id="save" type="push" />
44
+        </layout>
45
+    </layout>
46
+    <program>
47
+        <![CDATA[
48
+            var window = this;
49
+            window.setRefreshFunction = function(callback) {
50
+                window.refreshCallback = callback;
51
+            }
52
+            window.currentReminder = new Object();
53
+            this.windowTitle = "Typica - Edit Reminder";
54
+            var unitBox = findChildObject(this, 'productionunit');
55
+            unitBox.addItem("Kg");
56
+            unitBox.addItem("Lb");
57
+            unitBox.currentIndex = QSettings.value("script/report_unit", 1);
58
+            var condition = findChildObject(this, 'condition');
59
+            condition.addItem("Roasted Coffee Production");
60
+            condition.addItem("Days");
61
+            condition.addItem("Batches Roasted");
62
+            condition.addItem("Hours of Roasting Time");
63
+            var conditionpage = findChildObject(this, 'conditionvars');
64
+            condition['currentIndexChanged(int)'].connect(function(c) {
65
+                conditionpage.setCurrentIndex(c);
66
+            });
67
+            var namefield = findChildObject(this, 'name');
68
+            var descfield = findChildObject(this, 'description');
69
+            var sincefield = findChildObject(this, 'since');
70
+            var prodweight = findChildObject(this, 'productionweight');
71
+            var daysfield = findChildObject(this, 'days');
72
+            var batchesfield = findChildObject(this, 'batches');
73
+            var hoursfield = findChildObject(this, 'roastingtime');
74
+            var time = new QTime();
75
+            var deleteButton = findChildObject(this, 'delete');
76
+            deleteButton.enabled = false;
77
+            deleteButton.clicked.connect(function() {
78
+                var query = new QSqlQuery;
79
+                query.prepare("DELETE FROM reminders WHERE id = :id");
80
+                query.bind("id", window.currentReminder.dbid);
81
+                query.exec();
82
+                query = query.invalidate();
83
+                window.refreshCallback();
84
+                window.close();
85
+            });
86
+            var saveButton = findChildObject(this, 'save');
87
+            saveButton.clicked.connect(function() {
88
+                r = new Object();
89
+                r.title = namefield.text;
90
+                r.description = descfield.plainText;
91
+                r.start_year = sincefield.year();
92
+                r.start_month = sincefield.month();
93
+                r.start_day = sincefield.day();
94
+                r.start_time = sincefield.time.toString("hh:mm:ss");
95
+                switch(condition.currentIndex)
96
+                {
97
+                    case 0:
98
+                        r.condition = "PRODUCTIONWEIGHT";
99
+                        r.value = Number(prodweight.text);
100
+                        if(unitBox.currentIndex == 0)
101
+                        {
102
+                            r.unit = "KG";
103
+                        }
104
+                        else
105
+                        {
106
+                            r.unit = "LB";
107
+                        }
108
+                        break;
109
+                    case 1:
110
+                        r.condition = "DAYS";
111
+                        r.value = daysfield.text;
112
+                        break;
113
+                    case 2:
114
+                        r.condition = "PRODUCTIONBATCHES";
115
+                        r.value = batchesfield.text;
116
+                        break;
117
+                    case 3:
118
+                        r.condition = "PRODUCTIONHOURS";
119
+                        r.value = hoursfield.text;
120
+                        break;
121
+                }
122
+                var query = new QSqlQuery;
123
+                if(window.currentReminder.hasOwnProperty("dbid"))
124
+                {
125
+                    query.prepare("UPDATE reminders SET reminder = :reminder WHERE id = :id");
126
+                    query.bind("reminder", JSON.stringify(r));
127
+                    query.bind("id", window.currentReminder.dbid);
128
+                }
129
+                else
130
+                {
131
+                    query.prepare("INSERT INTO reminders (id, reminder) VALUES(default, :reminder)");
132
+                    query.bind("reminder", JSON.stringify(r));
133
+                }
134
+                query.exec();
135
+                query = query.invalidate();
136
+                window.refreshCallback();
137
+                window.close();
138
+            });
139
+            window.loadData = function(reminder) {
140
+                window.currentReminder = reminder;
141
+                deleteButton.enabled = reminder.hasOwnProperty("dbid");
142
+                if(reminder.hasOwnProperty("title"))
143
+                {
144
+                    namefield.text = reminder.title;
145
+                }
146
+                if(reminder.hasOwnProperty("description"))
147
+                {
148
+                    descfield.plainText = reminder.description;
149
+                }
150
+                if(reminder.hasOwnProperty("start_year") &&
151
+                   reminder.hasOwnProperty("start_month") &&
152
+                   reminder.hasOwnProperty("start_day"))
153
+                {
154
+                    sincefield.setDate(reminder.start_year, reminder.start_month, reminder.start_day);
155
+                }
156
+                if(reminder.hasOwnProperty("start_time"))
157
+                {
158
+                    sincefield.time = time.fromString(reminder.start_time, "hh:mm:ss");
159
+                }
160
+                if(reminder.hasOwnProperty("condition"))
161
+                {
162
+                    if(reminder.condition == "PRODUCTIONWEIGHT")
163
+                    {
164
+                        condition.setCurrentIndex(0);
165
+                        if(reminder.hasOwnProperty("value"))
166
+                        {
167
+                            prodweight.text = reminder.value;
168
+                        }
169
+                        if(reminder.unit == "KG")
170
+                        {
171
+                            unitBox.setCurrentIndex(0);
172
+                        }
173
+                        else
174
+                        {
175
+                            unitBox.setCurrentIndex(1);
176
+                        }
177
+                    }
178
+                    else if(reminder.condition == "DAYS")
179
+                    {
180
+                        condition.setCurrentIndex(1);
181
+                        if(reminder.hasOwnProperty("value"))
182
+                        {
183
+                            daysfield.text = reminder.value;
184
+                        }
185
+                    }
186
+                    else if(reminder.condition == "PRODUCTIONBATCHES")
187
+                    {
188
+                        condition.setCurrentIndex(2);
189
+                        if(reminder.hasOwnProperty("value"))
190
+                        {
191
+                            batchesfield.text = reminder.value;
192
+                        }
193
+                    }
194
+                    else if(reminder.condition == "PRODUCTIONHOURS")
195
+                    {
196
+                        condition.setCurrentIndex(3);
197
+                        if(reminder.hasOwnProperty("value"))
198
+                        {
199
+                            hoursfield.text = reminder.value;
200
+                        }
201
+                    }
202
+                }
203
+            }
204
+        ]]>
205
+    </program>
206
+</window>

+ 10
- 0
config/Windows/navigation.xml View File

@@ -293,6 +293,12 @@ type="push" />
293 293
 			query.exec("CREATE OR REPLACE FUNCTION log_use() RETURNS trigger AS $$ DECLARE i integer := array_lower(NEW.unroasted_id, 1); u integer := array_upper(NEW.unroasted_id, 1); BEGIN IF NEW.transaction_type = 'ROAST' THEN WHILE i <= u LOOP INSERT INTO use (time, item, quantity) VALUES(NEW.time, NEW.unroasted_id[i], NEW.unroasted_quantity[i]); i := i + 1; END LOOP; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql");
294 294
 			query.exec("UPDATE TypicaFeatures SET version = 4 WHERE feature = 'base-features'");
295 295
 		};
296
+                var DBUpdateReminders = function() {
297
+                    var query = new QSqlQuery;
298
+                    query.exec("CREATE TABLE IF NOT EXISTS reminders (id bigserial PRIMARY KEY NOT NULL, reminder text NOT NULL)");
299
+                    query.exec("UPDATE TypicaFeatures SET version = 5 WHERE feature = 'base-features'");
300
+                    query = query.invalidate();
301
+                };
296 302
 		
297 303
 		query = new QSqlQuery();
298 304
 		/* A table keeps track of database versioning information. This table is created
@@ -321,6 +327,10 @@ type="push" />
321 327
 			{
322 328
 				DBUpdateTriggers();
323 329
 			}
330
+                        if(query.value(2) < 5)
331
+                        {
332
+                            DBUpdateReminders();
333
+                        }
324 334
 		}
325 335
 		else
326 336
 		{

+ 24
- 23
config/config.xml View File

@@ -23,27 +23,28 @@
23 23
     <include src="Windows/cuppingsessionsummary.xml" />
24 24
     <include src="Windows/cuppingsummary.xml" />
25 25
     <include src="Windows/purchase.xml" />
26
-	<include src="Windows/invoicelist.xml" />
27
-	<include src="Windows/invoiceinfo.xml" />
28
-	<include src="Windows/editinvoice.xml" />
29
-	<include src="Windows/editinvoiceitem.xml" />
30
-	<include src="Windows/editfee.xml" />
31
-	<include src="Windows/optime.xml" />
32
-	<include src="Windows/greensales.xml" />
33
-	<include src="Windows/profilehistory.xml" />
34
-	<include src="Windows/editbatchdetails.xml" />
35
-	<include src="Windows/newsamplebatch.xml" />
36
-	<program>
37
-		<![CDATA[
38
-		Windows = new Object();
39
-		var loggingWindow;
40
-		var currentBatchInfo;
41
-		var navwindow = createWindow("navwindow");
42
-		var aadapt;
43
-		var badapt;
44
-		var azero;
45
-		var bzero;
46
-		navwindow.windowTitle = "Typica - Choose Your Path";
47
-		]]>
48
-	</program>
26
+    <include src="Windows/invoicelist.xml" />
27
+    <include src="Windows/invoiceinfo.xml" />
28
+    <include src="Windows/editinvoice.xml" />
29
+    <include src="Windows/editinvoiceitem.xml" />
30
+    <include src="Windows/editfee.xml" />
31
+    <include src="Windows/optime.xml" />
32
+    <include src="Windows/greensales.xml" />
33
+    <include src="Windows/profilehistory.xml" />
34
+    <include src="Windows/editbatchdetails.xml" />
35
+    <include src="Windows/newsamplebatch.xml" />
36
+    <include src="Windows/editreminder.xml" />
37
+    <program>
38
+        <![CDATA[
39
+            Windows = new Object();
40
+            var loggingWindow;
41
+            var currentBatchInfo;
42
+            var navwindow = createWindow("navwindow");
43
+            var aadapt;
44
+            var badapt;
45
+            var azero;
46
+            var bzero;
47
+            navwindow.windowTitle = "Typica - Choose Your Path";
48
+        ]]>
49
+    </program>
49 50
 </application>

Loading…
Cancel
Save