This commit is contained in:
Salvo 'LtWorf' Tomaselli 2015-07-14 11:01:35 +02:00
parent 98ac364dc7
commit 73dd14d9dd
10 changed files with 209 additions and 193 deletions

View File

@ -38,7 +38,7 @@ tests_path = 'test/'
def readfile(fname):
'''Reads a file as string and returns its content'''
fd = open(fname,encoding='utf-8')
fd = open(fname, encoding='utf-8')
expr = fd.read()
fd.close()
return expr

View File

@ -151,6 +151,3 @@ class user_interface (object):
str(e)
))
return r

View File

@ -77,7 +77,9 @@ class TokenizerException (Exception):
class ParserException (Exception):
pass
class CallableString(str):
'''
This is a string. However it is also callable.
@ -94,7 +96,8 @@ class CallableString(str):
context is a dictionary where to
each name is associated the relative relation
'''
return eval(self,context)
return eval(self, context)
class node (object):
@ -136,16 +139,16 @@ class node (object):
u"'%s' is not a valid relation name" % self.name)
return
#Expression from right to left, searching for binary operators
#this means that binary operators have lesser priority than
#unary operators.
#It finds the operator with lesser priority, uses it as root of this
#(sub)tree using everything on its left as left parameter (so building
#a left subtree with the part of the list located on left) and doing
#the same on right.
#Since it searches for strings, and expressions into parenthesis are
#within sub-lists, they won't be found here, ensuring that they will
#have highest priority.
# Expression from right to left, searching for binary operators
# this means that binary operators have lesser priority than
# unary operators.
# It finds the operator with lesser priority, uses it as root of this
# (sub)tree using everything on its left as left parameter (so building
# a left subtree with the part of the list located on left) and doing
# the same on right.
# Since it searches for strings, and expressions into parenthesis are
# within sub-lists, they won't be found here, ensuring that they will
# have highest priority.
for i in range(len(expression) - 1, -1, -1):
if expression[i] in b_operators: # Binary operator
self.kind = BINARY
@ -318,7 +321,7 @@ def tokenize(expression):
items = [] # List for the tokens
#This is a state machine. Initial status is determined by the starting of the
# This is a state machine. Initial status is determined by the starting of the
# expression. There are the following statuses:
#
# relation: this is the status if the expressions begins with something else than an
@ -327,7 +330,8 @@ def tokenize(expression):
# unary operator: this status is more complex, since it will be followed by a parameter AND a
# sub-expression.
# sub-expression: this status is entered when finding a '(' and will be exited when finding a ')'.
# means that the others open must be counted to determine which close is the right one.'''
# means that the others open must be counted to determine which close is
# the right one.
expression = expression.strip() # Removes initial and endind spaces
state = 0

View File

@ -101,7 +101,7 @@ class relation (object):
elif self.header.sharedAttributes(other.header) == len(self.header):
return other.projection(self.header)
raise Exception('Relations differ: [%s] [%s]' % (
','.join(self.header) , ','.join(other.header)
','.join(self.header), ','.join(other.header)
))
def selection(self, expr):
@ -111,7 +111,9 @@ class relation (object):
newt.header = header(self.header)
for i in self.content:
# Fills the attributes dictionary with the values of the tuple
attributes = {attr:i[j].autocast() for j, attr in enumerate(self.header)}
attributes = {attr: i[j].autocast()
for j, attr in enumerate(self.header)
}
try:
if eval(expr, attributes):
@ -129,7 +131,8 @@ class relation (object):
if (self.__class__ != other.__class__)or(self.header.sharedAttributes(other.header) != 0):
raise Exception(
'Unable to perform product on relations with colliding attributes')
'Unable to perform product on relations with colliding attributes'
)
newt = relation()
newt.header = header(self.header + other.header)
@ -277,7 +280,7 @@ class relation (object):
# Creating the header with all the fields, done like that because order is
# needed
h = (i for i in other.header if i not in shared)
newt.header = header(chain(self.header,h))
newt.header = header(chain(self.header, h))
# Shared ids of self
sid = self.header.getAttributesId(shared)
@ -296,13 +299,13 @@ class relation (object):
match = match and (i[sid[k]] == j[oid[k]])
if match:
item = chain(i,(j[l] for l in noid))
item = chain(i, (j[l] for l in noid))
newt.content.add(tuple(item))
added = True
# If it didn't partecipate, adds it
if not added:
item = chain(i,repeat('---',len(noid)))
item = chain(i, repeat('---', len(noid)))
newt.content.add(tuple(item))
return newt
@ -319,7 +322,7 @@ class relation (object):
# Creating the header with all the fields, done like that because order is
# needed
h = (i for i in other.header if i not in shared)
newt.header = header(chain(self.header,h))
newt.header = header(chain(self.header, h))
# Shared ids of self
sid = self.header.getAttributesId(shared)
@ -374,7 +377,7 @@ class relation (object):
col += 1
res = ""
for f,attr in enumerate(self.header):
for f, attr in enumerate(self.header):
res += "%s" % (attr.ljust(2 + m_len[f]))
for r in self.content:
@ -398,12 +401,11 @@ class relation (object):
affected = 0
attributes = {}
keys = dic.keys() # List of headers to modify
f_ids = self.header.getAttributesId(
keys) # List of indexes corresponding to keys
f_ids = self.header.getAttributesId(keys)
# new_content=[] #New content of the relation
for i in self.content:
for j,attr in enumerate(self.header):
for j, attr in enumerate(self.header):
attributes[attr] = i[j].autocast()
if eval(expr, attributes): # If expr is true, changing the tuple
@ -445,7 +447,7 @@ class relation (object):
deleting all the tuples that make expr true.
Returns the number of affected rows.'''
#Not necessary self._make_writable()
# Not necessary self._make_writable()
l = len(self.content)
self._readonly = False
@ -459,7 +461,7 @@ class header(tuple):
It is used within relations to know if requested operations are accepted'''
# Since relations are mutalbe we explicitly block hashing them
def __new__ (cls, fields):
def __new__(cls, fields):
return super(header, cls).__new__(cls, tuple(fields))
def __init__(self, *args, **kwargs):
@ -481,7 +483,7 @@ class header(tuple):
params is a dictionary of {old:new} names
'''
attrs = list(self)
for old,new in params.items():
for old, new in params.items():
if not is_valid_relation_name(new):
raise Exception('%s is not a valid attribute name' % new)
try:

View File

@ -25,9 +25,11 @@ import re
RELATION_NAME_REGEXP = r'^[_a-zA-Z]+[_a-zA-Z0-9]*$'
class rstring (str):
'''String subclass with some custom methods'''
def autocast(self):
'''
Returns the automatic cast for this

View File

@ -87,7 +87,10 @@ if __name__ == "__main__":
try:
from relational_gui import maingui, guihandler, about, surveyForm
except:
print ("Module relational_gui is missing.\nPlease install relational package.",file=sys.stderr)
print (
"Module relational_gui is missing.\nPlease install relational package.",
file=sys.stderr
)
sys.exit(3)
about.version = version
@ -103,7 +106,7 @@ if __name__ == "__main__":
form = guihandler.relForm(ui)
ui.setupUi(form)
f = QtGui.QFont()
size = f.pointSize();
size = f.pointSize()
if sys.platform.startswith('win'):
winFont = 'Cambria'
symbolFont = 'Segoe UI Symbol'
@ -113,19 +116,21 @@ if __name__ == "__main__":
symbolFont = f.family()
increment = 2
ui.lstHistory.setFont(QtGui.QFont(winFont,size+increment))
ui.txtMultiQuery.setFont(QtGui.QFont(winFont,size+increment))
ui.txtQuery.setFont(QtGui.QFont(winFont,size+increment))
ui.groupOperators.setFont(QtGui.QFont(winFont,size+increment))
ui.lstHistory.setFont(QtGui.QFont(winFont, size + increment))
ui.txtMultiQuery.setFont(QtGui.QFont(winFont, size + increment))
ui.txtQuery.setFont(QtGui.QFont(winFont, size + increment))
ui.groupOperators.setFont(QtGui.QFont(winFont, size + increment))
ui.cmdClearMultilineQuery.setFont(QtGui.QFont(symbolFont))
ui.cmdClearQuery.setFont(QtGui.QFont(symbolFont))
form.restore_settings()
m = enumerate(map(os.path.isfile, files))
invalid = ' '.join((files[i[0]] for i in (filter(lambda x: not x[1], m))))
invalid = ' '.join(
(files[i[0]] for i in (filter(lambda x: not x[1], m)))
)
if invalid:
print ("%s: not a file" % invalid,file=sys.stderr)
print ("%s: not a file" % invalid, file=sys.stderr)
printhelp(12)
if len(files):
form.loadRelation(files)
@ -137,7 +142,10 @@ if __name__ == "__main__":
try:
import relational_readline.linegui
except:
print ("Module relational_readline is missing.\nPlease install relational-cli package.",file=sys.stderr)
print (
"Module relational_readline is missing.\nPlease install relational-cli package.",
file=sys.stderr
)
sys.exit(3)
relational_readline.linegui.version = version
relational_readline.linegui.main(files)

