Initial commit

This commit is contained in:
Manuel Cortez 2018-10-28 01:00:38 +00:00
commit 1109efdca9
18 changed files with 651 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*__pycache__

0
__init__.py Normal file
View File

6
admin.py Normal file
View 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
View 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
View 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)

View 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)))

View 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
View File

11
models.py Executable file
View 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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

184
static/js/popper.min.js vendored Normal file
View 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
View 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(/&quot/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
View 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 &#169; 2018. Manuel Cortez.</div>
</div>
</footer>
</body>
</html>

View 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 "&laquo; 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 &raquo;" %}</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
View 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
View 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))