added sqlite demo

This commit is contained in:
thomashamburg 2025-10-13 08:33:30 +02:00
parent 9080196f6b
commit cd5d269483
46 changed files with 14615 additions and 0 deletions

16
demo/sqlite/countries.sql Normal file
View File

@ -0,0 +1,16 @@
.headers on
.mode table
WITH raw AS (
SELECT
value AS country
FROM json_each(readfile('j_countries.json'))
)
SELECT
json_extract(country, '$.translations.deu.common') AS german_name,
json_extract(country, '$.population') AS population,
json_extract(country, '$.capital[0]') AS capital,
json_extract(country, '$.tld[0]') AS tld,
json_extract(country, '$.timezones') AS timezones
FROM raw
ORDER BY german_name;

View File

@ -0,0 +1,19 @@
.mode csv
.headers on
.output result.csv
WITH raw AS (
SELECT
value AS country
FROM json_each(readfile('j_countries.json'))
)
SELECT
json_extract(country, '$.translations.deu.common') AS german_name,
json_extract(country, '$.population') AS population,
json_extract(country, '$.capital[0]') AS capital,
json_extract(country, '$.timezones[0]') AS timezones
FROM raw
ORDER BY german_name;
.output stdout

File diff suppressed because one or more lines are too long

1
demo/sqlite/readfile.sql Normal file
View File

@ -0,0 +1 @@
SELECT * FROM json_each(readfile('j_countries.json'));

196
demo/sqlite/result.csv Normal file
View File

@ -0,0 +1,196 @@
german_name,population,capital,timezones
Afghanistan,40218234,Kabul,UTC+04:30
Albanien,2837743,Tirana,UTC+01:00
Algerien,44700000,Algiers,UTC+01:00
Andorra,77265,"Andorra la Vella",UTC+01:00
Angola,32866268,Luanda,UTC+01:00
"Antigua und Barbuda",97928,"Saint John's",UTC-04:00
Argentinien,45376763,"Buenos Aires",UTC-03:00
Armenien,2963234,Yerevan,UTC+04:00
Aserbaidschan,10110116,Baku,UTC+04:00
Australien,25687041,Canberra,UTC+05:00
Bahamas,393248,Nassau,UTC-05:00
Bahrain,1701583,Manama,UTC+03:00
Bangladesch,164689383,Dhaka,UTC+06:00
Barbados,287371,Bridgetown,UTC-04:00
Belgien,11555997,Brussels,UTC+01:00
Belize,397621,Belmopan,UTC-06:00
Benin,12123198,Porto-Novo,UTC+01:00
Bhutan,771612,Thimphu,UTC+06:00
Bolivien,11673029,Sucre,UTC-04:00
"Bosnien und Herzegowina",3280815,Sarajevo,UTC+01:00
Botswana,2351625,Gaborone,UTC+02:00
Brasilien,212559409,"Brasília",UTC-05:00
Brunei,437483,"Bandar Seri Begawan",UTC+08:00
Bulgarien,6927288,Sofia,UTC+02:00
"Burkina Faso",20903278,Ouagadougou,UTC
Burundi,11890781,Gitega,UTC+02:00
Chile,19116209,Santiago,UTC-06:00
China,1402112000,Beijing,UTC+08:00
"Costa Rica",5094114,"San José",UTC-06:00
Deutschland,83240525,Berlin,UTC+01:00
Dominica,71991,Roseau,UTC-04:00
"Dominikanische Republik",10847904,"Santo Domingo",UTC-04:00
Dschibuti,988002,Djibouti,UTC+03:00
"Dänemark",5831404,Copenhagen,UTC-04:00
Ecuador,17643060,Quito,UTC-06:00
"El Salvador",6486201,"San Salvador",UTC-06:00
"Elfenbeinküste",26378275,Yamoussoukro,UTC
Eritrea,5352000,Asmara,UTC+03:00
Estland,1331057,Tallinn,UTC+02:00
Fidschi,896444,Suva,UTC+12:00
Finnland,5530719,Helsinki,UTC+02:00
Frankreich,67391582,Paris,UTC-10:00
Gabun,2225728,Libreville,UTC+01:00
Gambia,2416664,Banjul,UTC+00:00
Georgien,3714000,Tbilisi,UTC+04:00
Ghana,31072945,Accra,UTC
Grenada,112519,"St. George's",UTC-04:00
Griechenland,10715549,Athens,UTC+02:00
Guatemala,16858333,"Guatemala City",UTC-06:00
Guinea,13132792,Conakry,UTC
Guinea-Bissau,1967998,Bissau,UTC
Guyana,786559,Georgetown,UTC-04:00
Haiti,11402533,Port-au-Prince,UTC-05:00
Honduras,9904608,Tegucigalpa,UTC-06:00
Indien,1380004385,"New Delhi",UTC+05:30
Indonesien,273523621,Jakarta,UTC+07:00
Irak,40222503,Baghdad,UTC+03:00
Iran,83992953,Tehran,UTC+03:30
Irland,4994724,Dublin,UTC
Island,366425,Reykjavik,UTC
Israel,9216900,Jerusalem,UTC+02:00
Italien,59554023,Rome,UTC+01:00
Jamaika,2961161,Kingston,UTC-05:00
Japan,125836021,Tokyo,UTC+09:00
Jemen,29825968,"Sana'a",UTC+03:00
Jordanien,10203140,Amman,UTC+03:00
Kambodscha,16718971,"Phnom Penh",UTC+07:00
Kamerun,26545864,"Yaoundé",UTC+01:00
Kanada,38005238,Ottawa,UTC-08:00
"Kap Verde",555988,Praia,UTC-01:00
Kasachstan,18754440,Astana,UTC+05:00
Katar,2881060,Doha,UTC+03:00
Kenia,53771300,Nairobi,UTC+03:00
Kirgisistan,6591600,Bishkek,UTC+06:00
Kiribati,119446,"South Tarawa",UTC+12:00
Kolumbien,50882884,"Bogotá",UTC-05:00
Komoren,869595,Moroni,UTC+03:00
Kongo,5657000,Brazzaville,UTC+01:00
"Kongo (Dem. Rep.)",108407721,Kinshasa,UTC+01:00
Kosovo,1775378,Pristina,UTC+01:00
Kroatien,4047200,Zagreb,UTC+01:00
Kuba,11326616,Havana,UTC-05:00
Kuwait,4270563,"Kuwait City",UTC+03:00
Laos,7275556,Vientiane,UTC+07:00
Lesotho,2142252,Maseru,UTC+02:00
Lettland,1901548,Riga,UTC+02:00
Libanon,6825442,Beirut,UTC+02:00
Liberia,5057677,Monrovia,UTC
Libyen,6871287,Tripoli,UTC+01:00
Liechtenstein,38137,Vaduz,UTC+01:00
Litauen,2794700,Vilnius,UTC+02:00
Luxemburg,632275,Luxembourg,UTC+01:00
Madagaskar,27691019,Antananarivo,UTC+03:00
Malawi,19129955,Lilongwe,UTC+02:00
Malaysia,32365998,"Kuala Lumpur",UTC+08:00
Malediven,540542,"Malé",UTC+05:00
Mali,20250834,Bamako,UTC
Malta,525285,Valletta,UTC+01:00
Marokko,36910558,Rabat,UTC
Marshallinseln,59194,Majuro,UTC+12:00
Mauretanien,4649660,Nouakchott,UTC
Mauritius,1265740,"Port Louis",UTC+04:00
Mexiko,128932753,"Mexico City",UTC-08:00
Mikronesien,115021,Palikir,UTC+10:00
Moldawien,2617820,"Chișinău",UTC+02:00
Monaco,39244,Monaco,UTC+01:00
Mongolei,3278292,"Ulan Bator",UTC+07:00
Montenegro,621718,Podgorica,UTC+01:00
Mosambik,31255435,Maputo,UTC+02:00
Myanmar,54409794,Naypyidaw,UTC+06:30
Namibia,2540916,Windhoek,UTC+01:00
Nauru,10834,Yaren,UTC+12:00
Nepal,29136808,Kathmandu,UTC+05:45
Neuseeland,5084300,Wellington,UTC-11:00
Nicaragua,6624554,Managua,UTC-06:00
Niederlande,16655799,Amsterdam,UTC+01:00
Niger,24206636,Niamey,UTC+01:00
Nigeria,206139587,Abuja,UTC+01:00
Nordkorea,25778815,Pyongyang,UTC+09:00
Nordmazedonien,2077132,Skopje,UTC+01:00
Norwegen,5379475,Oslo,UTC+01:00
Oman,5106622,Muscat,UTC+04:00
Osttimor,1318442,Dili,UTC+09:00
Pakistan,220892331,Islamabad,UTC+05:00
Palau,18092,Ngerulmud,UTC+09:00
Panama,4314768,"Panama City",UTC-05:00
Papua-Neuguinea,8947027,"Port Moresby",UTC+10:00
Paraguay,7132530,"Asunción",UTC-04:00
Peru,32971846,Lima,UTC-05:00
Philippinen,109581085,Manila,UTC+08:00
Polen,37950802,Warsaw,UTC+01:00
Portugal,10305564,Lisbon,UTC-01:00
Ruanda,12952209,Kigali,UTC+02:00
"Rumänien",19286123,Bucharest,UTC+02:00
Russland,144104080,Moscow,UTC+03:00
Salomonen,686878,Honiara,UTC+11:00
Sambia,18383956,Lusaka,UTC+02:00
Samoa,198410,Apia,UTC+13:00
"San Marino",33938,"City of San Marino",UTC+01:00
Saudi-Arabien,34813867,Riyadh,UTC+03:00
Schweden,10353442,Stockholm,UTC+01:00
Schweiz,8654622,Bern,UTC+01:00
Senegal,16743930,Dakar,UTC
Serbien,6908224,Belgrade,UTC+01:00
Seychellen,98462,Victoria,UTC+04:00
"Sierra Leone",7976985,Freetown,UTC
Simbabwe,14862927,Harare,UTC+02:00
Singapur,5685807,Singapore,UTC+08:00
Slowakei,5458827,Bratislava,UTC+01:00
Slowenien,2100126,Ljubljana,UTC+01:00
Somalia,15893219,Mogadishu,UTC+03:00
Spanien,47351567,Madrid,UTC
"Sri Lanka",21919000,"Sri Jayawardenepura Kotte",UTC+05:30
"St. Kitts und Nevis",53192,Basseterre,UTC-04:00
"St. Lucia",183629,Castries,UTC-04:00
"St. Vincent und die Grenadinen",110947,Kingstown,UTC-04:00
Sudan,43849269,Khartoum,UTC+03:00
Suriname,586634,Paramaribo,UTC-03:00
Swasiland,1160164,Mbabane,UTC+02:00
Syrien,17500657,Damascus,UTC+02:00
"São Tomé und Príncipe",219161,"São Tomé",UTC
"Südafrika",59308690,Pretoria,UTC+02:00
"Südkorea",51780579,Seoul,UTC+09:00
"Südsudan",11193729,Juba,UTC+03:00
Tadschikistan,9537642,Dushanbe,UTC+05:00
Tansania,59734213,Dodoma,UTC+03:00
Thailand,69799978,Bangkok,UTC+07:00
Togo,8278737,"Lomé",UTC
Tonga,105697,"Nuku'alofa",UTC+13:00
"Trinidad und Tobago",1399491,"Port of Spain",UTC-04:00
Tschad,16425859,"N'Djamena",UTC+01:00
Tschechien,10698896,Prague,UTC+01:00
Tunesien,11818618,Tunis,UTC+01:00
Turkmenistan,6031187,Ashgabat,UTC+05:00
Tuvalu,11792,Funafuti,UTC+12:00
"Türkei",84339067,Ankara,UTC+03:00
Uganda,45741000,Kampala,UTC+03:00
Ukraine,44134693,Kyiv,UTC+02:00
Ungarn,9749763,Budapest,UTC+01:00
Uruguay,3473727,Montevideo,UTC-03:00
Usbekistan,34232050,Tashkent,UTC+05:00
Vanuatu,307150,"Port Vila",UTC+11:00
Vatikanstadt,451,"Vatican City",UTC+01:00
Venezuela,28435943,Caracas,UTC-04:00
"Vereinigte Arabische Emirate",9890400,"Abu Dhabi",UTC+04:00
"Vereinigte Staaten",329484123,"Washington, D.C.",UTC-12:00
"Vereinigtes Königreich",67215293,London,UTC-08:00
Vietnam,97338583,Hanoi,UTC+07:00
"Weißrussland",9398861,Minsk,UTC+03:00
"Zentralafrikanische Republik",4829764,Bangui,UTC+01:00
Zypern,1207361,Nicosia,UTC+02:00
"Ägypten",102334403,Cairo,UTC+02:00
"Äquatorialguinea",1402985,Malabo,UTC+01:00
"Äthiopien",114963583,"Addis Ababa",UTC+03:00
"Österreich",8917205,Vienna,UTC+01:00
1 german_name population capital timezones
2 Afghanistan 40218234 Kabul UTC+04:30
3 Albanien 2837743 Tirana UTC+01:00
4 Algerien 44700000 Algiers UTC+01:00
5 Andorra 77265 Andorra la Vella UTC+01:00
6 Angola 32866268 Luanda UTC+01:00
7 Antigua und Barbuda 97928 Saint John's UTC-04:00
8 Argentinien 45376763 Buenos Aires UTC-03:00
9 Armenien 2963234 Yerevan UTC+04:00
10 Aserbaidschan 10110116 Baku UTC+04:00
11 Australien 25687041 Canberra UTC+05:00
12 Bahamas 393248 Nassau UTC-05:00
13 Bahrain 1701583 Manama UTC+03:00
14 Bangladesch 164689383 Dhaka UTC+06:00
15 Barbados 287371 Bridgetown UTC-04:00
16 Belgien 11555997 Brussels UTC+01:00
17 Belize 397621 Belmopan UTC-06:00
18 Benin 12123198 Porto-Novo UTC+01:00
19 Bhutan 771612 Thimphu UTC+06:00
20 Bolivien 11673029 Sucre UTC-04:00
21 Bosnien und Herzegowina 3280815 Sarajevo UTC+01:00
22 Botswana 2351625 Gaborone UTC+02:00
23 Brasilien 212559409 Brasília UTC-05:00
24 Brunei 437483 Bandar Seri Begawan UTC+08:00
25 Bulgarien 6927288 Sofia UTC+02:00
26 Burkina Faso 20903278 Ouagadougou UTC
27 Burundi 11890781 Gitega UTC+02:00
28 Chile 19116209 Santiago UTC-06:00
29 China 1402112000 Beijing UTC+08:00
30 Costa Rica 5094114 San José UTC-06:00
31 Deutschland 83240525 Berlin UTC+01:00
32 Dominica 71991 Roseau UTC-04:00
33 Dominikanische Republik 10847904 Santo Domingo UTC-04:00
34 Dschibuti 988002 Djibouti UTC+03:00
35 Dänemark 5831404 Copenhagen UTC-04:00
36 Ecuador 17643060 Quito UTC-06:00
37 El Salvador 6486201 San Salvador UTC-06:00
38 Elfenbeinküste 26378275 Yamoussoukro UTC
39 Eritrea 5352000 Asmara UTC+03:00
40 Estland 1331057 Tallinn UTC+02:00
41 Fidschi 896444 Suva UTC+12:00
42 Finnland 5530719 Helsinki UTC+02:00
43 Frankreich 67391582 Paris UTC-10:00
44 Gabun 2225728 Libreville UTC+01:00
45 Gambia 2416664 Banjul UTC+00:00
46 Georgien 3714000 Tbilisi UTC+04:00
47 Ghana 31072945 Accra UTC
48 Grenada 112519 St. George's UTC-04:00
49 Griechenland 10715549 Athens UTC+02:00
50 Guatemala 16858333 Guatemala City UTC-06:00
51 Guinea 13132792 Conakry UTC
52 Guinea-Bissau 1967998 Bissau UTC
53 Guyana 786559 Georgetown UTC-04:00
54 Haiti 11402533 Port-au-Prince UTC-05:00
55 Honduras 9904608 Tegucigalpa UTC-06:00
56 Indien 1380004385 New Delhi UTC+05:30
57 Indonesien 273523621 Jakarta UTC+07:00
58 Irak 40222503 Baghdad UTC+03:00
59 Iran 83992953 Tehran UTC+03:30
60 Irland 4994724 Dublin UTC
61 Island 366425 Reykjavik UTC
62 Israel 9216900 Jerusalem UTC+02:00
63 Italien 59554023 Rome UTC+01:00
64 Jamaika 2961161 Kingston UTC-05:00
65 Japan 125836021 Tokyo UTC+09:00
66 Jemen 29825968 Sana'a UTC+03:00
67 Jordanien 10203140 Amman UTC+03:00
68 Kambodscha 16718971 Phnom Penh UTC+07:00
69 Kamerun 26545864 Yaoundé UTC+01:00
70 Kanada 38005238 Ottawa UTC-08:00
71 Kap Verde 555988 Praia UTC-01:00
72 Kasachstan 18754440 Astana UTC+05:00
73 Katar 2881060 Doha UTC+03:00
74 Kenia 53771300 Nairobi UTC+03:00
75 Kirgisistan 6591600 Bishkek UTC+06:00
76 Kiribati 119446 South Tarawa UTC+12:00
77 Kolumbien 50882884 Bogotá UTC-05:00
78 Komoren 869595 Moroni UTC+03:00
79 Kongo 5657000 Brazzaville UTC+01:00
80 Kongo (Dem. Rep.) 108407721 Kinshasa UTC+01:00
81 Kosovo 1775378 Pristina UTC+01:00
82 Kroatien 4047200 Zagreb UTC+01:00
83 Kuba 11326616 Havana UTC-05:00
84 Kuwait 4270563 Kuwait City UTC+03:00
85 Laos 7275556 Vientiane UTC+07:00
86 Lesotho 2142252 Maseru UTC+02:00
87 Lettland 1901548 Riga UTC+02:00
88 Libanon 6825442 Beirut UTC+02:00
89 Liberia 5057677 Monrovia UTC
90 Libyen 6871287 Tripoli UTC+01:00
91 Liechtenstein 38137 Vaduz UTC+01:00
92 Litauen 2794700 Vilnius UTC+02:00
93 Luxemburg 632275 Luxembourg UTC+01:00
94 Madagaskar 27691019 Antananarivo UTC+03:00
95 Malawi 19129955 Lilongwe UTC+02:00
96 Malaysia 32365998 Kuala Lumpur UTC+08:00
97 Malediven 540542 Malé UTC+05:00
98 Mali 20250834 Bamako UTC
99 Malta 525285 Valletta UTC+01:00
100 Marokko 36910558 Rabat UTC
101 Marshallinseln 59194 Majuro UTC+12:00
102 Mauretanien 4649660 Nouakchott UTC
103 Mauritius 1265740 Port Louis UTC+04:00
104 Mexiko 128932753 Mexico City UTC-08:00
105 Mikronesien 115021 Palikir UTC+10:00
106 Moldawien 2617820 Chișinău UTC+02:00
107 Monaco 39244 Monaco UTC+01:00
108 Mongolei 3278292 Ulan Bator UTC+07:00
109 Montenegro 621718 Podgorica UTC+01:00
110 Mosambik 31255435 Maputo UTC+02:00
111 Myanmar 54409794 Naypyidaw UTC+06:30
112 Namibia 2540916 Windhoek UTC+01:00
113 Nauru 10834 Yaren UTC+12:00
114 Nepal 29136808 Kathmandu UTC+05:45
115 Neuseeland 5084300 Wellington UTC-11:00
116 Nicaragua 6624554 Managua UTC-06:00
117 Niederlande 16655799 Amsterdam UTC+01:00
118 Niger 24206636 Niamey UTC+01:00
119 Nigeria 206139587 Abuja UTC+01:00
120 Nordkorea 25778815 Pyongyang UTC+09:00
121 Nordmazedonien 2077132 Skopje UTC+01:00
122 Norwegen 5379475 Oslo UTC+01:00
123 Oman 5106622 Muscat UTC+04:00
124 Osttimor 1318442 Dili UTC+09:00
125 Pakistan 220892331 Islamabad UTC+05:00
126 Palau 18092 Ngerulmud UTC+09:00
127 Panama 4314768 Panama City UTC-05:00
128 Papua-Neuguinea 8947027 Port Moresby UTC+10:00
129 Paraguay 7132530 Asunción UTC-04:00
130 Peru 32971846 Lima UTC-05:00
131 Philippinen 109581085 Manila UTC+08:00
132 Polen 37950802 Warsaw UTC+01:00
133 Portugal 10305564 Lisbon UTC-01:00
134 Ruanda 12952209 Kigali UTC+02:00
135 Rumänien 19286123 Bucharest UTC+02:00
136 Russland 144104080 Moscow UTC+03:00
137 Salomonen 686878 Honiara UTC+11:00
138 Sambia 18383956 Lusaka UTC+02:00
139 Samoa 198410 Apia UTC+13:00
140 San Marino 33938 City of San Marino UTC+01:00
141 Saudi-Arabien 34813867 Riyadh UTC+03:00
142 Schweden 10353442 Stockholm UTC+01:00
143 Schweiz 8654622 Bern UTC+01:00
144 Senegal 16743930 Dakar UTC
145 Serbien 6908224 Belgrade UTC+01:00
146 Seychellen 98462 Victoria UTC+04:00
147 Sierra Leone 7976985 Freetown UTC
148 Simbabwe 14862927 Harare UTC+02:00
149 Singapur 5685807 Singapore UTC+08:00
150 Slowakei 5458827 Bratislava UTC+01:00
151 Slowenien 2100126 Ljubljana UTC+01:00
152 Somalia 15893219 Mogadishu UTC+03:00
153 Spanien 47351567 Madrid UTC
154 Sri Lanka 21919000 Sri Jayawardenepura Kotte UTC+05:30
155 St. Kitts und Nevis 53192 Basseterre UTC-04:00
156 St. Lucia 183629 Castries UTC-04:00
157 St. Vincent und die Grenadinen 110947 Kingstown UTC-04:00
158 Sudan 43849269 Khartoum UTC+03:00
159 Suriname 586634 Paramaribo UTC-03:00
160 Swasiland 1160164 Mbabane UTC+02:00
161 Syrien 17500657 Damascus UTC+02:00
162 São Tomé und Príncipe 219161 São Tomé UTC
163 Südafrika 59308690 Pretoria UTC+02:00
164 Südkorea 51780579 Seoul UTC+09:00
165 Südsudan 11193729 Juba UTC+03:00
166 Tadschikistan 9537642 Dushanbe UTC+05:00
167 Tansania 59734213 Dodoma UTC+03:00
168 Thailand 69799978 Bangkok UTC+07:00
169 Togo 8278737 Lomé UTC
170 Tonga 105697 Nuku'alofa UTC+13:00
171 Trinidad und Tobago 1399491 Port of Spain UTC-04:00
172 Tschad 16425859 N'Djamena UTC+01:00
173 Tschechien 10698896 Prague UTC+01:00
174 Tunesien 11818618 Tunis UTC+01:00
175 Turkmenistan 6031187 Ashgabat UTC+05:00
176 Tuvalu 11792 Funafuti UTC+12:00
177 Türkei 84339067 Ankara UTC+03:00
178 Uganda 45741000 Kampala UTC+03:00
179 Ukraine 44134693 Kyiv UTC+02:00
180 Ungarn 9749763 Budapest UTC+01:00
181 Uruguay 3473727 Montevideo UTC-03:00
182 Usbekistan 34232050 Tashkent UTC+05:00
183 Vanuatu 307150 Port Vila UTC+11:00
184 Vatikanstadt 451 Vatican City UTC+01:00
185 Venezuela 28435943 Caracas UTC-04:00
186 Vereinigte Arabische Emirate 9890400 Abu Dhabi UTC+04:00
187 Vereinigte Staaten 329484123 Washington, D.C. UTC-12:00
188 Vereinigtes Königreich 67215293 London UTC-08:00
189 Vietnam 97338583 Hanoi UTC+07:00
190 Weißrussland 9398861 Minsk UTC+03:00
191 Zentralafrikanische Republik 4829764 Bangui UTC+01:00
192 Zypern 1207361 Nicosia UTC+02:00
193 Ägypten 102334403 Cairo UTC+02:00
194 Äquatorialguinea 1402985 Malabo UTC+01:00
195 Äthiopien 114963583 Addis Ababa UTC+03:00
196 Österreich 8917205 Vienna UTC+01:00

