Changed about and README to not point to galileo anymore

This commit is contained in:
Salvo 'LtWorf' Tomaselli
2013-12-27 00:31:43 +01:00
parent f31d0dea28
commit 556eecc118
25 changed files with 2212 additions and 2023 deletions

7
README
View File

@@ -2,6 +2,9 @@ To launch the application, run
./relational_gui.py
If it needs some dependencies, check this page:
http://galileo.dmi.unict.it/wiki/relational/doku.php?id=download#install
Language definition is here:
https://github.com/ltworf/relational/wiki/Grammar-and-language
If it needs some dependencies:
Qt4, Python 2.7, either PyQT4 or Pyside.

View File

@@ -37,6 +37,7 @@ rels={}
examples_path = 'samples/'
tests_path = 'test/'
def readfile(fname):
'''Reads a file as string and returns its content'''
fd = open(fname)
@@ -60,6 +61,7 @@ def load_relations():
rels[relname] = relation.relation('%s%s' % (examples_path, i))
print 'done'
def execute_tests():
py_bad = 0
@@ -72,7 +74,6 @@ def execute_tests():
ex_good = 0
ex_tot = 0
for i in os.listdir(tests_path):
if i.endswith('.query'):
q_tot += 1
@@ -112,7 +113,6 @@ def execute_tests():
if ex_bad > 0:
print colorize("Failed tests count: %d" % ex_bad, COLOR_RED)
print colorize("Total results", COLOR_CYAN)
if q_bad + py_bad + ex_bad == 0:
print colorize("No failed tests", COLOR_GREEN)
@@ -137,11 +137,9 @@ def run_exec_test(testname):
expr = readfile('%s%s.exec' % (tests_path, testname))
exec(expr, glob) # Evaluating the expression
expr = readfile('%s%s.result' % (tests_path, testname))
exp_result = eval(expr, rels) # Evaluating the expression
if isinstance(exp_result, dict):
fields_ok = True
@@ -160,6 +158,7 @@ def run_exec_test(testname):
print colorize('=====================================', COLOR_RED)
return False
def run_py_test(testname):
'''Runs a python test, which evaluates expressions directly rather than queries'''
print "Running expression python test: " + colorize(testname, COLOR_MAGENTA)
@@ -185,6 +184,7 @@ def run_py_test(testname):
print colorize('=====================================', COLOR_RED)
return False
def run_test(testname):
'''Runs a specific test executing the file
testname.query
@@ -194,22 +194,26 @@ def run_test(testname):
optimized'''
print "Running test: " + colorize(testname, COLOR_MAGENTA)
query=None;expr=None;o_query=None;o_expr=None
query = None
expr = None
o_query = None
o_expr = None
result_rel = None
result = None
o_result = None
try:
result_rel = relation.relation('%s%s.result' % (tests_path, testname))
query=unicode(readfile('%s%s.query' % (tests_path,testname)).strip(),'utf8')
query = unicode(
readfile('%s%s.query' % (tests_path, testname)).strip(), 'utf8')
o_query = optimizer.optimize_all(query, rels)
expr = parser.parse(query) # Converting expression to python string
result = eval(expr, rels) # Evaluating the expression
o_expr=parser.parse(o_query)#Converting expression to python string
o_expr = parser.parse(
o_query) # Converting expression to python string
o_result = eval(o_expr, rels) # Evaluating the expression
c_expr = parser.tree(query).toCode() # Converting to python code

View File

@@ -23,6 +23,7 @@ import httplib
import urllib
import relation
def send_survey(data):
'''Sends the survey. Data must be a dictionary.
returns the http response'''
@@ -33,14 +34,11 @@ def send_survey(data):
# sends the string
params = urllib.urlencode({'survey': post})
headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain"}
#connection = httplib.HTTPConnection('galileo.dmi.unict.it')
#connection.request("POST","/~ltworf/survey.php",params,headers)
headers = {"Content-type":
"application/x-www-form-urlencoded", "Accept": "text/plain"}
connection = httplib.HTTPConnection('feedback-ltworf.appspot.com')
connection.request("POST", "/feedback/relational", params, headers)
return connection.getresponse()
@@ -60,6 +58,7 @@ def check_latest_version():
class interface (object):
'''It is used to provide services to the user interfaces, in order to
reduce the amount of duplicated code present in different user interfaces.
'''

View File

