blob: 184bed087fb211ec521a538c56a15b725d70a37a [file] [log] [blame]
Marc Kupietz66bbd2d2022-07-19 17:53:49 +02001#!/usr/bin/env perl
2
Marc Kupietzffd6d2a2026-08-21 12:29:32 +02003our $VERSION = '0.98';
Marc Kupietzbf9bac02022-04-11 21:16:47 +02004
Marc Kupietzc82b15f2022-07-19 17:36:27 +02005use IDS::DeReKoVecs::Read;
Marc Kupietzdc22b982015-10-09 09:19:34 +02006use Mojolicious::Lite;
Marc Kupietzaf436142026-07-30 14:40:10 +09007use Mojo::Cache;
Marc Kupietzc4893362016-02-25 08:04:46 +01008use Mojo::JSON qw(decode_json encode_json to_json);
Marc Kupietz30ca4342017-11-22 21:21:20 +01009use base 'Mojolicious::Plugin';
10
Marc Kupietz247500f2015-10-09 11:29:01 +020011use Encode qw(decode encode);
Marc Kupietza5b90152016-03-15 17:39:19 +010012use Getopt::Std;
Marc Kupietze8e3ded2020-07-13 17:53:56 +020013#use Mojo::Server::Daemon;
Marc Kupietzffef9302017-11-07 15:58:01 +010014use Cwd;
Marc Kupietz66bfd952017-12-11 09:59:45 +010015
Marc Kupietzbf9bac02022-04-11 21:16:47 +020016my $mojo_config = $ENV{MOJO_CONFIG} // '../derekovecs-server.conf';
Marc Kupietzc0d41872021-02-25 16:33:22 +010017plugin Config => {file => $mojo_config};
18
Marc K20476c72021-03-11 12:18:01 +010019my $DEFAULT_VECS = app->config->{w2v}->{vecs} // "../models/dereko-2021-i.vecs";
Marc Kupietz49c5cf02024-08-06 10:30:44 +020020my $KORAP_URL = app->config->{w2v}->{korap_url} // "https://korap.ids-mannheim.de";
21
Marc Kupietzc0d41872021-02-25 16:33:22 +010022my $DEFAULT_NET_NAME = "";
23if ($DEFAULT_VECS=~ /\.vecs/) {
24 $DEFAULT_NET_NAME = $DEFAULT_VECS;
25 $DEFAULT_NET_NAME =~ s/\.vecs/.net/;
26}
Marc Kupietz9ae184c2023-11-04 18:21:54 +010027my $DEFAULT_NET = app->config->{w2v}->{net} // $DEFAULT_NET_NAME;
28my $DOWNTIME_CALENDAR_URL = app->config->{downtime_calendar_url} // '';
Marc Kupietz3eeec652024-11-18 18:30:04 +010029my $COMPARE_TO = app->config->{w2v}->{compare_to} // '';
30
Marc Kupietzffef9302017-11-07 15:58:01 +010031app->static->paths->[0] = getcwd;
32
Marc Kupietz1b856fa2019-12-07 23:01:43 +010033plugin 'Piwik';
Marc Kupietz2b8d44a2019-12-09 10:38:16 +010034plugin "RemoteAddr";
Marc Kupietz1b856fa2019-12-07 23:01:43 +010035plugin 'Util::RandomString' => {
36 piwik_rand_id => {
37 alphabet => '0123456789abcdef',
38 length => 16
39 }
40};
41
Marc Kupietzd4227392016-03-01 16:45:12 +010042plugin 'Log::Access';
Marc Kupietzb3422c12017-07-04 14:12:11 +020043plugin "RequestBase";
Marc Kupietz95104512019-12-05 10:13:05 +010044#plugin 'AutoReload';
45plugin Localize => {
46 dict => {
47 _ => sub { $_->locale },
48 },
Marc Kupietzbf9bac02022-04-11 21:16:47 +020049 resources => ['../derekovecs-server.dict']
Marc Kupietz95104512019-12-05 10:13:05 +010050};
Marc Kupietza5b90152016-03-15 17:39:19 +010051our $opt_i = 0; # latin1-input?
52our $opt_l = undef;
Marc Kupietza2e64502016-04-27 09:53:51 +020053our $opt_m;
Marc Kupietz6ed81872016-04-27 14:04:04 +020054our $opt_M;
Marc Kupietze8e3ded2020-07-13 17:53:56 +020055our $opt_n = $DEFAULT_NET;
Marc Kupietz43ee87e2016-04-25 10:50:08 +020056our $opt_d;
Marc Kupietzfa194262018-06-05 09:39:32 +020057our $opt_D;
Marc Kupietzda7b3492023-02-27 20:10:59 +010058our $opt_G;
Marc Kupietzbf9bac02022-04-11 21:16:47 +020059
60our $mergedEnd=0;
Marc Kupietzbf9bac02022-04-11 21:16:47 +020061our $opt_p = 5676;
Marc Kupietze8e3ded2020-07-13 17:53:56 +020062our $opt_C;
Marc Kupietza5b90152016-03-15 17:39:19 +010063
Marc Kupietzb3cd59f2026-08-21 10:47:45 +020064# Crawlers that collect training data for language models walk the
65# parameterised URLs of this server, and those are the most expensive thing it
66# does: every word is a URL of its own, every result links to the next words,
67# so the crawl space is the whole vocabulary and nothing of it can be served
68# from a cache. They are turned away before anything is computed.
69#
70# The list is a regular expression and can be replaced in the configuration
71# file, robots => {block_user_agents => '...'}; an empty value switches the
72# blocking off. Ordinary search engines are deliberately not in it - they are
73# kept out of the parameterised URLs by robots.txt and X-Robots-Tag instead,
74# and the entry page stays indexable.
75my $DEFAULT_BLOCKED_AGENTS = join('|',
76 'AI2Bot', 'Amazonbot', 'anthropic-ai', 'Bytespider', 'CCBot', 'ChatGPT-User',
77 'Claude-SearchBot', 'Claude-User', 'Claude-Web', 'ClaudeBot', 'cohere-ai',
78 'cohere-training-data-crawler', 'Diffbot', 'DuckAssistBot', 'FacebookBot',
79 'facebookexternalhit', 'FriendlyCrawler', 'GPTBot', 'ImagesiftBot',
80 'Kangaroo Bot', 'Meta-ExternalAgent', 'Meta-ExternalFetcher', 'meta-webindexer',
81 'OAI-SearchBot', 'omgili', 'PanguBot', 'Perplexity-User', 'PerplexityBot',
82 'Scrapy', 'SemrushBot-OCOB', 'Timpibot', 'TikTokSpider', 'YouBot'
83);
84
85my $blocked_agents = app->config->{robots}->{block_user_agents} // $DEFAULT_BLOCKED_AGENTS;
86my $blocked_agents_re = length($blocked_agents) ? qr/(?i:$blocked_agents)/ : undef;
87
88# robots.txt for the ones that read it. A reverse proxy that mounts derekovecs
89# below a path has to serve the site wide /robots.txt itself, crawlers only
90# ask for it at the root of the host - this one is for installations that own
91# their host name, and does no harm anywhere else.
92my $ROBOTS_TXT = join("\n",
93 '# Every URL with parameters is a result computed for one word, and the',
94 '# vocabulary is large enough that following them is an endless crawl.',
95 'User-agent: *',
96 'Disallow: /*?',
97 'Crawl-delay: 10',
98 '',
99 '# Collecting training data for language models is not what this service is',
100 '# here for. Please use the corpus itself, https://www.ids-mannheim.de/dereko',
101 (map { "User-agent: $_" }
102 (split(/\|/, $DEFAULT_BLOCKED_AGENTS),
103 # tokens that are only understood in robots.txt, there is no crawler of
104 # that name to match against
105 'Google-Extended', 'Applebot-Extended')),
106 'Disallow: /',
107 ''
108);
109
110hook before_dispatch => sub {
111 my $c = shift;
112
113 if ($c->req->url->path->to_string =~ m@(?:^|/)robots\.txt$@) {
114 return $c->render(data => $ROBOTS_TXT, format => 'txt');
115 }
116
117 # Only the entry page is worth indexing, everything else is computed.
118 if (length($c->req->url->query->to_string)) {
119 $c->res->headers->header('X-Robots-Tag' => 'noindex, nofollow');
120 $c->stash(noindex => 1);
121 }
122
123 my $ua = $c->req->headers->user_agent;
124 if ($blocked_agents_re && defined($ua) && $ua =~ $blocked_agents_re) {
125 $c->app->log->info('Blocked crawler ' . $c->remote_addr . ' "' . $ua . '" ' . $c->req->url);
126 $c->res->headers->header('X-Robots-Tag' => 'noindex, nofollow');
127 return $c->render(
128 text => "This service computes an answer for every request and is not a source of training data.\n"
129 . "The corpus behind it is documented at https://www.ids-mannheim.de/dereko\n",
130 status => 403,
131 format => 'txt'
132 );
133 }
134};
135
Marc Kupietzaf436142026-07-30 14:40:10 +0900136# Neighbourhood results are the biggest objects the server keeps around (one
137# hash plus one vector array per neighbour), so this cache is deliberately
138# small. It is per worker, i.e. the total footprint is
139# workers * max_keys * result size. Set cache => {max_keys => 0} to switch the
140# cache off.
141my $CACHE_MAX_KEYS = app->config->{cache}->{max_keys} // 50;
142my $cache = Mojo::Cache->new(max_keys => $CACHE_MAX_KEYS);
143
Marc Kupietz6ed81872016-04-27 14:04:04 +0200144my %marked;
Marc Kupietzc053d972019-01-10 10:41:51 +0100145my $title="";
Marc Kupietz793413b2016-04-02 21:48:57 +0200146my $training_args="";
147
Marc Kupietz846e1492024-09-11 19:10:31 +0200148sub usage {
Marc Kupietzc82b15f2022-07-19 17:36:27 +0200149 print STDERR <<EOF;
150non-server mode usage: MOJO_CONFIG=`pwd`/example.conf $0 [-h] [-d <file>]
151-h : this (help) message
152-d file : dump binary vecs as ascii text to <file>
153
154server-mode invocation:
155
156MOJO_CONFIG=`pwd`/example.conf morbo $0
157EOF
158 exit;
159}
Marc Kupietz6ed81872016-04-27 14:04:04 +0200160
Marc Kupietz846e1492024-09-11 19:10:31 +0200161getopts('d:D:Gil:p:m:n:M:Ch') or usage();
162
Marc Kupietzaf436142026-07-30 14:40:10 +0900163configure_cache(
164 no_cache => $opt_C,
165 collocators_max_keys => app->config->{cache}->{collocators_max_keys},
166 profiles_max_keys => app->config->{cache}->{profiles_max_keys}
167);
168
Marc Kupietz6ed81872016-04-27 14:04:04 +0200169if($opt_M) {
Marc Kupietzed930212016-04-27 15:42:38 +0200170 open my $handle, '<:encoding(UTF-8)', $opt_M
171 or die "Can't open '$opt_M' for reading: $!";
172 while(<$handle>) {
Marc Kupietz6ed81872016-04-27 14:04:04 +0200173 foreach my $mw (split /\s+/) {
174 $marked{$mw}=1
175 }
176 }
Marc Kupietzed930212016-04-27 15:42:38 +0200177 close($handle);
Marc Kupietz6ed81872016-04-27 14:04:04 +0200178}
Marc Kupietza5b90152016-03-15 17:39:19 +0100179
Marc Kupietze8e3ded2020-07-13 17:53:56 +0200180my $vecs_name = (@ARGV > 0 && -r $ARGV[0] ? $ARGV[0] : $DEFAULT_VECS);
181init_net($vecs_name, $opt_n, ($opt_i? 1 : 0), 1);
182if(open(FILE, "$vecs_name.args")) {
183 $training_args = <FILE>;
Marc Kupietz2cb667e2016-03-10 09:44:12 +0100184}
Marc Kupietze8e3ded2020-07-13 17:53:56 +0200185close(FILE);
186$title = fname2corpusname($vecs_name);
Marc Kupietzdc22b982015-10-09 09:19:34 +0200187
Marc Kupietze8e3ded2020-07-13 17:53:56 +0200188my $have_sprofiles = load_sprofiles($vecs_name);
Marc Kupietza51dcfa2018-03-19 16:22:05 +0100189
Marc Kupietzc0d41872021-02-25 16:33:22 +0100190if (app->config->{w2v}->{merge}) {
191 $opt_m = app->config->{w2v}->{merge};
192}
193
Marc Kupietza2e64502016-04-27 09:53:51 +0200194if($opt_m) {
195 $mergedEnd = mergeVectors($opt_m);
Marc Kupietzc053d972019-01-10 10:41:51 +0100196 $title = "<span class=\"merged\">" . $title . "</span> vs. " . fname2corpusname($opt_m);
Marc Kupietza2e64502016-04-27 09:53:51 +0200197}
198
Marc Kupietze5568a02018-12-20 11:42:02 +0100199
Marc Kupietz43ee87e2016-04-25 10:50:08 +0200200if($opt_d) { # -d: dump vecs and exit
201 dump_vecs($opt_d);
202 exit;
203}
204
Marc Kupietzfa194262018-06-05 09:39:32 +0200205if($opt_D) { # -D: dump vecs for numpy and exit
206 dump_for_numpy($opt_D);
207 exit;
208}
209
Marc Kupietze8e3ded2020-07-13 17:53:56 +0200210#my $daemon = Mojo::Server::Daemon->new(
211# app => app,
212# listen => ['http://'.($opt_l ? $opt_l : '*').":$opt_p"]
213#);
Marc Kupietza5b90152016-03-15 17:39:19 +0100214
Marc Kupietz5c3887d2016-04-28 08:53:35 +0200215if($opt_G) {
216 print "Filtering garbage\n";
Marc Kupietz56dbabe2019-12-10 14:33:57 +0100217 filter_garbage();
Marc Kupietzc0d41872021-02-25 16:33:22 +0100218 print "Finished filtering garbage\n";
Marc Kupietz5c3887d2016-04-28 08:53:35 +0200219}
220
Marc Kupietz554aff52017-11-09 14:42:09 +0100221get '*/js/*' => sub {
Marc Kupietzffef9302017-11-07 15:58:01 +0100222 my $c = shift;
223 my $url = $c->req->url;
Marc K20476c72021-03-11 12:18:01 +0100224 $url =~ s@/derekovecs/@/@g;
Marc Kupietzc48d3902026-08-21 12:13:45 +0200225 $c->app->log->debug("GET: " . $url);
Marc Kupietz56dbabe2019-12-10 14:33:57 +0100226 $c->reply->static($url);
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100227} => 'js';
Marc Kupietzffef9302017-11-07 15:58:01 +0100228
Marc Kupietza9270572018-03-17 15:17:07 +0100229get '*/css/*' => sub {
230 my $c = shift;
231 my $url = $c->req->url;
232 $url =~ s@/derekovecs/@/@g;
Marc Kupietzc48d3902026-08-21 12:13:45 +0200233 $c->app->log->debug("GET: " . $url);
Marc Kupietza9270572018-03-17 15:17:07 +0100234 $c->reply->static($url);
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100235} => 'css';
Marc Kupietza9270572018-03-17 15:17:07 +0100236
Marc Kupietzc053d972019-01-10 10:41:51 +0100237sub fname2corpusname {
238 ($_) = @_;
239 s@.*/@@;
Marc Kupietz86b50292019-02-17 21:03:59 +0100240 s@\.en@-en@;
Marc Kupietzc053d972019-01-10 10:41:51 +0100241 s@\..*@@;
242 return $_;
243}
244
Marc Kupietzcb43e492019-12-03 10:07:53 +0100245sub getWord {
246 ($_) = @_;
247 if ($_ =~ /^\d+/) {
248 return $_;
249 } else {
250 return getWordNumber($_);
251 }
252}
253
Marc Kupietza51dcfa2018-03-19 16:22:05 +0100254
Marc Kupietz7662ce12026-07-30 14:48:10 +0900255post '*/getVecsByRanks' => sub {
256 my $self = shift;
257 my $vec = getVecs($self->req->json);
258 $self->render(json => $vec);
259};
260
261post '/getVecsByRanks' => sub {
Marc Kupietz66bfd952017-12-11 09:59:45 +0100262 my $self = shift;
263 my $vec = getVecs($self->req->json);
264 $self->render(json => $vec);
265};
Marc Kupietz56dbabe2019-12-10 14:33:57 +0100266
Marc Kupietzf6080012021-03-12 09:14:42 +0100267any '*/getCollocationAssociation' => sub {
268 my $self = shift;
269 $self->render(data => getCollocationAssociation($self, getWord($self->param("w") ? $self->param("w") : $self->req->json), getWord($self->param("c"))), format=>'json');
270} => 'getCollocationAssociation';
271
272any '/getCollocationAssociation' => sub {
273 my $self = shift;
274 $self->render(data => getCollocationAssociation($self, getWord($self->param("w") ? $self->param("w") : $self->req->json), getWord($self->param("c"))), format=>'json');
275} => 'getCollocationAssociation1';
276
Marc Kupietze13a3552018-01-25 08:48:34 +0100277any '*/getClassicCollocators' => sub {
Marc Kupietze243efd2018-01-11 22:19:24 +0100278 my $self = shift;
Marc Kupietz3eeec652024-11-18 18:30:04 +0100279 $self->render(data => getClassicCollocatorsCached($self, getWord($self->param("w") ? $self->param("w",) : $self->req->json),
280 $COMPARE_TO), format=>'json');
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100281} => 'getClassicCollocators1';
Marc Kupietz56dbabe2019-12-10 14:33:57 +0100282
Marc Kupietze13a3552018-01-25 08:48:34 +0100283any '/getClassicCollocators' => sub {
Marc Kupietze243efd2018-01-11 22:19:24 +0100284 my $self = shift;
Marc Kupietz3eeec652024-11-18 18:30:04 +0100285 $self->render(data => getClassicCollocatorsCached($self, getWord($self->param("w") ? $self->param("w") : $self->req->json),
286 $COMPARE_TO), format=>'json');
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100287} => 'getClassicCollocators';
Marc Kupietz56dbabe2019-12-10 14:33:57 +0100288
Marc Kupietzd7760b42019-02-21 09:01:44 +0100289any '/getBiggestVocabDistances' => sub {
290 my $self = shift;
291 $self->render(data => getBiggestMergedDifferences(), format=>'json');
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100292} => 'getBiggestVocabDistances1';
Marc Kupietzd7760b42019-02-21 09:01:44 +0100293
294any '*/getBiggestVocabDistances' => sub {
295 my $self = shift;
296 $self->render(data => getBiggestMergedDifferences(), format=>'json');
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100297} => 'getBiggestVocabDistances';
Marc Kupietzd7760b42019-02-21 09:01:44 +0100298
Marc Kupietz33c79d32019-08-02 15:11:23 +0200299any '*/getPosWiseW2VCollocators' => sub {
300 my $self = shift;
Marc Kupietz0ab97392024-12-10 16:16:32 +0100301 my $format = $self->param("format") || 'json';
302 $self->render(data => getPosWiseW2VCollocators($self->param("w"),
Marc Kupietz33c79d32019-08-02 15:11:23 +0200303 ($self->param("max")? $self->param("max") : 200),
304 ($self->param("cutoff")? $self->param("cutoff") :750000),
Marc Kupietz0ab97392024-12-10 16:16:32 +0100305 ($self->param("threshold")? $self->param("threshold") : 0.2),
306 $format),
307 format=>$format);
Marc Kupietz33c79d32019-08-02 15:11:23 +0200308};
309
310any '/getPosWiseW2VCollocators' => sub {
311 my $self = shift;
Marc Kupietz0ab97392024-12-10 16:16:32 +0100312 my $format = $self->param("format") || 'json';
313 $self->render(data => getPosWiseW2VCollocators($self->param("w"),
Marc Kupietz33c79d32019-08-02 15:11:23 +0200314 ($self->param("max")? $self->param("max") : 200),
315 ($self->param("cutoff")? $self->param("cutoff") : 750000),
Marc Kupietz0ab97392024-12-10 16:16:32 +0100316 ($self->param("threshold")? $self->param("threshold") : 0.2),
317 $format),
318 format=>$format);
Marc Kupietz33c79d32019-08-02 15:11:23 +0200319};
320
Marc Kupietza51dcfa2018-03-19 16:22:05 +0100321any '*/getSimilarProfiles' => sub {
322 my $self = shift;
Marc Kupietzcb43e492019-12-03 10:07:53 +0100323 $self->render(data => getSimilarProfilesCached($self, getWord($self->param("w") ? $self->param("w") : $self->req->json)), format=>'json');
Marc Kupietza51dcfa2018-03-19 16:22:05 +0100324};
325
Marc Kupietzc987fa82018-03-21 12:14:25 +0100326any '/getSimilarProfiles' => sub {
327 my $self = shift;
Marc Kupietzcb43e492019-12-03 10:07:53 +0100328 $self->render(data => getSimilarProfilesCached($self, getWord($self->param("w") ? $self->param("w") : $self->req->json)), format=>'json');
Marc Kupietzc987fa82018-03-21 12:14:25 +0100329};
330
Marc Kupietz9f301572020-04-06 18:29:16 +0200331any '*/getWord' => sub {
332 my $self = shift;
333 my $w = $self->param("w");
334 my $rank = getWord($w);
335 my $status = 200;
336 if ($rank <= 0) {
337 $rank = -1;
338 $status = 404;
339 }
340 $self->render(data => encode_json({word => $w, frequencyRank => $rank}), format => 'json', status => $status);
341};
342
343any '/getWord' => sub {
344 my $self = shift;
345 my $w = $self->param("w");
346 my $rank = getWord($w);
347 my $status = 200;
348 if ($rank <= 0) {
349 $rank = -1;
350 $status = 404;
351 }
352 $self->render(data => encode_json({word => $w, frequencyRank => $rank}), format => 'json', status => $status);
353};
354
Marc Kupietz98ed1c02019-08-02 15:05:37 +0200355any '/getSimilarity' => sub {
356 my $self = shift;
357 my $w1 = $self->param("w1");
358 my $w2 = $self->param("w2");
359 $self->render(data => cos_similarity_as_json($w1, $w2), format=>'json');
360};
361
362any '*/getSimilarity' => sub {
363 my $self = shift;
364 my $w1 = $self->param("w1");
365 my $w2 = $self->param("w2");
366 $self->render(data => cos_similarity_as_json($w1, $w2), format=>'json');
367};
368
Marc Kupietz3576c622023-11-05 08:51:58 +0100369any '*/getDowntimeCalendar' => sub {
370 my $self = shift;
371 my $calendar = getDowntimeCalendar($DOWNTIME_CALENDAR_URL);
372 $self->render(data => $calendar, format=>'text');
373};
374
375any '/getDowntimeCalendar' => sub {
376 my $self = shift;
377 my $calendar = getDowntimeCalendar($DOWNTIME_CALENDAR_URL);
378 $self->render(data => $calendar, format=>'text');
379};
380
Marc Kupietzb36bc742023-11-05 17:46:11 +0100381any '/getVocabSize' => sub {
382 my $self = shift;
383 $self->render(data => getVocabSize(), format=>'json');
384};
385
386any '*/getVocabSize' => sub {
387 my $self = shift;
388 $self->render(data => getVocabSize(), format=>'json');
389};
390
Marc Kupietzaf708c22023-11-05 11:20:20 +0100391any '/getVersion' => sub {
392 my $self = shift;
Marc Kupietzb36bc742023-11-05 17:46:11 +0100393 $self->render(data => to_json($VERSION), format=>'json');
Marc Kupietzaf708c22023-11-05 11:20:20 +0100394};
395
396any '*/getVersion' => sub {
397 my $self = shift;
Marc Kupietzb36bc742023-11-05 17:46:11 +0100398 $self->render(data => to_json($VERSION), format=>'json');
Marc Kupietzaf708c22023-11-05 11:20:20 +0100399};
400
Marc Kupietzdea505e2023-11-05 11:42:36 +0100401any '/getModelName' => sub {
402 my $self = shift;
Marc Kupietzb36bc742023-11-05 17:46:11 +0100403 $self->render(data => to_json($title), format=>'json');
Marc Kupietzdea505e2023-11-05 11:42:36 +0100404};
405
406any '*/getModelName' => sub {
407 my $self = shift;
Marc Kupietzb36bc742023-11-05 17:46:11 +0100408 $self->render(data => to_json($title), format=>'json');
Marc Kupietzdea505e2023-11-05 11:42:36 +0100409};
410
Marc Kupietzdf3d4b52017-11-29 16:57:27 +0100411get '*/img/*' => sub {
412 my $c = shift;
413 my $url = $c->req->url;
414 $url =~ s@/derekovecs@@g;
Marc Kupietzc48d3902026-08-21 12:13:45 +0200415 $c->app->log->debug("GET: " . $url);
Marc Kupietzdf3d4b52017-11-29 16:57:27 +0100416 $c->reply->static($url);
417};
418
Marc Kupietzdc22b982015-10-09 09:19:34 +0200419get '/' => sub {
420 my $c = shift;
Marc Kupietzc48d3902026-08-21 12:13:45 +0200421 $c->app->log->debug("get: ".$c->req->url->to_abs);
Marc Kupietzdc22b982015-10-09 09:19:34 +0200422 my $word=$c->param('word');
Marc Kupietz2da2a812019-02-21 14:17:35 +0100423 my $no_nbs=$c->param('n') || ($opt_m? 50 : 100);
Marc Kupietz44bee3c2016-02-25 16:26:29 +0100424 my $no_iterations=$c->param('N') || 2000;
Marc Kupietzd4227392016-03-01 16:45:12 +0100425 my $perplexity=$c->param('perplexity') || 20;
Marc Kupietzc4d62f82016-03-01 11:04:24 +0100426 my $epsilon=$c->param('epsilon') || 5;
Marc Kupietzd7aea722016-03-02 11:59:12 +0100427 my $som=$c->param('som') || 0;
Marc Kupietza2e64502016-04-27 09:53:51 +0200428 my $searchBaseVocabFirst=$c->param('sbf') || 0;
Marc Kupietz6d9a6782016-03-23 17:25:25 +0100429 my $sort=$c->param('sort') || 0;
Marc Kupietzc469f3b2017-11-13 14:07:36 +0100430 my $csv=$c->param('csv') || 0;
Marc Kupietzb613b052016-04-28 14:11:59 +0200431 my $json=$c->param('json') || 0;
Marc Kupietzdb2dc7e2017-12-02 12:04:03 +0100432 my $cutoff=$c->param('cutoff') || 500000;
Marc Kupietzd91212f2017-11-13 10:05:09 +0100433 my $dedupe=$c->param('dedupe') || 0;
Marc Kupietzac707b32018-12-20 11:36:38 +0100434 my $nosp=$c->param('nosp') || 0;
Marc Kupietz6b2975c2016-03-18 21:59:33 +0100435 my $res;
Marc Kupietz7b2cbeb2016-02-25 11:22:00 +0100436 my @lists;
Marc Kupietz6b2975c2016-03-18 21:59:33 +0100437 my @collocations;
Marc Kupietzcddc8482019-12-04 08:57:33 +0100438 if(defined($word) && $word !~ /^\s*$/) {
Marc Kupietz7bc85fd2016-02-24 11:42:41 +0100439 $c->inactivity_timeout(300);
Marc Kupietz44bee3c2016-02-25 16:26:29 +0100440 $word =~ s/\s+/ /g;
Marc Kupietz3082fd02019-01-09 14:54:06 +0100441 if($opt_m && $word !~ /\|/) {
442 $word .= "|$word";
443 }
Marc Kupietz44bee3c2016-02-25 16:26:29 +0100444 for my $w (split(' *\| *', $word)) {
Marc Kupietz3082fd02019-01-09 14:54:06 +0100445 if($opt_m) {
446 if($searchBaseVocabFirst) {
447 $searchBaseVocabFirst=0;
448 } else {
449 $searchBaseVocabFirst=1;
450 }
451 }
Marc Kupietzaf436142026-07-30 14:40:10 +0900452 my $key = join("\x1c", $w, $cutoff, $no_nbs, $sort, $dedupe, $searchBaseVocabFirst, $nosp);
453 $res = $opt_C ? undef : $cache->get($key);
454 if (defined $res) {
Marc Kupietzc48d3902026-08-21 12:13:45 +0200455 $c->app->log->debug("Getting $w results from cache");
Marc Kupietza5b90152016-03-15 17:39:19 +0100456 } else {
Marc Kupietzc48d3902026-08-21 12:13:45 +0200457 $c->app->log->debug('Looking for neighbours of '.$w);
Marc Kupietz15987412017-11-07 15:56:58 +0100458 if($opt_i) {
Marc Kupietzac707b32018-12-20 11:36:38 +0100459 $res = get_neighbours(encode("iso-8859-1", $w), $no_nbs, $sort, $searchBaseVocabFirst, $cutoff, $dedupe, $nosp);
Marc Kupietz15987412017-11-07 15:56:58 +0100460 } else {
Marc Kupietzac707b32018-12-20 11:36:38 +0100461 $res = get_neighbours($w, $no_nbs, $sort, $searchBaseVocabFirst, $cutoff, $dedupe, $nosp);
Marc Kupietz15987412017-11-07 15:56:58 +0100462 }
Marc Kupietzaf436142026-07-30 14:40:10 +0900463 $cache->set($key => $res) unless $opt_C;
Marc Kupietza5b90152016-03-15 17:39:19 +0100464 }
Marc Kupietz6b2975c2016-03-18 21:59:33 +0100465 push(@lists, $res->{paradigmatic});
Marc Kupietz15987412017-11-07 15:56:58 +0100466 }
467 }
Marc Kupietz56844a22019-08-02 15:12:19 +0200468
Marc Kupietz000ad862016-02-26 14:59:12 +0100469 $word =~ s/ *\| */ | /g;
Marc Kupietzb613b052016-04-28 14:11:59 +0200470 if($json) {
471 return $c->render(json => {word => $word, list => \@lists, collocators=>$res->{syntagmatic}});
Marc Kupietzc469f3b2017-11-13 14:07:36 +0100472 } elsif($csv) {
473 my $csv_data="";
474 for (my $i=0; $i <= $no_nbs; $i++) {
475 $csv_data .= $res->{paradigmatic}->[$i]->{word} . ", ";
476 }
477 for (my $i=0; $i < $no_nbs; $i++) {
478 $csv_data .= $res->{syntagmatic}->[$i]->{word} . ", ";
479 }
480 chop $csv_data;
481 chop $csv_data;
Marc Kupietz56dbabe2019-12-10 14:33:57 +0100482 $csv_data .= "\n";
Marc Kupietzc469f3b2017-11-13 14:07:36 +0100483 return $c->render(text=>$csv_data);
Marc Kupietzb613b052016-04-28 14:11:59 +0200484 } else {
Marc Kupietzd7760b42019-02-21 09:01:44 +0100485 my $distantWords="";
486 if(!defined($word) || $word !~ /^\s*$/) {
487 $distantWords = getBiggestMergedDifferences();
488 }
Marc Kupietz9ae184c2023-11-04 18:21:54 +0100489 $c->render(
490 template => "index",
491 title => $title,
492 word => $word,
493 distantWords => $distantWords,
494 cutoff => $cutoff,
495 no_nbs => $no_nbs,
496 no_iterations => $no_iterations,
497 epsilon => $epsilon,
498 perplexity => $perplexity,
499 show_som => $som,
500 searchBaseVocabFirst => $searchBaseVocabFirst,
501 sort => $sort,
502 training_args => $training_args,
503 mergedEnd => $mergedEnd,
504 haveSProfiles => $have_sprofiles,
505 dedupe => $dedupe,
506 marked => \%marked,
507 lists => \@lists,
Marc Kupietzeacc63f2023-11-06 15:39:13 +0100508 collocators => $res->{syntagmatic},
509 version => $VERSION,
Marc Kupietz49c5cf02024-08-06 10:30:44 +0200510 korap_url => $KORAP_URL,
Marc Kupietz9ae184c2023-11-04 18:21:54 +0100511 );
Marc Kupietzb613b052016-04-28 14:11:59 +0200512 }
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100513} => "paradigmaticAndSyntagmaticNbs";
Marc Kupietzdc22b982015-10-09 09:19:34 +0200514
Marc Kupietz30ca4342017-11-22 21:21:20 +0100515helper(bitvec2window => sub {
516 my ($self, $n) = @_;
517 my $str = unpack("B32", pack("N", $n));
518 $str =~ s/^\d{22}//;
519 $str =~ s/^(\d{5})/$1x/;
520 $str =~ s/0/ยท/g;
521 $str =~ s/1/+/g;
522 return $str;
523 });
524
Marc Kupietz6017daf2022-05-19 09:22:49 +0200525if(app->config->{Piwik} && app->config->{Piwik}->{url}) {
Marc Kupietz3e3e3262022-04-12 23:11:45 +0200526 hook(
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100527 after_render => sub {
528 my $c = shift;
529
530 # Only track valid routes
531 my $route = $c->current_route or return;
532
533 # This won't forward personalized information
534 my $hash = {
Marc Kupietz251de9f2020-01-14 16:12:05 +0100535 action_url => $c->req->url->to_abs,
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100536 action_name => $route,
Marc Kupietz251de9f2020-01-14 16:12:05 +0100537 ua => $c->req->headers->user_agent,
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100538 urlref => '',
539 send_image => 0,
540 dnt => 0,
Marc Kupietz251de9f2020-01-14 16:12:05 +0100541 cip => $c->remote_addr,
542 lang => $c->req->headers->accept_language,
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100543 uid => $c->random_string('piwik_rand_id')
544 };
Marc Kupietz251de9f2020-01-14 16:12:05 +0100545 # $c->app->log->info("PIWIK: counting " . $hash->{action_url} . "\nremote:" . $c->remote_addr);
546 # $c->app->log->info("PIWIK: tag " . $c->piwik_tag);
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100547
548 # Send track
549 $c->piwik->api_p(Track => $hash)->wait;
Marc Kupietz251de9f2020-01-14 16:12:05 +0100550
551 # $c->app->log->info("PIWIK: counted.");
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100552 }
Marc Kupietz3e3e3262022-04-12 23:11:45 +0200553 );
554}
Marc Kupietz1b856fa2019-12-07 23:01:43 +0100555
Marc Kupietzbf9bac02022-04-11 21:16:47 +0200556app->renderer->paths([app->home->rel_file('../templates')]);
Marc Kupietze8e3ded2020-07-13 17:53:56 +0200557app->start;
558#$daemon->run;
Marc Kupietz95104512019-12-05 10:13:05 +0100559# app->start;
Marc Kupietzdc22b982015-10-09 09:19:34 +0200560
Marc Kupietz95104512019-12-05 10:13:05 +0100561# exit;