70
demo/sqlite/sudoku.sql Normal file
View File

@ -0,0 +1,70 @@
WITH RECURSIVE
input(sud) AS (
VALUES('53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79')
),
digits(z, lp) AS (
VALUES('1', 1)
UNION ALL
SELECT CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9
),
x(s, ind) AS (
SELECT sud, instr(sud, '.') FROM input
UNION ALL
SELECT
substr(s, 1, ind-1) || z || substr(s, ind+1),
instr(substr(s, 1, ind-1) || z || substr(s, ind+1), '.')
FROM x, digits AS z
WHERE ind>0
AND NOT EXISTS (
SELECT 1
FROM digits AS lp
WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1)
OR z.z = substr(s, ((ind-1)%9) + (lp-1)*9 + 1, 1)
OR z.z = substr(s, (((ind-1)/3) % 3)*3
+ ((ind-1)/27)*27
+ lp + ((lp-1)/3)*6, 1)
)
),
solved(s) AS (
SELECT s FROM x WHERE ind=0
),
-- Build 9 pretty rows for both input and solved using digits.lp = 1..9
grid_rows(name, rn, line) AS (
SELECT 'input', d.lp,
substr(sud, (d.lp-1)*9+1, 3)
|| ' | ' ||
substr(sud, (d.lp-1)*9+4, 3)
|| ' | ' ||
substr(sud, (d.lp-1)*9+7, 3)
FROM input CROSS JOIN digits d
UNION ALL
SELECT 'solved', d.lp,
substr(s, (d.lp-1)*9+1, 3)
|| ' | ' ||
substr(s, (d.lp-1)*9+4, 3)
|| ' | ' ||
substr(s, (d.lp-1)*9+7, 3)
FROM solved CROSS JOIN digits d
),
-- Insert horizontal separators after rows 3 and 6 by computing an ordering key
grid_with_sep AS (
SELECT name, rn*2-1 AS ord, line FROM grid_rows
UNION ALL
SELECT name, rn*2 AS ord, '----+-----+----' FROM grid_rows WHERE rn IN (3,6)
)
-- Single final UNIONed selection, ordered by group + position
SELECT line FROM (
SELECT 1 AS grp, ord AS ord, line FROM grid_with_sep WHERE name='input'
UNION ALL
SELECT 2 AS grp, 0 AS ord, '' AS line
UNION ALL
SELECT 3 AS grp, ord AS ord, line FROM grid_with_sep WHERE name='solved'
) ORDER BY grp, ord;

View File

@ -0,0 +1,14 @@
CREATE TABLE ship_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL,
subsystem TEXT NOT NULL,
severity TEXT CHECK(severity IN ('CRITICAL', 'ALERT', 'WARNING', 'NOTICE', 'INFO')) NOT NULL DEFAULT 'INFO',
color TEXT NOT NULL,
message TEXT NOT NULL
);
CREATE TABLE crew_member (
id INTEGER PRIMARY KEY AUTOINCREMENT,
rank TEXT NOT NULL,
name TEXT NOT NULL
);

95
lcars_v2/embed/crew.json Normal file
View File

@ -0,0 +1,95 @@
[
"Ensign Beckett Mariner",
"Ensign Brad Boimler",
"Captain Carol Freeman",
"Commander Jack Ransom",
"Ensign Barsa Orsino",
"Lieutenant Commander D'Vana Tendi",
"Lieutenant Commander Sam Rutherford",
"Lieutenant Commander Shaxs",
"Ensign Liora Vance",
"Ensign Rylan Sato",
"Lieutenant Jarek Torin",
"Lieutenant Kira Dallin",
"Lieutenant T'Lara Venn",
"Lieutenant Shonnie Velar",
"Lieutenant Commander Aric Thorne",
"Lieutenant Commander Selene Marvik",
"Lieutenant Commander Jovan Kreel",
"Lieutenant Orin Kallis",
"Ensign Mira Talon",
"Ensign Fynn Darvik",
"Lieutenant Commander Elara Voss",
"Lieutenant Zev Ralyn",
"Ensign Daxia Morn",
"Lieutenant Varek Solis",
"Ensign Tylen Kael",
"Lieutenant Commander Nira Falco",
"Lieutenant Kael Dorran",
"Ensign Saren Vale",
"Ensign Tova Lin",
"Lieutenant Commander Ryn Talor",
"Lieutenant Draven Korr",
"Ensign Lyra Kenning",
"Ensign Joren Pax",
"Lieutenant Commander Calix Arden",
"Lieutenant Selan Vey",
"Ensign Aricel Taren",
"Ensign Velin Daro",
"Lieutenant Caris Vennor",
"Lieutenant Kellen Dray",
"Lieutenant Risa Talven",
"Lieutenant Commander Thalen Voss",
"Lieutenant Commander Sariah Quell",
"Ensign Orin Talvik",
"Ensign Lyric Selden",
"Lieutenant Commander Varen Korr",
"Lieutenant Elara Vynn",
"Ensign Jax Talmar",
"Lieutenant Commander Neris Vay",
"Lieutenant Draven Solis",
"Ensign Tavia Korlen",
"Ensign Ryn Paxil",
"Lieutenant Commander Kira Dalen",
"Lieutenant Zev Ardin",
"Ensign Lyra Taven",
"Ensign Fynn Velar",
"Lieutenant Commander Calen Rhos",
"Lieutenant Selan Vaylen",
"Ensign Aricel Dorran",
"Ensign Tylen Korr",
"Lieutenant Commander Nira Talos",
"Lieutenant Kael Venn",
"Ensign Saren Daro",
"Ensign Tova Vennor",
"Lieutenant Commander Ryn Arden",
"Lieutenant Draven Voss",
"Ensign Lyra Talin",
"Ensign Joren Vay",
"Lieutenant Commander Calix Talven",
"Lieutenant Selan Korr",
"Ensign Aricel Vynn",
"Ensign Velin Talor",
"Lieutenant Caris Vaylen",
"Lieutenant Kellen Rhos",
"Lieutenant Risa Vennor",
"Lieutenant Commander Thalen Daro",
"Lieutenant Commander Sariah Voss",
"Ensign Orin Vaylen",
"Ensign Lyric Talven",
"Lieutenant Commander Varen Talos",
"Lieutenant Elara Solis",
"Ensign Jax Vennor",
"Lieutenant Commander Neris Talor",
"Lieutenant Draven Vaylen",
"Ensign Tavia Dorran",
"Ensign Ryn Voss",
"Lieutenant Commander Kira Arden",
"Lieutenant Zev Vay",
"Ensign Lyra Daro",
"Ensign Fynn Talor",
"Lieutenant Commander Calen Venn",
"Lieutenant Selan Talvik",
"Ensign Aricel Rhos"
]

1066
lcars_v2/embed/messages.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,312 @@
// modified version, see original:
// https://github.com/dtomasi/go-event-bus/blob/main/event_bus.go
package eventbus
import (
"sync"
"sync/atomic"
)
type Rec map[string]interface{}
// Event holds topic name and data.
type Event struct {
Data Rec
Topic string
wg *sync.WaitGroup
}
// Done calls Done on sync.WaitGroup if set.
func (e *Event) Done() {
if e.wg != nil {
e.wg.Done()
}
}
// CallbackFunc Defines a CallbackFunc.
type CallbackFunc func(topic string, data Rec)
// EventChannel is a channel which can accept an Event.
type EventChannel chan Event
// NewEventChannel Creates a new EventChannel.
func NewEventChannel() EventChannel {
return make(EventChannel)
}
// dataChannelSlice is a slice of DataChannels.
type eventChannelSlice []EventChannel
// EventBus stores the information about subscribers interested for a particular topic.
type EventBus struct {
mu sync.RWMutex
subscribers map[string]eventChannelSlice
stats *Stats
}
// NewEventBus returns a new EventBus instance.
func NewEventBus() *EventBus {
return &EventBus{ //nolint:exhaustivestruct
subscribers: map[string]eventChannelSlice{},
stats: newStats(),
}
}
// getSubscribingChannels returns all subscribing channels including wildcard matches.
func (eb *EventBus) getSubscribingChannels(topic string) eventChannelSlice {
subChannels := eventChannelSlice{}
for topicName := range eb.subscribers {
if topicName == topic || matchWildcard(topicName, topic) {
subChannels = append(subChannels, eb.subscribers[topicName]...)
}
}
return subChannels
}
// doPublish is publishing events to channels internally.
func (eb *EventBus) doPublish(channels eventChannelSlice, evt Event) {
eb.mu.RLock()
defer eb.mu.RUnlock()
go func(channels eventChannelSlice, evt Event) {
for _, ch := range channels {
ch <- evt
}
}(channels, evt)
}
// Code from https://github.com/minio/minio/blob/master/pkg/wildcard/match.go
func matchWildcard(pattern, name string) bool {
if pattern == "" {
return name == pattern
}
if pattern == "*" {
return true
}
// Does only wildcard '*' match.
return deepMatchRune([]rune(name), []rune(pattern), true)
}
// Code from https://github.com/minio/minio/blob/master/pkg/wildcard/match.go
func deepMatchRune(str, pattern []rune, simple bool) bool { //nolint:unparam
for len(pattern) > 0 {
switch pattern[0] {
default:
if len(str) == 0 || str[0] != pattern[0] {
return false
}
case '*':
return deepMatchRune(str, pattern[1:], simple) ||
(len(str) > 0 && deepMatchRune(str[1:], pattern, simple))
}
str = str[1:]
pattern = pattern[1:]
}
return len(str) == 0 && len(pattern) == 0
}
// PublishAsync data to a topic asynchronously
// This function returns a bool channel which indicates that all subscribers where called.
func (eb *EventBus) PublishAsync(topic string, data Rec) {
eb.doPublish(
eb.getSubscribingChannels(topic),
Event{
Data: data,
Topic: topic,
wg: nil,
})
eb.stats.incPublishedCountByTopic(topic)
}
// PublishAsyncOnce same as PublishAsync but makes sure that topic is only published once.
func (eb *EventBus) PublishAsyncOnce(topic string, data Rec) {
if eb.stats.GetPublishedCountByTopic(topic) > 0 {
return
}
eb.PublishAsync(topic, data)
}
// Publish data to a topic and wait for all subscribers to finish
// This function creates a waitGroup internally. All subscribers must call Done() function on Event.
func (eb *EventBus) Publish(topic string, data Rec) Rec {
wg := sync.WaitGroup{}
channels := eb.getSubscribingChannels(topic)
wg.Add(len(channels))
eb.doPublish(
channels,
Event{
Data: data,
Topic: topic,
wg: &wg,
})
wg.Wait()
eb.stats.incPublishedCountByTopic(topic)
return data
}
// PublishOnce same as Publish but makes sure only published once on topic.
func (eb *EventBus) PublishOnce(topic string, data Rec) Rec {
if eb.stats.GetPublishedCountByTopic(topic) > 0 {
return nil
}
return eb.Publish(topic, data)
}
// Subscribe to a topic passing a EventChannel.
func (eb *EventBus) Subscribe(topic string) EventChannel {
ch := make(EventChannel)
eb.SubscribeChannel(topic, ch)
eb.stats.incSubscriberCountByTopic(topic)
return ch
}
// SubscribeChannel subscribes to a given Channel.
func (eb *EventBus) SubscribeChannel(topic string, ch EventChannel) {
eb.mu.Lock()
defer eb.mu.Unlock()
if prev, found := eb.subscribers[topic]; found {
eb.subscribers[topic] = append(prev, ch)
} else {
eb.subscribers[topic] = append([]EventChannel{}, ch)
}
eb.stats.incSubscriberCountByTopic(topic)
}
// SubscribeCallback provides a simple wrapper that allows to directly register CallbackFunc instead of channels.
func (eb *EventBus) SubscribeCallback(topic string, callable CallbackFunc) {
ch := NewEventChannel()
eb.SubscribeChannel(topic, ch)
go func(callable CallbackFunc) {
evt := <-ch
callable(evt.Topic, evt.Data)
evt.Done()
}(callable)
eb.stats.incSubscriberCountByTopic(topic)
}
// HasSubscribers Check if a topic has subscribers.
func (eb *EventBus) HasSubscribers(topic string) bool {
return len(eb.getSubscribingChannels(topic)) > 0
}
// Stats returns the stats map.
func (eb *EventBus) Stats() *Stats {
return eb.stats
}
type TopicStats struct {
Name string
PublishedCount *SafeCounter
SubscriberCount *SafeCounter
}
type topicStatsMap map[string]*TopicStats
type Stats struct {
data topicStatsMap
}
func newStats() *Stats {
return &Stats{
data: map[string]*TopicStats{},
}
}
func (s *Stats) getOrCreateTopicStats(topicName string) *TopicStats {
_, ok := s.data[topicName]
if !ok {
s.data[topicName] = &TopicStats{
Name: topicName,
PublishedCount: NewSafeCounter(),
SubscriberCount: NewSafeCounter(),
}
}
return s.data[topicName]
}
func (s *Stats) incSubscriberCountByTopic(topicName string) {
s.getOrCreateTopicStats(topicName).SubscriberCount.Inc()
}
func (s *Stats) GetSubscriberCountByTopic(topicName string) int {
return s.getOrCreateTopicStats(topicName).SubscriberCount.Value()
}
func (s *Stats) incPublishedCountByTopic(topicName string) {
s.getOrCreateTopicStats(topicName).PublishedCount.Inc()
}
func (s *Stats) GetPublishedCountByTopic(topicName string) int {
return s.getOrCreateTopicStats(topicName).PublishedCount.Value()
}
func (s *Stats) GetTopicStats() []*TopicStats {
var tStatsSlice []*TopicStats
for _, tStats := range s.data {
tStatsSlice = append(tStatsSlice, tStats)
}
return tStatsSlice
}
func (s *Stats) GetTopicStatsByName(topicName string) *TopicStats {
return s.getOrCreateTopicStats(topicName)
}
// SafeCounter is a concurrency safe counter.
type SafeCounter struct {
v *uint64
}
// NewSafeCounter creates a new counter.
func NewSafeCounter() *SafeCounter {
return &SafeCounter{
v: new(uint64),
}
}
// Value returns the current value.
func (c *SafeCounter) Value() int {
return int(atomic.LoadUint64(c.v))
}
// IncBy increments the counter by given delta.
func (c *SafeCounter) IncBy(add uint) {
atomic.AddUint64(c.v, uint64(add))
}
// Inc increments the counter by 1.
func (c *SafeCounter) Inc() {
c.IncBy(1)
}
// DecBy decrements the counter by given delta.
func (c *SafeCounter) DecBy(dec uint) {
atomic.AddUint64(c.v, ^uint64(dec-1))
}
// Dec decrements the counter by 1.
func (c *SafeCounter) Dec() {
c.DecBy(1)
}

102
lcars_v2/experiments.go Normal file
View File

@ -0,0 +1,102 @@
package main
import (
"encoding/json"
"fmt"
"ld/server"
"strings"
)
func readCrew(server *server.Server) (string, error) {
content, err := embedded.ReadFile("embed/crew.json")
if err != nil {
return "", err
}
// Create a slice to hold the parsed names
var crewNames []string
// Parse the JSON
err = json.Unmarshal(content, &crewNames)
if err != nil {
fmt.Println("Error parsing JSON:", err)
return "", err
}
const insertStmt = "INSERT INTO crew_member ( rank, name) VALUES (?, ?) ;"
insstmt, err := server.StateDB.DB().Prepare(insertStmt)
if err != nil {
return "", err
}
defer insstmt.Close()
// Print the results
for _, text := range crewNames {
rank, name := splitRank(text)
if rank == "" {
rank = "ERROR"
}
// fmt.Printf("%d: rank: %s name: %s\n", i+1, rank, name)
_, err = insstmt.Exec(rank, name)
if err != nil {
return "", err
}
}
// fmt.Println(string(content))
return "", nil
}
// splitRank separates the rank (all tokens except the last two) from the crewman's name (last two tokens)
func splitRank(fullName string) (rank, name string) {
tokens := strings.Fields(fullName)
if len(tokens) < 2 {
return fullName, "" // fallback if malformed
}
nameTokens := tokens[len(tokens)-2:] // last 2 tokens as name
rankTokens := tokens[:len(tokens)-2] // everything else as rank
name = strings.Join(nameTokens, " ")
rank = strings.Join(rankTokens, " ")
return rank, name
}
type Message struct {
Timestamp string `json:"timestamp"`
Subsystem string `json:"subsystem"`
Severity string `json:"severity"`
Color string `json:"color"`
Message string `json:"message"`
}
func readMessages(server *server.Server) error {
content, err := embedded.ReadFile("embed/messages.json")
if err != nil {
return err
}
var messages []Message
if err := json.Unmarshal(content, &messages); err != nil {
return err
}
const insertStmt = "INSERT INTO ship_messages ( timestamp, subsystem, severity, color, message) VALUES (?,?,?,?,?) ;"
insstmt, err := server.StateDB.DB().Prepare(insertStmt)
if err != nil {
return err
}
defer insstmt.Close()
// For demonstration, print the parsed messages
for _, m := range messages {
// fmt.Printf("[%s] %s (%s) - %s\n", m.Timestamp, m.Subsystem, m.Severity, m.Message)
_, err = insstmt.Exec(m.Timestamp, m.Subsystem, m.Severity, m.Color, m.Message)
if err != nil {
fmt.Println(err)
return err
}
}
return nil
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
document.addEventListener("touchstart", function() {},false);
let mybutton = document.getElementById("topBtn");
window.onscroll = function() {scrollFunction()};
function scrollFunction() {
if (document.body.scrollTop > 200 || document.documentElement.scrollTop > 200) {
mybutton.style.display = "block";
} else {
mybutton.style.display = "none";
}
}
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
function playSoundAndRedirect(audioId, url) {
// var audio = document.getElementById(audioId);
// audio.play();
// audio.onended = function() {
// console.log("Audio has ended", url);
window.location.href = url;
// };
}
function goToAnchor(anchorId) {
window.location.hash = anchorId;
}
// Accordion drop-down
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var accordionContent = this.nextElementSibling;
if (accordionContent.style.maxHeight){
accordionContent.style.maxHeight = null;
} else {
accordionContent.style.maxHeight = accordionContent.scrollHeight + "px";
}
});
}
// LCARS keystroke sound (not to be used with hyperlinks)
const LCARSkeystroke = document.getElementById('LCARSkeystroke');
const allPlaySoundButtons = document.querySelectorAll('.playSoundButton');
allPlaySoundButtons.forEach(button => {
button.addEventListener('click', function() {
LCARSkeystroke.pause();
LCARSkeystroke.currentTime = 0; // Reset to the beginning of the sound
LCARSkeystroke.play();
});
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,231 @@
<!DOCTYPE html>
<html>
<head>
<title>Lower Decks PADD</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="date=no">
<link rel="stylesheet" type="text/css" href="../assets/lower-decks-padd.css">
</head>
<body>
<!-- <audio id="audio1" src="assets/beep1.mp3" preload="auto"></audio>
<audio id="audio2" src="assets/beep2.mp3" preload="auto"></audio>
<audio id="audio3" src="assets/beep3.mp3" preload="auto"></audio>
<audio id="audio4" src="assets/beep4.mp3" preload="auto"></audio> -->
<div class="wrap-all">
<div class="wrap">
<div class="left-frame-top">
<!--
*** LCARS PANEL BUTTON ***
Replace the hashtag '#' with a real URL (or not) in the following <button> tag. If you do not want a sound effect for this link, replace the <button> element with the following <div> + <a> elements:
<div class="panel-1">
<a href="#">LCARS</a>
</div>
-->
<a href="/" class="panel-1-button">LCARS</a>
<!-- <button onclick="playSoundAndRedirect('audio2', '/')" class="panel-1-button">LCARS</button> -->
<div class="panel-2">02<span class="hop">-262000</span></div>
</div>
<div class="right-frame-top">
<div class="banner">LCARS 57436.2</div>
<div class="data-cascade-button-group">
<div class="data-wrapper">
<div class="data-column">
<div class="dc-row-1 font-arctic-ice">47</div>
<div class="dc-row-2">31</div>
<div class="dc-row-3">28</div>
<div class="dc-row-4">94</div>
</div>
<div class="data-column">
<div class="dc-row-1">329</div>
<div class="dc-row-2 font-night-rain">128</div>
<div class="dc-row-3">605</div>
<div class="dc-row-4">704</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-night-rain">39725514862</div>
<div class="dc-row-2 font-arctic-ice">51320259663</div>
<div class="dc-row-3 font-alpha-blue">21857221984</div>
<div class="dc-row-4">40372566301</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-arctic-ice">56</div>
<div class="dc-row-2 font-night-rain">04</div>
<div class="dc-row-3 font-night-rain">40</div>
<div class="dc-row-4 font-night-rain">35</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-arctic-ice">614</div>
<div class="dc-row-2 font-arctic-ice">883</div>
<div class="dc-row-3 font-alpha-blue">109</div>
<div class="dc-row-4">297</div>
</div>
<div class="data-column">
<div class="dc-row-1 darkspace darkfont">000</div>
<div class="dc-row-2 darkspace font-alpha-blue">13</div>
<div class="dc-row-3 darkspace font-arctic-ice">05</div>
<div class="dc-row-4 darkspace font-night-rain">25</div>
</div>
<div class="data-column">
<div class="dc-row-1">48</div>
<div class="dc-row-2 font-night-rain">07</div>
<div class="dc-row-3">38</div>
<div class="dc-row-4">62</div>
</div>
<div class="data-column">
<div class="dc-row-1">416</div>
<div class="dc-row-2 font-night-rain">001</div>
<div class="dc-row-3">888</div>
<div class="dc-row-4">442</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-night-rain">86225514862</div>
<div class="dc-row-2 font-arctic-ice">31042009183</div>
<div class="dc-row-3 font-alpha-blue">74882306985</div>
<div class="dc-row-4">54048523421</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-alpha-blue">10</div>
<div class="dc-row-2">80</div>
<div class="dc-row-3 font-night-rain">31</div>
<div class="dc-row-4 font-alpha-blue">85</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-alpha-blue">87</div>
<div class="dc-row-2">71</div>
<div class="dc-row-3 font-night-rain">40</div>
<div class="dc-row-4 font-night-rain">26</div>
</div>
<div class="data-column">
<div class="dc-row-1">98</div>
<div class="dc-row-2">63</div>
<div class="dc-row-3 font-night-rain">52</div>
<div class="dc-row-4 font-alpha-blue">71</div>
</div>
<div class="data-column">
<div class="dc-row-1">118</div>
<div class="dc-row-2">270</div>
<div class="dc-row-3">395</div>
<div class="dc-row-4">260</div>
</div>
<div class="data-column">
<div class="dc-row-1">8675309</div>
<div class="dc-row-2 font-night-rain">7952705</div>
<div class="dc-row-3">9282721</div>
<div class="dc-row-4">4981518</div>
</div>
<div class="data-column">
<div class="dc-row-1 darkspace darkfont">000</div>
<div class="dc-row-2 darkspace font-alpha-blue">99</div>
<div class="dc-row-3 darkspace font-arctic-ice">10</div>
<div class="dc-row-4 darkspace font-night-rain">84</div>
</div>
<div class="data-column">
<div class="dc-row-1">65821407321</div>
<div class="dc-row-2 font-alpha-blue">54018820533</div>
<div class="dc-row-3 font-night-rain">27174523016</div>
<div class="dc-row-4">38954062564</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-arctic-ice">999</div>
<div class="dc-row-2 font-arctic-ice">202</div>
<div class="dc-row-3 font-alpha-blue">574</div>
<div class="dc-row-4">293</div>
</div>
<div class="data-column">
<div class="dc-row-1">3872</div>
<div class="dc-row-2 font-night-rain">1105</div>
<div class="dc-row-3">1106</div>
<div class="dc-row-4 font-alpha-blue">7411</div>
</div>
</div>
<nav>
<!--
*** MAIN NAVIGATION BUTTONS ***
Replace the hashtag '#' with a real URL (or not).
If you don't want sound effects, replace the <button> element with a basic <a> tag shown here in this comment:
<a href="#">01</a>
<a href="#">02</a>
<a href="#">03</a>
<a href="#">04</a>
-->
<button onclick="playSoundAndRedirect('audio2', 'crew.html')">01</button>
<button onclick="playSoundAndRedirect('audio2', '#')">02</button>
<button onclick="playSoundAndRedirect('audio2', '#')">03</button>
<button onclick="playSoundAndRedirect('audio2', '#')">04</button>
</nav>
</div>
<div class="bar-panel first-bar-panel">
<div class="bar-1"> </div>
<div class="bar-2"> </div>
<div class="bar-3"> </div>
<div class="bar-4"> </div>
<div class="bar-5"> </div>
</div>
</div>
</div>
<div class="divider">
<div class="block-left"> </div>
<div class="block-right">
<div class="block-row">
<div class="bar-11"> </div>
<div class="bar-12"> </div>
<div class="bar-13"> </div>
<div class="bar-14">
<div class="blockhead"> </div>
</div>
</div>
</div>
</div>
<div class="wrap">
<div class="left-frame">
<!--
** SCROLL TO TOP OF PAGE BUTTON **
This button is initially hidden, and is styled like a panel in the sidebar. It appears at the bottom of the page after vertical scrolling. If you don't want the sound effect, replace with this:
<button onclick="topFunction()" id="topBtn"><span class="hop">screen</span> top</button>
-->
<button onclick="topFunction(); playSoundAndRedirect('audio4', '#')" id="topBtn"><span
class="hop">screen</span> top</button>
<div>
<div class="panel-3">03<span class="hop">-111968</span></div>
<div class="panel-4">04<span class="hop">-041969</span></div>
<div class="panel-5">05<span class="hop">-1701D</span></div>
<div class="panel-6">06<span class="hop">-071984</span></div>
</div>
<div>
<div class="panel-7">07<span class="hop">-081940</span></div>
</div>
</div>
<div class="right-frame">
<div class="bar-panel">
<div class="bar-6"> </div>
<div class="bar-7"> </div>
<div class="bar-8"> </div>
<div class="bar-9"> </div>
<div class="bar-10"> </div>
</div>
<main>
<h1>Crew Sheet</h1>
</main>
<footer>
<!-- Your copyright information is only a suggestion and you can choose to delete it. -->
Content Copyright &#169; 2025 ld.hedeler.com <br>
<!-- The following attribution must not be removed: -->
LCARS Inspired Website Template by <a href="https://www.thelcars.com">www.TheLCARS.com</a>.
</footer>
</div>
</div>
</div>
<script type="text/javascript" src="assets/lcars.js"></script>
<div class="headtrim"> </div>
<div class="baseboard"> </div>
</body>
</html>

View File

@ -0,0 +1,232 @@
<!DOCTYPE html>
<html>
<head>
<title>Lower Decks PADD</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="date=no">
<link rel="stylesheet" type="text/css" href="../assets/lower-decks-padd.css">
<script type="speculationrules">
</script>
</head>
<body>
<!-- <audio id="audio1" src="assets/beep1.mp3" preload="auto"></audio>
<audio id="audio2" src="assets/beep2.mp3" preload="auto"></audio>
<audio id="audio3" src="assets/beep3.mp3" preload="auto"></audio>
<audio id="audio4" src="assets/beep4.mp3" preload="auto"></audio> -->
<div class="wrap-all">
<div class="wrap">
<div class="left-frame-top">
<!--
*** LCARS PANEL BUTTON ***
Replace the hashtag '#' with a real URL (or not) in the following <button> tag. If you do not want a sound effect for this link, replace the <button> element with the following <div> + <a> elements:
<div class="panel-1">
<a href="#">LCARS</a>
</div>
-->
<button class="panel-1-button">LCARS</button>
<div class="panel-2">02<span class="hop">-262000</span></div>
</div>
<div class="right-frame-top">
<div class="banner">LCARS 57436.2</div>
<div class="data-cascade-button-group">
<div class="data-wrapper">
<div class="data-column">
<div class="dc-row-1 font-arctic-ice">47</div>
<div class="dc-row-2">31</div>
<div class="dc-row-3">28</div>
<div class="dc-row-4">94</div>
</div>
<div class="data-column">
<div class="dc-row-1">329</div>
<div class="dc-row-2 font-night-rain">128</div>
<div class="dc-row-3">605</div>
<div class="dc-row-4">704</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-night-rain">39725514862</div>
<div class="dc-row-2 font-arctic-ice">51320259663</div>
<div class="dc-row-3 font-alpha-blue">21857221984</div>
<div class="dc-row-4">40372566301</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-arctic-ice">56</div>
<div class="dc-row-2 font-night-rain">04</div>
<div class="dc-row-3 font-night-rain">40</div>
<div class="dc-row-4 font-night-rain">35</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-arctic-ice">614</div>
<div class="dc-row-2 font-arctic-ice">883</div>
<div class="dc-row-3 font-alpha-blue">109</div>
<div class="dc-row-4">297</div>
</div>
<div class="data-column">
<div class="dc-row-1 darkspace darkfont">000</div>
<div class="dc-row-2 darkspace font-alpha-blue">13</div>
<div class="dc-row-3 darkspace font-arctic-ice">05</div>
<div class="dc-row-4 darkspace font-night-rain">25</div>
</div>
<div class="data-column">
<div class="dc-row-1">48</div>
<div class="dc-row-2 font-night-rain">07</div>
<div class="dc-row-3">38</div>
<div class="dc-row-4">62</div>
</div>
<div class="data-column">
<div class="dc-row-1">416</div>
<div class="dc-row-2 font-night-rain">001</div>
<div class="dc-row-3">888</div>
<div class="dc-row-4">442</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-night-rain">86225514862</div>
<div class="dc-row-2 font-arctic-ice">31042009183</div>
<div class="dc-row-3 font-alpha-blue">74882306985</div>
<div class="dc-row-4">54048523421</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-alpha-blue">10</div>
<div class="dc-row-2">80</div>
<div class="dc-row-3 font-night-rain">31</div>
<div class="dc-row-4 font-alpha-blue">85</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-alpha-blue">87</div>
<div class="dc-row-2">71</div>
<div class="dc-row-3 font-night-rain">40</div>
<div class="dc-row-4 font-night-rain">26</div>
</div>
<div class="data-column">
<div class="dc-row-1">98</div>
<div class="dc-row-2">63</div>
<div class="dc-row-3 font-night-rain">52</div>
<div class="dc-row-4 font-alpha-blue">71</div>
</div>
<div class="data-column">
<div class="dc-row-1">118</div>
<div class="dc-row-2">270</div>
<div class="dc-row-3">395</div>
<div class="dc-row-4">260</div>
</div>
<div class="data-column">
<div class="dc-row-1">8675309</div>
<div class="dc-row-2 font-night-rain">7952705</div>
<div class="dc-row-3">9282721</div>
<div class="dc-row-4">4981518</div>
</div>
<div class="data-column">
<div class="dc-row-1 darkspace darkfont">000</div>
<div class="dc-row-2 darkspace font-alpha-blue">99</div>
<div class="dc-row-3 darkspace font-arctic-ice">10</div>
<div class="dc-row-4 darkspace font-night-rain">84</div>
</div>
<div class="data-column">
<div class="dc-row-1">65821407321</div>
<div class="dc-row-2 font-alpha-blue">54018820533</div>
<div class="dc-row-3 font-night-rain">27174523016</div>
<div class="dc-row-4">38954062564</div>
</div>
<div class="data-column">
<div class="dc-row-1 font-arctic-ice">999</div>
<div class="dc-row-2 font-arctic-ice">202</div>
<div class="dc-row-3 font-alpha-blue">574</div>
<div class="dc-row-4">293</div>
</div>
<div class="data-column">
<div class="dc-row-1">3872</div>
<div class="dc-row-2 font-night-rain">1105</div>
<div class="dc-row-3">1106</div>
<div class="dc-row-4 font-alpha-blue">7411</div>
</div>
</div>
<nav>
<!--
*** MAIN NAVIGATION BUTTONS ***
Replace the hashtag '#' with a real URL (or not).
If you don't want sound effects, replace the <button> element with a basic <a> tag shown here in this comment:
<a href="#">01</a>
<a href="#">02</a>
<a href="#">03</a>
<a href="#">04</a>
-->
<a href="crew">01</a>
<button onclick="playSoundAndRedirect('audio2', '#')">02</button>
<button onclick="playSoundAndRedirect('audio2', '#')">03</button>
<button onclick="playSoundAndRedirect('audio2', '#')">04</button>
</nav>
</div>
<div class="bar-panel first-bar-panel">
<div class="bar-1"> </div>
<div class="bar-2"> </div>
<div class="bar-3"> </div>
<div class="bar-4"> </div>
<div class="bar-5"> </div>
</div>
</div>
</div>
<div class="divider">
<div class="block-left"> </div>
<div class="block-right">
<div class="block-row">
<div class="bar-11"> </div>
<div class="bar-12"> </div>
<div class="bar-13"> </div>
<div class="bar-14">
<div class="blockhead"> </div>
</div>
</div>
</div>
</div>
<div class="wrap">
<div class="left-frame">
<!--
** SCROLL TO TOP OF PAGE BUTTON **
This button is initially hidden, and is styled like a panel in the sidebar. It appears at the bottom of the page after vertical scrolling. If you don't want the sound effect, replace with this:
<button onclick="topFunction()" id="topBtn"><span class="hop">screen</span> top</button>
-->
<button onclick="topFunction(); playSoundAndRedirect('audio4', '#')" id="topBtn"><span
class="hop">screen</span> top</button>
<div>
<div class="panel-3">03<span class="hop">-111968</span></div>
<div class="panel-4">04<span class="hop">-041969</span></div>
<div class="panel-5">05<span class="hop">-1701D</span></div>
<div class="panel-6">06<span class="hop">-071984</span></div>
</div>
<div>
<div class="panel-7">07<span class="hop">-081940</span></div>
</div>
</div>
<div class="right-frame">
<div class="bar-panel">
<div class="bar-6"> </div>
<div class="bar-7"> </div>
<div class="bar-8"> </div>
<div class="bar-9"> </div>
<div class="bar-10"> </div>
</div>
<main>
<h1>Lower Decks PADD</h1>
</main>
<footer>
<!-- Your copyright information is only a suggestion and you can choose to delete it. -->
Content Copyright &#169; 2025 ld.hedeler.com <br>
<!-- The following attribution must not be removed: -->
LCARS Inspired Website Template by <a href="https://www.thelcars.com">www.TheLCARS.com</a>.
</footer>
</div>
</div>
</div>
<script type="text/javascript" src="assets/lcars.js"></script>
<div class="headtrim"> </div>
<div class="baseboard"> </div>
</body>
</html>

View File

@ -0,0 +1,39 @@
package main
import (
"log"
"net/http"
"path/filepath"
"strings"
)
func main() {
// Folder to serve
dir := "."
// File server handler
fs := http.FileServer(http.Dir(dir))
// Wrap the file server to add caching headers
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ext := strings.ToLower(filepath.Ext(r.URL.Path))
// Set long caching for static assets
switch ext {
case ".css", ".js", ".woff", ".woff2", ".ttf", ".eot", ".svg", ".png", ".jpg", ".jpeg", ".gif":
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
default:
// Optional: short caching for HTML so you always get latest page
w.Header().Set("Cache-Control", "no-cache")
}
fs.ServeHTTP(w, r)
})
addr := ":8080"
log.Printf("Serving %s on http://localhost%s\n", dir, addr)
err := http.ListenAndServe(addr, handler)
if err != nil {
log.Fatal(err)
}
}

18
lcars_v2/go.mod Normal file
View File

@ -0,0 +1,18 @@
module ld
go 1.25.0
require modernc.org/sqlite v1.39.0
require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/sys v0.34.0 // indirect
modernc.org/libc v1.66.3 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)

49
lcars_v2/go.sum Normal file
View File

@ -0,0 +1,49 @@
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM=
modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ=
modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.39.0 h1:6bwu9Ooim0yVYA7IZn9demiQk/Ejp0BtTjBWFLymSeY=
modernc.org/sqlite v1.39.0/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

View File

@ -0,0 +1,94 @@
package interval
import (
"errors"
"math/rand"
"sync"
"time"
)
type MutexMap[K comparable, V any] struct {
mutex sync.Mutex
m map[K]V
}
func (m *MutexMap[K, V]) Get(key K) (V, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
v, ok := m.m[key]
if !ok {
return v, errors.New("unknown key")
}
return v, nil
}
func (m *MutexMap[K, V]) Set(key K, value V) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.m[key] = value
}
func (m *MutexMap[K, V]) Delete(key K) {
m.mutex.Lock()
defer m.mutex.Unlock()
delete(m.m, key)
}
var stopChannels = MutexMap[int, chan bool]{
m: make(map[int]chan bool),
}
// SetInterval schedules a repeating task to be executed at a specified interval.
func SetInterval(f func(), milliseconds int) (id int) {
for {
id = rand.Int()
if _, err := stopChannels.Get(id); err == nil {
continue // ID collision, keep looking for another unique random value
}
break
}
stop := make(chan bool)
stopChannels.Set(id, stop)
ticker := time.NewTicker(time.Duration(milliseconds) * time.Millisecond)
go func() {
for {
select {
case <-stop:
ticker.Stop()
return
case <-ticker.C:
f()
}
}
}()
return
}
// ClearInterval stops a scheduled interval identified by the specified interval ID.
func ClearInterval(id int) error {
stop, err := stopChannels.Get(id)
if err != nil {
return err
}
stop <- true
stopChannels.Delete(id)
return nil
}
// SetTimeout schedules a one-time task to be executed after a specified interval.
func SetTimeout(f func(), milliseconds int) {
timer := time.NewTimer(time.Duration(milliseconds) * time.Millisecond)
go func() {
<-timer.C
timer.Stop()
f()
}()
}

217
lcars_v2/main.go Normal file
View File

@ -0,0 +1,217 @@
package main
import (
"embed"
"fmt"
"ld/eventbus"
"ld/interval"
"ld/server"
"ld/sqlite"
"log"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
)
const (
exitCodeErr = 1
exitCodeInterrupt = 2
)
var AppRoot = "./" // path for supporting files that sit in app root folder in production
//go:embed frontend/*
var frontend embed.FS
//go:embed embed
var embedded embed.FS
// main specific variables
var ExecutableName string
func main() {
fmt.Println("Here")
err := run()
if err != nil {
os.Exit(exitCodeErr)
}
ExecutableName, err = getExecutableName()
if err != nil {
os.Exit(exitCodeErr)
}
// readCrew()
// readMessages()
ebus := eventbus.NewEventBus()
interval.SetInterval(func() {
fmt.Println("------------------")
}, 10)
runEventbus(ebus)
run()
}
func runEventbus(ebus *eventbus.EventBus) {
// Create a new instance
eventChannel := eventbus.NewEventChannel()
// Subscribe to "foo:baz" - or use a wildcard like "foo:*"
ebus.SubscribeChannel("foo:baz", eventChannel)
ebus.SubscribeChannel("pups-klo", eventChannel)
ebus.SubscribeChannel("ömme*", eventChannel)
eventChannelTopic := ebus.Subscribe("ömmels")
// Subscribe with existing channel use
// eventbus.SubscribeChannel("foo:*", eventChannel)
// Wait for the incoming event on the channel
go func() {
for evt := range eventChannel {
fmt.Println("FIRST", evt.Topic, evt.Data)
evt.Done()
}
}()
go func() {
for evt := range eventChannel {
fmt.Println("SECOND", evt.Topic, evt.Data)
evt.Done()
}
}()
go func() {
for evt := range eventChannelTopic {
fmt.Println("ÖMMELS", evt.Topic, evt.Data)
evt.Done()
}
}()
for i := 0; i < 1000; i++ {
ebus.Publish("foo:baz", eventbus.Rec{"value": i})
ebus.Publish("pups-klo", eventbus.Rec{"value": i})
ebus.Publish("pups-klo", eventbus.Rec{"value": i})
ebus.Publish("ömmels", eventbus.Rec{"value": i})
ebus.Publish("ömmels", eventbus.Rec{"value": i * 10})
ebus.Publish("ömmels", eventbus.Rec{"value": i * 100})
}
}
func run() error {
// setting up logging to file
logFileName := AppRoot + ExecutableName + ".log"
logFile, err := os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Printf("error opening file: %v", err)
os.Exit(exitCodeErr)
}
defer logFile.Close()
log.SetOutput(logFile)
stateDB, err := createStateDB(true)
if err != nil {
log.Fatalf("Failed to create internal StateDB: %v", err)
}
// setting up the server
server, err := server.New(
logFileName,
stateDB,
embedded,
)
if err != nil {
log.Fatalf("server-app not created - error: %v", err)
}
// tasks.SetupTasks(server)
err = server.Start()
if err != nil {
fmt.Printf("server not started - error: %v \n", err)
log.Fatalf("server not started - error: %v", err)
}
readCrew(server)
readMessages(server)
return nil
// listen for os shutdown events, report them into log file and exit application
chanOS := make(chan os.Signal, 2)
signal.Notify(chanOS, os.Interrupt, syscall.SIGTERM)
go func() {
<-chanOS
log.Println("shutting down request signal received")
server.Close()
os.Exit(exitCodeInterrupt)
}()
// setting up the routes, hooking up API endpoints with backend functions
// routes.SetupRoutes(server)
return nil
}
// some internal functions
func getExecutableName() (string, error) {
name, err := os.Executable()
if err != nil {
return "", err
}
name = filepath.Base(name)
return strings.TrimSuffix(name, filepath.Ext(name)), nil
}
func createStateDB(StateDBDelete bool) (*sqlite.Database, error) {
// fileName := fmt.Sprintf("state-%s.db", ulid.Make())
fileName := "state.db"
if StateDBDelete {
_, err := os.Stat(fileName)
if err == nil {
err := os.Remove(fileName)
if err != nil {
log.Fatal("error deleting statedb-file:", err)
}
}
}
db, err := sqlite.New(fileName)
if err != nil {
return nil, err
}
err = db.Open()
if err != nil {
return nil, err
}
query, err := embedded.ReadFile("embed/create_state_db.sql")
if err != nil {
return nil, err
}
_, err = db.DB().Exec(string(query))
if err != nil {
return nil, err
}
return db, nil
}

2
lcars_v2/notes.txt Normal file
View File

@ -0,0 +1,2 @@
<a target="_blank" href="https://icons8.com/icon/21039/star-trek">Nächste-Generation Abzeichen</a> Icon von <a target="_blank" href="https://icons8.com">Icons8</a>
<a href="https://www.flaticon.com/free-icons/geek" title="geek icons">Geek icons created by Pixel perfect - Flaticon</a>

105
lcars_v2/server/server.go Normal file
View File

@ -0,0 +1,105 @@
// Copyright 2024 codeM GmbH
// Author: Thomas Hedeler
package server
import (
"embed"
"ld/eventbus"
"ld/interval"
"ld/sqlite"
"log"
)
type Server struct {
SQLiteVersion string
ServerInfo map[string]any
StateDB *sqlite.Database
Embedded embed.FS
LogFileName string
TokenDuration int // TODO einbauen
Secret []byte
Header string
intervalID int
Ebus *eventbus.EventBus
Tasks map[string]TaskFunc
}
func New(
logfilename string,
StateDB *sqlite.Database,
embedded embed.FS,
) (*Server, error) {
// creating the server
return &Server{
LogFileName: logfilename,
StateDB: StateDB,
Embedded: embedded,
Tasks: make(map[string]func(s *Server) error),
}, nil
}
func (s *Server) Start() error {
// query, err := s.Embedded.ReadFile("embed/win/server_info.sql")
// if err != nil {
// return err
// }
// res, err := s.StundenDB.ReadRecords(string(query))
// if err != nil {
// return err
// }
// s.ServerInfo = res[0]
// err = inits.LoadLogins(s.StundenDB, s.StateDB)
// if err != nil {
// return err
// }
// err = inits.LoadTasks(s.StundenDB, s.StateDB)
// if err != nil {
// return err
// }
// // start the task engine
// if s.Production {
// s.intervalID = interval.SetInterval(s.interval, 60000) // check for executable tasks every 60 seconds
// } else {
// s.intervalID = interval.SetInterval(s.interval, 30000) // check for executable tasks every 30 seconds
// }
return nil
}
func (s *Server) Close() error {
// stop the task engine
err := interval.ClearInterval(s.intervalID)
if err != nil {
log.Print(err)
}
err = s.StateDB.Close()
if err != nil {
log.Print(err)
}
return err
}
// func to dispatch routes to all parts of the application:
// they receive references to the server and the current fiber context via closures
// this way all functions have access to server properties and can handle the
// incoming requests themselves.
// type HandlerFunc = func(s *Server, c *fiber.Ctx) error
// func (s *Server) Handler(handler HandlerFunc) func(c *fiber.Ctx) error {
// return func(c *fiber.Ctx) error {
// return handler(s, c)
// }
// }
// signature for internal tasks
type TaskFunc = func(s *Server) error

View File

@ -0,0 +1,118 @@
package server
import (
"log"
"time"
)
// this function schedules the tasks and will be called periodically, see server.Start()
func (s *Server) interval() {
// read scheduled task list from stateDB
// check for next executable task:
// - if there is one or more tasks ready for execution then select one of them.
// - if there is a selected task, update its next execution field and execute the task
defer func() {
if r := recover(); r != nil {
// fmt.Println("Recovered from panic in worker:", r)
log.Printf("recovered from panic in taskengine: %v ", r)
}
}()
tasks, err := s.StateDB.ReadRecords("SELECT * FROM tasks ORDER by next_execution limit 1;")
if err != nil {
log.Printf("error in taskengine: %s ", err)
return
}
if len(tasks) < 1 {
log.Printf("error in taskengine: %s ", "found no task for execution")
return
}
task := tasks[0] // pick the one task with the smallest next execution time, see previous sql statement
task_name, haveTask := task["task_name"].(string)
if !haveTask {
log.Printf("error in taskengine: task %s is of wrong type", task["task_name"])
return
}
nextExecution := task["next_execution"].(int64)
startTime := task["start_time"].(int64)
execInterval := task["interval"].(int64)
nowSeconds := time.Now().Unix()
if nowSeconds < nextExecution { // task execution is not yet due
return
}
// calculate next execution time
for nextExecution = startTime; nowSeconds > nextExecution; nextExecution += execInterval {
// add as many intervals to the starttime until the next execution lies in the future
}
task["start_time"] = task["next_execution"]
task["next_execution"] = nextExecution
/*
no_executions INTEGER, -- how often executed
duration INTEGER, -- duration of the last exec in ms
no_errors INTEGER, -- error count
last_error_text TEXT,
*/
if count, ok := task["no_executions"].(int64); ok {
task["no_executions"] = count + 1
}
// update next_execution in state database
_, err = s.StateDB.UpsertRecord("tasks", "task_id", task)
if err != nil {
log.Printf("error in taskengine: cannot update task record - before execution %s ", err)
return
}
task_func, haveTask := s.Tasks[task_name] // select the function with the matching name
if !haveTask {
log.Printf("error in taskengine: task %s is not defined", task_name)
}
if haveTask {
start := time.Now()
// if !s.Production {
// fmt.Println("Taskengine: executing task:", task_name, start)
// }
err = task_func(s) // finally execute the task; attention: a task that panics will kill the server!
task["duration"] = int(time.Since(start).Milliseconds())
if err != nil {
log.Printf("taskengine: execution task: %s failed with error: %s ", task_name, err)
task["last_error_text"] = err.Error()
if count, ok := task["no_errors"].(int64); ok {
task["no_errors"] = count + 1
}
// if !s.Production {
// fmt.Println("Taskengine: failed task:", task_name, err)
// }
} else {
// if !s.Production {
// fmt.Println("Taskengine: successfully completed task:", task_name, time.Now())
// }
}
_, err = s.StateDB.UpsertRecord("tasks", "task_id", task)
if err != nil {
log.Printf("error in taskengine: cannot update task record - after execution %s ", err)
return
}
}
}

428
lcars_v2/sqlite/database.go Normal file
View File

@ -0,0 +1,428 @@
package sqlite // name the package as you see fit
import (
"database/sql"
"errors"
"fmt"
"os"
"strconv"
"strings"
_ "modernc.org/sqlite"
)
// This is the data type to exchange data with the database
type Record = map[string]any
type Database struct {
databaseName string
database *sql.DB
}
type Transaction struct {
tx *sql.Tx
err error
}
type Action func(tx *sql.Tx) error
func New(DBName string) (*Database, error) {
return &Database{databaseName: DBName}, nil
}
func (d *Database) Close() error {
return d.database.Close()
}
// provides access to the internal database object
func (d *Database) DB() *sql.DB {
return d.database
}
func (d *Database) Name() string {
return d.databaseName
}
func (d *Database) Open() (err error) {
d.database, err = openSqliteDB(d.databaseName)
return err
}
func (d *Database) OpenInMemory() (err error) {
d.database, err = sql.Open("sqlite", ":memory:")
return err
}
func openSqliteDB(databasefilename string) (*sql.DB, error) {
_, err := os.Stat(databasefilename)
if errors.Is(err, os.ErrNotExist) {
return createDB(databasefilename)
}
if err != nil {
return nil, err
}
return sql.Open("sqlite", databasefilename)
}
func createDB(dbfileName string) (*sql.DB, error) {
query := `
PRAGMA page_size = 4096;
PRAGMA synchronous = off;
PRAGMA foreign_keys = off;
PRAGMA journal_mode = WAL;
PRAGMA user_version = 1;
`
db, err := sql.Open("sqlite", dbfileName)
if err != nil {
return nil, err
}
_, err = db.Exec(query)
if err != nil {
return nil, err
}
return db, nil
}
func (d *Database) TableList() (result []Record, err error) {
return d.ReadRecords("select name from sqlite_master where type='table';")
}
func (d *Database) ReadTable(tablename string) (result []Record, err error) {
return d.ReadRecords(fmt.Sprintf("select * from '%s';", tablename))
}
func (d *Database) ReadRecords(query string, args ...any) (result []Record, err error) {
rows, err := d.DB().Query(query, args...)
if err != nil {
return result, err
}
defer rows.Close()
return Rows2records(rows)
}
func (d *Database) GetRecord(tablename string, idfield string, key any) (result Record, err error) {
query := fmt.Sprintf("select * from %s where %s = ?;", tablename, idfield)
res, err := d.DB().Query(query, key)
if err != nil {
return result, err
}
defer res.Close()
return Rows2record(res)
}
func (d *Database) UpsertRecord(tablename string, idfield string, record Record) (result Record, err error) {
return upsert(d.DB(), tablename, idfield, record)
}
func (d *Database) DeleteRecord(tablename string, idfield string, id any) (err error) {
return deleteRecord(d.DB(), tablename, idfield, id)
}
// *sql.DB and *sql.Tx both have a method named 'Query',
// this way they can both be passed into upsert and deleteRecord function
type iquery interface {
Query(query string, args ...any) (*sql.Rows, error)
}
func upsert(t iquery, tablename string, idfield string, record Record) (result Record, err error) {
fields := []string{}
data := []any{}
for k, v := range record {
fields = append(fields, k)
data = append(data, v)
}
query, err := buildUpsertCommand(tablename, idfield, fields)
if err != nil {
return result, err
}
res, err := t.Query(query, data...) // res contains the full record - see SQLite: RETURNING *
if err != nil {
return result, err
}
defer res.Close()
return Rows2record(res)
}
func deleteRecord(t iquery, tablename string, idfield string, id any) (err error) {
query := fmt.Sprintf("DELETE FROM \"%s\" WHERE \"%s\" = ?;", tablename, idfield)
_, err = t.Query(query, id)
return err
}
func buildUpsertCommand(tablename string, idfield string, fields []string) (string, error) {
var sb strings.Builder
sb.Grow(256 + len(fields)*20) // rough preallocation
// INSERT INTO
sb.WriteString(`INSERT INTO "`)
sb.WriteString(tablename)
sb.WriteString(`"(`)
for i, f := range fields {
sb.WriteString(` "`)
sb.WriteString(f)
sb.WriteByte('"')
if i < len(fields)-1 {
sb.WriteByte(',')
}
}
sb.WriteString(")\n\tVALUES(")
// VALUES
for i := 0; i < len(fields); i++ {
sb.WriteString(" ?")
sb.Write(strconv.AppendInt(nil, int64(i+1), 10))
if i < len(fields)-1 {
sb.WriteByte(',')
}
}
sb.WriteString(")\n\tON CONFLICT(\"")
sb.WriteString(tablename)
sb.WriteString(`"."`)
sb.WriteString(idfield)
sb.WriteString("\")\n\tDO UPDATE SET ")
// UPDATE SET
for i, f := range fields {
sb.WriteByte('"')
sb.WriteString(f)
sb.WriteString(`"= ?`)
sb.Write(strconv.AppendInt(nil, int64(i+1), 10))
if i < len(fields)-1 {
sb.WriteByte(',')
}
}
sb.WriteString("\n\tRETURNING *;")
return sb.String(), nil
}
// func buildUpsertCommand(tablename string, idfield string, fields []string) (result string, err error) {
// pname := map[string]string{} // assign correct index for parameter name
// // parameter position, starts at 1 in sql! So it needs to be calculated by function pname inside template
// for i, k := range fields {
// pname[k] = strconv.Itoa(i + 1)
// }
// funcMap := template.FuncMap{
// "pname": func(fieldname string) string {
// return pname[fieldname]
// },
// }
// tableDef := struct {
// Tablename string
// KeyField string
// LastField int
// FieldNames []string
// }{
// Tablename: tablename,
// KeyField: idfield,
// LastField: len(fields) - 1,
// FieldNames: fields,
// }
// var templString = `{{$last := .LastField}}INSERT INTO "{{ .Tablename }}"({{ range $i,$el := .FieldNames }} "{{$el}}"{{if ne $i $last}},{{end}}{{end}})
// VALUES({{ range $i,$el := .FieldNames }} ?{{pname $el}}{{if ne $i $last}},{{end}}{{end}})
// ON CONFLICT("{{ .Tablename }}"."{{.KeyField}}")
// DO UPDATE SET {{ range $i,$el := .FieldNames }}"{{$el}}"= ?{{pname $el}}{{if ne $i $last}},{{end}}{{end}}
// RETURNING *;`
// dbTempl, err := template.New("upsertDB").Funcs(funcMap).Parse(templString)
// if err != nil {
// return result, err
// }
// var templBytes bytes.Buffer
// err = dbTempl.Execute(&templBytes, tableDef)
// if err != nil {
// return result, err
// }
// return templBytes.String(), nil
// }
func Rows2record(rows *sql.Rows) (Record, error) {
columns, err := rows.Columns()
if err != nil {
return nil, err
}
values := make([]any, len(columns))
valuePtrs := make([]any, len(columns))
for i := range values {
valuePtrs[i] = &values[i]
}
result := Record{}
for rows.Next() {
if err := rows.Scan(valuePtrs...); err != nil {
return nil, err
}
for i, col := range columns {
result[col] = values[i]
}
}
if len(result) == 0 {
return nil, errors.New("no rows found")
}
return result, nil
}
func Rows2records(rows *sql.Rows) ([]Record, error) {
columns, err := rows.Columns()
if err != nil {
return nil, err
}
recLength := len(columns)
results := []Record{}
for rows.Next() {
values := make([]any, recLength)
valuePtrs := make([]any, recLength)
for i := range values {
valuePtrs[i] = &values[i]
}
record := Record{}
if err := rows.Scan(valuePtrs...); err != nil {
return nil, err
}
for i, col := range columns {
record[col] = values[i]
}
results = append(results, record)
}
if len(results) == 0 {
return nil, errors.New("no rows found")
}
return results, nil
}
func (d *Database) Version() (string, error) {
result := ""
sqliteversion, err := d.ReadRecords("SELECT sqlite_version();")
if len(sqliteversion) == 1 {
result = sqliteversion[0]["sqlite_version()"].(string)
}
return result, err
}
func (d *Database) UserVersion() (int64, error) {
var result int64
userversion, err := d.ReadRecords("PRAGMA user_version;")
if len(userversion) == 1 {
result = userversion[0]["user_version"].(int64)
}
return result, err
}
func (d *Database) Begin() *Transaction {
tx, err := d.database.Begin()
return &Transaction{tx, err}
}
func (t *Transaction) Next(action Action) *Transaction {
if t.err != nil {
return t
}
t.err = action(t.tx)
return t
}
func (t *Transaction) End() error {
if t.err != nil {
err := t.tx.Rollback()
if err != nil {
t.err = errors.Join(t.err, err)
}
return t.err
}
t.err = t.tx.Commit()
return t.err
}
func (t *Transaction) GetRecord(tablename string, idfield string, key any, output Record) *Transaction {
if t.err != nil {
return t
}
query := fmt.Sprintf("select * from %s where %s = ?;", tablename, idfield)
res, err := t.tx.Query(query, key)
if err != nil {
t.err = err
return t
}
defer res.Close()
result, err := Rows2record(res)
if err != nil {
t.err = err
return t
}
for k := range output {
delete(output, k)
}
for k, v := range result {
output[k] = v
}
return t
}
func (t *Transaction) UpsertRecord(tablename string, idfield string, record Record, output Record) *Transaction {
if t.err != nil {
return t
}
result, err := upsert(t.tx, tablename, idfield, record)
if err != nil {
t.err = err
return t
}
for k := range output {
delete(output, k)
}
for k, v := range result {
output[k] = v
}
return t
}
func (t *Transaction) DeleteRecord(tablename string, idfield string, id any) *Transaction {
if t.err != nil {
return t
}
err := deleteRecord(t.tx, tablename, idfield, id)
if err != nil {
t.err = err
}
return t
}
// returns a value of the provided type, if the field exist and if it can be cast into the provided type parameter
func Value[T any](rec Record, field string) (value T, ok bool) {
var v any
if v, ok = rec[field]; ok {
value, ok = v.(T)
}
return
}
// don't report an error if there are simply just 'no rows found'
func NoRowsOk(recs []Record, err error) ([]Record, error) {
if err != nil && err.Error() != "no rows found" {
return recs, err
}
return recs, nil
}