View File

@ -80,7 +80,8 @@ class creatorForm(QtWidgets.QDialog):
self.table.setItem(1, 1, i11)
def create_relation(self):
h = (self.table.item(0, i).text() for i in range(self.table.columnCount()))
h = (self.table.item(0, i).text()
for i in range(self.table.columnCount()))
try:
header = relation.header(h)

View File

@ -79,7 +79,8 @@ class relForm(QtWidgets.QMainWindow):
query = self.ui.txtQuery.text()
try:
result = optimizer.optimize_all(query, self.user_interface.relations)
result = optimizer.optimize_all(
query, self.user_interface.relations)
self.ui.txtQuery.setText(result)
except Exception as e:
self.error(e)
@ -106,7 +107,7 @@ class relForm(QtWidgets.QMainWindow):
if self.multiline:
return self._run_multiline()
#Single line query
# Single line query
query = self.ui.txtQuery.text()
res_rel = self.ui.txtResult.text() # result relation's name
@ -149,7 +150,7 @@ class relForm(QtWidgets.QMainWindow):
self.ui.table.addTopLevelItem(item)
# Sets columns
for i,attr in enumerate(rel.header):
for i, attr in enumerate(rel.header):
self.ui.table.headerItem().setText(i, attr)
self.ui.table.resizeColumnToContents(
i) # Must be done in order to avoid too small columns
@ -201,7 +202,7 @@ class relForm(QtWidgets.QMainWindow):
self.user_interface.set_relation(i.text(), result)
self.updateRelations()
def error(self,exception):
def error(self, exception):
print (exception)
QtWidgets.QMessageBox.information(
None, QtWidgets.QApplication.translate("Form", "Error"),
@ -217,12 +218,13 @@ class relForm(QtWidgets.QMainWindow):
"Form", "Insert the name for the new relation"),
QtWidgets.QLineEdit.Normal, ''
)
if res[1] == False:# or len(res[0]) == 0:
if res[1] == False: # or len(res[0]) == 0:
return None
name = res[0]
if not rtypes.is_valid_relation_name(name):
r = QtWidgets.QApplication.translate(
"Form", str("Wrong name for destination relation: %s." % name)
"Form", str(
"Wrong name for destination relation: %s." % name)
)
QtWidgets.QMessageBox.information(
self, QtWidgets.QApplication.translate("Form", "Error"), r
@ -256,8 +258,9 @@ class relForm(QtWidgets.QMainWindow):
def restore_settings(self):
# self.settings.value('session_name','default').toString()
self.setMultiline(self.settings.value('multiline','false')=='true')
self.ui.txtMultiQuery.setPlainText(self.settings.value('multiline/query',''))
self.setMultiline(self.settings.value('multiline', 'false') == 'true')
self.ui.txtMultiQuery.setPlainText(
self.settings.value('multiline/query', ''))
try:
self.restoreGeometry(self.settings.value('maingui/geometry'))
self.restoreState(self.settings.value('maingui/windowState'))
@ -299,7 +302,7 @@ class relForm(QtWidgets.QMainWindow):
continue
try:
self.user_interface.load(f,name)
self.user_interface.load(f, name)
except Exception as e:
self.error(e)
continue

View File

@ -24,7 +24,6 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from relational import maintenance
class surveyForm (QtWidgets.QWidget):
'''This class is the form used for the survey, needed to intercept the events.