@@ -34,7 +34,9 @@ import parser
from cStringIO import StringIO
from tokenize import generate_tokens
sel_op=('//=','**=','and','not','in','//','**','<<','>>','==','!=','>=','<=','+=','-=','*=','/=','%=','or','+','-','*','/','&','|','^','~','<','>','%','=','(',')',',','[',']')
sel_op = (
'//=', '**=', 'and', 'not', 'in', '//', '**', '<<', '>>', '==', '!=', '>=', '<=', '+=', '-=',
'*=', '/=', '%=', 'or', '+', '-', '*', '/', '&', '|', '^', '~', '<', '>', '%', '=', '(', ')', ',', '[', ']')
PRODUCT = parser.PRODUCT
DIFFERENCE = parser.DIFFERENCE
@@ -64,6 +66,7 @@ def replace_node(replace,replacement):
replace.right = replacement.right
replace.left = replacement.left
def recoursive_scan(function, node, rels=None):
'''Does a recoursive optimization on the tree.
@@ -115,6 +118,7 @@ def duplicated_select(n):
return changes + recoursive_scan(duplicated_select, n)
def futile_union_intersection_subtraction(n):
'''This function locates things like r r, and replaces them with r.
R R --> R
@@ -170,6 +174,7 @@ def futile_union_intersection_subtraction(n):
return changes + recoursive_scan(futile_union_intersection_subtraction, n)
def down_to_unions_subtractions_intersections(n):
'''This funcion locates things like σ i==2 (c d), where the union
can be a subtraction and an intersection and replaces them with
@@ -200,6 +205,7 @@ def down_to_unions_subtractions_intersections(n):
return changes + recoursive_scan(down_to_unions_subtractions_intersections, n)
def duplicated_projection(n):
'''This function locates thing like π i ( π j (R)) and replaces
them with π i (R)'''
@@ -211,6 +217,7 @@ def duplicated_projection(n):
return changes + recoursive_scan(duplicated_projection, n)
def selection_inside_projection(n):
'''This function locates things like σ j (π k(R)) and
converts them into π k(σ j (R))'''
@@ -226,6 +233,7 @@ def selection_inside_projection(n):
return changes + recoursive_scan(selection_inside_projection, n)
def swap_union_renames(n):
'''This function locates things like
ρ a➡b(R) ρ a➡b(Q)
@@ -263,6 +271,7 @@ def swap_union_renames(n):
return changes + recoursive_scan(swap_union_renames, n)
def futile_renames(n):
'''This function purges renames like id->id'''
changes = 0
@@ -276,7 +285,8 @@ def futile_renames(n):
for i in n.prop.split(','):
q = i.split(ARROW)
_vars[q[0].strip()] = q[1].strip()
#Scans dictionary to locate things like "a->b,b->c" and replace them with "a->c"
# Scans dictionary to locate things like "a->b,b->c" and replace them
# with "a->c"
for key in list(_vars.keys()):
try:
value = _vars[key]
@@ -297,6 +307,7 @@ def futile_renames(n):
return changes + recoursive_scan(futile_renames, n)
def subsequent_renames(n):
'''This function removes redoundant subsequent renames joining them into one'''
@@ -317,7 +328,8 @@ def subsequent_renames(n):
for i in n.prop.split(','):
q = i.split(ARROW)
_vars[q[0].strip()] = q[1].strip()
#Scans dictionary to locate things like "a->b,b->c" and replace them with "a->c"
# Scans dictionary to locate things like "a->b,b->c" and replace them
# with "a->c"
for key in list(_vars.keys()):
try:
value = _vars[key]
@@ -345,9 +357,11 @@ def subsequent_renames(n):
return changes + recoursive_scan(subsequent_renames, n)
class level_string(str):
level = 0
def tokenize_select(expression):
'''This function returns the list of tokens present in a
selection. The expression can contain parenthesis.
@@ -368,7 +382,6 @@ def tokenize_select(expression):
except:
pass
level = 0
for i in range(len(l)):
l[i] = level_string(l[i])
@@ -381,6 +394,7 @@ def tokenize_select(expression):
return l
def swap_rename_projection(n):
'''This function locates things like π k(ρ j(R))
and replaces them with ρ j(π k(R)).
@@ -422,9 +436,9 @@ def swap_rename_projection(n):
n.child.prop += i + ','
n.child.prop = n.child.prop[:-1]
return changes + recoursive_scan(swap_rename_projection, n)
def swap_rename_select(n):
'''This function locates things like σ k(ρ j(R)) and replaces
them with ρ j(σ k(R)). Renaming the attributes used in the
@@ -449,7 +463,8 @@ def swap_rename_select(n):
if len(splitted) == 1:
_tokens[i] = _vars[_tokens[i].split('.')[0]]
else:
_tokens[i]=_vars[_tokens[i].split('.')[0]]+'.'+splitted[1]
_tokens[i] = _vars[
_tokens[i].split('.')[0]] + '.' + splitted[1]
# Swapping operators
n.name = RENAME
@@ -462,6 +477,7 @@ def swap_rename_select(n):
return changes + recoursive_scan(swap_rename_select, n)
def select_union_intersect_subtract(n):
'''This function locates things like σ i(a) σ q(a)
and replaces them with σ (i OR q) (a)
@@ -498,6 +514,7 @@ def select_union_intersect_subtract(n):
return changes + recoursive_scan(select_union_intersect_subtract, n)
def selection_and_product(n, rels):
'''This function locates things like σ k (R*Q) and converts them into
σ l (σ j (R) * σ i (Q)). Where j contains only attributes belonging to R,
@@ -597,7 +614,9 @@ def selection_and_product(n,rels):
return changes + recoursive_scan(selection_and_product, n, rels)
general_optimizations=[duplicated_select,down_to_unions_subtractions_intersections,duplicated_projection,selection_inside_projection,subsequent_renames,swap_rename_select,futile_union_intersection_subtraction,swap_union_renames,swap_rename_projection,select_union_intersect_subtract]
general_optimizations = [
duplicated_select, down_to_unions_subtractions_intersections, duplicated_projection, selection_inside_projection,
subsequent_renames, swap_rename_select, futile_union_intersection_subtraction, swap_union_renames, swap_rename_projection, select_union_intersect_subtract]
specific_optimizations = [selection_and_product]
if __name__ == "__main__":

View File

@@ -43,6 +43,7 @@ tokenize=parser.tokenize
tree = parser.tree
# End of the stuff
def optimize_all(expression, rels, specific=True, general=True, debug=None):
'''This function performs all the available optimizations.
@@ -73,15 +74,18 @@ def optimize_all(expression,rels,specific=True,general=True,debug=None):
if specific:
for i in optimizations.specific_optimizations:
res = i(n, rels) # Performs the optimization
if res!=0 and dbg: debug.append(n.__str__())
if res != 0 and dbg:
debug.append(n.__str__())
total += res
if general:
for i in optimizations.general_optimizations:
res = i(n) # Performs the optimization
if res!=0 and dbg: debug.append(n.__str__())
if res != 0 and dbg:
debug.append(n.__str__())
total += res
return n.__str__()
def specific_optimize(expression, rels):
'''This function performs specific optimizations. Means that it will need to
know the fields used by the relations.
@@ -92,6 +96,7 @@ def specific_optimize(expression,rels):
Return value: this will return an optimized version of the expression'''
return optimize_all(expression, rels, specific=True, general=False)
def general_optimize(expression):
'''This function performs general optimizations. Means that it will not need to
know the fields used by the relations
@@ -110,7 +115,8 @@ if __name__=="__main__":
# a= tokenize(u"π a,b (a*b)")
# a=tokenize("(a-b*c)*(b-c)")
import relation,optimizations
import relation
import optimizations
'''rels={}
rels["P1"]= relation.relation("/home/salvo/dev/relational/trunk/samples/people.csv")
@@ -132,7 +138,8 @@ if __name__=="__main__":
(π id,name,chief,age (σ chief == i and age > a ((ρ age➡a,id➡i (π id,age (people)))*people)))ᐅᐊ(σ skill == 'C' (skills))
'''
#print specific_optimize("σ name==skill and age>21 and id==indice and skill=='C'(P1ᐅᐊS1)",rels)
# print specific_optimize("σ name==skill and age>21 and id==indice and
# skill=='C'(P1ᐅᐊS1)",rels)
# print n
# print n.result_format(rels)

View File

@@ -23,6 +23,7 @@ import optimizer
import multiprocessing
import parser
def execute(tree, rels):
'''This funcion executes a query in parallel.
Tree is the tree describing the query (usually obtained with
@@ -36,6 +37,7 @@ def execute(tree,rels):
p.join()
return result
def __p_exec__(tree, rels, q):
'''q is the queue used for communication'''
if tree.kind == parser.RELATION:
@@ -52,10 +54,11 @@ def __p_exec__(tree,rels,q):
q.put(result)
elif tree.kind == parser.BINARY:
left_q = multiprocessing.Queue()
left_p = multiprocessing.Process(target=__p_exec__, args=(tree.left,rels,left_q,))
left_p = multiprocessing.Process(
target=__p_exec__, args=(tree.left, rels, left_q,))
right_q = multiprocessing.Queue()
right_p = multiprocessing.Process(target=__p_exec__, args=(tree.right,rels,right_q,))
right_p = multiprocessing.Process(
target=__p_exec__, args=(tree.right, rels, right_q,))
# Spawn the children
left_p.start()
@@ -72,6 +75,8 @@ def __p_exec__(tree,rels,q):
result = __p_exec_binary__(tree, left, right)
q.put(result)
return
def __p_exec_binary__(tree, left, right):
if tree.name == '*':
return left.product(right)
@@ -92,12 +97,14 @@ def __p_exec_binary__(tree,left,right):
else: # tree.name=='ᐅFULLᐊ':
return left.outer(right)
def __p_exec_unary__(tree, rel):
if tree.name == 'π': # Projection
tree.prop = tree.prop.replace(' ', '').split(',')
result = rel.projection(tree.prop)
elif tree.name == "ρ": # Rename
#tree.prop='{\"%s\"}' % tree.prop.replace(',','\",\"').replace('➡','\":\"').replace(' ','')
# tree.prop='{\"%s\"}' %
# tree.prop.replace(',','\",\"').replace('➡','\":\"').replace(' ','')
d = {}
tree.prop = tree.prop.replace(' ', '')
for i in tree.prop.split(','):
@@ -108,4 +115,3 @@ def __p_exec_unary__(tree,rel):
else: # Selection
result = rel.selection(tree.prop)
return result

View File

@@ -61,18 +61,26 @@ SELECTION=u'σ'
RENAME = u'ρ'
ARROW = u''
b_operators=(PRODUCT,DIFFERENCE,UNION,INTERSECTION,DIVISION,JOIN,JOIN_LEFT,JOIN_RIGHT,JOIN_FULL) # List of binary operators
b_operators = (PRODUCT, DIFFERENCE, UNION, INTERSECTION, DIVISION,
JOIN, JOIN_LEFT, JOIN_RIGHT, JOIN_FULL) # List of binary operators
u_operators = (PROJECTION, SELECTION, RENAME) # List of unary operators
# Associates operator with python method
op_functions={PRODUCT:'product',DIFFERENCE:'difference',UNION:'union',INTERSECTION:'intersection',DIVISION:'division',JOIN:'join',JOIN_LEFT:'outer_left',JOIN_RIGHT:'outer_right',JOIN_FULL:'outer',PROJECTION:'projection',SELECTION:'selection',RENAME:'rename'}
op_functions = {
PRODUCT: 'product', DIFFERENCE: 'difference', UNION: 'union', INTERSECTION: 'intersection', DIVISION: 'division', JOIN: 'join',
JOIN_LEFT: 'outer_left', JOIN_RIGHT: 'outer_right', JOIN_FULL: 'outer', PROJECTION: 'projection', SELECTION: 'selection', RENAME: 'rename'}
class TokenizerException (Exception):
pass
class ParserException (Exception):
pass
class node (object):
'''This class is a node of a relational expression. Leaves are relations and internal nodes are operations.
The kind property says if the node is a binary operator, unary operator or relation.
@@ -103,7 +111,8 @@ class node (object):
self.kind = RELATION
self.name = expression[0]
if not rtypes.is_valid_relation_name(self.name):
raise ParserException(u"'%s' is not a valid relation name" % self.name)
raise ParserException(
u"'%s' is not a valid relation name" % self.name)
return
'''Expression from right to left, searching for binary operators
@@ -122,10 +131,12 @@ class node (object):
self.name = expression[i]
if len(expression[:i]) == 0:
raise ParserException(u"Expected left operand for '%s'" % self.name)
raise ParserException(
u"Expected left operand for '%s'" % self.name)
if len(expression[i + 1:]) == 0:
raise ParserException(u"Expected right operand for '%s'" % self.name)
raise ParserException(
u"Expected right operand for '%s'" % self.name)
self.left = node(expression[:i])
self.right = node(expression[i + 1:])
@@ -137,7 +148,8 @@ class node (object):
self.name = expression[i]
if len(expression) <= i + 2:
raise ParserException(u"Expected more tokens in '%s'"%self.name)
raise ParserException(
u"Expected more tokens in '%s'" % self.name)
self.prop = expression[1 + i].strip()
self.child = node(expression[2 + i])
@@ -145,6 +157,7 @@ class node (object):
return
raise ParserException(u"Unable to parse tokens")
pass
def toCode(self):
'''This method converts the tree into a python code object'''
code = self.toPython()
@@ -162,7 +175,8 @@ class node (object):
if self.name == PROJECTION:
prop = '\"%s\"' % prop.replace(' ', '').replace(',', '\",\"')
elif self.name == RENAME:
prop='{\"%s\"}' % prop.replace(',','\",\"').replace(ARROW,'\":\"').replace(' ','')
prop = '{\"%s\"}' % prop.replace(
',', '\",\"').replace(ARROW, '\":\"').replace(' ', '')
else: # Selection
prop = '\"%s\"' % prop
@@ -170,6 +184,7 @@ class node (object):
else:
return self.name
pass
def printtree(self, level=0):
'''returns a representation of the tree using indentation'''
r = ''
@@ -184,6 +199,7 @@ class node (object):
r += self.child.printtree(level + 1)
return '\n' + r
def get_left_leaf(self):
'''This function returns the leftmost leaf in the tree. It is needed by some optimizations.'''
if self.kind == RELATION:
@@ -193,7 +209,6 @@ class node (object):
elif self.kind == BINARY:
return self.left.get_left_leaf()
def result_format(self, rels):
'''This function returns a list containing the fields that the resulting relation will have.
It requires a dictionary where keys are the names of the relations and the values are
@@ -229,6 +244,7 @@ class node (object):
return _fields
elif self.name in (JOIN, JOIN_LEFT, JOIN_RIGHT, JOIN_FULL):
return list(set(self.left.result_format(rels)).union(set(self.right.result_format(rels))))
def __eq__(self, other):
if not (isinstance(other, node) and self.name == other.name and self.kind == other.kind):
return False
@@ -258,6 +274,7 @@ class node (object):
return (le + self.name + re)
def _find_matching_parenthesis(expression, start=0, openpar=u'(', closepar=u')'):
'''This function returns the position of the matching
close parenthesis to the 1st open parenthesis found
@@ -271,6 +288,7 @@ def _find_matching_parenthesis(expression,start=0,openpar=u'(',closepar=u')'):
if par_count == 0:
return i # Closing parenthesis of the parameter
def tokenize(expression):
'''This function converts an expression into a list where
every token of the expression is an item of a list. Expressions into
@@ -307,23 +325,29 @@ def tokenize(expression):
state = 2
end = _find_matching_parenthesis(expression)
if end == None:
raise TokenizerException("Missing matching ')' in '%s'" %expression)
raise TokenizerException(
"Missing matching ')' in '%s'" % expression)
# Appends the tokenization of the content of the parenthesis
items.append(tokenize(expression[1:end]))
# Removes the entire parentesis and content from the expression
expression = expression[end + 1:].strip()
elif expression.startswith((u"σ", u"π", u"ρ")): # Unary 2 bytes
items.append(expression[0:1]) #Adding operator in the top of the list
expression=expression[1:].strip() #Removing operator from the expression
items.append(expression[0:1])
#Adding operator in the top of the list
expression = expression[
1:].strip() # Removing operator from the expression
if expression.startswith('('): # Expression with parenthesis, so adding what's between open and close without tokenization
par=expression.find('(',_find_matching_parenthesis(expression))
par = expression.find(
'(', _find_matching_parenthesis(expression))
else: # Expression without parenthesis, so adding what's between start and parenthesis as whole
par = expression.find('(')
items.append(expression[:par].strip()) #Inserting parameter of the operator
expression=expression[par:].strip() #Removing parameter from the expression
items.append(expression[:par].strip())
#Inserting parameter of the operator
expression = expression[
par:].strip() # Removing parameter from the expression
elif expression.startswith((u"÷", u"", u"", u"*", u"-")):
items.append(expression[0])
expression = expression[1:].strip() # 1 char from the expression
@@ -336,26 +360,29 @@ def tokenize(expression):
expression = expression[i + 1:].strip()
state = 4
elif re.match(r'[_0-9A-Za-z]', expression[0]) == None: # At this point we only have relation names, so we raise errors for anything else
raise TokenizerException("Unexpected '%c' in '%s'" % (expression[0],expression))
raise TokenizerException(
"Unexpected '%c' in '%s'" % (expression[0], expression))
else: # Relation (hopefully)
if state == 1: # Previous was a relation, appending to the last token
i = items.pop()
items.append(i + expression[0])
expression=expression[1:].strip() #1 char from the expression
expression = expression[
1:].strip() # 1 char from the expression
else:
state = 1
items.append(expression[0])
expression=expression[1:].strip() #1 char from the expression
expression = expression[
1:].strip() # 1 char from the expression
return items
def tree(expression):
'''This function parses a relational algebra expression into a tree and returns
the root node using the Node class defined in this module.'''
return node(tokenize(expression))
def parse(expr):
'''This function parses a relational algebra expression, converting it into python,
executable by eval function to get the result of the expression.

View File

@@ -21,10 +21,13 @@
import parser
class TypeException(Exception):
pass
class Query(object):
def __init__(self, query):
if not isinstance(query, unicode):

View File

@@ -23,7 +23,9 @@
from rtypes import *
import csv
class relation (object):
'''This objects defines a relation (as a group of consistent tuples) and operations
A relation can be represented using a table
Calling an operation and providing a non relation parameter when it is expected will
@@ -122,8 +124,10 @@ class relation (object):
if eval(expr, attributes):
newt.content.add(i)
except Exception, e:
raise Exception("Failed to evaluate %s\n%s" % (expr,e.__str__()))
raise Exception(
"Failed to evaluate %s\n%s" % (expr, e.__str__()))
return newt
def product(self, other):
'''Cartesian product, attributes must be different to avoid collisions
Doing this operation on relations with colliding attributes will
@@ -131,7 +135,8 @@ class relation (object):
It is possible to use rename on attributes and then use the product'''
if (self.__class__ != other.__class__)or(self.header.sharedAttributes(other.header) != 0):
raise Exception('Unable to perform product on relations with colliding attributes')
raise Exception(
'Unable to perform product on relations with colliding attributes')
newt = relation()
newt.header = header(self.header.attributes + other.header.attributes)
@@ -140,7 +145,6 @@ class relation (object):
newt.content.add(i + j)
return newt
def projection(self, * attributes):
'''Projection operator, takes many parameters, for each field to use.
Can also use a single parameter with a list.
@@ -202,7 +206,8 @@ class relation (object):
It is possible to use projection and rename to make headers match.'''
other = self._rearrange_(other) # Rearranges attributes' order
if (self.__class__ != other.__class__)or(self.header != other.header):
raise Exception('Unable to perform intersection on relations with different attributes')
raise Exception(
'Unable to perform intersection on relations with different attributes')
newt = relation()
newt.header = header(list(self.header.attributes))
@@ -217,12 +222,14 @@ class relation (object):
It is possible to use projection and rename to make headers match.'''
other = self._rearrange_(other) # Rearranges attributes' order
if (self.__class__ != other.__class__)or(self.header != other.header):
raise Exception('Unable to perform difference on relations with different attributes')
raise Exception(
'Unable to perform difference on relations with different attributes')
newt = relation()
newt.header = header(list(self.header.attributes))
newt.content = self.content.difference(other.content)
return newt
def division(self, other):
'''Division operator
The division is a binary operation that is written as R ÷ S. The
@@ -233,8 +240,8 @@ class relation (object):
'''
# d_headers are the headers from self that aren't also headers in other
d_headers=list(set(self.header.attributes) - set(other.header.attributes))
d_headers = list(
set(self.header.attributes) - set(other.header.attributes))
'''
Wikipedia defines the division as follows:
@@ -261,12 +268,14 @@ class relation (object):
It is possible to use projection and rename to make headers match.'''
other = self._rearrange_(other) # Rearranges attributes' order
if (self.__class__ != other.__class__)or(self.header != other.header):
raise Exception('Unable to perform union on relations with different attributes')
raise Exception(
'Unable to perform union on relations with different attributes')
newt = relation()
newt.header = header(list(self.header.attributes))
newt.content = self.content.union(other.content)
return newt
def thetajoin(self, other, expr):
'''Defined as product and then selection with the given expression.'''
return self.product(other).selection(expr)
@@ -347,11 +356,13 @@ class relation (object):
shared attributes, it will behave as cartesian product.'''
# List of attributes in common between the relations
shared=list(set(self.header.attributes).intersection(set(other.header.attributes)))
shared = list(set(self.header.attributes)
.intersection(set(other.header.attributes)))
newt = relation() # Creates the new relation
#Adding to the headers all the fields, done like that because order is needed
# Adding to the headers all the fields, done like that because order is
# needed
newt.header = header(list(self.header.attributes))
for i in other.header.attributes:
if i not in shared:
@@ -382,10 +393,12 @@ class relation (object):
newt.content.add(tuple(item))
return newt
def __eq__(self, other):
'''Returns true if the relations are the same, ignoring order of items.
This operation is rather heavy, since it requires sorting and comparing.'''
other=self._rearrange_(other) #Rearranges attributes' order so can compare tuples directly
other = self._rearrange_(
other) # Rearranges attributes' order so can compare tuples directly
if (self.__class__ != other.__class__)or(self.header != other.header):
return False # Both parameters must be a relation
@@ -393,8 +406,6 @@ class relation (object):
if set(self.header.attributes) != set(other.header.attributes):
return False
# comparing content
return self.content == other.content
@@ -416,7 +427,6 @@ class relation (object):
for f in range(len(self.header.attributes)):
res += "%s" % (self.header.attributes[f].ljust(2 + m_len[f]))
for r in self.content:
col = 0
res += "\n"
@@ -438,7 +448,8 @@ 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) # List of indexes corresponding to keys
# new_content=[] #New content of the relation
for i in self.content:
@@ -492,14 +503,15 @@ class relation (object):
for j in range(len(self.header.attributes)):
attributes[self.header.attributes[j]] = self._autocast(i[j])
if not eval(expr, attributes):
affected -= 1
new_content.add(i)
self.content = new_content
return affected
class header (object):
'''This class defines the header of a relation.
It is used within relations to know if requested operations are accepted'''
@@ -517,7 +529,6 @@ class header (object):
def __repr__(self):
return "header(%s)" % (self.attributes.__repr__())
def rename(self, old, new):
'''Renames a field. Doesn't check if it is a duplicate.
Returns True if the field was renamed, False otherwise'''
@@ -542,6 +553,7 @@ class header (object):
def __eq__(self, other):
return self.attributes == other.attributes
def __ne__(self, other):
return self.attributes != other.attributes
@@ -553,4 +565,3 @@ class header (object):
if i == self.attributes[j]:
res.append(j)
return res

View File

@@ -24,8 +24,11 @@
import datetime
import re
class rstring (str):
'''String subclass with some custom methods'''
def isInt(self):
'''Returns true if the string represents an int number
it only considers as int numbers the strings matching
@@ -36,6 +39,7 @@ class rstring (str):
return False
else:
return True
def isFloat(self):
'''Returns true if the string represents a float number
it only considers as float numbers, the strings matching
@@ -58,7 +62,8 @@ class rstring (str):
except:
pass
r= re.match(r'^([0-9]{1,4})(\\|-|/)([0-9]{1,2})(\\|-|/)([0-9]{1,2})$',self)
r = re.match(
r'^([0-9]{1,4})(\\|-|/)([0-9]{1,2})(\\|-|/)([0-9]{1,2})$', self)
if r == None:
self._isdate = False
self._date = None
@@ -84,8 +89,12 @@ class rstring (str):
except:
self.isDate()
return self._date
class rdate (object):
'''Represents a date'''
def __init__(self, date):
'''date: A string representing a date'''
if not isinstance(date, rstring):
@@ -99,23 +108,32 @@ class rdate (object):
def __hash__(self):
return self.intdate.__hash__()
def __str__(self):
return self.intdate.__str__()
def __add__(self, days):
res = self.intdate + datetime.timedelta(days)
return rdate(res.__str__())
def __eq__(self, other):
return self.intdate == other.intdate
def __ge__(self, other):
return self.intdate >= other.intdate
def __gt__(self, other):
return self.intdate > other.intdate
def __le__(self, other):
return self.intdate <= other.intdate
def __lt__(self, other):
return self.intdate < other.intdate
def __ne__(self, other):
return self.intdate != other.intdate
def __sub__(self, other):
return (self.intdate - other.intdate).days

View File

@@ -23,6 +23,7 @@ import signal
import sys
from relational import *
def terminate(*a):
'''Restores the terminal and terminates'''
curses.nocbreak()
@@ -32,6 +33,7 @@ def terminate(*a):
sys.exit(0)
def main():
global stdscr
# Initializes the signal
@@ -52,15 +54,11 @@ def main():
win = curses.panel.new_panel(curses.newwin(termsize[0], termsize[1]))
# win.window().box()
win.window().addstr(0, (termsize[1] / 2) - 5, "Relational")
win.window().refresh()
# curses.napms(1000)
query = curses.panel.new_panel(curses.newwin(3, termsize[1], 1, 0))
query.window().box()
query.window().addstr(1, 1, "")
@@ -71,9 +69,7 @@ def main():
# curses.napms(1000)
# qq=curses.textpad.Textbox(stdscr)
'''win = curses.newwin(0, 0)#New windows for the entire screen
#stdscr.border(0)
stdscr.addstr(str(dir (win)))
@@ -97,13 +93,11 @@ def main():
if c == 27: # Escape
squery += add_symbol(symselect)
# elif c==curses.KEY_BACKSPACE: #Delete
elif c == 13:
squery = squery[:-1]
else:
squery+=chr(c);
squery += chr(c)
query.window().box()
query.top()
@@ -111,10 +105,13 @@ def main():
query.window().addstr(1, 1, squery)
query.window().refresh()
def init_symbol_list(termsize, create=True):
if create:
p=curses.panel.new_panel(curses.newwin(15,16,2,termsize[1]/2-7))
p = curses.panel.new_panel(
curses.newwin(15, 16, 2, termsize[1] / 2 - 7))
else:
p = termsize
p.window().box()
@@ -135,13 +132,15 @@ def init_symbol_list(termsize,create=True):
# p.hide()
return p
def add_symbol(p):
'''Shows the panel to add a symbol
and then returns the choosen symbol itself'''
init_symbol_list(p, False)
d_={'0':'*','1':'-','2':'','3':'','4':'ᐅᐊ','5':'ᐅLEFTᐊ','6':'ᐅRIGHTᐊ','7':'ᐅFULLᐊ','8':'σ','9':'ρ','a':'π','b':''}
d_ = {'0': '*', '1': '-', '2': '', '3': '', '4': 'ᐅᐊ', '5': 'ᐅLEFTᐊ',
'6': 'ᐅRIGHTᐊ', '7': 'ᐅFULLᐊ', '8': 'σ', '9': 'ρ', 'a': 'π', 'b': ''}
p.show()
p.top()
@@ -156,6 +155,7 @@ def add_symbol(p):
char = ''
return char
def spaces(t):
'''Returns a number of spaces specified t'''
s = ''

View File

@@ -42,6 +42,7 @@ def printver(exit=True):
if exit:
sys.exit(0)
def printhelp(code=0):
print "Relational"
print
@@ -92,7 +93,6 @@ if __name__ == "__main__":
from PySide import QtCore, QtGui
pyqt = False
if pyqt:
try:
from relational_gui import maingui, guihandler, about, surveyForm
@@ -106,14 +106,13 @@ if __name__ == "__main__":
print >> sys.stderr, "Module relational_pyside is missing.\nPlease install relational package."
sys.exit(3)
about.version = version
surveyForm.version = version
guihandler.version = version
app = QtGui.QApplication(sys.argv)
app.setOrganizationName('None');
app.setApplicationName('relational');
app.setOrganizationName('None')
app.setApplicationName('relational')
ui = maingui.Ui_MainWindow()
Form = guihandler.relForm(ui)
@@ -146,4 +145,3 @@ if __name__ == "__main__":
sys.exit(3)
relational_readline.linegui.version = version
relational_readline.linegui.main(files)

View File

@@ -37,7 +37,9 @@ except:
version = 0
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(510, 453)
@@ -96,14 +98,16 @@ class Ui_Dialog(object):
self.verticalLayout_7.setObjectName("verticalLayout_7")
if (webk):
self.webView = QtWebKit.QWebView(self.tab_2)
self.webView.setUrl(QtCore.QUrl("http://galileo.dmi.unict.it/wiki/relational/doku.php"))
self.webView.setUrl(
QtCore.QUrl("https://github.com/ltworf/relational/wiki/Grammar-and-language"))
self.webView.setObjectName("webView")
self.verticalLayout_7.addWidget(self.webView)
else:
self.webLink = QtGui.QLabel(self.groupBox)
self.webLink.setFont(font)
self.webLink.setObjectName("lblLink")
self.webLink.setText(QtGui.QApplication.translate("Dialog", "<a href=\"http://galileo.dmi.unict.it/wiki/relational/\">Relational's website</a>", None,))
self.webLink.setText(QtGui.QApplication.translate(
"Dialog", "<a href=\"https://github.com/ltworf/relational\">Relational's website</a>", None,))
self.webLink.setOpenExternalLinks(True)
self.webLink.setTextFormat(QtCore.Qt.AutoText)
self.webLink.setTextInteractionFlags(
@@ -126,24 +130,39 @@ class Ui_Dialog(object):
self.retranslateUi(Dialog)
self.tabWidget.setCurrentIndex(0)
QtCore.QObject.connect(self.buttonBox,QtCore.SIGNAL("accepted()"),Dialog.accept)
QtCore.QObject.connect(self.buttonBox,QtCore.SIGNAL("rejected()"),Dialog.reject)
QtCore.QObject.connect(
self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept)
QtCore.QObject.connect(
self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Documentation", None, QtGui.QApplication.UnicodeUTF8))
self.groupBox.setTitle(QtGui.QApplication.translate("Dialog", "Relational", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("Dialog", "Relational", None, QtGui.QApplication.UnicodeUTF8))
self.label_3.setText(QtGui.QApplication.translate("Dialog", "Version "+version, None, QtGui.QApplication.UnicodeUTF8))
self.label_3.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.groupBox_3.setTitle(QtGui.QApplication.translate("Dialog", "Author", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setText(QtGui.QApplication.translate("Dialog", "Salvo \"LtWorf\" Tomaselli &lt;<a href=\"mailto:tiposchi@tiscali.it\">tiposchi@tiscali.it</a>&gt;<br>Emilio Di Prima &lt;emiliodiprima[at]msn[dot]com&gt; (For the windows setup)", None, QtGui.QApplication.UnicodeUTF8))
Dialog.setWindowTitle(QtGui.QApplication.translate(
"Dialog", "Documentation", None, QtGui.QApplication.UnicodeUTF8))
self.groupBox.setTitle(QtGui.QApplication.translate(
"Dialog", "Relational", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate(
"Dialog", "Relational", None, QtGui.QApplication.UnicodeUTF8))
self.label_3.setText(QtGui.QApplication.translate(
"Dialog", "Version " + version, None, QtGui.QApplication.UnicodeUTF8))
self.label_3.setTextInteractionFlags(
QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
self.groupBox_3.setTitle(QtGui.QApplication.translate(
"Dialog", "Author", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setText(QtGui.QApplication.translate(
"Dialog", "Salvo \"LtWorf\" Tomaselli &lt;<a href=\"mailto:tiposchi@tiscali.it\">tiposchi@tiscali.it</a>&gt;<br>Emilio Di Prima &lt;emiliodiprima[at]msn[dot]com&gt; (For the windows setup)", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setOpenExternalLinks(True)
self.label_2.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.groupBox_2.setTitle(QtGui.QApplication.translate("Dialog", "Links", None, QtGui.QApplication.UnicodeUTF8))
self.label_4.setText(QtGui.QApplication.translate("Dialog", "<a href=\"http://galileo.dmi.unict.it/wiki/relational/\">http://galileo.dmi.unict.it/wiki/relational/</a>", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setTextInteractionFlags(
QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
self.groupBox_2.setTitle(QtGui.QApplication.translate(
"Dialog", "Links", None, QtGui.QApplication.UnicodeUTF8))
self.label_4.setText(QtGui.QApplication.translate(
"Dialog", "<a href=\"https://github.com/ltworf/relational/\">https://github.com/ltworf/relational/</a>", None, QtGui.QApplication.UnicodeUTF8))
self.label_4.setOpenExternalLinks(True)
self.label_4.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QtGui.QApplication.translate("Dialog", "About", None, QtGui.QApplication.UnicodeUTF8))
self.label_4.setTextInteractionFlags(
QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QtGui.QApplication.translate(
"Dialog", "About", None, QtGui.QApplication.UnicodeUTF8))
self.textEdit.setHtml(QtGui.QApplication.translate("Dialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><title>GNU General Public License - GNU Project - Free Software Foundation (FSF)</title><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
@@ -286,9 +305,10 @@ class Ui_Dialog(object):
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">The hypothetical commands `show w\' and `show c\' should show the appropriate parts of the General Public License. Of course, your program\'s commands might be different; for a GUI interface, you would use an “about box”. </p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see &lt;http://www.gnu.org/licenses/&gt;. </p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read &lt;http://www.gnu.org/philosophy/why-not-lgpl.html&gt;. </p></body></html>", None, QtGui.QApplication.UnicodeUTF8))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.License), QtGui.QApplication.translate("Dialog", "License", None, QtGui.QApplication.UnicodeUTF8))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QtGui.QApplication.translate("Dialog", "Docs", None, QtGui.QApplication.UnicodeUTF8))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.License), QtGui.QApplication.translate(
"Dialog", "License", None, QtGui.QApplication.UnicodeUTF8))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QtGui.QApplication.translate(
"Dialog", "Docs", None, QtGui.QApplication.UnicodeUTF8))
if __name__ == "__main__":
@@ -299,4 +319,3 @@ if __name__ == "__main__":
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())

View File

@@ -34,15 +34,20 @@ def get_py_str(a):
return unicode(a.toUtf8(), 'utf-8')
return a # Already a python string in PySide
def set_utf8_text(component, text):
if not pyqt:
component.setText(text)
else:
component.setText(QtCore.QString.fromUtf8(text))
def get_filename(filename):
if pyqt:
return str(filename.toUtf8())
return filename[0]
def add_list_item(l, item):
if pyqt:
history_item = QtCore.QString()

View File

@@ -28,12 +28,14 @@ import rel_edit
class creatorForm(QtGui.QDialog):
def __init__(self, rel=None):
QtGui.QDialog.__init__(self)
self.setSizeGripEnabled(True)
self.result_relation = None
self.rel = rel
def setUi(self, ui):
self.ui = ui
self.table = self.ui.table
@@ -42,6 +44,7 @@ class creatorForm(QtGui.QDialog):
self.setup_empty()
else:
self.setup_relation(self.rel)
def setup_relation(self, rel):
self.table.insertRow(0)
@@ -60,6 +63,7 @@ class creatorForm(QtGui.QDialog):
self.table.setItem(self.table.rowCount() - 1, j, item)
pass
def setup_empty(self):
self.table.insertColumn(0)
self.table.insertColumn(0)
@@ -79,15 +83,18 @@ class creatorForm(QtGui.QDialog):
self.table.setItem(0, 1, i01)
self.table.setItem(1, 0, i10)
self.table.setItem(1, 1, i11)
def create_relation(self):
hlist = []
for i in range(self.table.columnCount()):
hlist.append(compatibility.get_py_str(self.table.item(0,i).text()))
hlist.append(
compatibility.get_py_str(self.table.item(0, i).text()))
try:
header = relation.header(hlist)
except Exception, e:
QtGui.QMessageBox.information(None,QtGui.QApplication.translate("Form", "Error"),"%s\n%s" % (QtGui.QApplication.translate("Form", "Header error!"),e.__str__()) )
QtGui.QMessageBox.information(None, QtGui.QApplication.translate("Form", "Error"), "%s\n%s" % (
QtGui.QApplication.translate("Form", "Header error!"), e.__str__()))
return None
r = relation.relation()
r.header = header
@@ -96,9 +103,11 @@ class creatorForm(QtGui.QDialog):
hlist = []
for j in range(self.table.columnCount()):
try:
hlist.append(compatibility.get_py_str(self.table.item(i,j).text()))
hlist.append(
compatibility.get_py_str(self.table.item(i, j).text()))
except:
QtGui.QMessageBox.information(None,QtGui.QApplication.translate("Form", "Error"),QtGui.QApplication.translate("Form", "Unset value in %d,%d!"% (i+1,j+1)) )
QtGui.QMessageBox.information(None, QtGui.QApplication.translate(
"Form", "Error"), QtGui.QApplication.translate("Form", "Unset value in %d,%d!" % (i + 1, j + 1)))
return None
r.content.add(tuple(hlist))
return r
@@ -111,20 +120,25 @@ class creatorForm(QtGui.QDialog):
if self.result_relation != None:
QtGui.QDialog.accept(self)
pass
def reject(self):
self.result_relation = None
QtGui.QDialog.reject(self)
pass
def addColumn(self):
self.table.insertColumn(self.table.columnCount())
pass
def addRow(self):
self.table.insertRow(1)
pass
def deleteColumn(self):
if self.table.columnCount() > 1:
self.table.removeColumn(self.table.currentColumn())
pass
def deleteRow(self):
if self.table.rowCount() > 2:
self.table.removeRow(self.table.currentRow())
@@ -149,5 +163,6 @@ def edit_relation(rel=None):
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
r=relation.relation("/home/salvo/dev/relational/trunk/samples/people.csv")
r = relation.relation(
"/home/salvo/dev/relational/trunk/samples/people.csv")
print edit_relation(r)

View File

@@ -26,7 +26,6 @@ except:
from PySide import QtCore, QtGui
from relational import relation, parser, optimizer, rtypes
import about
@@ -55,14 +54,17 @@ class relForm(QtGui.QMainWindow):
online = maintenance.check_latest_version()
if online > version:
r=QtGui.QApplication.translate("Form", "New version available online: %s." % online)
r = QtGui.QApplication.translate(
"Form", "New version available online: %s." % online)
elif online == version:
r=QtGui.QApplication.translate("Form", "Latest version installed.")
r = QtGui.QApplication.translate(
"Form", "Latest version installed.")
else:
r=QtGui.QApplication.translate("Form", "You are using an unstable version.")
QtGui.QMessageBox.information(self,QtGui.QApplication.translate("Form", "Version"),r)
r = QtGui.QApplication.translate(
"Form", "You are using an unstable version.")
QtGui.QMessageBox.information(
self, QtGui.QApplication.translate("Form", "Version"), r)
def load_query(self, *index):
self.ui.txtQuery.setText(self.savedQ.itemData(index[0]).toString())
@@ -81,9 +83,8 @@ class relForm(QtGui.QMainWindow):
result = optimizer.optimize_all(query, self.relations)
compatibility.set_utf8_text(self.ui.txtQuery, result)
except Exception, e:
QtGui.QMessageBox.information(None,QtGui.QApplication.translate("Form", "Error"),"%s\n%s" % (QtGui.QApplication.translate("Form", "Check your query!"),e.__str__()) )
QtGui.QMessageBox.information(None, QtGui.QApplication.translate("Form", "Error"), "%s\n%s" %
(QtGui.QApplication.translate("Form", "Check your query!"), e.__str__()))
def resumeHistory(self, item):
itm = compatibility.get_py_str(item.text()).split(' = ', 1)
@@ -94,10 +95,12 @@ class relForm(QtGui.QMainWindow):
'''Executes the query'''
query = compatibility.get_py_str(self.ui.txtQuery.text())
res_rel=compatibility.get_py_str(self.ui.txtResult.text())#result relation's name
res_rel = compatibility.get_py_str(
self.ui.txtResult.text()) # result relation's name
if not rtypes.is_valid_relation_name(res_rel):
QtGui.QMessageBox.information(self,QtGui.QApplication.translate("Form", "Error"),QtGui.QApplication.translate("Form", "Wrong name for destination relation."))
QtGui.QMessageBox.information(self, QtGui.QApplication.translate(
"Form", "Error"), QtGui.QApplication.translate("Form", "Wrong name for destination relation."))
return
try:
@@ -106,22 +109,27 @@ class relForm(QtGui.QMainWindow):
print query, "-->", expr # Printing debug
result = eval(expr, self.relations) # Evaluating the expression
self.relations[res_rel]=result #Add the relation to the dictionary
self.relations[
res_rel] = result # Add the relation to the dictionary
self.updateRelations() # update the list
self.selectedRelation = result
self.showRelation(self.selectedRelation) #Show the result in the table
self.showRelation(self.selectedRelation)
# Show the result in the table
except Exception, e:
print e.__unicode__()
QtGui.QMessageBox.information(None,QtGui.QApplication.translate("Form", "Error"),u"%s\n%s" % (QtGui.QApplication.translate("Form", "Check your query!"),e.__unicode__()) )
QtGui.QMessageBox.information(None, QtGui.QApplication.translate("Form", "Error"), u"%s\n%s" %
(QtGui.QApplication.translate("Form", "Check your query!"), e.__unicode__()))
return
# Adds to history
item=u'%s = %s' % (compatibility.get_py_str(self.ui.txtResult.text()),compatibility.get_py_str(self.ui.txtQuery.text()))
item = u'%s = %s' % (compatibility.get_py_str(
self.ui.txtResult.text()), compatibility.get_py_str(self.ui.txtQuery.text()))
# item=item.decode('utf-8'))
compatibility.add_list_item(self.ui.lstHistory, item)
self.qcounter += 1
compatibility.set_utf8_text(self.ui.txtResult,u"_last%d"% self.qcounter) #Sets the result relation name to none
compatibility.set_utf8_text(self.ui.txtResult, u"_last%d" %
self.qcounter) # Sets the result relation name to none
def showRelation(self, rel):
'''Shows the selected relation into the table'''
@@ -143,11 +151,12 @@ class relForm(QtGui.QMainWindow):
# Sets columns
for i in range(len(rel.header.attributes)):
self.ui.table.headerItem().setText(i, rel.header.attributes[i])
self.ui.table.resizeColumnToContents(i) #Must be done in order to avoid too small columns
self.ui.table.resizeColumnToContents(
i) # Must be done in order to avoid too small columns
def printRelation(self, item):
self.selectedRelation=self.relations[compatibility.get_py_str(item.text())]
self.selectedRelation = self.relations[
compatibility.get_py_str(item.text())]
self.showRelation(self.selectedRelation)
def showAttributes(self, item):
@@ -162,25 +171,31 @@ class relForm(QtGui.QMainWindow):
for i in self.relations:
if i != "__builtins__":
self.ui.lstRelations.addItem(i)
def saveRelation(self):
filename = QtGui.QFileDialog.getSaveFileName(self,QtGui.QApplication.translate("Form", "Save Relation"),"",QtGui.QApplication.translate("Form", "Relations (*.csv)"))
filename = QtGui.QFileDialog.getSaveFileName(self, QtGui.QApplication.translate(
"Form", "Save Relation"), "", QtGui.QApplication.translate("Form", "Relations (*.csv)"))
filename = compatibility.get_filename(filename)
if (len(filename) == 0): # Returns if no file was selected
return
self.selectedRelation.save(filename)
return
def unloadRelation(self):
for i in self.ui.lstRelations.selectedItems():
del self.relations[compatibility.get_py_str(i.text())]
self.updateRelations()
def editRelation(self):
import creator
for i in self.ui.lstRelations.selectedItems():
result=creator.edit_relation(self.relations[compatibility.get_py_str(i.text())])
result = creator.edit_relation(
self.relations[compatibility.get_py_str(i.text())])
if result != None:
self.relations[compatibility.get_py_str(i.text())] = result
self.updateRelations()
def newRelation(self):
import creator
result = creator.edit_relation()
@@ -190,7 +205,8 @@ class relForm(QtGui.QMainWindow):
res = QtGui.QInputDialog.getText(
self,
QtGui.QApplication.translate("Form", "New relation"),
QtGui.QApplication.translate("Form", "Insert the name for the new relation"),
QtGui.QApplication.translate(
"Form", "Insert the name for the new relation"),
QtGui.QLineEdit.Normal, '')
if res[1] == False or len(res[0]) == 0:
return
@@ -199,22 +215,26 @@ class relForm(QtGui.QMainWindow):
name = compatibility.get_py_str(res[0])
if not rtypes.is_valid_relation_name(name):
r=QtGui.QApplication.translate("Form", str("Wrong name for destination relation: %s." % name))
QtGui.QMessageBox.information(self,QtGui.QApplication.translate("Form", "Error"),r)
r = QtGui.QApplication.translate(
"Form", str("Wrong name for destination relation: %s." % name))
QtGui.QMessageBox.information(
self, QtGui.QApplication.translate("Form", "Error"), r)
return
try:
self.relations[name] = result
except Exception, e:
print e
QtGui.QMessageBox.information(None,QtGui.QApplication.translate("Form", "Error"),"%s\n%s" % (QtGui.QApplication.translate("Form", "Check your query!"),e.__str__()) )
QtGui.QMessageBox.information(None, QtGui.QApplication.translate("Form", "Error"), "%s\n%s" %
(QtGui.QApplication.translate("Form", "Check your query!"), e.__str__()))
return
self.updateRelations()
def closeEvent(self, event):
self.save_settings()
event.accept()
def save_settings(self):
# self.settings.setValue("width",)
pass
@@ -245,7 +265,8 @@ class relForm(QtGui.QMainWindow):
It shouldn't be called giving filename but not giving name.'''
# Asking for file to load
if filename == None:
filename = QtGui.QFileDialog.getOpenFileName(self,QtGui.QApplication.translate("Form", "Load Relation"),"",QtGui.QApplication.translate("Form", "Relations (*.csv);;Text Files (*.txt);;All Files (*)"))
filename = QtGui.QFileDialog.getOpenFileName(self, QtGui.QApplication.translate(
"Form", "Load Relation"), "", QtGui.QApplication.translate("Form", "Relations (*.csv);;Text Files (*.txt);;All Files (*)"))
filename = compatibility.get_filename(filename)
# Default relation's name
@@ -259,7 +280,9 @@ class relForm(QtGui.QMainWindow):
defname = defname[:-4]
if name == None: # Prompt dialog to insert name for the relation
res=QtGui.QInputDialog.getText(self, QtGui.QApplication.translate("Form", "New relation"),QtGui.QApplication.translate("Form", "Insert the name for the new relation"),
res = QtGui.QInputDialog.getText(
self, QtGui.QApplication.translate("Form", "New relation"), QtGui.QApplication.translate(
"Form", "Insert the name for the new relation"),
QtGui.QLineEdit.Normal, defname)
if res[1] == False or len(res[0]) == 0:
return
@@ -268,44 +291,58 @@ class relForm(QtGui.QMainWindow):
name = compatibility.get_py_str(res[0])
if not rtypes.is_valid_relation_name(name):
r=QtGui.QApplication.translate("Form", str("Wrong name for destination relation: %s." % name))
QtGui.QMessageBox.information(self,QtGui.QApplication.translate("Form", "Error"),r)
r = QtGui.QApplication.translate(
"Form", str("Wrong name for destination relation: %s." % name))
QtGui.QMessageBox.information(
self, QtGui.QApplication.translate("Form", "Error"), r)
return
try:
self.relations[name] = relation.relation(filename)
except Exception, e:
print e
QtGui.QMessageBox.information(None,QtGui.QApplication.translate("Form", "Error"),"%s\n%s" % (QtGui.QApplication.translate("Form", "Check your query!"),e.__str__()) )
QtGui.QMessageBox.information(None, QtGui.QApplication.translate("Form", "Error"), "%s\n%s" %
(QtGui.QApplication.translate("Form", "Check your query!"), e.__str__()))
return
self.updateRelations()
def addProduct(self):
self.addSymbolInQuery(u"*")
def addDifference(self):
self.addSymbolInQuery(u"-")
def addUnion(self):
self.addSymbolInQuery(u"")
def addIntersection(self):
self.addSymbolInQuery(u"")
def addDivision(self):
self.addSymbolInQuery(u"÷")
def addOLeft(self):
self.addSymbolInQuery(u"ᐅLEFTᐊ")
def addJoin(self):
self.addSymbolInQuery(u"ᐅᐊ")
def addORight(self):
self.addSymbolInQuery(u"ᐅRIGHTᐊ")
def addOuter(self):
self.addSymbolInQuery(u"ᐅFULLᐊ")
def addProjection(self):
self.addSymbolInQuery(u"π")
def addSelection(self):
self.addSymbolInQuery(u"σ")
def addRename(self):
self.addSymbolInQuery(u"ρ")
def addArrow(self):
self.addSymbolInQuery(u"")

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'relational_gui/maingui.ui'
#
# Created: Fri Dec 27 00:05:54 2013
# Created: Fri Dec 27 00:23:51 2013
# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'relational_gui/rel_edit.ui'
#
# Created: Fri Dec 27 00:05:54 2013
# Created: Fri Dec 27 00:23:51 2013
# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'relational_gui/survey.ui'
#
# Created: Fri Dec 27 00:05:54 2013
# Created: Fri Dec 27 00:23:51 2013
# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'relational_pyside/maingui.ui'
#
# Created: Fri Dec 27 00:05:55 2013
# Created: Fri Dec 27 00:23:51 2013
# by: pyside-uic 0.2.15 running on PySide 1.2.1
#
# WARNING! All changes made in this file will be lost!

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'relational_pyside/rel_edit.ui'
#
# Created: Fri Dec 27 00:05:55 2013
# Created: Fri Dec 27 00:23:51 2013
# by: pyside-uic 0.2.15 running on PySide 1.2.1
#
# WARNING! All changes made in this file will be lost!

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'relational_pyside/survey.ui'
#
# Created: Fri Dec 27 00:05:54 2013
# Created: Fri Dec 27 00:23:51 2013
# by: pyside-uic 0.2.15 running on PySide 1.2.1
#
# WARNING! All changes made in this file will be lost!

View File

@@ -17,7 +17,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# author Salvo "LtWorf" Tomaselli <tiposchi@tiscali.it>
# Initial readline code from http://www.doughellmann.com/PyMOTW/readline/index.html
# Initial readline code from
# http://www.doughellmann.com/PyMOTW/readline/index.html
import readline
import logging
@@ -31,7 +32,9 @@ from xtermcolor import colorize
PROMPT_COLOR = 0xffff00
ERROR_COLOR = 0xff0000
class SimpleCompleter(object):
'''Handles completion'''
def __init__(self, options):
@@ -95,11 +98,15 @@ class SimpleCompleter(object):
relations = {}
completer=SimpleCompleter(['SURVEY','LIST','LOAD ','UNLOAD ','HELP ','QUIT','SAVE ','_PRODUCT ','_UNION ','_INTERSECTION ','_DIFFERENCE ','_JOIN ','_LJOIN ','_RJOIN ','_FJOIN ','_PROJECTION ','_RENAME_TO ','_SELECTION ','_RENAME ','_DIVISION '])
completer = SimpleCompleter(
['SURVEY', 'LIST', 'LOAD ', 'UNLOAD ', 'HELP ', 'QUIT', 'SAVE ', '_PRODUCT ', '_UNION ', '_INTERSECTION ',
'_DIFFERENCE ', '_JOIN ', '_LJOIN ', '_RJOIN ', '_FJOIN ', '_PROJECTION ', '_RENAME_TO ', '_SELECTION ', '_RENAME ', '_DIVISION '])
def load_relation(filename, defname=None):
if not os.path.isfile(filename):
print >> sys.stderr, colorize("%s is not a file" % filename,ERROR_COLOR)
print >> sys.stderr, colorize(
"%s is not a file" % filename, ERROR_COLOR)
return None
f = filename.split('/')
@@ -109,7 +116,8 @@ def load_relation(filename,defname=None):
defname = defname[:-4]
if not rtypes.is_valid_relation_name(defname):
print >> sys.stderr, colorize("%s is not a valid relation name" % defname,ERROR_COLOR)
print >> sys.stderr, colorize(
"%s is not a valid relation name" % defname, ERROR_COLOR)
return
try:
relations[defname] = relation.relation(filename)
@@ -121,18 +129,21 @@ def load_relation(filename,defname=None):
print >>sys.stderr, colorize(e, ERROR_COLOR)
return None
def survey():
'''performs a survey'''
from relational import maintenance
post = {'software': 'Relational algebra (cli)', 'version': version}
fields=('System','Country','School','Age','How did you find','email (only if you want a reply)','Comments')
fields = ('System', 'Country', 'School', 'Age', 'How did you find',
'email (only if you want a reply)', 'Comments')
for i in fields:
a = raw_input('%s: ' % i)
post[i] = a
maintenance.send_survey(post)
def help(command):
'''Prints help on the various functions'''
p = command.split(' ', 1)
@@ -225,6 +236,7 @@ def exec_line(command):
else:
exec_query(command)
def replacements(query):
'''This funcion replaces ascii easy operators with the correct ones'''
query = query.replace(u'_PRODUCT', u'*')
@@ -242,6 +254,7 @@ def replacements(query):
query = query.replace(u'_DIVISION', u'÷')
return query
def exec_query(command):
'''This function executes a query and prints the result on the screen
if the command terminates with ";" the result will not be printed
@@ -255,8 +268,6 @@ def exec_query(command):
else:
printrel = True
# Performs replacements for weird operators
command = replacements(command)
@@ -270,7 +281,6 @@ def exec_query(command):
relname = 'last_'
query = command
# Execute query
try:
pyquery = parser.parse(query)
@@ -288,6 +298,7 @@ def exec_query(command):
except Exception, e:
print colorize(e, ERROR_COLOR)
def main(files=[]):
print colorize('> ', PROMPT_COLOR) + "; Type HELP to get the HELP"
print colorize('> ', PROMPT_COLOR) + "; Completion is activated using the tab (if supported by the terminal)"
@@ -301,7 +312,6 @@ def main(files=[]):
readline.parse_and_bind('set editing-mode emacs')
readline.set_completer_delims(" ")
while True:
try:
line = raw_input(colorize('> ', PROMPT_COLOR))
@@ -317,4 +327,3 @@ def main(files=[]):
if __name__ == "__main__":
main()