196
sqlite/countries.csv Normal file
View File

@ -0,0 +1,196 @@
german_name,population,capital,timezones
Afghanistan,40218234,Kabul,"[""UTC+04:30""]"
Albanien,2837743,Tirana,"[""UTC+01:00""]"
Algerien,44700000,Algiers,"[""UTC+01:00""]"
Andorra,77265,"Andorra la Vella","[""UTC+01:00""]"
Angola,32866268,Luanda,"[""UTC+01:00""]"
"Antigua und Barbuda",97928,"Saint John's","[""UTC-04:00""]"
Argentinien,45376763,"Buenos Aires","[""UTC-03:00""]"
Armenien,2963234,Yerevan,"[""UTC+04:00""]"
Aserbaidschan,10110116,Baku,"[""UTC+04:00""]"
Australien,25687041,Canberra,"[""UTC+05:00"",""UTC+06:30"",""UTC+07:00"",""UTC+08:00"",""UTC+09:30"",""UTC+10:00"",""UTC+10:30"",""UTC+11:30""]"
Bahamas,393248,Nassau,"[""UTC-05:00""]"
Bahrain,1701583,Manama,"[""UTC+03:00""]"
Bangladesch,164689383,Dhaka,"[""UTC+06:00""]"
Barbados,287371,Bridgetown,"[""UTC-04:00""]"
Belgien,11555997,Brussels,"[""UTC+01:00""]"
Belize,397621,Belmopan,"[""UTC-06:00""]"
Benin,12123198,Porto-Novo,"[""UTC+01:00""]"
Bhutan,771612,Thimphu,"[""UTC+06:00""]"
Bolivien,11673029,Sucre,"[""UTC-04:00""]"
"Bosnien und Herzegowina",3280815,Sarajevo,"[""UTC+01:00""]"
Botswana,2351625,Gaborone,"[""UTC+02:00""]"
Brasilien,212559409,"Brasília","[""UTC-05:00"",""UTC-04:00"",""UTC-03:00"",""UTC-02:00""]"
Brunei,437483,"Bandar Seri Begawan","[""UTC+08:00""]"
Bulgarien,6927288,Sofia,"[""UTC+02:00""]"
"Burkina Faso",20903278,Ouagadougou,"[""UTC""]"
Burundi,11890781,Gitega,"[""UTC+02:00""]"
Chile,19116209,Santiago,"[""UTC-06:00"",""UTC-04:00""]"
China,1402112000,Beijing,"[""UTC+08:00""]"
"Costa Rica",5094114,"San José","[""UTC-06:00""]"
Deutschland,83240525,Berlin,"[""UTC+01:00""]"
Dominica,71991,Roseau,"[""UTC-04:00""]"
"Dominikanische Republik",10847904,"Santo Domingo","[""UTC-04:00""]"
Dschibuti,988002,Djibouti,"[""UTC+03:00""]"
"Dänemark",5831404,Copenhagen,"[""UTC-04:00"",""UTC-03:00"",""UTC-01:00"",""UTC"",""UTC+01:00""]"
Ecuador,17643060,Quito,"[""UTC-06:00"",""UTC-05:00""]"
"El Salvador",6486201,"San Salvador","[""UTC-06:00""]"
"Elfenbeinküste",26378275,Yamoussoukro,"[""UTC""]"
Eritrea,5352000,Asmara,"[""UTC+03:00""]"
Estland,1331057,Tallinn,"[""UTC+02:00""]"
Fidschi,896444,Suva,"[""UTC+12:00""]"
Finnland,5530719,Helsinki,"[""UTC+02:00""]"
Frankreich,67391582,Paris,"[""UTC-10:00"",""UTC-09:30"",""UTC-09:00"",""UTC-08:00"",""UTC-04:00"",""UTC-03:00"",""UTC+01:00"",""UTC+02:00"",""UTC+03:00"",""UTC+04:00"",""UTC+05:00"",""UTC+10:00"",""UTC+11:00"",""UTC+12:00""]"
Gabun,2225728,Libreville,"[""UTC+01:00""]"
Gambia,2416664,Banjul,"[""UTC+00:00""]"
Georgien,3714000,Tbilisi,"[""UTC+04:00""]"
Ghana,31072945,Accra,"[""UTC""]"
Grenada,112519,"St. George's","[""UTC-04:00""]"
Griechenland,10715549,Athens,"[""UTC+02:00""]"
Guatemala,16858333,"Guatemala City","[""UTC-06:00""]"
Guinea,13132792,Conakry,"[""UTC""]"
Guinea-Bissau,1967998,Bissau,"[""UTC""]"
Guyana,786559,Georgetown,"[""UTC-04:00""]"
Haiti,11402533,Port-au-Prince,"[""UTC-05:00""]"
Honduras,9904608,Tegucigalpa,"[""UTC-06:00""]"
Indien,1380004385,"New Delhi","[""UTC+05:30""]"
Indonesien,273523621,Jakarta,"[""UTC+07:00"",""UTC+08:00"",""UTC+09:00""]"
Irak,40222503,Baghdad,"[""UTC+03:00""]"
Iran,83992953,Tehran,"[""UTC+03:30""]"
Irland,4994724,Dublin,"[""UTC""]"
Island,366425,Reykjavik,"[""UTC""]"
Israel,9216900,Jerusalem,"[""UTC+02:00""]"
Italien,59554023,Rome,"[""UTC+01:00""]"
Jamaika,2961161,Kingston,"[""UTC-05:00""]"
Japan,125836021,Tokyo,"[""UTC+09:00""]"
Jemen,29825968,"Sana'a","[""UTC+03:00""]"
Jordanien,10203140,Amman,"[""UTC+03:00""]"
Kambodscha,16718971,"Phnom Penh","[""UTC+07:00""]"
Kamerun,26545864,"Yaoundé","[""UTC+01:00""]"
Kanada,38005238,Ottawa,"[""UTC-08:00"",""UTC-07:00"",""UTC-06:00"",""UTC-05:00"",""UTC-04:00"",""UTC-03:30""]"
"Kap Verde",555988,Praia,"[""UTC-01:00""]"
Kasachstan,18754440,Astana,"[""UTC+05:00"",""UTC+06:00""]"
Katar,2881060,Doha,"[""UTC+03:00""]"
Kenia,53771300,Nairobi,"[""UTC+03:00""]"
Kirgisistan,6591600,Bishkek,"[""UTC+06:00""]"
Kiribati,119446,"South Tarawa","[""UTC+12:00"",""UTC+13:00"",""UTC+14:00""]"
Kolumbien,50882884,"Bogotá","[""UTC-05:00""]"
Komoren,869595,Moroni,"[""UTC+03:00""]"
Kongo,5657000,Brazzaville,"[""UTC+01:00""]"
"Kongo (Dem. Rep.)",108407721,Kinshasa,"[""UTC+01:00"",""UTC+02:00""]"
Kosovo,1775378,Pristina,"[""UTC+01:00""]"
Kroatien,4047200,Zagreb,"[""UTC+01:00""]"
Kuba,11326616,Havana,"[""UTC-05:00""]"
Kuwait,4270563,"Kuwait City","[""UTC+03:00""]"
Laos,7275556,Vientiane,"[""UTC+07:00""]"
Lesotho,2142252,Maseru,"[""UTC+02:00""]"
Lettland,1901548,Riga,"[""UTC+02:00""]"
Libanon,6825442,Beirut,"[""UTC+02:00""]"
Liberia,5057677,Monrovia,"[""UTC""]"
Libyen,6871287,Tripoli,"[""UTC+01:00""]"
Liechtenstein,38137,Vaduz,"[""UTC+01:00""]"
Litauen,2794700,Vilnius,"[""UTC+02:00""]"
Luxemburg,632275,Luxembourg,"[""UTC+01:00""]"
Madagaskar,27691019,Antananarivo,"[""UTC+03:00""]"
Malawi,19129955,Lilongwe,"[""UTC+02:00""]"
Malaysia,32365998,"Kuala Lumpur","[""UTC+08:00""]"
Malediven,540542,"Malé","[""UTC+05:00""]"
Mali,20250834,Bamako,"[""UTC""]"
Malta,525285,Valletta,"[""UTC+01:00""]"
Marokko,36910558,Rabat,"[""UTC""]"
Marshallinseln,59194,Majuro,"[""UTC+12:00""]"
Mauretanien,4649660,Nouakchott,"[""UTC""]"
Mauritius,1265740,"Port Louis","[""UTC+04:00""]"
Mexiko,128932753,"Mexico City","[""UTC-08:00"",""UTC-07:00"",""UTC-06:00""]"
Mikronesien,115021,Palikir,"[""UTC+10:00"",""UTC+11:00""]"
Moldawien,2617820,"Chișinău","[""UTC+02:00""]"
Monaco,39244,Monaco,"[""UTC+01:00""]"
Mongolei,3278292,"Ulan Bator","[""UTC+07:00"",""UTC+08:00""]"
Montenegro,621718,Podgorica,"[""UTC+01:00""]"
Mosambik,31255435,Maputo,"[""UTC+02:00""]"
Myanmar,54409794,Naypyidaw,"[""UTC+06:30""]"
Namibia,2540916,Windhoek,"[""UTC+01:00""]"
Nauru,10834,Yaren,"[""UTC+12:00""]"
Nepal,29136808,Kathmandu,"[""UTC+05:45""]"
Neuseeland,5084300,Wellington,"[""UTC-11:00"",""UTC-10:00"",""UTC+12:00"",""UTC+12:45"",""UTC+13:00""]"
Nicaragua,6624554,Managua,"[""UTC-06:00""]"
Niederlande,16655799,Amsterdam,"[""UTC+01:00""]"
Niger,24206636,Niamey,"[""UTC+01:00""]"
Nigeria,206139587,Abuja,"[""UTC+01:00""]"
Nordkorea,25778815,Pyongyang,"[""UTC+09:00""]"
Nordmazedonien,2077132,Skopje,"[""UTC+01:00""]"
Norwegen,5379475,Oslo,"[""UTC+01:00""]"
Oman,5106622,Muscat,"[""UTC+04:00""]"
Osttimor,1318442,Dili,"[""UTC+09:00""]"
Pakistan,220892331,Islamabad,"[""UTC+05:00""]"
Palau,18092,Ngerulmud,"[""UTC+09:00""]"
Panama,4314768,"Panama City","[""UTC-05:00""]"
Papua-Neuguinea,8947027,"Port Moresby","[""UTC+10:00""]"
Paraguay,7132530,"Asunción","[""UTC-04:00""]"
Peru,32971846,Lima,"[""UTC-05:00""]"
Philippinen,109581085,Manila,"[""UTC+08:00""]"
Polen,37950802,Warsaw,"[""UTC+01:00""]"
Portugal,10305564,Lisbon,"[""UTC-01:00"",""UTC""]"
Ruanda,12952209,Kigali,"[""UTC+02:00""]"
"Rumänien",19286123,Bucharest,"[""UTC+02:00""]"
Russland,144104080,Moscow,"[""UTC+03:00"",""UTC+04:00"",""UTC+06:00"",""UTC+07:00"",""UTC+08:00"",""UTC+09:00"",""UTC+10:00"",""UTC+11:00"",""UTC+12:00""]"
Salomonen,686878,Honiara,"[""UTC+11:00""]"
Sambia,18383956,Lusaka,"[""UTC+02:00""]"
Samoa,198410,Apia,"[""UTC+13:00""]"
"San Marino",33938,"City of San Marino","[""UTC+01:00""]"
Saudi-Arabien,34813867,Riyadh,"[""UTC+03:00""]"
Schweden,10353442,Stockholm,"[""UTC+01:00""]"
Schweiz,8654622,Bern,"[""UTC+01:00""]"
Senegal,16743930,Dakar,"[""UTC""]"
Serbien,6908224,Belgrade,"[""UTC+01:00""]"
Seychellen,98462,Victoria,"[""UTC+04:00""]"
"Sierra Leone",7976985,Freetown,"[""UTC""]"
Simbabwe,14862927,Harare,"[""UTC+02:00""]"
Singapur,5685807,Singapore,"[""UTC+08:00""]"
Slowakei,5458827,Bratislava,"[""UTC+01:00""]"
Slowenien,2100126,Ljubljana,"[""UTC+01:00""]"
Somalia,15893219,Mogadishu,"[""UTC+03:00""]"
Spanien,47351567,Madrid,"[""UTC"",""UTC+01:00""]"
"Sri Lanka",21919000,"Sri Jayawardenepura Kotte","[""UTC+05:30""]"
"St. Kitts und Nevis",53192,Basseterre,"[""UTC-04:00""]"
"St. Lucia",183629,Castries,"[""UTC-04:00""]"
"St. Vincent und die Grenadinen",110947,Kingstown,"[""UTC-04:00""]"
Sudan,43849269,Khartoum,"[""UTC+03:00""]"
Suriname,586634,Paramaribo,"[""UTC-03:00""]"
Swasiland,1160164,Mbabane,"[""UTC+02:00""]"
Syrien,17500657,Damascus,"[""UTC+02:00""]"
"São Tomé und Príncipe",219161,"São Tomé","[""UTC""]"
"Südafrika",59308690,Pretoria,"[""UTC+02:00""]"
"Südkorea",51780579,Seoul,"[""UTC+09:00""]"
"Südsudan",11193729,Juba,"[""UTC+03:00""]"
Tadschikistan,9537642,Dushanbe,"[""UTC+05:00""]"
Tansania,59734213,Dodoma,"[""UTC+03:00""]"
Thailand,69799978,Bangkok,"[""UTC+07:00""]"
Togo,8278737,"Lomé","[""UTC""]"
Tonga,105697,"Nuku'alofa","[""UTC+13:00""]"
"Trinidad und Tobago",1399491,"Port of Spain","[""UTC-04:00""]"
Tschad,16425859,"N'Djamena","[""UTC+01:00""]"
Tschechien,10698896,Prague,"[""UTC+01:00""]"
Tunesien,11818618,Tunis,"[""UTC+01:00""]"
Turkmenistan,6031187,Ashgabat,"[""UTC+05:00""]"
Tuvalu,11792,Funafuti,"[""UTC+12:00""]"
"Türkei",84339067,Ankara,"[""UTC+03:00""]"
Uganda,45741000,Kampala,"[""UTC+03:00""]"
Ukraine,44134693,Kyiv,"[""UTC+02:00""]"
Ungarn,9749763,Budapest,"[""UTC+01:00""]"
Uruguay,3473727,Montevideo,"[""UTC-03:00""]"
Usbekistan,34232050,Tashkent,"[""UTC+05:00""]"
Vanuatu,307150,"Port Vila","[""UTC+11:00""]"
Vatikanstadt,451,"Vatican City","[""UTC+01:00""]"
Venezuela,28435943,Caracas,"[""UTC-04:00""]"
"Vereinigte Arabische Emirate",9890400,"Abu Dhabi","[""UTC+04:00""]"
"Vereinigte Staaten",329484123,"Washington, D.C.","[""UTC-12:00"",""UTC-11:00"",""UTC-10:00"",""UTC-09:00"",""UTC-08:00"",""UTC-07:00"",""UTC-06:00"",""UTC-05:00"",""UTC-04:00"",""UTC+10:00"",""UTC+12:00""]"
"Vereinigtes Königreich",67215293,London,"[""UTC-08:00"",""UTC-05:00"",""UTC-04:00"",""UTC-03:00"",""UTC-02:00"",""UTC"",""UTC+01:00"",""UTC+02:00"",""UTC+06:00""]"
Vietnam,97338583,Hanoi,"[""UTC+07:00""]"
"Weißrussland",9398861,Minsk,"[""UTC+03:00""]"
"Zentralafrikanische Republik",4829764,Bangui,"[""UTC+01:00""]"
Zypern,1207361,Nicosia,"[""UTC+02:00""]"
"Ägypten",102334403,Cairo,"[""UTC+02:00""]"
"Äquatorialguinea",1402985,Malabo,"[""UTC+01:00""]"
"Äthiopien",114963583,"Addis Ababa","[""UTC+03:00""]"
"Österreich",8917205,Vienna,"[""UTC+01:00""]"
1 german_name population capital timezones
2 Afghanistan 40218234 Kabul ["UTC+04:30"]
3 Albanien 2837743 Tirana ["UTC+01:00"]
4 Algerien 44700000 Algiers ["UTC+01:00"]
5 Andorra 77265 Andorra la Vella ["UTC+01:00"]
6 Angola 32866268 Luanda ["UTC+01:00"]
7 Antigua und Barbuda 97928 Saint John's ["UTC-04:00"]
8 Argentinien 45376763 Buenos Aires ["UTC-03:00"]
9 Armenien 2963234 Yerevan ["UTC+04:00"]
10 Aserbaidschan 10110116 Baku ["UTC+04:00"]
11 Australien 25687041 Canberra ["UTC+05:00","UTC+06:30","UTC+07:00","UTC+08:00","UTC+09:30","UTC+10:00","UTC+10:30","UTC+11:30"]
12 Bahamas 393248 Nassau ["UTC-05:00"]
13 Bahrain 1701583 Manama ["UTC+03:00"]
14 Bangladesch 164689383 Dhaka ["UTC+06:00"]
15 Barbados 287371 Bridgetown ["UTC-04:00"]
16 Belgien 11555997 Brussels ["UTC+01:00"]
17 Belize 397621 Belmopan ["UTC-06:00"]
18 Benin 12123198 Porto-Novo ["UTC+01:00"]
19 Bhutan 771612 Thimphu ["UTC+06:00"]
20 Bolivien 11673029 Sucre ["UTC-04:00"]
21 Bosnien und Herzegowina 3280815 Sarajevo ["UTC+01:00"]
22 Botswana 2351625 Gaborone ["UTC+02:00"]
23 Brasilien 212559409 Brasília ["UTC-05:00","UTC-04:00","UTC-03:00","UTC-02:00"]
24 Brunei 437483 Bandar Seri Begawan ["UTC+08:00"]
25 Bulgarien 6927288 Sofia ["UTC+02:00"]
26 Burkina Faso 20903278 Ouagadougou ["UTC"]
27 Burundi 11890781 Gitega ["UTC+02:00"]
28 Chile 19116209 Santiago ["UTC-06:00","UTC-04:00"]
29 China 1402112000 Beijing ["UTC+08:00"]
30 Costa Rica 5094114 San José ["UTC-06:00"]
31 Deutschland 83240525 Berlin ["UTC+01:00"]
32 Dominica 71991 Roseau ["UTC-04:00"]
33 Dominikanische Republik 10847904 Santo Domingo ["UTC-04:00"]
34 Dschibuti 988002 Djibouti ["UTC+03:00"]
35 Dänemark 5831404 Copenhagen ["UTC-04:00","UTC-03:00","UTC-01:00","UTC","UTC+01:00"]
36 Ecuador 17643060 Quito ["UTC-06:00","UTC-05:00"]
37 El Salvador 6486201 San Salvador ["UTC-06:00"]
38 Elfenbeinküste 26378275 Yamoussoukro ["UTC"]
39 Eritrea 5352000 Asmara ["UTC+03:00"]
40 Estland 1331057 Tallinn ["UTC+02:00"]
41 Fidschi 896444 Suva ["UTC+12:00"]
42 Finnland 5530719 Helsinki ["UTC+02:00"]
43 Frankreich 67391582 Paris ["UTC-10:00","UTC-09:30","UTC-09:00","UTC-08:00","UTC-04:00","UTC-03:00","UTC+01:00","UTC+02:00","UTC+03:00","UTC+04:00","UTC+05:00","UTC+10:00","UTC+11:00","UTC+12:00"]
44 Gabun 2225728 Libreville ["UTC+01:00"]
45 Gambia 2416664 Banjul ["UTC+00:00"]
46 Georgien 3714000 Tbilisi ["UTC+04:00"]
47 Ghana 31072945 Accra ["UTC"]
48 Grenada 112519 St. George's ["UTC-04:00"]
49 Griechenland 10715549 Athens ["UTC+02:00"]
50 Guatemala 16858333 Guatemala City ["UTC-06:00"]
51 Guinea 13132792 Conakry ["UTC"]
52 Guinea-Bissau 1967998 Bissau ["UTC"]
53 Guyana 786559 Georgetown ["UTC-04:00"]
54 Haiti 11402533 Port-au-Prince ["UTC-05:00"]
55 Honduras 9904608 Tegucigalpa ["UTC-06:00"]
56 Indien 1380004385 New Delhi ["UTC+05:30"]
57 Indonesien 273523621 Jakarta ["UTC+07:00","UTC+08:00","UTC+09:00"]
58 Irak 40222503 Baghdad ["UTC+03:00"]
59 Iran 83992953 Tehran ["UTC+03:30"]
60 Irland 4994724 Dublin ["UTC"]
61 Island 366425 Reykjavik ["UTC"]
62 Israel 9216900 Jerusalem ["UTC+02:00"]
63 Italien 59554023 Rome ["UTC+01:00"]
64 Jamaika 2961161 Kingston ["UTC-05:00"]
65 Japan 125836021 Tokyo ["UTC+09:00"]
66 Jemen 29825968 Sana'a ["UTC+03:00"]
67 Jordanien 10203140 Amman ["UTC+03:00"]
68 Kambodscha 16718971 Phnom Penh ["UTC+07:00"]
69 Kamerun 26545864 Yaoundé ["UTC+01:00"]
70 Kanada 38005238 Ottawa ["UTC-08:00","UTC-07:00","UTC-06:00","UTC-05:00","UTC-04:00","UTC-03:30"]
71 Kap Verde 555988 Praia ["UTC-01:00"]
72 Kasachstan 18754440 Astana ["UTC+05:00","UTC+06:00"]
73 Katar 2881060 Doha ["UTC+03:00"]
74 Kenia 53771300 Nairobi ["UTC+03:00"]
75 Kirgisistan 6591600 Bishkek ["UTC+06:00"]
76 Kiribati 119446 South Tarawa ["UTC+12:00","UTC+13:00","UTC+14:00"]
77 Kolumbien 50882884 Bogotá ["UTC-05:00"]
78 Komoren 869595 Moroni ["UTC+03:00"]
79 Kongo 5657000 Brazzaville ["UTC+01:00"]
80 Kongo (Dem. Rep.) 108407721 Kinshasa ["UTC+01:00","UTC+02:00"]
81 Kosovo 1775378 Pristina ["UTC+01:00"]
82 Kroatien 4047200 Zagreb ["UTC+01:00"]
83 Kuba 11326616 Havana ["UTC-05:00"]
84 Kuwait 4270563 Kuwait City ["UTC+03:00"]
85 Laos 7275556 Vientiane ["UTC+07:00"]
86 Lesotho 2142252 Maseru ["UTC+02:00"]
87 Lettland 1901548 Riga ["UTC+02:00"]
88 Libanon 6825442 Beirut ["UTC+02:00"]
89 Liberia 5057677 Monrovia ["UTC"]
90 Libyen 6871287 Tripoli ["UTC+01:00"]
91 Liechtenstein 38137 Vaduz ["UTC+01:00"]
92 Litauen 2794700 Vilnius ["UTC+02:00"]
93 Luxemburg 632275 Luxembourg ["UTC+01:00"]
94 Madagaskar 27691019 Antananarivo ["UTC+03:00"]
95 Malawi 19129955 Lilongwe ["UTC+02:00"]
96 Malaysia 32365998 Kuala Lumpur ["UTC+08:00"]
97 Malediven 540542 Malé ["UTC+05:00"]
98 Mali 20250834 Bamako ["UTC"]
99 Malta 525285 Valletta ["UTC+01:00"]
100 Marokko 36910558 Rabat ["UTC"]
101 Marshallinseln 59194 Majuro ["UTC+12:00"]
102 Mauretanien 4649660 Nouakchott ["UTC"]
103 Mauritius 1265740 Port Louis ["UTC+04:00"]
104 Mexiko 128932753 Mexico City ["UTC-08:00","UTC-07:00","UTC-06:00"]
105 Mikronesien 115021 Palikir ["UTC+10:00","UTC+11:00"]
106 Moldawien 2617820 Chișinău ["UTC+02:00"]
107 Monaco 39244 Monaco ["UTC+01:00"]
108 Mongolei 3278292 Ulan Bator ["UTC+07:00","UTC+08:00"]
109 Montenegro 621718 Podgorica ["UTC+01:00"]
110 Mosambik 31255435 Maputo ["UTC+02:00"]
111 Myanmar 54409794 Naypyidaw ["UTC+06:30"]
112 Namibia 2540916 Windhoek ["UTC+01:00"]
113 Nauru 10834 Yaren ["UTC+12:00"]
114 Nepal 29136808 Kathmandu ["UTC+05:45"]
115 Neuseeland 5084300 Wellington ["UTC-11:00","UTC-10:00","UTC+12:00","UTC+12:45","UTC+13:00"]
116 Nicaragua 6624554 Managua ["UTC-06:00"]
117 Niederlande 16655799 Amsterdam ["UTC+01:00"]
118 Niger 24206636 Niamey ["UTC+01:00"]
119 Nigeria 206139587 Abuja ["UTC+01:00"]
120 Nordkorea 25778815 Pyongyang ["UTC+09:00"]
121 Nordmazedonien 2077132 Skopje ["UTC+01:00"]
122 Norwegen 5379475 Oslo ["UTC+01:00"]
123 Oman 5106622 Muscat ["UTC+04:00"]
124 Osttimor 1318442 Dili ["UTC+09:00"]
125 Pakistan 220892331 Islamabad ["UTC+05:00"]
126 Palau 18092 Ngerulmud ["UTC+09:00"]
127 Panama 4314768 Panama City ["UTC-05:00"]
128 Papua-Neuguinea 8947027 Port Moresby ["UTC+10:00"]
129 Paraguay 7132530 Asunción ["UTC-04:00"]
130 Peru 32971846 Lima ["UTC-05:00"]
131 Philippinen 109581085 Manila ["UTC+08:00"]
132 Polen 37950802 Warsaw ["UTC+01:00"]
133 Portugal 10305564 Lisbon ["UTC-01:00","UTC"]
134 Ruanda 12952209 Kigali ["UTC+02:00"]
135 Rumänien 19286123 Bucharest ["UTC+02:00"]
136 Russland 144104080 Moscow ["UTC+03:00","UTC+04:00","UTC+06:00","UTC+07:00","UTC+08:00","UTC+09:00","UTC+10:00","UTC+11:00","UTC+12:00"]
137 Salomonen 686878 Honiara ["UTC+11:00"]
138 Sambia 18383956 Lusaka ["UTC+02:00"]
139 Samoa 198410 Apia ["UTC+13:00"]
140 San Marino 33938 City of San Marino ["UTC+01:00"]
141 Saudi-Arabien 34813867 Riyadh ["UTC+03:00"]
142 Schweden 10353442 Stockholm ["UTC+01:00"]
143 Schweiz 8654622 Bern ["UTC+01:00"]
144 Senegal 16743930 Dakar ["UTC"]
145 Serbien 6908224 Belgrade ["UTC+01:00"]
146 Seychellen 98462 Victoria ["UTC+04:00"]
147 Sierra Leone 7976985 Freetown ["UTC"]
148 Simbabwe 14862927 Harare ["UTC+02:00"]
149 Singapur 5685807 Singapore ["UTC+08:00"]
150 Slowakei 5458827 Bratislava ["UTC+01:00"]
151 Slowenien 2100126 Ljubljana ["UTC+01:00"]
152 Somalia 15893219 Mogadishu ["UTC+03:00"]
153 Spanien 47351567 Madrid ["UTC","UTC+01:00"]
154 Sri Lanka 21919000 Sri Jayawardenepura Kotte ["UTC+05:30"]
155 St. Kitts und Nevis 53192 Basseterre ["UTC-04:00"]
156 St. Lucia 183629 Castries ["UTC-04:00"]
157 St. Vincent und die Grenadinen 110947 Kingstown ["UTC-04:00"]
158 Sudan 43849269 Khartoum ["UTC+03:00"]
159 Suriname 586634 Paramaribo ["UTC-03:00"]
160 Swasiland 1160164 Mbabane ["UTC+02:00"]
161 Syrien 17500657 Damascus ["UTC+02:00"]
162 São Tomé und Príncipe 219161 São Tomé ["UTC"]
163 Südafrika 59308690 Pretoria ["UTC+02:00"]
164 Südkorea 51780579 Seoul ["UTC+09:00"]
165 Südsudan 11193729 Juba ["UTC+03:00"]
166 Tadschikistan 9537642 Dushanbe ["UTC+05:00"]
167 Tansania 59734213 Dodoma ["UTC+03:00"]
168 Thailand 69799978 Bangkok ["UTC+07:00"]
169 Togo 8278737 Lomé ["UTC"]
170 Tonga 105697 Nuku'alofa ["UTC+13:00"]
171 Trinidad und Tobago 1399491 Port of Spain ["UTC-04:00"]
172 Tschad 16425859 N'Djamena ["UTC+01:00"]
173 Tschechien 10698896 Prague ["UTC+01:00"]
174 Tunesien 11818618 Tunis ["UTC+01:00"]
175 Turkmenistan 6031187 Ashgabat ["UTC+05:00"]
176 Tuvalu 11792 Funafuti ["UTC+12:00"]
177 Türkei 84339067 Ankara ["UTC+03:00"]
178 Uganda 45741000 Kampala ["UTC+03:00"]
179 Ukraine 44134693 Kyiv ["UTC+02:00"]
180 Ungarn 9749763 Budapest ["UTC+01:00"]
181 Uruguay 3473727 Montevideo ["UTC-03:00"]
182 Usbekistan 34232050 Tashkent ["UTC+05:00"]
183 Vanuatu 307150 Port Vila ["UTC+11:00"]
184 Vatikanstadt 451 Vatican City ["UTC+01:00"]
185 Venezuela 28435943 Caracas ["UTC-04:00"]
186 Vereinigte Arabische Emirate 9890400 Abu Dhabi ["UTC+04:00"]
187 Vereinigte Staaten 329484123 Washington, D.C. ["UTC-12:00","UTC-11:00","UTC-10:00","UTC-09:00","UTC-08:00","UTC-07:00","UTC-06:00","UTC-05:00","UTC-04:00","UTC+10:00","UTC+12:00"]
188 Vereinigtes Königreich 67215293 London ["UTC-08:00","UTC-05:00","UTC-04:00","UTC-03:00","UTC-02:00","UTC","UTC+01:00","UTC+02:00","UTC+06:00"]
189 Vietnam 97338583 Hanoi ["UTC+07:00"]
190 Weißrussland 9398861 Minsk ["UTC+03:00"]
191 Zentralafrikanische Republik 4829764 Bangui ["UTC+01:00"]
192 Zypern 1207361 Nicosia ["UTC+02:00"]
193 Ägypten 102334403 Cairo ["UTC+02:00"]
194 Äquatorialguinea 1402985 Malabo ["UTC+01:00"]
195 Äthiopien 114963583 Addis Ababa ["UTC+03:00"]
196 Österreich 8917205 Vienna ["UTC+01:00"]

