Embla ræður eins og stendur við eftirfarandi tegundir fyrirspurna fyrir {{ display_name }}.
+ Þetta er ekki tæmandi listi, þar sem hún skilur einnig ýmis tilbrigði við þessar spurningar.
+
+ {% endblock %}
\ No newline at end of file
diff --git a/templates/spotify-connection.html b/templates/spotify-connection.html
new file mode 100644
index 00000000..2e65e4b1
--- /dev/null
+++ b/templates/spotify-connection.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/spotify-instructions.html b/templates/spotify-instructions.html
new file mode 100644
index 00000000..96128e1c
--- /dev/null
+++ b/templates/spotify-instructions.html
@@ -0,0 +1,17 @@
+{% extends "iot-info.html" %}
+
+
+{% block content %}
+
+
Fyrirspurnaleiðbeiningar
+
+
Embla ræður eins og stendur við eftirfarandi tegundir fyrirspurna fyrir {{ display_name }}.
+ Þetta er ekki tæmandi listi, þar sem hún skilur einnig ýmis tilbrigði við þessar spurningar.
+
+
Eina skipunin sem þú munt nokkurn tímann þurfa
+
+
+ - Spilaðu Þorparann með Pálma Gunnarssyni.
+
+
+ {% endblock %}
\ No newline at end of file
diff --git a/tests/files/populate_testdb.sql b/tests/files/populate_testdb.sql
index 4a1c6b3b..84decb21 100644
--- a/tests/files/populate_testdb.sql
+++ b/tests/files/populate_testdb.sql
@@ -24,4 +24,5 @@ INSERT INTO queries ("id", "timestamp", "interpretations", "question", "bquestio
INSERT INTO querydata ("client_id", "key", "created", "modified", "data") VALUES
('123456789', 'name', '2020-09-26 21:12:56.20056', '2020-09-26 21:21:15.873656', '{"full": "Sveinbjörn Þórðarson", "first": "Sveinbjörn", "gender": "kk"}'),
+('QueryTesting123', 'iot', '2022-09-26 21:13:56.20056', '2022-10-26 15:21:15.873656', '{"iot_lights": {"philips_hue": {"credentials": {"username": "DummyData", "ip_address": "127.0.0.1"}}}}'),
('9A30D6B7-F0C9-48CF-A567-4E9E7D8997C5', 'name', '2020-09-26 22:13:11.164278', '2020-09-28 14:50:52.701844', '{"full": "Sveinbjörn Þórðarson", "first": "Sveinbjörn", "gender": "kk"}');
diff --git a/tests/files/smartlights.json b/tests/files/smartlights.json
new file mode 100644
index 00000000..5817f861
--- /dev/null
+++ b/tests/files/smartlights.json
@@ -0,0 +1,344 @@
+{
+ "turn_on": [
+ "Kveiktu ljósin",
+ "Kveiktu ljósin fyrir mig",
+ "Kveiktu ljósin í eldhúsinu",
+ "Kveiktu ljósin í eldhúsinu fyrir mig",
+ "Kveiktu ljósið í eldhúsinu",
+ "Kveiktu ljósið í eldhúsinu fyrir mig",
+ "Kveiktu öll ljós",
+ "Kveiktu öll ljós fyrir mig",
+ "Kveiktu öll ljósin",
+ "Kveiktu öll ljósin fyrir mig",
+ "Kveiktu öll ljósin í svefnherberginu",
+ "Kveiktu öll ljósin í svefnherberginu fyrir mig",
+ "Kveiktu ljós allsstaðar",
+ "Kveiktu ljós allsstaðar fyrir mig",
+ "Kveiktu ljósin allsstaðar",
+ "Kveiktu ljósin allsstaðar fyrir mig",
+ "Kveiktu ljósin alls staðar",
+ "Kveiktu ljósin alls staðar fyrir mig",
+ "Kveiktu lampann á ganginum",
+ "Kveiktu lampann á ganginum fyrir mig",
+ "Kveiktu loftljósin á skrifstofunni",
+ "Kveiktu loftljósin á skrifstofunni fyrir mig",
+ "Vinsamlegast kveiktu ljósin",
+ "Vinsamlegast kveiktu ljósin fyrir mig",
+ "Vinsamlegast kveiktu ljósin í eldhúsinu",
+ "Vinsamlegast kveiktu ljósin í eldhúsinu fyrir mig",
+ "Vinsamlegast kveiktu ljósið í eldhúsinu",
+ "Vinsamlegast kveiktu ljósið í eldhúsinu fyrir mig",
+ "Vinsamlegast kveiktu öll ljós",
+ "Vinsamlegast kveiktu öll ljós fyrir mig",
+ "Vinsamlegast kveiktu öll ljósin",
+ "Vinsamlegast kveiktu öll ljósin fyrir mig",
+ "Vinsamlegast kveiktu öll ljósin í svefnherberginu",
+ "Vinsamlegast kveiktu öll ljósin í svefnherberginu fyrir mig",
+ "Vinsamlegast kveiktu ljós allsstaðar",
+ "Vinsamlegast kveiktu ljós allsstaðar fyrir mig",
+ "Vinsamlegast kveiktu ljósin allsstaðar",
+ "Vinsamlegast kveiktu ljósin allsstaðar fyrir mig",
+ "Vinsamlegast kveiktu ljósin alls staðar",
+ "Vinsamlegast kveiktu ljósin alls staðar fyrir mig",
+ "Vinsamlegast kveiktu lampann á ganginum",
+ "Vinsamlegast kveiktu lampann á ganginum fyrir mig",
+ "Vinsamlegast kveiktu loftljósin á skrifstofunni",
+ "Vinsamlega kveiktu loftljósin á skrifstofunni fyrir mig",
+ "Værirðu til í að kveikja ljósin",
+ "Værirðu til í að kveikja ljósin fyrir mig",
+ "Værirðu til í að kveikja ljósin í eldhúsinu",
+ "Værirðu til í að kveikja ljósin í eldhúsinu fyrir mig",
+ "Værirðu til í að kveikja ljósið í eldhúsinu",
+ "Værirðu til í að kveikja ljósið í eldhúsinu fyrir mig",
+ "Værirðu til í að kveikja öll ljós",
+ "Værirðu til í að kveikja öll ljós fyrir mig",
+ "Værirðu til í að kveikja öll ljósin",
+ "Værirðu til í að kveikja öll ljósin fyrir mig",
+ "Værirðu til í að kveikja öll ljósin í svefnherberginu",
+ "Værirðu til í að kveikja öll ljósin í svefnherberginu fyrir mig",
+ "Værirðu til í að kveikja ljós allsstaðar",
+ "Værirðu til í að kveikja ljós allsstaðar fyrir mig",
+ "Værirðu til í að kveikja ljósin allsstaðar",
+ "Værirðu til í að kveikja ljósin allsstaðar fyrir mig",
+ "Værirðu til í að kveikja ljósin alls staðar",
+ "Værirðu til í að kveikja ljósin alls staðar fyrir mig",
+ "Værirðu til í að kveikja lampann á ganginum",
+ "Værirðu til í að kveikja lampann á ganginum fyrir mig",
+ "Værirðu til í að kveikja loftljósin á skrifstofunni",
+ "Værirðu til í að kveikja loftljósin á skrifstofunni fyrir mig",
+ "Gætirðu kveikt ljósin",
+ "Gætirðu kveikt ljósin fyrir mig",
+ "Gætirðu kveikt ljósin í eldhúsinu",
+ "Gætirðu kveikt ljósin í eldhúsinu fyrir mig",
+ "Gætirðu kveikt ljósið í eldhúsinu",
+ "Gætirðu kveikt ljósið í eldhúsinu fyrir mig",
+ "Gætirðu kveikt öll ljós",
+ "Gætirðu kveikt öll ljós fyrir mig",
+ "Gætirðu kveikt öll ljósin",
+ "Gætirðu kveikt öll ljósin fyrir mig",
+ "Gætirðu kveikt öll ljósin í svefnherberginu",
+ "Gætirðu kveikt öll ljósin í svefnherberginu fyrir mig",
+ "Gætirðu kveikt ljós allsstaðar",
+ "Gætirðu kveikt ljós allsstaðar fyrir mig",
+ "Gætirðu kveikt ljósin allsstaðar",
+ "Gætirðu kveikt ljósin allsstaðar fyrir mig",
+ "Gætirðu kveikt ljósin alls staðar",
+ "Gætirðu kveikt ljósin alls staðar fyrir mig",
+ "Gætirðu kveikt lampann á ganginum",
+ "Gætirðu kveikt lampann á ganginum fyrir mig",
+ "Gætirðu kveikt loftljósin á skrifstofunni",
+ "Gætirðu nokkuð kveikt loftljósin á skrifstofunni fyrir mig"
+ ],
+ "turn_off": [
+ "Slökktu ljósin",
+ "Slökktu ljósin fyrir mig",
+ "Slökktu ljósin í eldhúsinu",
+ "Slökktu ljósin í eldhúsinu fyrir mig",
+ "Slökktu ljósið í eldhúsinu",
+ "Slökktu ljósið í eldhúsinu fyrir mig",
+ "Slökktu öll ljós",
+ "Slökktu öll ljós fyrir mig",
+ "Slökktu öll ljósin",
+ "Slökktu öll ljósin fyrir mig",
+ "Slökktu öll ljósin í svefnherberginu",
+ "Slökktu öll ljósin í svefnherberginu fyrir mig",
+ "Slökktu ljós allsstaðar",
+ "Slökktu ljós allsstaðar fyrir mig",
+ "Slökktu ljósin allsstaðar",
+ "Slökktu ljósin allsstaðar fyrir mig",
+ "Slökktu ljósin alls staðar",
+ "Slökktu ljósin alls staðar fyrir mig",
+ "Slökktu lampann á ganginum",
+ "Slökktu lampann á ganginum fyrir mig",
+ "Slökktu loftljósin á skrifstofunni",
+ "Slökktu loftljósin á skrifstofunni fyrir mig",
+ "Vinsamlegast slökktu ljósin",
+ "Vinsamlegast slökktu ljósin fyrir mig",
+ "Vinsamlegast slökktu ljósin í eldhúsinu",
+ "Vinsamlegast slökktu ljósin í eldhúsinu fyrir mig",
+ "Vinsamlegast slökktu ljósið í eldhúsinu",
+ "Vinsamlegast slökktu ljósið í eldhúsinu fyrir mig",
+ "Vinsamlegast slökktu öll ljós",
+ "Vinsamlegast slökktu öll ljós fyrir mig",
+ "Vinsamlegast slökktu öll ljósin",
+ "Vinsamlegast slökktu öll ljósin fyrir mig",
+ "Vinsamlegast slökktu öll ljósin í svefnherberginu",
+ "Vinsamlegast slökktu öll ljósin í svefnherberginu fyrir mig",
+ "Vinsamlegast slökktu ljós allsstaðar",
+ "Vinsamlegast slökktu ljós allsstaðar fyrir mig",
+ "Vinsamlegast slökktu ljósin allsstaðar",
+ "Vinsamlegast slökktu ljósin allsstaðar fyrir mig",
+ "Vinsamlegast slökktu ljósin alls staðar",
+ "Vinsamlegast slökktu ljósin alls staðar fyrir mig",
+ "Vinsamlegast slökktu lampann á ganginum",
+ "Vinsamlegast slökktu lampann á ganginum fyrir mig",
+ "Vinsamlegast slökktu loftljósin á skrifstofunni",
+ "Vinsamlega slökktu loftljósin á skrifstofunni fyrir mig",
+ "Værirðu til í að slökkva ljósin",
+ "Værirðu til í að slökkva ljósin fyrir mig",
+ "Værirðu til í að slökkva ljósin í eldhúsinu",
+ "Værirðu til í að slökkva ljósin í eldhúsinu fyrir mig",
+ "Værirðu til í að slökkva ljósið í eldhúsinu",
+ "Værirðu til í að slökkva ljósið í eldhúsinu fyrir mig",
+ "Værirðu til í að slökkva öll ljós",
+ "Værirðu til í að slökkva öll ljós fyrir mig",
+ "Værirðu til í að slökkva öll ljósin",
+ "Værirðu til í að slökkva öll ljósin fyrir mig",
+ "Værirðu til í að slökkva öll ljósin í svefnherberginu",
+ "Værirðu til í að slökkva öll ljósin í svefnherberginu fyrir mig",
+ "Værirðu til í að slökkva ljós allsstaðar",
+ "Værirðu til í að slökkva ljós allsstaðar fyrir mig",
+ "Værirðu til í að slökkva ljósin allsstaðar",
+ "Værirðu til í að slökkva ljósin allsstaðar fyrir mig",
+ "Værirðu til í að slökkva ljósin alls staðar",
+ "Værirðu til í að slökkva ljósin alls staðar fyrir mig",
+ "Værirðu til í að slökkva lampann á ganginum",
+ "Værirðu til í að slökkva lampann á ganginum fyrir mig",
+ "Værirðu til í að slökkva loftljósin á skrifstofunni",
+ "Værirðu til í að slökkva loftljósin á skrifstofunni fyrir mig",
+ "Gætirðu slökkt ljósin",
+ "Gætirðu slökkt ljósin fyrir mig",
+ "Gætirðu slökkt ljósin í eldhúsinu",
+ "Gætirðu slökkt ljósin í eldhúsinu fyrir mig",
+ "Gætirðu slökkt ljósið í eldhúsinu",
+ "Gætirðu slökkt ljósið í eldhúsinu fyrir mig",
+ "Gætirðu slökkt öll ljós",
+ "Gætirðu slökkt öll ljós fyrir mig",
+ "Gætirðu slökkt öll ljósin",
+ "Gætirðu slökkt öll ljósin fyrir mig",
+ "Gætirðu slökkt öll ljósin í svefnherberginu",
+ "Gætirðu slökkt öll ljósin í svefnherberginu fyrir mig",
+ "Gætirðu slökkt ljós allsstaðar",
+ "Gætirðu slökkt ljós allsstaðar fyrir mig",
+ "Gætirðu slökkt ljósin allsstaðar",
+ "Gætirðu slökkt ljósin allsstaðar fyrir mig",
+ "Gætirðu slökkt ljósin alls staðar",
+ "Gætirðu slökkt ljósin alls staðar fyrir mig",
+ "Gætirðu slökkt lampann á ganginum",
+ "Gætirðu slökkt lampann á ganginum fyrir mig",
+ "Gætirðu slökkt loftljósin á skrifstofunni",
+ "Gætirðu nokkuð slökkt loftljósin á skrifstofunni fyrir mig"
+ ],
+ "increase_brightness": [
+ "Hækkaðu birtuna í eldhúsinu",
+ "Hækkaðu birtuna í eldhúsinu fyrir mig",
+ "Hækkaðu birtuna á ljósinu í eldhúsinu",
+ "Hækkaðu birtuna á ljósinu í eldhúsinu fyrir mig",
+ "Hækkaðu birtuna á öllum ljósunum",
+ "Hækkaðu birtuna á öllum ljósunum fyrir mig",
+ "Hækkaðu birtuna á öllum ljósum í svefnherberginu",
+ "Hækkaðu birtuna á öllum ljósum í svefnherberginu fyrir mig",
+ "Hækkaðu birtuna allsstaðar",
+ "Hækkaðu birtuna allsstaðar fyrir mig",
+ "Hækkaðu birtuna alls staðar",
+ "Hækkaðu birtuna alls staðar fyrir mig",
+ "Hækkaðu birtuna á lampanum á ganginum",
+ "Hækkaðu birtu lampans á ganginum fyrir mig",
+ "Hækkaðu birtu loftljósanna á skrifstofunni",
+ "Hækkaðu birtuna í loftljósunum á skrifstofunni fyrir mig",
+ "Vinsamlegast hækkaðu birtuna í eldhúsinu",
+ "Vinsamlegast hækkaðu birtuna í eldhúsinu fyrir mig",
+ "Vinsamlegast hækkaðu birtuna á ljósinu í eldhúsinu",
+ "Vinsamlegast hækkaðu birtuna á ljósinu í eldhúsinu fyrir mig",
+ "Vinsamlegast hækkaðu birtuna á öllum ljósunum",
+ "Vinsamlegast hækkaðu birtuna á öllum ljósunum fyrir mig",
+ "Vinsamlegast hækkaðu birtuna á öllum ljósum í svefnherberginu",
+ "Vinsamlegast hækkaðu birtuna á öllum ljósum í svefnherberginu fyrir mig",
+ "Vinsamlegast hækkaðu birtuna allsstaðar",
+ "Vinsamlegast hækkaðu birtuna allsstaðar fyrir mig",
+ "Vinsamlegast hækkaðu birtuna alls staðar",
+ "Vinsamlegast hækkaðu birtuna alls staðar fyrir mig",
+ "Vinsamlegast hækkaðu birtuna á lampanum á ganginum",
+ "Vinsamlegast hækkaðu birtu lampans á ganginum fyrir mig",
+ "Vinsamlegast hækkaðu birtu loftljósanna á skrifstofunni",
+ "Vinsamlega hækkaðu birtuna í loftljósunum á skrifstofunni fyrir mig",
+ "Værirðu til í að hækka birtuna í eldhúsinu",
+ "Værirðu til í að hækka birtuna í eldhúsinu fyrir mig",
+ "Værirðu til í að hækka birtuna á ljósinu í eldhúsinu",
+ "Værirðu til í að hækka birtuna á ljósinu í eldhúsinu fyrir mig",
+ "Værirðu til í að hækka birtuna á öllum ljósunum",
+ "Værirðu til í að hækka birtuna á öllum ljósunum fyrir mig",
+ "Værirðu til í að hækka birtuna á öllum ljósum í svefnherberginu",
+ "Værirðu til í að hækka birtuna á öllum ljósum í svefnherberginu fyrir mig",
+ "Værirðu til í að hækka birtuna allsstaðar",
+ "Værirðu til í að hækka birtuna allsstaðar fyrir mig",
+ "Værirðu til í að hækka birtuna alls staðar",
+ "Værirðu til í að hækka birtuna alls staðar fyrir mig",
+ "Værirðu til í að hækka birtuna á lampanum á ganginum",
+ "Værirðu til í að hækka birtu lampans á ganginum fyrir mig",
+ "Værirðu til í að hækka birtu loftljósanna á skrifstofunni",
+ "Værirðu til í að hækka birtuna í loftljósunum á skrifstofunni fyrir mig",
+ "Gætirðu hækkað birtuna í eldhúsinu",
+ "Gætirðu hækkað birtuna í eldhúsinu fyrir mig",
+ "Gætirðu hækkað birtuna á ljósinu í eldhúsinu",
+ "Gætirðu hækkað birtuna á ljósinu í eldhúsinu fyrir mig",
+ "Gætirðu hækkað birtuna á öllum ljósunum",
+ "Gætirðu hækkað birtuna á öllum ljósunum fyrir mig",
+ "Gætirðu hækkað birtuna á öllum ljósum í svefnherberginu",
+ "Gætirðu hækkað birtuna á öllum ljósum í svefnherberginu fyrir mig",
+ "Gætirðu hækkað birtuna allsstaðar",
+ "Gætirðu hækkað birtuna allsstaðar fyrir mig",
+ "Gætirðu hækkað birtuna alls staðar",
+ "Gætirðu hækkað birtuna alls staðar fyrir mig",
+ "Gætirðu hækkað birtuna á lampanum á ganginum",
+ "Gætirðu hækkað birtu lampans á ganginum fyrir mig",
+ "Gætirðu hækkað birtu loftljósanna á skrifstofunni",
+ "Gætirðu nokkuð hækkað birtuna í loftljósunum á skrifstofunni fyrir mig"
+ ],
+ "decrease_brightness": [
+ "Lækkaðu birtuna í eldhúsinu",
+ "Lækkaðu birtuna í eldhúsinu fyrir mig",
+ "Lækkaðu birtuna á ljósinu í eldhúsinu",
+ "Lækkaðu birtuna á ljósinu í eldhúsinu fyrir mig",
+ "Lækkaðu birtuna á öllum ljósunum",
+ "Lækkaðu birtuna á öllum ljósunum fyrir mig",
+ "Lækkaðu birtuna á öllum ljósum í svefnherberginu",
+ "Lækkaðu birtuna á öllum ljósum í svefnherberginu fyrir mig",
+ "Lækkaðu birtuna allsstaðar",
+ "Lækkaðu birtuna allsstaðar fyrir mig",
+ "Lækkaðu birtuna alls staðar",
+ "Lækkaðu birtuna alls staðar fyrir mig",
+ "Lækkaðu birtuna á lampanum á ganginum",
+ "Lækkaðu birtu lampans á ganginum fyrir mig",
+ "Lækkaðu birtu loftljósanna á skrifstofunni",
+ "Lækkaðu birtuna í loftljósunum á skrifstofunni fyrir mig",
+ "Vinsamlegast lækkaðu birtuna í eldhúsinu",
+ "Vinsamlegast lækkaðu birtuna í eldhúsinu fyrir mig",
+ "Vinsamlegast lækkaðu birtuna á ljósinu í eldhúsinu",
+ "Vinsamlegast lækkaðu birtuna á ljósinu í eldhúsinu fyrir mig",
+ "Vinsamlegast lækkaðu birtuna á öllum ljósunum",
+ "Vinsamlegast lækkaðu birtuna á öllum ljósunum fyrir mig",
+ "Vinsamlegast lækkaðu birtuna á öllum ljósum í svefnherberginu",
+ "Vinsamlegast lækkaðu birtuna á öllum ljósum í svefnherberginu fyrir mig",
+ "Vinsamlegast lækkaðu birtuna allsstaðar",
+ "Vinsamlegast lækkaðu birtuna allsstaðar fyrir mig",
+ "Vinsamlegast lækkaðu birtuna alls staðar",
+ "Vinsamlegast lækkaðu birtuna alls staðar fyrir mig",
+ "Vinsamlegast lækkaðu birtuna á lampanum á ganginum",
+ "Vinsamlegast lækkaðu birtu lampans á ganginum fyrir mig",
+ "Vinsamlegast lækkaðu birtu loftljósanna á skrifstofunni",
+ "Vinsamlega lækkaðu birtuna í loftljósunum á skrifstofunni fyrir mig",
+ "Værirðu til í að lækka birtuna í eldhúsinu",
+ "Værirðu til í að lækka birtuna í eldhúsinu fyrir mig",
+ "Værirðu til í að lækka birtuna á ljósinu í eldhúsinu",
+ "Værirðu til í að lækka birtuna á ljósinu í eldhúsinu fyrir mig",
+ "Værirðu til í að lækka birtuna á öllum ljósunum",
+ "Værirðu til í að lækka birtuna á öllum ljósunum fyrir mig",
+ "Værirðu til í að lækka birtuna á öllum ljósum í svefnherberginu",
+ "Værirðu til í að lækka birtuna á öllum ljósum í svefnherberginu fyrir mig",
+ "Værirðu til í að lækka birtuna allsstaðar",
+ "Værirðu til í að lækka birtuna allsstaðar fyrir mig",
+ "Værirðu til í að lækka birtuna alls staðar",
+ "Værirðu til í að lækka birtuna alls staðar fyrir mig",
+ "Værirðu til í að lækka birtuna á lampanum á ganginum",
+ "Værirðu til í að lækka birtu lampans á ganginum fyrir mig",
+ "Værirðu til í að lækka birtu loftljósanna á skrifstofunni",
+ "Værirðu til í að lækka birtuna í loftljósunum á skrifstofunni fyrir mig",
+ "Gætirðu lækkað birtuna í eldhúsinu",
+ "Gætirðu lækkað birtuna í eldhúsinu fyrir mig",
+ "Gætirðu lækkað birtuna á ljósinu í eldhúsinu",
+ "Gætirðu lækkað birtuna á ljósinu í eldhúsinu fyrir mig",
+ "Gætirðu lækkað birtuna á öllum ljósunum",
+ "Gætirðu lækkað birtuna á öllum ljósunum fyrir mig",
+ "Gætirðu lækkað birtuna á öllum ljósum í svefnherberginu",
+ "Gætirðu lækkað birtuna á öllum ljósum í svefnherberginu fyrir mig",
+ "Gætirðu lækkað birtuna allsstaðar",
+ "Gætirðu lækkað birtuna allsstaðar fyrir mig",
+ "Gætirðu lækkað birtuna alls staðar",
+ "Gætirðu lækkað birtuna alls staðar fyrir mig",
+ "Gætirðu lækkað birtuna á lampanum á ganginum",
+ "Gætirðu lækkað birtu lampans á ganginum fyrir mig",
+ "Gætirðu lækkað birtu loftljósanna á skrifstofunni",
+ "Gætirðu nokkuð lækkað birtuna í loftljósunum á skrifstofunni fyrir mig"
+ ],
+ "change_color": [
+ "Eldhúsljós græn",
+ "Eldhúsljósin græn",
+ "Fjólublá ljós á ganginum",
+ "Gerðu lampann grænan",
+ "Gerðu ljósið í eldhúsinu grænt",
+ "Gerðu lýsinguna græna í stofunni",
+ "Geturðu gert loftljósin blá",
+ "Gætirðu gert loftljósin blá",
+ "Rautt ljós í eldhúsinu",
+ "Værirðu nokkuð til í að gera loftljósin blá fyrir mig",
+ "Værirðu til í að gera loftljósin blá"
+ ],
+ "change_scene": [
+ "Kveiktu á senunni diskó",
+ "Kveiktu á diskóstemningu",
+ "Kveiktu á diskó stemningu",
+ "Stilltu á senuna rómó",
+ "Stilltu á rómó senuna",
+ "Settu á senuna rómó",
+ "Settu á rómó senuna",
+ "Kveiktu á senunni diskó í eldhúsinu",
+ "Kveiktu á diskóstemningu í eldhúsinu",
+ "Kveiktu á diskó stemningu í eldhúsinu",
+ "Stilltu á senuna rómó í eldhúsinu",
+ "Stilltu á rómó senuna í eldhúsinu",
+ "Stilltu á rómó senuna í eldhúsinu",
+ "Settu á senuna rómó á ganginum",
+ "Settu á rómó senuna á skrifstofunni"
+ ]
+}
\ No newline at end of file
diff --git a/tests/test_queries.py b/tests/test_queries.py
index cab7263d..10216443 100755
--- a/tests/test_queries.py
+++ b/tests/test_queries.py
@@ -21,15 +21,18 @@
"""
-from typing import Dict, Optional, Any
+from typing import Dict, List, Optional, Any
import re
import os
import sys
import pytest
+import json as jsonlib
+from pathlib import Path
from copy import deepcopy
from datetime import datetime, timedelta
from urllib.parse import urlencode
+from unittest.mock import patch
from flask.testing import FlaskClient
@@ -1046,6 +1049,46 @@ def test_geography(client: FlaskClient) -> None:
assert "Noregi" in json["answer"]
+def test_iot(client: FlaskClient) -> None:
+ json = qmcall(
+ client, {"q": "breyttu litnum á ljósunum í eldhúsinu í rauðan"}, "IoT"
+ )
+ assert "ég var að kveikja ljósin! " in json["answer"]
+
+ json = qmcall(client, {"q": "settu á grænan lit í eldhúsinu"}, "IoT")
+ assert "ég var að kveikja ljósin! " in json["answer"]
+
+ json = qmcall(client, {"q": "stilltu lit ljóssins í eldhúsinu á grænan"}, "IoT")
+ assert "ég var að kveikja ljósin! " in json["answer"]
+
+ json = qmcall(client, {"q": "kveiktu á ljósunum í eldhúsinu"}, "IoT")
+ assert "ég var að kveikja ljósin! " in json["answer"]
+
+ json = qmcall(client, {"q": "hækkaðu birtuna í eldhúsinu"}, "IoT")
+ assert "ég var að kveikja ljósin! " in json["answer"]
+
+ json = qmcall(client, {"q": "gerðu meiri birtu í eldhúsinu"}, "IoT")
+ assert "ég var að kveikja ljósin! " in json["answer"]
+
+ json = qmcall(client, {"q": "gerðu eldhúsið bjartara"}, "IoT")
+ assert "ég var að kveikja ljósin! " in json["answer"]
+
+ json = qmcall(client, {"q": "gerðu birtu ljóssins inni í eldhúsi meiri"}, "IoT")
+ assert "ég var að kveikja ljósin! " in json["answer"]
+
+ json = qmcall(client, {"q": "slökktu ljósið inni í eldhúsi"}, "IoT")
+ assert "ég var að kveikja ljósin! " in json["answer"]
+
+ # json = qmcall(client, {"q": "gerðu meiri birtu inni í eldhúsi"}, "IoT")
+ # assert "ég var að kveikja ljósin! " in json["answer"]
+
+ # json = qmcall(client, {"q": "gerðu ljósið inni í eldhúsi minna bjart"}, "IoT")
+ # assert "ég var að kveikja ljósin! " in json["answer"]
+
+ # json = qmcall(client, {"q": "gerðu grænt í eldhúsinu"}, "IoT")
+ # assert "ég var að kveikja ljósin! " in json["answer"]
+
+
@pytest.mark.skipif(not has_ja_api_key(), reason="no Ja.is API key on test server")
def test_ja(client: FlaskClient) -> None:
"""Ja.is module."""
@@ -1373,6 +1416,34 @@ def caseless_in(a: str, b: str) -> bool:
assert "2:00" in json["answer"]
+def test_smartlights(client: FlaskClient) -> None:
+ """Smartlights module"""
+ q_file = Path(__file__).parent.resolve() / "files" / "smartlights.json"
+ qs = jsonlib.loads(q_file.read_text())
+ assert isinstance(qs, dict)
+ qkey: str
+ ql: List[str]
+ for qkey, ql in qs.items():
+ for q in ql:
+ resp = qmcall(client, {"q": q}, "Smartlights")
+ assert resp["key"] == qkey
+
+
+def test_smartspeakers(client: FlaskClient) -> None:
+ """Smartspeakers module (mocked SonosClient)"""
+ # TODO: Add more tests!
+ with patch("queries.extras.sonos.SonosClient") as SonosMock:
+ resp = qmcall(
+ client,
+ {"q": "Kveiktu á útvarpsstöðinni rondó", "client_id": "9cc79e5f6b7c65c9"},
+ "Smartspeakers",
+ )
+ assert resp["key"] == "radio"
+ SonosMock.assert_called()
+ name, args, _ = SonosMock.mock_calls[-1]
+ assert 'play_radio_stream' in name and 'rondo' in args[0]
+
+
def test_special(client: FlaskClient) -> None:
"""Special module."""
diff --git a/tools/grammar_gen.py b/tools/grammar_gen.py
index 036bb040..22940aa1 100755
--- a/tools/grammar_gen.py
+++ b/tools/grammar_gen.py
@@ -27,7 +27,15 @@
Use --help to see more information on usage.
"""
-from typing import Callable, Iterable, Iterator, List, Optional, Union
+from typing import (
+ Callable,
+ Iterable,
+ Iterator,
+ List,
+ Optional,
+ Union,
+ Match,
+)
import re
import sys
@@ -35,7 +43,7 @@
from pathlib import Path
from functools import lru_cache
-from islenska.basics import BinEntry
+from islenska.basics import MarkOrder
# Hack to make this Python program executable from the tools subdirectory
@@ -53,6 +61,7 @@
# TODO: Create random traversal functionality (itertools.dropwhile?)
# TODO: Allow replacing special terminals (no, sérnafn, lo, ...) with words
+# TODO: Unwrap recursion (for dealing with complex recursive grammar items as in Greynir.grammar)
ColorF = Callable[[str], str]
_reset: str = "\033[0m"
@@ -73,47 +82,6 @@
pink: ColorF = lambda s: f"\033[95m{s}{_reset}"
lightcyan: ColorF = lambda s: f"\033[96m{s}{_reset}"
-nonverb_variant_order = [
- ("esb", "evb", "fsb", "fvb", "mst", "vb", "sb"),
- ("kk", "kvk", "hk"),
- ("nf", "þf", "þgf", "ef"),
- ("et", "ft"),
- ("gr",),
- ("0", "1", "2", "3"),
-]
-verb_variant_order = [
- ("gm", "mm"),
- ("lhnt", "nh", "fh", "vh", "bh"),
- ("þt", "nt"),
- ("1p", "2p", "3p"),
- ("et", "ft"),
- ("0", "1", "2", "3"),
-]
-_order_len = max(len(nonverb_variant_order), len(verb_variant_order))
-
-_orderings = {
- "hk": (
- "NFET",
- "ÞFET",
- "ÞGFET",
- "EFET",
- "NFFT",
- "ÞFFT",
- "ÞGFFT",
- "EFFT",
- "NFETgr",
- "ÞFETgr",
- "ÞGFETgr",
- "EFETgr",
- "NFFTgr",
- "ÞFFTgr",
- "ÞGFFTgr",
- "EFFTgr",
- ),
-}
-# kk = kvk = hk
-_orderings["kk"] = _orderings["kvk"] = _orderings["hk"]
-
# Grammar item type
_GIType = Union[Nonterminal, Terminal]
# BÍN, for word lookups
@@ -122,41 +90,31 @@
# Mebibyte
MiB = 1024 * 1024
-# Preamble with a hacke in case we aren't testing a query grammar
+# Preamble hack in case we aren't testing a query grammar
# (prevents an error in the QueryGrammar class)
PREAMBLE = """
-QueryRoot →
- Query
+QueryRoot → Query
Query → ""
"""
-
-def _binentry_to_int(w: BinEntry) -> List[int]:
- """Used for pretty ordering of variants in output :)."""
- try:
- return [_orderings[w.ofl].index(w.mark)]
- except (KeyError, ValueError):
- pass
-
- # Fallback, manually compute order
- val = [0 for _ in range(_order_len)]
- if w.ofl == "so":
- var_order = verb_variant_order
- else:
- var_order = nonverb_variant_order
-
- for x, v_list in enumerate(var_order):
- for y, v in enumerate(v_list):
- if v in w.mark.casefold():
- val[-x] = y + 1
- break
- return val
-
-
# Word categories which should have some variant specified
-_STRICT_CATEGORIES = frozenset(("no", "so", "lo"))
+_STRICT_CATEGORIES = frozenset(
+ (
+ "no",
+ "kk",
+ "kvk",
+ "hk",
+ "so",
+ "lo",
+ "fn",
+ "pfn",
+ "gr",
+ "rt",
+ "to",
+ )
+)
@lru_cache(maxsize=500) # VERY useful cache
@@ -167,51 +125,36 @@ def get_wordform(gi: BIN_LiteralTerminal) -> str:
"""
global strict
word, cat, variants = gi.first, gi.category, "".join(gi.variants).casefold()
- ll = BIN.lookup_lemmas(word)
+ bin_entries = BIN.lookup_lemmas(word)[1]
if strict:
# Strictness checks on usage of
# single-quoted terminals in the grammar
- assert len(ll[1]) > 0, f"Meaning not found, use root of word for: {gi.name}"
+ assert (
+ len(bin_entries) > 0
+ ), f"Meaning not found, use root of word for: {gi.name}"
assert (
cat is not None
), f"Specify category for single quoted terminal: {gi.name}"
# Filter by word category
- assert len(list(filter(lambda m: m.ofl == cat, ll[1]))) < 2, (
+ assert len(list(filter(lambda m: m.ofl == cat, bin_entries))) < 2, (
"Category not specific enough, "
"single quoted terminal has "
- f"multiple meanings: {gi.name}"
+ f"multiple possible categories: {gi.name}"
)
if cat in _STRICT_CATEGORIES:
assert (
len(variants) > 0
- ), f"Specify variant for single quoted terminal: {gi.name}"
- else:
- assert len(variants) == 0, f"Too many variants for atviksorð: {gi.name}"
+ ), f"Specify variants for single quoted terminal: {gi.name}"
- if not cat and len(ll[1]) > 0:
+ if not cat and len(bin_entries) > 0:
# Guess category from lemma lookup
- cat = ll[1][0].ofl
-
- # Have correct order of variants for form lookup (otherwise it doesn't work)
- spec: List[str] = ["" for _ in range(_order_len)]
- if cat == "so":
- # Verb variants
- var_order = verb_variant_order
- else:
- # Nonverb variants
- var_order = nonverb_variant_order
+ cat = bin_entries[0].ofl
- # Re-order correctly
- for i, v_list in enumerate(var_order):
- for v in v_list:
- if v in variants:
- spec[i] = v
-
- wordforms = BIN.lookup_forms(
+ wordforms = BIN.lookup_variants(
word,
- cat or None, # type: ignore
- "".join(spec),
+ cat or "",
+ variants or "",
)
if len(wordforms) == 0:
@@ -227,19 +170,51 @@ def get_wordform(gi: BIN_LiteralTerminal) -> str:
# author of grammar should maybe use double-quotes instead
return lightred(f"({'|'.join(wf.bmynd for wf in wordforms)})")
- # Sort wordforms in a logical order
- wordforms.sort(key=_binentry_to_int)
+ # Sort wordforms in a canonical order
+ wordforms.sort(key=lambda ks: MarkOrder.index(ks.ofl, ks.mark))
# Join all matched wordforms together (within parenthesis)
return lightcyan(f"({'|'.join(wf.bmynd for wf in wordforms)})")
+def _break_up_line(line: List[str], break_indices: List[int]) -> Iterable[List[str]]:
+ """
+ Breaks up a single line containing parenthesized word forms
+ and yields lines with all combinations of the word forms.
+ """
+ for comb in itertools.product(
+ *[set(line[i].lstrip("(").rstrip(")").split("|")) for i in break_indices]
+ ):
+ yield [
+ comb[break_indices.index(i)] if i in break_indices else line[i]
+ for i in range(len(line))
+ ]
+
+
+def expander(it: Iterable[List[str]]) -> Iterable[List[str]]:
+ """
+ Expand lines in iterator that include (word form 1|word form 2|...) items.
+ """
+ for line in it:
+ paren_indices = [
+ i
+ for i, w in enumerate(line)
+ if w.startswith("(") and w.endswith(")")
+ # ^ We can do this as ansi color is disabled when fully expanding lines
+ ]
+ if paren_indices:
+ yield from _break_up_line(line, paren_indices)
+ else:
+ yield line
+
+
def generate_from_cfg(
grammar: QueryGrammar,
*,
root: Optional[Union[Nonterminal, str]] = None,
depth: Optional[int] = None,
n: Optional[int] = None,
+ expand: Optional[bool] = False,
) -> Iterable[str]:
"""
Generates an iterator of all sentences from
@@ -276,7 +251,14 @@ def generate_from_cfg(
for pt in grammar.nt_dict[root]
)
- # n=None means return all sentences, otherwise return n sentences
+ if expand:
+ # Expand condensed lines
+ # (containing parenthesized word forms)
+ # into separate lines
+ iter = expander(iter)
+
+ # n=None means return all sentences,
+ # otherwise return n sentences
iter = itertools.islice(iter, 0, n)
return (" ".join(sl) for sl in iter)
@@ -310,6 +292,9 @@ def _generate_all(
# (note: there are probably more recursive
# nonterminals, they can be added here)
_RECURSIVE_NT = re.compile(r"^Nl([/_][a-zA-Z0-9]+)*$")
+_PLACEHOLDER_RE = re.compile(r"{([\w]+?)}")
+_PLACEHOLDER_PREFIX = "GENERATORPLACEHOLDER_"
+_PLACEHOLDER_PREFIX_LEN = len(_PLACEHOLDER_PREFIX)
def _generate_one(
@@ -320,6 +305,9 @@ def _generate_one(
# Special handling of Nl nonterminal,
# since it is recursive
yield [pink(f"<{gi.name}>")]
+ elif gi.name.startswith(_PLACEHOLDER_PREFIX):
+ # Placeholder nonterminal (replaces)
+ yield [blue(f"{{{gi.name[_PLACEHOLDER_PREFIX_LEN:]}}}")]
elif isinstance(gi, Nonterminal):
if gi.is_optional and gi.name.endswith("*"):
# Star nonterminal, signify using brackets and '...'
@@ -359,49 +347,58 @@ def _generate_one(
)
parser.add_argument(
"files",
- nargs="+",
- help="File/s containing the grammar fragments",
+ nargs="*",
+ help="file/s containing the grammar fragments. "
+ "Always loads Greynir.grammar, so no file has to be specified "
+ "when generating sentences from Greynir.grammar",
)
parser.add_argument(
"-r",
"--root",
default="Query",
- help="Root nonterminal to start from",
+ help="root nonterminal to start from",
)
parser.add_argument(
"-d",
"--depth",
type=int,
- help="Maximum depth of the generated sentences",
+ help="maximum depth of the generated sentences",
)
parser.add_argument(
"-n",
"--num",
type=int,
- help="Maximum number of sentences to generate",
+ help="maximum number of sentences to generate",
+ )
+ parser.add_argument(
+ "-e",
+ "--expand",
+ action="store_true",
+ help="expand lines with multiple interpretations into separate lines",
)
parser.add_argument(
"-s",
"--strict",
action="store_true",
- help="Enable strict mode, adds some opinionated assertions about the grammar",
+ help="enable strict mode, adds some opinionated assertions about the grammar",
)
parser.add_argument(
"-c",
"--color",
action="store_true",
- help="Enables colored output (to stdout only)",
+ help="enables colored output, when not fully "
+ "expanding lines or writing output to file",
)
parser.add_argument(
"-o",
"--output",
type=Path,
- help="Write output to file instead of stdout (faster)",
+ help="write output to file instead of stdout (faster)",
)
parser.add_argument(
"--force",
action="store_true",
- help="Forcefully overwrite output file, ignoring any warnings",
+ help="forcefully overwrite output file, ignoring any warnings",
)
parser.add_argument("--max-size", type=int, help="Maximum output filesize in MiB.")
args = parser.parse_args()
@@ -416,16 +413,16 @@ def _generate_one(
if args.output:
p = args.output
assert isinstance(p, Path)
- try:
- p.touch(exist_ok=False) # Raise error if we are overwriting a file
- except FileExistsError:
- if not args.force:
- print("Output file already exists!")
- exit(1)
+ if (p.is_file() or p.exists()) and not args.force:
+ print("Output file already exists!")
+ exit(1)
- if not args.color or p is not None:
- # Undefine color functions
+ # Expand and writing to file disables color
+ args.color = args.color and not args.expand and p is None
+
+ if not args.color:
useless: ColorF = lambda s: s
+ # Undefine color functions
[
bold,
black,
@@ -446,14 +443,39 @@ def _generate_one(
] = [useless] * 16
grammar_fragments: str = PREAMBLE
+
+ # We replace {...} format strings with a placeholder
+ placeholder_defs: str = ""
+
+ def placeholder_func(m: Match[str]) -> str:
+ """
+ Replaces {...} format strings in grammar with an empty nonterminal.
+ We then handle these nonterminals specifically in _generate_one().
+ """
+ global placeholder_defs
+ new_nt = f"{_PLACEHOLDER_PREFIX}{m.group(1)}"
+ # Create empty production for this nonterminal ('keep'-tag just in case)
+ placeholder_defs += f"\n{new_nt} → ∅\n$tag(keep) {new_nt}\n"
+ # Replace format string with reference to new nonterminal
+ return new_nt
+
for file in [BIN_Parser._GRAMMAR_FILE] + args.files: # type: ignore
with open(file, "r") as f:
grammar_fragments += "\n"
- grammar_fragments += f.read()
+ grammar_fragments += _PLACEHOLDER_RE.sub(placeholder_func, f.read())
+
+ # Add all the placeholder nonterminal definitions we added
+ grammar_fragments += placeholder_defs
+ if len(args.files) == 0:
+ # Generate Greynir.grammar by default
+ grammar_fragments = grammar_fragments.replace('Query → ""', "Query → S0", 1)
# Initialize QueryGrammar class from grammar files
grammar = QueryGrammar()
- grammar.read_from_generator(args.files[0], iter(grammar_fragments.split("\n")))
+ grammar.read_from_generator(
+ args.files[0] if args.files else BIN_Parser._GRAMMAR_FILE, # type: ignore
+ iter(grammar_fragments.split("\n")),
+ )
# Create sentence generator
g = generate_from_cfg(
@@ -461,6 +483,7 @@ def _generate_one(
root=args.root,
depth=args.depth,
n=args.num,
+ expand=args.expand,
)
if p is not None: