"""
# This file is part of GNU Dico.
# Copyright (C) 2008-2010, 2012-2015, 2023 Wojciech Polak
#
# GNU Dico is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GNU Dico is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Dico. If not, see .
"""
import re
import hashlib
import socket
from typing import Match
from django.conf import settings
try:
from django.core.urlresolvers import reverse
except ImportError:
from django.urls import reverse
from django.core.cache import cache
from django.shortcuts import render
from django.utils.encoding import force_bytes
from django.utils.translation import gettext as _
from .dicoclient import dicoclient
try:
from wikitrans import wiki2html
except ImportError:
wiki2html = None
print('WARNING: wikitrans is not installed.')
CACHE_KEY_PREFIX = 'dicoweb/'
def onerror(err: str, dfl=None) -> dict:
try:
act = settings.ONERROR[err]
except KeyError:
act = dfl
return act
def index(request):
page = {
'robots': 'index',
}
selects = {}
server: str
mtc = {}
result = {}
markup_style = 'default'
port = 2628
sid = request.COOKIES.get('dicoweb_sid', '')
request.session.set_expiry(0)
accept_lang = request.META.get('HTTP_ACCEPT_LANGUAGE', '').split(',')
for i, lang in enumerate(accept_lang):
accept_lang[i] = lang.split(';')[0]
if 'server' in request.session:
server = request.session['server']
else:
server = settings.DICT_SERVERS[0]
server = request.GET.get('server', server)
if server not in settings.DICT_SERVERS:
server = settings.DICT_SERVERS[0]
request.session['server'] = server
if len(settings.DICT_SERVERS) > 1:
selects['sv'] = HtmlOptions(settings.DICT_SERVERS, server)
key = hashlib.md5(force_bytes(
'%s/%s' % (sid, server.encode('ascii', 'backslashreplace'))))
sid = key.hexdigest()
query_type = 'search'
if 'define' in request.GET:
query_type = 'define'
else:
query_type = 'search'
database = request.GET.get('db', '*')
strategy = request.GET.get('strategy', '.')
key_databases = f'{CACHE_KEY_PREFIX}databases/{server}'
key_strategies = f'{CACHE_KEY_PREFIX}strategies/{server}'
databases = cache.get(key_databases)
strategies = cache.get(key_strategies)
if server.find(':') != -1:
s = server.split(':', 1)
server = s[0]
port = int(s[1])
if not databases or not strategies:
dc = dicoclient.DicoClient()
try:
dc.open(server, port)
dc.timeout = settings.DICT_TIMEOUT
databases = dc.show_databases()['databases']
strategies = dc.show_strategies()['strategies']
dc.close()
except (socket.timeout, socket.error, dicoclient.DicoNotConnectedError):
return render(request, 'index.html', {'selects': selects})
cache.set(key_databases, databases, timeout=86400)
cache.set(key_strategies, strategies, timeout=86400)
for s in strategies:
s[1] = _(s[1])
databases.insert(0, ['!', _('First match')])
databases.insert(0, ['*', _('All')])
strategies.insert(0, ['.', _('Default')])
selects['db'] = HtmlOptions(databases, database)
selects['st'] = HtmlOptions(strategies, strategy)
q = request.GET.get('q', '')
if 'q' in request.GET and q != '':
langkey = '*'
if database == '*':
langkey = ','.join(accept_lang)
key = hashlib.md5(force_bytes(
'%s:%d/%s/%s/%s/%s/%s' % (server, port, langkey, query_type,
database, strategy,
q.encode('ascii', 'backslashreplace'))))
key = key.hexdigest()
result = cache.get(CACHE_KEY_PREFIX + key)
if not result:
try:
dc = dicoclient.DicoClient()
dc.timeout = settings.DICT_TIMEOUT
dc.open(server, port)
dc.option('MIME')
if database == '*' and 'lang' in dc.server_capas:
dc.option('LANG', ': ' + ' '.join(accept_lang))
if 'markup-wiki' in dc.server_capas:
if dc.option('MARKUP', 'wiki'):
markup_style = 'wiki'
if database == 'dbinfo':
result = dc.show_info(q)
elif query_type == 'define':
result = dc.define(database, q)
else:
result = dc.match(database, strategy, q)
dc.close()
result['markup_style'] = markup_style
cache.set(CACHE_KEY_PREFIX + key, result, timeout=3600)
except (socket.timeout, socket.error,
dicoclient.DicoNotConnectedError):
return render(request, 'index.html', {'selects': selects})
# get last match results
if sid and query_type == 'search':
cache.set(f'{CACHE_KEY_PREFIX}{sid}/last_match', key, timeout=3600)
else:
key = cache.get(f'{CACHE_KEY_PREFIX}{sid}/last_match')
if key is not None:
mtc = cache.get(CACHE_KEY_PREFIX + key)
if not mtc:
mtc = result
mtc['dbnames'] = {}
if 'matches' in mtc:
for m in mtc['matches']:
for d in databases:
if d[0] == m:
mtc['dbnames'][m] = d[1]
break
if database == 'dbinfo':
q = ''
if q != '':
page['title'] = q + ' - '
page['robots'] = 'noindex,nofollow'
if 'definitions' in result:
rx1 = re.compile('{+(.*?)}+', re.DOTALL)
for i, df in enumerate(result['definitions']):
if 'content-type' in df:
if df['content-type'].startswith('text/x-wiki') and wiki2html:
lang = df['x-wiki-language'] \
if 'x-wiki-language' in df else 'en'
wikiparser = wiki2html.HtmlWiktionaryMarkup(
text=df['desc'],
html_base='?q=',
lang=lang)
wikiparser.parse()
df['desc'] = str(wikiparser)
df['format_html'] = True
elif df['content-type'].startswith('text/html'):
df['format_html'] = True
elif df['content-type'].startswith('text/'):
df['format_html'] = False
else:
act = onerror(
'UNSUPPORTED_CONTENT_TYPE',
{
'action': 'replace',
'message': 'Article cannot be displayed due to unsupported content type'
})
if act['action'] == 'delete':
del result['definitions'][i]
result['count'] -= 1
elif act['action'] == 'replace':
df['desc'] = act['message']
df['format_html'] = (
act['format_html'] if 'format_html' in act
else False)
result['definitions'][i] = df
elif act['action'] == 'display':
df['format_html'] = (
act['format_html'] if 'format_html' in act
else False)
result['definitions'][i] = df
elif settings.DEBUG:
raise AssertionError("""
Article cannot be displayed due to unsupported content type (%s).
Additionally, ONERROR['UNSUPPORTED_CONTENT_TYPE'] has unsupported value (%s).
""" % (df['content-type'], act))
else:
del result['definitions'][i]
result['count'] -= 1
else:
df['desc'] = re.sub('_(.*?)_', '\\1', df['desc'])
df['desc'] = re.sub(rx1, __subs1, df['desc'])
if result['count'] == 0:
result = {'error': 552, 'msg': 'No match'}
return render(request, 'index.html', {'page': page,
'q': q,
'mtc': mtc,
'result': result,
'selects': selects, })
def opensearch(request):
url_query = request.build_absolute_uri(reverse('index'))
url_media = request.build_absolute_uri(settings.MEDIA_URL)
return render(request, 'opensearch.xml', {
'url_query': url_query,
'url_media': url_media},
content_type='application/xml')
def __subs1(match: Match) -> str:
s = re.sub(r' +', ' ', match.group(1))
return '%s' \
% (s.replace('\n', ''), s.replace('\n', ''), s)
class HtmlOptions:
"""
Utility class to help generate HTML options for select element
"""
def __init__(self, lst=None, value=''):
self.lst = lst or []
self.value = value
def html(self) -> str:
buf = []
for opt in self.lst:
if len(opt) == 2:
if not opt[1]:
opt[1] = opt[0]
try:
opt[1] = opt[1].decode('utf-8')
except Exception:
pass
if opt[0] == self.value:
buf.append('' % (
opt[0], opt[1]))
else:
buf.append('' % (opt[0],
opt[1]))
else:
if opt == self.value:
buf.append(
'' % (opt, opt))
else:
buf.append('' % (opt, opt))
return '\n'.join(buf)