1
sqlite/countries.json Normal file

File diff suppressed because one or more lines are too long

15
sqlite/countries.sql Normal file
View File

@ -0,0 +1,15 @@
.headers on
.mode table
WITH raw AS (
SELECT
value AS country
FROM json_each(readfile('countries.json'))
)
SELECT
json_extract(country, '$.translations.deu.common') AS german_name,
json_extract(country, '$.population') AS population,
json_extract(country, '$.capital[0]') AS capital,
json_extract(country, '$.timezones') AS timezones
FROM raw
ORDER BY german_name;

18
sqlite/countries_csv.sql Normal file
View File

@ -0,0 +1,18 @@
.mode csv
.headers on
.output countries.csv
WITH raw AS (
SELECT
value AS country
FROM json_each(readfile('countries.json'))
)
SELECT
json_extract(country, '$.translations.deu.common') AS german_name,
json_extract(country, '$.population') AS population,
json_extract(country, '$.capital[0]') AS capital,
json_extract(country, '$.timezones') AS timezones
FROM raw
ORDER BY german_name;
.output stdout

86
sqlite/countries_import.sh Executable file
View File

@ -0,0 +1,86 @@
#!/bin/bash
# Script to import REST Countries JSON data into SQLite
# Usage: ./import_countries.sh countries.json
JSON_FILE="${1:-countries.json}"
DB_FILE="countries.db"
# Check if JSON file exists
if [ ! -f "$JSON_FILE" ]; then
echo "Error: $JSON_FILE not found!"
echo "Usage: $0 <path-to-countries.json>"
exit 1
fi
echo "Creating SQLite database: $DB_FILE"
# Remove existing database if present
rm -f "$DB_FILE"
# Create database and import JSON
sqlite3 "$DB_FILE" << 'EOF'
-- Create table with JSON column
CREATE TABLE countries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data JSON NOT NULL
);
-- Enable JSON mode for import
.mode json
-- Read and insert JSON data
.import countries.json countries_temp
-- The import creates a temporary table, now we need to move data
-- Since the JSON file is an array, we need to handle it properly
EOF
# Alternative approach: Use jq to process JSON array and insert row by row
echo "Processing JSON data..."
sqlite3 "$DB_FILE" << 'EOF'
-- If direct import doesn't work, we'll insert via stdin
CREATE TABLE IF NOT EXISTS countries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data JSON NOT NULL
);
EOF
# Use jq to split array into individual objects and insert
jq -c '.[]' "$JSON_FILE" | while IFS= read -r country; do
sqlite3 "$DB_FILE" "INSERT INTO countries (data) VALUES (json('$country'));"
done
echo "Import complete!"
echo "Database created: $DB_FILE"
echo ""
echo "Running sample queries..."
echo ""
# Sample queries
sqlite3 -column -header "$DB_FILE" << 'EOF'
-- Count total countries
SELECT COUNT(*) as total_countries FROM countries;
-- Get countries with German names, population, and timezones
SELECT
json_extract(data, '$.name.common') as country_name,
json_extract(data, '$.name.nativeName.deu.official') as german_name,
json_extract(data, '$.population') as population,
json_extract(data, '$.timezones') as timezones
FROM countries
WHERE json_extract(data, '$.name.nativeName.deu.official') IS NOT NULL
LIMIT 10;
-- Get all German translations (from translations field)
SELECT
json_extract(data, '$.name.common') as country_name,
json_extract(data, '$.translations.deu.official') as german_official,
json_extract(data, '$.translations.deu.common') as german_common
FROM countries
LIMIT 10;
EOF
echo ""
echo "Database ready! You can now query with: sqlite3 $DB_FILE"

