Initial commit
This commit is contained in:
commit
1109efdca9
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*__pycache__
|
0
__init__.py
Normal file
0
__init__.py
Normal file
6
admin.py
Normal file
6
admin.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
8
apps.py
Normal file
8
apps.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class SentenceFinderConfig(AppConfig):
|
||||||
|
name = 'find_phrase'
|
10
forms.py
Executable file
10
forms.py
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
class SearchForm(forms.Form):
|
||||||
|
""" Handles the creation of a basic search form so users can type the words they would like to earch in the database.
|
||||||
|
Edit the language field for adding more languages, just follow the examples provided."""
|
||||||
|
|
||||||
|
search_term = forms.CharField(label="Search words")
|
||||||
|
language = forms.ChoiceField(label="Language", choices=(("rus", "Russian"), ("eng", "English"), ("spa", "Spanish")), required=True)
|
45
management/commands/import_sentences.py
Normal file
45
management/commands/import_sentences.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
""" management command for importing sentences from the Tatoeba's sentences CSV file to the database engine."""
|
||||||
|
import csv
|
||||||
|
# if using django in debug mode this will come on handy
|
||||||
|
from django import db
|
||||||
|
db.reset_queries()
|
||||||
|
|
||||||
|
from itertools import islice
|
||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
from sentence_finder.models import phrase
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Re-imports phrases from the Tatoeva database. It is needed to have the file sentences.csv in the root of Django (where the file manage.py is located)'
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('languages', type=str, help='Comma separated languages (ISO 639-3) to import to db (example: spa,rus,eng')
|
||||||
|
|
||||||
|
def get_data(self, languages):
|
||||||
|
""" Helper function to retrieve data from the csv file in a generator, so memory usage will not be a problem.
|
||||||
|
row[1] contains the language code in ISO 639-3. """
|
||||||
|
with open("sentences.csv", "r") as csvfile:
|
||||||
|
for row in csv.reader(csvfile, delimiter="\t"):
|
||||||
|
if row[1] in languages:
|
||||||
|
yield phrase(phrase=row[2], language=row[1])
|
||||||
|
|
||||||
|
def bulk_create_iter(self, iterable, batch_size=10000):
|
||||||
|
"""Bulk create supporting generators. Returns only count of created objects."""
|
||||||
|
created = 0
|
||||||
|
while True:
|
||||||
|
objects = phrase.objects.bulk_create(islice(iterable, batch_size))
|
||||||
|
created += len(objects)
|
||||||
|
if not objects:
|
||||||
|
break
|
||||||
|
return created
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
"""Command processor. Receive all arguments and call to the appropiate helper functions."""
|
||||||
|
languages = options["languages"].split(",")
|
||||||
|
self.stdout.write("Extracting phrases for the following languages: {langs}".format(langs=languages))
|
||||||
|
self.stdout.write("Removing data from database...")
|
||||||
|
# we must delete all previous data as there are no way of Insert_of_update when adding rows in bulk.
|
||||||
|
phrase.objects.all().delete()
|
||||||
|
self.stdout.write("Importing new data...")
|
||||||
|
data = self.get_data(languages)
|
||||||
|
self.stdout.write(str(self.bulk_create_iter(data, 10000)))
|
22
migrations/0001_initial.py
Normal file
22
migrations/0001_initial.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 2.1.1 on 2018-10-27 20:06
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='phrase',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('language', models.CharField(max_length=3)),
|
||||||
|
('phrase', models.CharField(max_length=65536)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
migrations/__init__.py
Normal file
0
migrations/__init__.py
Normal file
11
models.py
Executable file
11
models.py
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class phrase(models.Model):
|
||||||
|
""" Default model created for a sentence. It contains just language and the sentence itself.
|
||||||
|
This model is not exposed to the admin interface because it is not intended to be filled that way, see management/commands/import.phrases for more details."""
|
||||||
|
|
||||||
|
language = models.CharField(max_length=3)
|
||||||
|
phrase = models.CharField(max_length=64*1024)
|
7
static/css/bootstrap.min.css
vendored
Normal file
7
static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
7
static/js/bootstrap.min.js
vendored
Normal file
7
static/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
static/js/jquery.js
vendored
Normal file
2
static/js/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
184
static/js/popper.min.js
vendored
Normal file
184
static/js/popper.min.js
vendored
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||||
|
typeof define === 'function' && define.amd ? define(factory) :
|
||||||
|
(global.Popper = factory());
|
||||||
|
}(this, (function () { 'use strict';
|
||||||
|
|
||||||
|
Object.assign||Object.defineProperty(Object,'assign',{enumerable:!1,configurable:!0,writable:!0,value:function value(a){if(a===void 0||null===a)throw new TypeError('Cannot convert first argument to object');var b=Object(a);for(var c=1;c<arguments.length;c++){var d=arguments[c];if(void 0!==d&&null!==d){d=Object(d);var e=Object.keys(d);for(var f=0,g=e.length;f<g;f++){var h=e[f],j=Object.getOwnPropertyDescriptor(d,h);void 0!==j&&j.enumerable&&(b[h]=d[h]);}}}return b}});
|
||||||
|
|
||||||
|
if(!window.requestAnimationFrame){var lastTime=0,vendors=['ms','moz','webkit','o'];for(var x=0;x<vendors.length&&!window.requestAnimationFrame;++x)window.requestAnimationFrame=window[vendors[x]+'RequestAnimationFrame'],window.cancelAnimationFrame=window[vendors[x]+'CancelAnimationFrame']||window[vendors[x]+'CancelRequestAnimationFrame'];window.requestAnimationFrame||(window.requestAnimationFrame=function(a){var b=new Date().getTime(),c=Math.max(0,16-(b-lastTime)),d=window.setTimeout(function(){a(b+c);},c);return lastTime=b+c,d}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(a){clearTimeout(a);});}
|
||||||
|
|
||||||
|
function findIndex(a,b,c){var d=a.filter(function(e){return e[b]===c})[0];return a.indexOf(d)}
|
||||||
|
|
||||||
|
function getOffsetParent(a){var b=a.offsetParent;return b&&'BODY'!==b.nodeName?b:window.document.documentElement}
|
||||||
|
|
||||||
|
function getStyleComputedProperty(a,b){if(1!==a.nodeType)return[];var c=window.getComputedStyle(a,null);return c[b]}
|
||||||
|
|
||||||
|
function getParentNode(a){return a.parentNode||a.host}
|
||||||
|
|
||||||
|
function getScrollParent(a){return a===window.document?window.document.body.scrollTop?window.document.body:window.document.documentElement:-1!==['scroll','auto'].indexOf(getStyleComputedProperty(a,'overflow'))||-1!==['scroll','auto'].indexOf(getStyleComputedProperty(a,'overflow-x'))||-1!==['scroll','auto'].indexOf(getStyleComputedProperty(a,'overflow-y'))?a===window.document.body?getScrollParent(getParentNode(a)):a:getParentNode(a)?getScrollParent(getParentNode(a)):a}
|
||||||
|
|
||||||
|
function getOffsetRect(a){var b=window.document.documentElement,c=void 0;return c=a===b?{width:Math.max(b.clientWidth,window.innerWidth||0),height:Math.max(b.clientHeight,window.innerHeight||0),left:0,top:0}:{width:a.offsetWidth,height:a.offsetHeight,left:a.offsetLeft,top:a.offsetTop},c.right=c.left+c.width,c.bottom=c.top+c.height,c}
|
||||||
|
|
||||||
|
function isFixed(a){return a!==window.document.body&&(!('fixed'!==getStyleComputedProperty(a,'position'))||(getParentNode(a)?isFixed(getParentNode(a)):a))}
|
||||||
|
|
||||||
|
function getPosition(a){var b=getOffsetParent(a),c=isFixed(b);return c?'fixed':'absolute'}
|
||||||
|
|
||||||
|
function getBoundingClientRect(a){var b=a.getBoundingClientRect();return{left:b.left,top:b.top,right:b.right,bottom:b.bottom,width:b.right-b.left,height:b.bottom-b.top}}
|
||||||
|
|
||||||
|
function getOffsetRectRelativeToCustomParent(a,b){var c=2<arguments.length&&void 0!==arguments[2]&&arguments[2],d=3<arguments.length&&void 0!==arguments[3]&&arguments[3],e=getBoundingClientRect(a),f=getBoundingClientRect(b);if(c&&!d){var j=getScrollParent(b);f.top+=j.scrollTop,f.bottom+=j.scrollTop,f.left+=j.scrollLeft,f.right+=j.scrollLeft;}var g={top:e.top-f.top,left:e.left-f.left,bottom:e.top-f.top+e.height,right:e.left-f.left+e.width,width:e.width,height:e.height},h=b.scrollTop,i=b.scrollLeft;return g.top+=h,g.bottom+=h,g.left+=i,g.right+=i,g}
|
||||||
|
|
||||||
|
function getBoundaries(a,b,c){var d={},e=getOffsetParent(a),f=getScrollParent(a);if('window'===c){var g=window.document.body,h=window.document.documentElement,i=Math.max(g.scrollHeight,g.offsetHeight,h.clientHeight,h.scrollHeight,h.offsetHeight),j=Math.max(g.scrollWidth,g.offsetWidth,h.clientWidth,h.scrollWidth,h.offsetWidth);d={top:0,right:j,bottom:i,left:0};}else if('viewport'===c){var _g=getOffsetRect(e),_h=getPosition(a);d='fixed'===_h?{top:0,right:window.document.documentElement.clientWidth,bottom:window.document.documentElement.clientHeight,left:0}:{top:0-_g.top,right:window.document.documentElement.clientWidth-_g.left,bottom:window.document.documentElement.clientHeight-_g.top,left:0-_g.left};}else d=f===c||'scrollParent'===c?getOffsetRectRelativeToCustomParent(f,e):getOffsetRectRelativeToCustomParent(c,e);if(e.contains(f)){var _g2=f.scrollLeft,_h2=f.scrollTop;d.right+=_g2,d.bottom+=_h2;}return d.left+=b,d.top+=b,d.right-=b,d.bottom-=b,d}
|
||||||
|
|
||||||
|
function getOuterSizes(a){var b=a.style.display,c=a.style.visibility;a.style.display='block',a.style.visibility='hidden';var d=window.getComputedStyle(a),e=parseFloat(d.marginTop)+parseFloat(d.marginBottom),f=parseFloat(d.marginLeft)+parseFloat(d.marginRight),g={width:a.offsetWidth+f,height:a.offsetHeight+e};return a.style.display=b,a.style.visibility=c,g}
|
||||||
|
|
||||||
|
function getPopperClientRect(a){return Object.assign({},a,{right:a.left+a.width,bottom:a.top+a.height})}
|
||||||
|
|
||||||
|
function getSupportedPropertyName(a){var b=['','ms','webkit','moz','o'];for(var c=0;c<b.length;c++){var d=b[c]?b[c]+a.charAt(0).toUpperCase()+a.slice(1):a;if('undefined'!=typeof window.document.body.style[d])return d}return null}
|
||||||
|
|
||||||
|
function isFunction(a){return a&&'[object Function]'==={}.toString.call(a)}
|
||||||
|
|
||||||
|
function isModifierRequired(a,b,c){return!!a.filter(function(d){if(d.name===c)return!0;return d.name!==b&&!1}).length}
|
||||||
|
|
||||||
|
function isNumeric(a){return''!==a&&!isNaN(parseFloat(a))&&isFinite(a)}
|
||||||
|
|
||||||
|
function isTransformed(a){return a!==window.document.body&&('none'!==getStyleComputedProperty(a,'transform')||(getParentNode(a)?isTransformed(getParentNode(a)):a))}
|
||||||
|
|
||||||
|
function runModifiers(a,b,c){var d=void 0===c?a:a.slice(0,findIndex(a,'name',c));return d.forEach(function(e){e.enabled&&isFunction(e.function)&&(b=e.function(b,e));}),b}
|
||||||
|
|
||||||
|
function setStyle(a,b){Object.keys(b).forEach(function(c){var d='';-1!==['width','height','top','right','bottom','left'].indexOf(c)&&isNumeric(b[c])&&(d='px'),a.style[c]=b[c]+d;});}
|
||||||
|
|
||||||
|
var Utils = {findIndex:findIndex,getBoundaries:getBoundaries,getBoundingClientRect:getBoundingClientRect,getOffsetParent:getOffsetParent,getOffsetRectRelativeToCustomParent:getOffsetRectRelativeToCustomParent,getOuterSizes:getOuterSizes,getPopperClientRect:getPopperClientRect,getPosition:getPosition,getScrollParent:getScrollParent,getStyleComputedProperty:getStyleComputedProperty,getSupportedPropertyName:getSupportedPropertyName,isFixed:isFixed,isFunction:isFunction,isModifierRequired:isModifierRequired,isNumeric:isNumeric,isTransformed:isTransformed,runModifiers:runModifiers,setStyle:setStyle};
|
||||||
|
|
||||||
|
var nativeHints=['native code','[object MutationObserverConstructor]'];var isNative = (function(a){return nativeHints.some(function(b){return-1<(a||'').toString().indexOf(b)})});
|
||||||
|
|
||||||
|
var longerTimeoutBrowsers=['Edge','Trident','Firefox']; var timeoutDuration=0;for(var a=0;a<longerTimeoutBrowsers.length;a+=1)if(0<=navigator.userAgent.indexOf(longerTimeoutBrowsers[a])){timeoutDuration=1;break}function microtaskDebounce(a){var b=!1,c=0,d=document.createElement('span'),e=new MutationObserver(function(){a(),b=!1;});return e.observe(d,{childList:!0}),function(){b||(b=!0,d.textContent=''+c,c+=1);}}function taskDebounce(a){var b=!1;return function(){b||(b=!0,setTimeout(function(){b=!1,a();},timeoutDuration));}}var supportsNativeMutationObserver=isNative(window.MutationObserver);var debounce = supportsNativeMutationObserver?microtaskDebounce:taskDebounce;
|
||||||
|
|
||||||
|
function getOffsets(a,b,c,d){d=d.split('-')[0];var e={};e.position=a.position;var f='fixed'===e.position,g=a.isParentTransformed,h=getOffsetParent(f&&g?c:b),i=getOffsetRectRelativeToCustomParent(c,h,f,g),j=getOuterSizes(b);return-1===['right','left'].indexOf(d)?(e.left=i.left+i.width/2-j.width/2,e.top='top'===d?i.top-j.height:i.bottom):(e.top=i.top+i.height/2-j.height/2,e.left='left'===d?i.left-j.width:i.right),e.width=j.width,e.height=j.height,{popper:e,reference:i}}
|
||||||
|
|
||||||
|
function setupEventListeners(a,b,c,d){if(c.updateBound=d,window.addEventListener('resize',c.updateBound,{passive:!0}),'window'!==b.boundariesElement){var e=getScrollParent(a);(e===window.document.body||e===window.document.documentElement)&&(e=window),e.addEventListener('scroll',c.updateBound,{passive:!0}),c.scrollElement=e;}}
|
||||||
|
|
||||||
|
function removeEventListeners(a,b){return window.removeEventListener('resize',b.updateBound),b.scrollElement&&b.scrollElement.removeEventListener('scroll',b.updateBound),b.updateBound=null,b.scrollElement=null,b}
|
||||||
|
|
||||||
|
function sortModifiers(c,d){if(c.order<d.order)return-1;return c.order>d.order?1:0}
|
||||||
|
|
||||||
|
function applyStyle(a){var b={position:a.offsets.popper.position},c=Math.round(a.offsets.popper.left),d=Math.round(a.offsets.popper.top),e=getSupportedPropertyName('transform');return a.instance.options.gpuAcceleration&&e?(b[e]='translate3d('+c+'px, '+d+'px, 0)',b.top=0,b.left=0):(b.left=c,b.top=d),Object.assign(b,a.styles),setStyle(a.instance.popper,b),a.instance.popper.setAttribute('x-placement',a.placement),a.offsets.arrow&&setStyle(a.arrowElement,a.offsets.arrow),a}function applyStyleOnLoad(a,b,c){return b.setAttribute('x-placement',c.placement),c}
|
||||||
|
|
||||||
|
function arrow(a,b){var c=b.element;if('string'==typeof c&&(c=a.instance.popper.querySelector(c)),!c)return a;if(!a.instance.popper.contains(c))return console.warn('WARNING: `arrowElement` must be child of its popper element!'),a;if(!isModifierRequired(a.instance.modifiers,'arrow','keepTogether'))return console.warn('WARNING: keepTogether modifier is required by arrow modifier in order to work, be sure to include it before arrow!'),a;var d={},e=a.placement.split('-')[0],f=getPopperClientRect(a.offsets.popper),g=a.offsets.reference,h=-1!==['left','right'].indexOf(e),i=h?'height':'width',j=h?'top':'left',k=h?'left':'top',l=h?'bottom':'right',m=getOuterSizes(c)[i];g[l]-m<f[j]&&(a.offsets.popper[j]-=f[j]-(g[l]-m)),g[j]+m>f[l]&&(a.offsets.popper[j]+=g[j]+m-f[l]);var n=g[j]+g[i]/2-m/2,o=n-getPopperClientRect(a.offsets.popper)[j];return o=Math.max(Math.min(f[i]-m,o),0),d[j]=o,d[k]='',a.offsets.arrow=d,a.arrowElement=c,a}
|
||||||
|
|
||||||
|
function getOppositePlacement(a){var b={left:'right',right:'left',bottom:'top',top:'bottom'};return a.replace(/left|right|bottom|top/g,function(c){return b[c]})}
|
||||||
|
|
||||||
|
function getOppositeVariation(a){if('end'===a)return'start';return'start'===a?'end':a}
|
||||||
|
|
||||||
|
function flip(a,b){if(a.flipped&&a.placement===a.originalPlacement)return a;var c=getBoundaries(a.instance.popper,b.padding,b.boundariesElement),d=a.placement.split('-')[0],e=getOppositePlacement(d),f=a.placement.split('-')[1]||'',g=[];return g='flip'===b.behavior?[d,e]:b.behavior,g.forEach(function(h,i){if(d!==h||g.length===i+1)return a;d=a.placement.split('-')[0],e=getOppositePlacement(d);var j=getPopperClientRect(a.offsets.popper),k='left'===d&&Math.floor(j.left)<Math.floor(c.left)||'right'===d&&Math.floor(j.right)>Math.floor(c.right)||'top'===d&&Math.floor(j.top)<Math.floor(c.top)||'bottom'===d&&Math.floor(j.bottom)>Math.floor(c.bottom),l=-1!==['top','bottom'].indexOf(d),m=!!b.flipVariations&&(l&&'start'===f&&Math.floor(j.left)<Math.floor(c.left)||l&&'end'===f&&Math.floor(j.right)>Math.floor(c.right)||!l&&'start'===f&&Math.floor(j.top)<Math.floor(c.top)||!l&&'end'===f&&Math.floor(j.bottom)>Math.floor(c.bottom));(k||m)&&(a.flipped=!0,k&&(d=g[i+1]),m&&(f=getOppositeVariation(f)),a.placement=d+(f?'-'+f:''),a.offsets.popper=getOffsets(a.instance.state,a.instance.popper,a.instance.reference,a.placement).popper,a=runModifiers(a.instance.modifiers,a,'flip'));}),a}
|
||||||
|
|
||||||
|
function keepTogether(a){var b=getPopperClientRect(a.offsets.popper),c=a.offsets.reference,d=Math.floor,e=a.placement.split('-')[0];return-1===['top','bottom'].indexOf(e)?(b.bottom<d(c.top)&&(a.offsets.popper.top=d(c.top)-b.height),b.top>d(c.bottom)&&(a.offsets.popper.top=d(c.bottom))):(b.right<d(c.left)&&(a.offsets.popper.left=d(c.left)-b.width),b.left>d(c.right)&&(a.offsets.popper.left=d(c.right))),a}
|
||||||
|
|
||||||
|
function offset(a,b){var c=a.placement,d=a.offsets.popper,e=void 0;return isNumeric(b.offset)?e=[b.offset,0]:(e=b.offset.split(' '),e=e.map(function(f,g){var h=f.match(/(\d*\.?\d*)(.*)/),i=+h[1],j=h[2],k=-1!==c.indexOf('right')||-1!==c.indexOf('left');if(1===g&&(k=!k),'%'===j||'%r'===j){var l=getPopperClientRect(a.offsets.reference),m=void 0;return m=k?l.height:l.width,m/100*i}if('%p'===j){var _l=getPopperClientRect(a.offsets.popper),_m=void 0;return _m=k?_l.height:_l.width,_m/100*i}if('vh'===j||'vw'===j){var _l2=void 0;return _l2='vh'===j?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0),_l2/100*i}return'px'===j?+i:+f})),-1===a.placement.indexOf('left')?-1===a.placement.indexOf('right')?-1===a.placement.indexOf('top')?-1!==a.placement.indexOf('bottom')&&(d.left+=e[0],d.top+=e[1]||0):(d.left+=e[0],d.top-=e[1]||0):(d.top+=e[0],d.left+=e[1]||0):(d.top+=e[0],d.left-=e[1]||0),a}
|
||||||
|
|
||||||
|
function preventOverflow(c,d){var e=d.boundariesElement||getOffsetParent(c.instance.popper),f=getBoundaries(c.instance.popper,d.padding,e);d.boundaries=f;var g=d.priority,h=getPopperClientRect(c.offsets.popper),i={left:function left(){var j=h.left;return h.left<f.left&&!shouldOverflowBoundary(c,d,'left')&&(j=Math.max(h.left,f.left)),{left:j}},right:function right(){var j=h.left;return h.right>f.right&&!shouldOverflowBoundary(c,d,'right')&&(j=Math.min(h.left,f.right-h.width)),{left:j}},top:function top(){var j=h.top;return h.top<f.top&&!shouldOverflowBoundary(c,d,'top')&&(j=Math.max(h.top,f.top)),{top:j}},bottom:function bottom(){var j=h.top;return h.bottom>f.bottom&&!shouldOverflowBoundary(c,d,'bottom')&&(j=Math.min(h.top,f.bottom-h.height)),{top:j}}};return g.forEach(function(j){c.offsets.popper=Object.assign(h,i[j]());}),c}function shouldOverflowBoundary(c,d,e){return!!d.escapeWithReference&&(c.flipped&&isSameAxis(c.originalPlacement,e)||!!isSameAxis(c.originalPlacement,e)||!0)}function isSameAxis(c,d){var e=c.split('-')[0],f=d.split('-')[0];return e===f||e===getOppositePlacement(d)}
|
||||||
|
|
||||||
|
function shift(a){var b=a.placement,c=b.split('-')[0],d=b.split('-')[1];if(d){var e=a.offsets.reference,f=getPopperClientRect(a.offsets.popper),g={y:{start:{top:e.top},end:{top:e.top+e.height-f.height}},x:{start:{left:e.left},end:{left:e.left+e.width-f.width}}},h=-1===['bottom','top'].indexOf(c)?'y':'x';a.offsets.popper=Object.assign(f,g[h][d]);}return a}
|
||||||
|
|
||||||
|
function hide(a){if(!isModifierRequired(a.instance.modifiers,'hide','preventOverflow'))return console.warn('WARNING: preventOverflow modifier is required by hide modifier in order to work, be sure to include it before hide!'),a;var b=a.offsets.reference,c=a.instance.modifiers.filter(function(d){return'preventOverflow'===d.name})[0].boundaries;if(b.bottom<c.top||b.left>c.right||b.top>c.bottom||b.right<c.left){if(!0===a.hide)return a;a.hide=!0,a.instance.popper.setAttribute('x-out-of-boundaries','');}else{if(!1===a.hide)return a;a.hide=!1,a.instance.popper.removeAttribute('x-out-of-boundaries');}return a}
|
||||||
|
|
||||||
|
var modifiersFunctions = {applyStyle:applyStyle,arrow:arrow,flip:flip,keepTogether:keepTogether,offset:offset,preventOverflow:preventOverflow,shift:shift,hide:hide};var modifiersOnLoad={applyStyleOnLoad:applyStyleOnLoad};
|
||||||
|
|
||||||
|
var classCallCheck = function (instance, Constructor) {
|
||||||
|
if (!(instance instanceof Constructor)) {
|
||||||
|
throw new TypeError("Cannot call a class as a function");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var createClass = function () {
|
||||||
|
function defineProperties(target, props) {
|
||||||
|
for (var i = 0; i < props.length; i++) {
|
||||||
|
var descriptor = props[i];
|
||||||
|
descriptor.enumerable = descriptor.enumerable || false;
|
||||||
|
descriptor.configurable = true;
|
||||||
|
if ("value" in descriptor) descriptor.writable = true;
|
||||||
|
Object.defineProperty(target, descriptor.key, descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function (Constructor, protoProps, staticProps) {
|
||||||
|
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||||
|
if (staticProps) defineProperties(Constructor, staticProps);
|
||||||
|
return Constructor;
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var get = function get(object, property, receiver) {
|
||||||
|
if (object === null) object = Function.prototype;
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(object, property);
|
||||||
|
|
||||||
|
if (desc === undefined) {
|
||||||
|
var parent = Object.getPrototypeOf(object);
|
||||||
|
|
||||||
|
if (parent === null) {
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
return get(parent, property, receiver);
|
||||||
|
}
|
||||||
|
} else if ("value" in desc) {
|
||||||
|
return desc.value;
|
||||||
|
} else {
|
||||||
|
var getter = desc.get;
|
||||||
|
|
||||||
|
if (getter === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getter.call(receiver);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var set = function set(object, property, value, receiver) {
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(object, property);
|
||||||
|
|
||||||
|
if (desc === undefined) {
|
||||||
|
var parent = Object.getPrototypeOf(object);
|
||||||
|
|
||||||
|
if (parent !== null) {
|
||||||
|
set(parent, property, value, receiver);
|
||||||
|
}
|
||||||
|
} else if ("value" in desc && desc.writable) {
|
||||||
|
desc.value = value;
|
||||||
|
} else {
|
||||||
|
var setter = desc.set;
|
||||||
|
|
||||||
|
if (setter !== undefined) {
|
||||||
|
setter.call(receiver, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
var DEFAULTS={placement:'bottom',gpuAcceleration:!0,modifiers:{shift:{order:100,enabled:!0,function:modifiersFunctions.shift},offset:{order:200,enabled:!0,function:modifiersFunctions.offset,offset:0},preventOverflow:{order:300,enabled:!0,function:modifiersFunctions.preventOverflow,priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,function:modifiersFunctions.keepTogether},arrow:{order:500,enabled:!0,function:modifiersFunctions.arrow,element:'[x-arrow]'},flip:{order:600,enabled:!0,function:modifiersFunctions.flip,behavior:'flip',padding:5,boundariesElement:'viewport'},hide:{order:700,enabled:!0,function:modifiersFunctions.hide},applyStyle:{order:800,enabled:!0,function:modifiersFunctions.applyStyle,onLoad:modifiersOnLoad.applyStyleOnLoad}}};var Popper=function(){function Popper(a,b){var _this=this,c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};return classCallCheck(this,Popper),this.Defaults=DEFAULTS,this.update=debounce(this.update.bind(this)),this.scheduleUpdate=function(){return requestAnimationFrame(_this.update)},this.state={isDestroyed:!1,isCreated:!1},this.reference=a.jquery?a[0]:a,this.popper=b.jquery?b[0]:b,this.options=Object.assign({},DEFAULTS,c),this.modifiers=Object.keys(DEFAULTS.modifiers).map(function(d){return Object.assign({name:d},DEFAULTS.modifiers[d])}),this.modifiers=this.modifiers.map(function(d){var e=c.modifiers&&c.modifiers[d.name]||{},f=Object.assign({},d,e);return f}),c.modifiers&&(this.options.modifiers=Object.assign({},DEFAULTS.modifiers,c.modifiers),Object.keys(c.modifiers).forEach(function(d){if(void 0===DEFAULTS.modifiers[d]){var e=c.modifiers[d];e.name=d,_this.modifiers.push(e);}})),this.modifiers=this.modifiers.sort(sortModifiers),this.modifiers.forEach(function(d){d.enabled&&isFunction(d.onLoad)&&d.onLoad(_this.reference,_this.popper,_this.options,d);}),this.state.position=getPosition(this.reference),this.state.isParentTransformed=isTransformed(this.popper.parentNode),this.update(),setupEventListeners(this.reference,this.options,this.state,this.scheduleUpdate),this}return createClass(Popper,[{key:'update',value:function update(){var a={instance:this,styles:{},flipped:!1};this.state.position=getPosition(this.reference),setStyle(this.popper,{position:this.state.position}),this.state.isDestroyed||(a.placement=this.options.placement,a.originalPlacement=this.options.placement,a.offsets=getOffsets(this.state,this.popper,this.reference,a.placement),a=runModifiers(this.modifiers,a),this.state.isCreated?isFunction(this.state.updateCallback)&&this.state.updateCallback(a):(this.state.isCreated=!0,isFunction(this.state.createCallback)&&this.state.createCallback(a)));}},{key:'onCreate',value:function onCreate(a){return this.state.createCallback=a,this}},{key:'onUpdate',value:function onUpdate(a){return this.state.updateCallback=a,this}},{key:'destroy',value:function destroy(){return this.state.isDestroyed=!0,this.popper.removeAttribute('x-placement'),this.popper.style.left='',this.popper.style.position='',this.popper.style.top='',this.popper.style[getSupportedPropertyName('transform')]='',this.state=removeEventListeners(this.reference,this.state),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}}]),Popper}();Popper.Utils=Utils;
|
||||||
|
|
||||||
|
return Popper;
|
||||||
|
|
||||||
|
})));
|
215
static/js/share-vk.js
Normal file
215
static/js/share-vk.js
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
(function() {
|
||||||
|
if (!window.VK) window.VK = {};
|
||||||
|
if (VK.Share) return;
|
||||||
|
|
||||||
|
var head = document.getElementsByTagName('head')[0],
|
||||||
|
tpl = function(a, b) {return a.replace(/\{(\w+?)\}/g, function(c, d) {return b[d] !== void 0 ? b[d] : ''})};
|
||||||
|
|
||||||
|
VK.Share = {
|
||||||
|
_popups: [],
|
||||||
|
_gens: [],
|
||||||
|
_base_domain: '',
|
||||||
|
_maxCommentLength: 4096,
|
||||||
|
_ge: function(id) {
|
||||||
|
return document.getElementById(id);
|
||||||
|
},
|
||||||
|
button: function(gen, but, index) {
|
||||||
|
if (!gen) gen = {};
|
||||||
|
if (gen === gen.toString()) gen = {url: gen.toString()};
|
||||||
|
if (!gen.url) gen.url = VK.Share._loc;
|
||||||
|
gen.url = (gen.url || '').replace(/"/g, '');
|
||||||
|
|
||||||
|
gen.comment = (gen.comment || '');
|
||||||
|
if (gen.comment.length > this._maxCommentLength) {
|
||||||
|
gen.comment = gen.comment.substr(0, this._maxCommentLength - 3).trim() + '...';
|
||||||
|
}
|
||||||
|
if (!but) but = {type: 'round'};
|
||||||
|
if (but === but.toString()) but = {type: 'round', text: but};
|
||||||
|
if (!but.text) but.text = '\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c';
|
||||||
|
|
||||||
|
var old = true, count_style = 'display: none;';
|
||||||
|
if (index === undefined) {
|
||||||
|
gen.count = 0;
|
||||||
|
gen.shared = (but.type == 'button' || but.type == 'round') ? false : true;
|
||||||
|
this._gens.push(gen);
|
||||||
|
this._popups.push(false);
|
||||||
|
index = this._popups.length - 1;
|
||||||
|
old = false;
|
||||||
|
} else {
|
||||||
|
if ((gen.count = this._gens[index].count) && (but.type == 'button' || but.type == 'round')) {
|
||||||
|
count_style = '';
|
||||||
|
}
|
||||||
|
gen.shared = this._gens[index].shared;
|
||||||
|
this._gens[index] = gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._base_domain) {
|
||||||
|
for (var elem = head.firstChild; elem; elem = elem.nextSibling) {
|
||||||
|
var m;
|
||||||
|
if (elem.tagName && elem.tagName.toLowerCase() == 'script' && (m = elem.src.match(/(https?:\/\/(?:[a-z0-9_\-\.]*\.){0,2}(?:vk\.com|vkontakte\.ru)\/)js\/api\/share\.js(?:\?|$)/))) {
|
||||||
|
this._base_domain = m[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._base_domain = this._base_domain.replace('vkontakte.ru', 'vk.com');
|
||||||
|
if (!this._base_domain) {
|
||||||
|
this._base_domain = '//vk.com/';
|
||||||
|
}
|
||||||
|
if (!old && (but.type == 'button' || but.type == 'round')) {
|
||||||
|
var elem = document.createElement('script');
|
||||||
|
elem.src = this._base_domain + 'share.php?act=count&index=' + index + '&url=' + encodeURIComponent(gen.url);
|
||||||
|
head.appendChild(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
var radius = '-webkit-border-radius:{v};-moz-border-radius:{v};border-radius:{v};',
|
||||||
|
strs = {
|
||||||
|
cssreset: '-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;',
|
||||||
|
domain: this._base_domain,
|
||||||
|
table: '<table cellspacing="0" cellpadding="0" style="position: relative; cursor: pointer; width: auto; line-height: normal; border: 0; direction: ltr;" ',
|
||||||
|
is2x: window.devicePixelRatio >= 2 ? '_2x' : '',
|
||||||
|
i: index,
|
||||||
|
a2: '</a>',
|
||||||
|
td2: '</td>',
|
||||||
|
font: 'font: 400 12px Arial, Helvetica, sans-serif;letter-spacing: 0.1px;text-shadow: none;',
|
||||||
|
radiusl: tpl(radius, {v: '2px 0px 0px 2px'}),
|
||||||
|
radiusr: tpl(radius, {v: '0px 2px 2px 0px'}),
|
||||||
|
radius: tpl(radius, {v: '2px'}),
|
||||||
|
text: but.text,
|
||||||
|
acolor: 'color: #33567f;',
|
||||||
|
bg: 'background: #6287AE;-webkit-transition: background 200ms linear;transition: background 200ms linear;',
|
||||||
|
};
|
||||||
|
strs.td1 = tpl('<td style="vertical-align: middle;{font}{cssreset}">', strs);
|
||||||
|
strs.a = tpl('<a href="{domain}share.php?url=' + encodeURIComponent(gen.url) + '" onmouseup="this._btn=event.button;this.blur();" onclick="return VK.Share.click({i}, this);"', strs);
|
||||||
|
strs.a1 = tpl('{a} style="{acolor}text-decoration: none;{font}line-height: 16px;{cssreset}">', strs);
|
||||||
|
strs.a3 = tpl('{a} style="display: inline-block;text-decoration: none;{cssreset}">', strs);
|
||||||
|
strs.sprite = tpl("background-size: 19px 59px;background-image: url('{domain}images/icons/like_widget{is2x}.png');", strs);
|
||||||
|
strs.logo = tpl('<div style="{sprite}height: 8px;width: 14px;margin: 4px 0 3px;{cssreset}"></div>', strs);
|
||||||
|
|
||||||
|
if (but.type == 'round' || but.type == 'round_nocount' || but.type == 'button' || but.type == 'button_nocount') {
|
||||||
|
return tpl('{table}id="vkshare{i}" onmouseover="VK.Share.change(1, {i});" onmouseout="VK.Share.change(0, {i});" onmousedown="VK.Share.change(2, {i});" onmouseup="VK.Share.change(1, {i});"><tr style="line-height: normal;">{td1}{a} style="border: 0;display: block;{bg}{radiusl}padding: 2px 6px 4px;{cssreset}">{logo}{a2}{td2}{td1}{a} style="color: #FFF;text-decoration: none;border: 0;{bg}{radiusr}{font}line-height: 16px;display:block;padding: 2px 6px 4px 0;height: 15px;{cssreset}">{text}{a2}{td2}'+ ((but.type == 'round' || but.type == 'button') ? '{td1}{a} style="text-decoration: none;{font}line-height: 15px;-webkit-font-smoothing: subpixel-antialiased;' + count_style + '{cssreset}"><div style="{sprite};background-position: 0 -49px;margin: 5px 0 0 4px;width: 5px; height: 10px;position: absolute; z-index:100;{cssreset}"></div><div id="vkshare_cnt{i}" style="border: 1px solid #adbdcc;background: #FFF;font-size:11px;padding: 2px 5px;margin-left: 8px;color: #55677d;z-index: 99;{radius}{cssreset}">' + gen.count + '</div>{a2}{td2}' : '') + '</tr></table>', strs);
|
||||||
|
} else if (but.type == 'link') {
|
||||||
|
return tpl('{table}onmouseover="this.rows[0].cells[1].firstChild.style.textDecoration=\'underline\'" onmouseout="this.rows[0].cells[1].firstChild.style.textDecoration=\'none\'"><tr style="line-height: normal;">{td1}{a1}<img src="{domain}images/icons/share_link{is2x}.png" width="16" height="16" style="vertical-align: top;margin-right: 8px;"/>{a2}{td2}{td1}{a1}{text}{a2}{td2}</tr></table>', strs);
|
||||||
|
} else if (but.type == 'link_noicon') {
|
||||||
|
return tpl('{a3}<span style="{acolor}position: relative;{font}line-height: normal;{cssreset}" onmouseover="this.style.textDecoration=\'underline\'" onmouseout="this.style.textDecoration=\'none\'">{text}</span>{a2}', strs);
|
||||||
|
} else {
|
||||||
|
return tpl('{a3}<span style="position: relative;padding: 0;{cssreset}">{text}</span>{a2}', strs);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
change: function(state, index) {
|
||||||
|
var el = this._ge('vkshare' + index),
|
||||||
|
color = ['#6287AE','#678EB4','#5D7FA4'][state],
|
||||||
|
els = [el.rows[0].cells[0].firstChild, el.rows[0].cells[1].firstChild];
|
||||||
|
for (var i in els) {
|
||||||
|
els[i].style.backgroundColor = color;
|
||||||
|
els[i].style.color = '#FFF';
|
||||||
|
if (state == 2) {
|
||||||
|
els[i].style.paddingTop = '3px';
|
||||||
|
els[i].style.paddingBottom = '3px';
|
||||||
|
} else {
|
||||||
|
els[i].style.paddingTop = '2px';
|
||||||
|
els[i].style.paddingBottom = '4px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
click: function(index, el) {
|
||||||
|
var e = window.event;
|
||||||
|
if (e) {
|
||||||
|
if (!e.which && el._btn) e.which = (el._btn & 1 ? 1 : (el._btn & 2 ? 3 : (el._btn & 4 ? 2 : 0)));
|
||||||
|
if (e.which == 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var details = this._gens[index];
|
||||||
|
if (!details.shared) {
|
||||||
|
VK.Share.count(index, details.count + 1);
|
||||||
|
details.shared = true;
|
||||||
|
}
|
||||||
|
var undefined;
|
||||||
|
if (details.noparse === undefined) {
|
||||||
|
details.noparse = details.title && details.description && details.image;
|
||||||
|
}
|
||||||
|
details.noparse = details.noparse ? 1 : 0;
|
||||||
|
|
||||||
|
var params = {},
|
||||||
|
fields = ['title', 'description', 'image', 'noparse', 'comment'];
|
||||||
|
|
||||||
|
for (var i = 0; i < fields.length; ++i) {
|
||||||
|
if (details[fields[i]]) {
|
||||||
|
params[fields[i]] = details[fields[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var popupName = '_blank',
|
||||||
|
width = 650,
|
||||||
|
height = 610,
|
||||||
|
left = Math.max(0, (screen.width - width) / 2),
|
||||||
|
top = Math.max(0, (screen.height - height) / 2),
|
||||||
|
url = this._base_domain + 'share.php?url=' + encodeURIComponent(details.url),
|
||||||
|
popupParams = 'width='+width+',height='+height+',left='+left+',top='+top+',menubar=0,toolbar=0,location=0,status=0',
|
||||||
|
popup = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var doc_dom = '', loc_hos = '';
|
||||||
|
try {
|
||||||
|
doc_dom = document.domain;
|
||||||
|
loc_hos = location.host;
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
if (doc_dom != loc_hos) {
|
||||||
|
var ua = navigator.userAgent.toLowerCase();
|
||||||
|
if (!/opera/i.test(ua) && /msie/i.test(ua)) {
|
||||||
|
throw 'wont work';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popup = this._popups[index] = window.open('', popupName, popupParams);
|
||||||
|
var text = '<form accept-charset="UTF-8" action="' + url + '" method="POST" id="share_form">';
|
||||||
|
for (var i in params) {
|
||||||
|
text += '<input type="hidden" name="' + i + '" value="' + params[i].toString().replace(/"/g, '&myquot;').replace(/"/ig, '&myquot') + '" />';
|
||||||
|
}
|
||||||
|
text += '</form>';
|
||||||
|
text += '<script type="text/javascript">document.getElementById("share_form").submit()</script>';
|
||||||
|
|
||||||
|
text = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' +
|
||||||
|
'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">' +
|
||||||
|
'<head><meta http-equiv="content-type" content="text/html; charset=windows-1251" /></head>' +
|
||||||
|
'<body>' + text + '</body></html>';
|
||||||
|
popup.document.write(text);
|
||||||
|
popup.focus();
|
||||||
|
} catch (e) { // ie with changed domain.
|
||||||
|
try {
|
||||||
|
if (popup) {
|
||||||
|
popup.close();
|
||||||
|
}
|
||||||
|
url += '?';
|
||||||
|
for (var i in params) {
|
||||||
|
url += encodeURIComponent(i) + '=' + encodeURIComponent(params[i]) + '&';
|
||||||
|
}
|
||||||
|
popup = this._popups[index] = window.open(url, popupName, popupParams);
|
||||||
|
popup.focus();
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
count: function(index, count) {
|
||||||
|
this._gens[index].count = count;
|
||||||
|
var elem = this._ge('vkshare'+index);
|
||||||
|
if (elem) {
|
||||||
|
var counter = this._ge('vkshare_cnt'+index);
|
||||||
|
if (counter) {
|
||||||
|
if (count) counter.innerHTML = count;
|
||||||
|
elem.rows[0].cells[2].firstChild.style.display = count ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
VK.Share._loc = location.toString();
|
||||||
|
} catch(e) {
|
||||||
|
VK.Share._loc = 'http://vk.com/';
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
try{if (window.stManager) stManager.done('api/share.js');}catch(e){}
|
29
templates/base.html
Executable file
29
templates/base.html
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% comment "about" %}
|
||||||
|
This is a base template that can (and should) be overridden or replaced in the project. This file is here so the application will be able to work decoupled from any project, but it should be changed when plugging this application into any other project.
|
||||||
|
{% end comment %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ LANGUAGE_CODE }}">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{% block head_title %}{% trans "Sentence finder" %}{% endblock %}</title>
|
||||||
|
<script src="{% static "js/jquery.js" %}"></script>
|
||||||
|
<script src="{% static "js/popper.min.js" %}"></script>
|
||||||
|
<script src="{% static "js/bootstrap.min.js" %}"></script>
|
||||||
|
<link rel="stylesheet" href="{% static "css/bootstrap.min.css" %}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<h1>{% block title %}Find sentences{% endblock %}</h1>
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</main>
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="copyright pull-left">Copyright © 2018. Manuel Cortez.</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
55
templates/sentence_finder/index.html
Executable file
55
templates/sentence_finder/index.html
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load markdown_deux_tags %}
|
||||||
|
{% block head_title %}{% trans "Find a sentence" %}{% endblock %}
|
||||||
|
{% block title %}{% trans "Find a sentence" %}{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<script src='https://code.responsivevoice.org/responsivevoice.js'></script>
|
||||||
|
<section aria-label="{% trans "Search" %}">
|
||||||
|
<form method="GET">
|
||||||
|
{{ search_form.as_p }}
|
||||||
|
<button>{% trans "Search" %}</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
{% if posts %}
|
||||||
|
<h2>{% trans "Showing results" %}</h2>
|
||||||
|
<script type="text/javascript">
|
||||||
|
responsiveVoice.setDefaultVoice("{{lang}} Female");
|
||||||
|
</script>
|
||||||
|
<ul>
|
||||||
|
{% for post in posts %}
|
||||||
|
<li><a href="javascript:void(0);" onclick="responsiveVoice.speak('{{ post.phrase|addslashes }}')">{{ post.phrase }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<div class="pagination">
|
||||||
|
<span class="step-links">
|
||||||
|
{% if posts.has_previous %}
|
||||||
|
<a href="?page=1{{ extra_params }}">{% trans "« first" %}</a>
|
||||||
|
<a href="?page={{ posts.previous_page_number }}{{ extra_params }}">{% trans "previous" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
<span class="current">
|
||||||
|
Page {{ posts.number }} of {{ posts.paginator.num_pages }}.
|
||||||
|
</span>
|
||||||
|
{% if posts.has_next %}
|
||||||
|
<a href="?page={{ posts.next_page_number }}{{ extra_params }}">{% trans "next" %}</a>
|
||||||
|
<a href="?page={{ posts.paginator.num_pages }}{{ extra_params }}">{% trans "last »" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<h2>How it works?</h2>
|
||||||
|
|
||||||
|
<p>This simple Django application has been created to help me studying russian sentences and later has been extended to add multiple languages. It works by querying a database wich already contains the complete list of sentences found in the <a href="https://tatoeba.org/">Tatoeba project </a> for the supported languages. Right now, the database contains {{total}} sentences, so things may go slow from time to time once a search has been started. Every page will contain a maximum of 100 sentences, and you can go to the next, previous, first or last page, if there are more than 100 results.</p>
|
||||||
|
|
||||||
|
<p>Additionally, you can click in any sentence to hear how it should be spoken.</p>
|
||||||
|
|
||||||
|
<h2>Caveats</h2>
|
||||||
|
|
||||||
|
<ul><li>Searches are made with "like"statements in the DJango ORM. It means that they are not exact and searching a word may find other similar ones (for example, searching читаю may return sentences including similar words like прочитаю, считаю, читают; searching testing may include results like protesting, etc).</li>
|
||||||
|
<li>Text to speech is made with the Google Text to Speech engine and should work in All HTML 5 compatible browsers (Firefox and Chrome in windows have been tested, and Chrome in Android).</li></ul>
|
||||||
|
|
||||||
|
<h2>Resources</h2>
|
||||||
|
|
||||||
|
<p>For this application, I have used the <a href="https://tatoeba.org"/>Tatoeba project</a> for extracting all phrases from their <a href="https://tatoeba.org/downloads">CSV sentence files,</a> and the <a href="https://responsivevoice.org/">responsive voice library</a> for generating a cross platform text to speech result.</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
8
urls.py
Executable file
8
urls.py
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
""" urlpatterns for this application."""
|
||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = "sentence_finder"
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.index, name="find")
|
||||||
|
]
|
41
views.py
Executable file
41
views.py
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""This small application will search sentences in the database. Database has been filled previously with data from the Tatoeba project."""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||||
|
from .models import phrase
|
||||||
|
from .forms import SearchForm
|
||||||
|
|
||||||
|
# Language dictionary. Keys should be language codes (ISO 639-3) (eng for english, spa for spanish, rus for russian and so on)
|
||||||
|
# value should be the language as will be used to generate text to speech responses in the response.js library
|
||||||
|
# (English, Russian, Spanish Latin American, etc).
|
||||||
|
# Obviously, there should be phrases already loaded in the database for all of the following languages.
|
||||||
|
languages = dict(eng="English", rus="Russian", spa="Spanish Latin American")
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
""" Index method. From this method the search of sentences is processed, though if user didn't want to search anything we will show some informative page here."""
|
||||||
|
will_search = False
|
||||||
|
search_term = request.GET.get("search_term")
|
||||||
|
language = request.GET.get("language")
|
||||||
|
if search_term != None:
|
||||||
|
will_search = True
|
||||||
|
# prefill the form with search_term and language just in case these values are different than None.
|
||||||
|
# It is useful so users will see the same data in their form even when the page has been reloaded due to a submit.
|
||||||
|
# if these values are None, the form will be blank.
|
||||||
|
search_form = SearchForm(initial=dict(search_term=search_term, language=language))
|
||||||
|
if will_search == True:
|
||||||
|
# ToDo: perhaps should I look to implement an "exact" search expresion for search_term here?
|
||||||
|
posts = phrase.objects.filter(phrase__contains=search_term, language__exact=language)
|
||||||
|
paginator = Paginator(posts, 100)
|
||||||
|
# get page number from request args. If there is no page argument, then set page to 1.
|
||||||
|
page = request.GET.get('page')
|
||||||
|
if page == None: page = 1
|
||||||
|
posts = paginator.page(page)
|
||||||
|
# Add extra arguments (search_term and language) so other links will be loaded correctly in the paginator.
|
||||||
|
extra_params = ""
|
||||||
|
if search_term != None:
|
||||||
|
extra_params = "&search_term={0}&language={1}".format(search_term, language)
|
||||||
|
if will_search:
|
||||||
|
return render(request, 'sentence_finder/index.html', dict(posts=posts, search_form=search_form, extra_params=extra_params, lang=languages.get(language)))
|
||||||
|
else:
|
||||||
|
return render(request, 'sentence_finder/index.html', dict(total=phrase.objects.count(), search_form=search_form))
|
Loading…
Reference in New Issue
Block a user