137
sqlite/extract.sql Normal file
View File

@ -0,0 +1,137 @@
-- Attach a new database with the specified name
ATTACH DATABASE 'food.db' AS food;
ATTACH DATABASE 'import.db' AS src;
-- ATTACH DATABASE 'food10k.db.orig' AS src;
ATTACH DATABASE ':memory:' AS cache;
-- ATTACH DATABASE 'memory.db' AS cache;
SELECT time(), "Creating tables and temporary tables.";
CREATE TABLE IF NOT EXISTS food.product (
id INTEGER PRIMARY KEY,
_id TEXT,
name TEXT,
created_t INTEGER,
last_modified_t INTEGER,
countries TEXT,
creator_id INTEGER,
brand_owner_id INTEGER,
nutrition_grades
);
CREATE TABLE IF NOT EXISTS food.creator (
creator_id INTEGER PRIMARY KEY AUTOINCREMENT,
creator TEXT
);
CREATE TABLE IF NOT EXISTS cache.creator (
creator_id INTEGER PRIMARY KEY AUTOINCREMENT,
creator TEXT
);
CREATE TABLE IF NOT EXISTS food.brand_owner (
brand_owner_id INTEGER PRIMARY KEY AUTOINCREMENT,
brand_owner TEXT
);
CREATE TABLE IF NOT EXISTS food.product_nutriment_temp (
product_id INTEGER,
nutriment_key TEXT,
nutriment_value ANY
);
CREATE TABLE IF NOT EXISTS food.product_nutriment (
product_id INTEGER,
nutriment_id INTEGER,
nutriment_value ANY
);
CREATE TABLE IF NOT EXISTS food.nutriment (
nutriment_id INTEGER PRIMARY KEY AUTOINCREMENT,
nutriment TEXT
);
CREATE TABLE IF NOT EXISTS cache.brand_owner (
brand_owner_id INTEGER PRIMARY KEY AUTOINCREMENT,
brand_owner TEXT
);
CREATE TABLE IF NOT EXISTS cache.product (
id INTEGER PRIMARY KEY,
_id TEXT,
name TEXT,
created_t INTEGER,
last_modified_t INTEGER,
countries TEXT,
creator TEXT,
brand_owner TEXT,
nutrition_grades
);
SELECT time(), "Reading and caching base data.";
INSERT INTO cache.product (id, _id, name, created_t, last_modified_t, countries, creator, brand_owner, nutrition_grades) SELECT ID,
json_extract(data, '$._id'), json_extract(data, '$.product_name'),
json_extract(data, '$.created_t'), json_extract(data, '$.last_modified_t'),
json_extract(data, '$.countries'), json_extract(data, '$.creator'), json_extract(data, '$.brand_owner'),
json_extract(data, '$.nutrition_grades')
from foodraw;
SELECT time(), "Inserting cached data into creator table.";
INSERT INTO food.creator (creator )SELECT DISTINCT creator from cache.product ORDER BY creator;
INSERT INTO cache.creator (creator )SELECT DISTINCT creator from cache.product ORDER BY creator;
SELECT time(), "Inserting cached data into brand_owner table.";
INSERT INTO food.brand_owner (brand_owner )SELECT DISTINCT brand_owner from cache.product ORDER BY brand_owner;
INSERT INTO cache.brand_owner (brand_owner )SELECT DISTINCT brand_owner from cache.product ORDER BY brand_owner;
SELECT time(), "Inserting cached data into product table.";
INSERT INTO food.product (id, _id, name, created_t, last_modified_t, countries, creator_id, brand_owner_id, nutrition_grades)
SELECT id, _id, name, created_t, last_modified_t, countries, creator.creator_id as creator_id, brand_owner.brand_owner_id as brand_owner_id, nutrition_grades
FROM cache.product
LEFT JOIN cache.creator on cache.product.creator = cache.creator.creator
LEFT JOIN cache.brand_owner on cache.product.brand_owner = cache.brand_owner.brand_owner
where cache.product.name is not null and cache.product.name != "";
CREATE VIEW food.product_view as
SELECT id, _id, name, created_t, last_modified_t, countries, nutrition_grades, brand_owner.brand_owner, creator.creator FROM product
LEFT JOIN brand_owner on product.brand_owner_id = brand_owner.brand_owner_id
LEFT JOIN creator on product.creator_id = creator.creator_id
;
SELECT time(), "Reading nutriments and writing product_nutriment_temp table.";
INSERT INTO food.product_nutriment_temp ( product_id, nutriment_key, nutriment_value) select foodraw.id, key, value
from foodraw, json_each( data ->> '$.nutriments' );
INSERT INTO food.nutriment (nutriment) SELECT DISTINCT nutriment_key from food.product_nutriment_temp order by nutriment_key;
SELECT time(), "Writing product_nutriment table.";
INSERT INTO food.product_nutriment (product_id, nutriment_id, nutriment_value)
SELECT product_id, nutriment_id, nutriment_value from product_nutriment_temp JOIN nutriment on product_nutriment_temp.nutriment_key = nutriment.nutriment
WHERE nutriment_value != ""
;
CREATE VIEW food.nutriment_view as
SELECT product_id, nutriment, nutriment_value from product_nutriment NATURAL JOIN nutriment;
DROP TABLE food.product_nutriment_temp;
CREATE INDEX product_nutriment_product_product_id on product_nutriment(product_id);
CREATE INDEX product_nutriment_product_nutriment_id on product_nutriment(nutriment_id);
CREATE INDEX product_creator_id on product(creator_id);
CREATE INDEX product_brandowner_id on product(brand_owner_id);
CREATE INDEX product_name on product(name);
DETACH DATABASE food;
DETACH DATABASE src;
DETACH DATABASE cache;

27
sqlite/import.sh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash
# Define the name of the SQLite commands file
Imp_SQL_FILE="import_json.sql" # sql file perform the raw import
Ext_SQL_FILE="extract.sql" # sql file to extract the data from the imported db
# Database file names:
IMP_DB_FILE="import.db"
TGT_DB_FILE="food.db"
MEMORY_DB_FILE="memory.db"
# cleanup from previous runs
[ -e $IMP_DB_FILE ] && echo "$(date +"%T")|deleting $IMP_DB_FILE file" && rm $IMP_DB_FILE
[ -e $TGT_DB_FILE ] && echo "$(date +"%T")|deleting $TGT_DB_FILE file"&& rm $TGT_DB_FILE
[ -e $MEMORY_DB_FILE ] && echo "$(date +"%T")|deleting $TGT_DB_FILE file"&& rm $MEMORY_DB_FILE
# Execute SQLite commands from the SQL file
echo "$(date +"%T")|importing json file started"
sqlite3 "$IMP_DB_FILE" < "$Imp_SQL_FILE" 2>/dev/null
echo "$(date +"%T")|importing json file finished"
# Execute SQLite commands from the SQL file
sqlite3 < "$Ext_SQL_FILE"
echo "$(date +"%T")|creating $TGT_DB_FILE finished"

7
sqlite/import_json.sql Normal file
View File

@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS foodraw (
data JSON NOT NULL,
id INTEGER PRIMARY KEY AUTOINCREMENT
);
.import off1000lines.jsonl foodraw

1000
sqlite/off1000lines.jsonl Normal file

File diff suppressed because one or more lines are too long

70
sqlite/sudoku.sql Normal file
View File

@ -0,0 +1,70 @@
WITH RECURSIVE
input(sud) AS (
VALUES('53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79')
),
digits(z, lp) AS (
VALUES('1', 1)
UNION ALL
SELECT CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9
),
x(s, ind) AS (
SELECT sud, instr(sud, '.') FROM input
UNION ALL
SELECT
substr(s, 1, ind-1) || z || substr(s, ind+1),
instr(substr(s, 1, ind-1) || z || substr(s, ind+1), '.')
FROM x, digits AS z
WHERE ind>0
AND NOT EXISTS (
SELECT 1
FROM digits AS lp
WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1)
OR z.z = substr(s, ((ind-1)%9) + (lp-1)*9 + 1, 1)
OR z.z = substr(s, (((ind-1)/3) % 3)*3
+ ((ind-1)/27)*27
+ lp + ((lp-1)/3)*6, 1)
)
),
solved(s) AS (
SELECT s FROM x WHERE ind=0
),
-- Build 9 pretty rows for both input and solved using digits.lp = 1..9
grid_rows(name, rn, line) AS (
SELECT 'input', d.lp,
substr(sud, (d.lp-1)*9+1, 3)
|| ' | ' ||
substr(sud, (d.lp-1)*9+4, 3)
|| ' | ' ||
substr(sud, (d.lp-1)*9+7, 3)
FROM input CROSS JOIN digits d
UNION ALL
SELECT 'solved', d.lp,
substr(s, (d.lp-1)*9+1, 3)
|| ' | ' ||
substr(s, (d.lp-1)*9+4, 3)
|| ' | ' ||
substr(s, (d.lp-1)*9+7, 3)
FROM solved CROSS JOIN digits d
),
-- Insert horizontal separators after rows 3 and 6 by computing an ordering key
grid_with_sep AS (
SELECT name, rn*2-1 AS ord, line FROM grid_rows
UNION ALL
SELECT name, rn*2 AS ord, '----+-----+----' FROM grid_rows WHERE rn IN (3,6)
)
-- Single final UNIONed selection, ordered by group + position
SELECT line FROM (
SELECT 1 AS grp, ord AS ord, line FROM grid_with_sep WHERE name='input'
UNION ALL
SELECT 2 AS grp, 0 AS ord, '' AS line
UNION ALL
SELECT 3 AS grp, ord AS ord, line FROM grid_with_sep WHERE name='solved'
) ORDER BY grp, ord;