New way of checking for new versions

master
Salvo 'LtWorf' Tomaselli 2013-12-27 00:16:12 +07:00
parent c637890795
commit 5e4a703f56
6 changed files with 105 additions and 83 deletions

@ -2,6 +2,8 @@
- Better tokenizer, gives more indicative errors - Better tokenizer, gives more indicative errors
- Parser gives more indicative errors - Parser gives more indicative errors
- Improved select_union_intersect_subtract optimization to avoid parenthesis whenever possible - Improved select_union_intersect_subtract optimization to avoid parenthesis whenever possible
- Moved feedback service, and added the code for it
- Different way of checking the latest version
1.1 1.1
- Incorrect relational operations now raise an exception instead of returning None - Incorrect relational operations now raise an exception instead of returning None

@ -1,7 +1,7 @@
application: feedback-ltworf application: feedback-ltworf
version: 2 version: 1
runtime: python27 runtime: python27
api_version: 1 api_version: 3
threadsafe: true threadsafe: true
handlers: handlers:

@ -9,16 +9,22 @@ application = micro_webapp2.WSGIApplication()
def m(request, *args, **kwargs): def m(request, *args, **kwargs):
return "" return ""
@application.route("/version/<id>")
def show_version(request, *args, **kwargs):
if kwargs["id"] == "relational":
return "1.2"
return "No version"
@application.route('/feedback/<id>') @application.route('/feedback/<id>')
def mail_sender(request, *args, **kwargs): def mail_sender(request, *args, **kwargs):
if request.method != "POST": if request.method != "POST":
return "" return ""
message = "" message = ""
for k,v in request.POST.iteritems(): for k,v in request.POST.iteritems():
message += "%s: %s\n" % (k,v) message += "%s: %s\n" % (k,v)
if kwargs["id"] == "relational": if kwargs["id"] == "relational":
from google.appengine.api import mail from google.appengine.api import mail

@ -1,20 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Relational # Relational
# Copyright (C) 2008 Salvo "LtWorf" Tomaselli # Copyright (C) 2008 Salvo "LtWorf" Tomaselli
# #
# Relation is free software: you can redistribute it and/or modify # Relation is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
# author Salvo "LtWorf" Tomaselli <tiposchi@tiscali.it> # author Salvo "LtWorf" Tomaselli <tiposchi@tiscali.it>
# #
# Stuff non-related to relational algebra, but used for maintenance. # Stuff non-related to relational algebra, but used for maintenance.
@ -26,7 +26,7 @@ import relation
def send_survey(data): def send_survey(data):
'''Sends the survey. Data must be a dictionary. '''Sends the survey. Data must be a dictionary.
returns the http response''' returns the http response'''
post='' post=''
for i in data.keys(): for i in data.keys():
post+='%s: %s\n' %(i,data[i]) post+='%s: %s\n' %(i,data[i])
@ -36,11 +36,11 @@ def send_survey(data):
headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain"} headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain"}
#connection = httplib.HTTPConnection('galileo.dmi.unict.it') #connection = httplib.HTTPConnection('galileo.dmi.unict.it')
#connection.request("POST","/~ltworf/survey.php",params,headers) #connection.request("POST","/~ltworf/survey.php",params,headers)
connection = httplib.HTTPConnection('feedback-ltworf.appspot.com') connection = httplib.HTTPConnection('feedback-ltworf.appspot.com')
connection.request("POST","/feedback/relational",params,headers) connection.request("POST","/feedback/relational",params,headers)
return connection.getresponse() return connection.getresponse()
@ -48,54 +48,48 @@ def check_latest_version():
'''Returns the latest version available. '''Returns the latest version available.
Heavely dependent on server and server configurations Heavely dependent on server and server configurations
not granted to work forever.''' not granted to work forever.'''
connection = httplib.HTTPConnection('galileo.dmi.unict.it') connection = httplib.HTTPConnection('feedback-ltworf.appspot.com')
connection.request("GET","/svn/relational/tags/") connection.request("GET","/version/relational")
r=connection.getresponse() r=connection.getresponse()
#html #html
s=r.read() s=r.read()
if len(s)==0: if len(s)==0:
return None return None
return s.strip()
l= s[s.find('<ul>')+4:s.find('</ul>')].split('\n')
l.sort()
a=l[len(l)-1]
s=a.find('"')+1
return a[s:a.find('"',s)-1]
class interface (object): class interface (object):
'''It is used to provide services to the user interfaces, in order to '''It is used to provide services to the user interfaces, in order to
reduce the amount of duplicated code present in different user interfaces. reduce the amount of duplicated code present in different user interfaces.
''' '''
def __init__(self): def __init__(self):
self.rels= {} self.rels= {}
def load(self,filename,name): def load(self,filename,name):
'''Loads a relation from file, and gives it a name to '''Loads a relation from file, and gives it a name to
be used in subsequent queries.''' be used in subsequent queries.'''
pass pass
def unload(self,name): def unload(self,name):
'''Unloads an existing relation.''' '''Unloads an existing relation.'''
pass pass
def store(self,filename,name): def store(self,filename,name):
'''Stores a relation to file.''' '''Stores a relation to file.'''
pass pass
def get_relation(self,name): def get_relation(self,name):
'''Returns the relation corresponding to name.''' '''Returns the relation corresponding to name.'''
pass pass
def set_relation(self,name,rel): def set_relation(self,name,rel):
'''Sets the relation corresponding to name.''' '''Sets the relation corresponding to name.'''
pass pass
def execute(self,query,relname='last_'): def execute(self,query,relname='last_'):
'''Executes a query, returns the result and if '''Executes a query, returns the result and if
relname is not None, adds the result to the relname is not None, adds the result to the
dictionary, with the name given in relname.''' dictionary, with the name given in relname.'''
pass pass

@ -112,6 +112,8 @@ if __name__ == "__main__":
guihandler.version=version guihandler.version=version
app = QtGui.QApplication(sys.argv) app = QtGui.QApplication(sys.argv)
app.setOrganizationName('None');
app.setApplicationName('relational');
ui = maingui.Ui_MainWindow() ui = maingui.Ui_MainWindow()
Form = guihandler.relForm(ui) Form = guihandler.relForm(ui)
@ -120,6 +122,7 @@ if __name__ == "__main__":
Form.setFont(QtGui.QFont("Dejavu Sans Bold")) Form.setFont(QtGui.QFont("Dejavu Sans Bold"))
ui.setupUi(Form) ui.setupUi(Form)
Form.restore_settings()
for i in range(len(files)): for i in range(len(files)):
if not os.path.isfile(files[i]): if not os.path.isfile(files[i]):

@ -1,38 +1,43 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Relational # Relational
# Copyright (C) 2008 Salvo "LtWorf" Tomaselli # Copyright (C) 2008 Salvo "LtWorf" Tomaselli
# #
# Relational is free software: you can redistribute it and/or modify # Relational is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
# author Salvo "LtWorf" Tomaselli <tiposchi@tiscali.it> # author Salvo "LtWorf" Tomaselli <tiposchi@tiscali.it>
import sys
import os
import pickle
try: try:
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
except: except:
from PySide import QtCore, QtGui from PySide import QtCore, QtGui
from relational import relation, parser, optimizer,rtypes from relational import relation, parser, optimizer,rtypes
import sys
import about import about
import survey import survey
import os
import surveyForm import surveyForm
import maingui import maingui
import compatibility import compatibility
class relForm(QtGui.QMainWindow): class relForm(QtGui.QMainWindow):
def __init__(self,ui): def __init__(self,ui):
QtGui.QMainWindow.__init__(self) QtGui.QMainWindow.__init__(self)
self.About=None self.About=None
@ -42,23 +47,26 @@ class relForm(QtGui.QMainWindow):
self.selectedRelation=None self.selectedRelation=None
self.ui=ui self.ui=ui
self.qcounter=1 #Query counter self.qcounter=1 #Query counter
self.settings = QtCore.QSettings()
def checkVersion(self): def checkVersion(self):
from relational import maintenance from relational import maintenance
online=maintenance.check_latest_version() online=maintenance.check_latest_version()
if online>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: elif online==version:
r=QtGui.QApplication.translate("Form", "Latest version installed.") r=QtGui.QApplication.translate("Form", "Latest version installed.")
else: else:
r=QtGui.QApplication.translate("Form", "You are using an unstable version.") r=QtGui.QApplication.translate("Form", "You are using an unstable version.")
QtGui.QMessageBox.information(self,QtGui.QApplication.translate("Form", "Version"),r) QtGui.QMessageBox.information(self,QtGui.QApplication.translate("Form", "Version"),r)
def load_query(self,*index): def load_query(self,*index):
self.ui.txtQuery.setText(self.savedQ.itemData(index[0]).toString()) self.ui.txtQuery.setText(self.savedQ.itemData(index[0]).toString())
def undoOptimize(self): def undoOptimize(self):
'''Undoes the optimization on the query, popping one item from the undo list''' '''Undoes the optimization on the query, popping one item from the undo list'''
if self.undo!=None: if self.undo!=None:
@ -67,37 +75,37 @@ class relForm(QtGui.QMainWindow):
def optimize(self): def optimize(self):
'''Performs all the possible optimizations on the query''' '''Performs all the possible optimizations on the query'''
self.undo=self.ui.txtQuery.text() #Storing the query in undo list self.undo=self.ui.txtQuery.text() #Storing the query in undo list
query=compatibility.get_py_str(self.ui.txtQuery.text()) query=compatibility.get_py_str(self.ui.txtQuery.text())
try: try:
result=optimizer.optimize_all(query,self.relations) result=optimizer.optimize_all(query,self.relations)
compatibility.set_utf8_text(self.ui.txtQuery,result) compatibility.set_utf8_text(self.ui.txtQuery,result)
except Exception, e: 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): def resumeHistory(self,item):
itm=compatibility.get_py_str(item.text()).split(' = ',1) itm=compatibility.get_py_str(item.text()).split(' = ',1)
compatibility.set_utf8_text(self.ui.txtResult,itm[0]) compatibility.set_utf8_text(self.ui.txtResult,itm[0])
compatibility.set_utf8_text(self.ui.txtQuery,itm[1]) compatibility.set_utf8_text(self.ui.txtQuery,itm[1])
def execute(self): def execute(self):
'''Executes the query''' '''Executes the query'''
query=compatibility.get_py_str(self.ui.txtQuery.text()) 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): 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 return
try: try:
#Converting string to utf8 and then from qstring to normal string #Converting string to utf8 and then from qstring to normal string
expr=parser.parse(query)#Converting expression to python code expr=parser.parse(query)#Converting expression to python code
print query,"-->" , expr #Printing debug print query,"-->" , expr #Printing debug
result=eval(expr,self.relations) #Evaluating the expression 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.updateRelations() #update the list
self.selectedRelation=result self.selectedRelation=result
@ -111,44 +119,44 @@ class relForm(QtGui.QMainWindow):
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')) #item=item.decode('utf-8'))
compatibility.add_list_item(self.ui.lstHistory,item) compatibility.add_list_item(self.ui.lstHistory,item)
self.qcounter+=1 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): def showRelation(self,rel):
'''Shows the selected relation into the table''' '''Shows the selected relation into the table'''
self.ui.table.clear() self.ui.table.clear()
if rel==None: #No relation to show if rel==None: #No relation to show
self.ui.table.setColumnCount(1) self.ui.table.setColumnCount(1)
self.ui.table.headerItem().setText(0,"Empty relation") self.ui.table.headerItem().setText(0,"Empty relation")
return return
self.ui.table.setColumnCount(len(rel.header.attributes)) self.ui.table.setColumnCount(len(rel.header.attributes))
#Set content #Set content
for i in rel.content: for i in rel.content:
item = QtGui.QTreeWidgetItem() item = QtGui.QTreeWidgetItem()
for j in range(len(i)): for j in range(len(i)):
item.setText(j, i[j]) item.setText(j, i[j])
self.ui.table.addTopLevelItem(item) self.ui.table.addTopLevelItem(item)
#Sets columns #Sets columns
for i in range(len(rel.header.attributes)): for i in range(len(rel.header.attributes)):
self.ui.table.headerItem().setText(i,rel.header.attributes[i]) 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): 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) self.showRelation(self.selectedRelation)
def showAttributes(self,item): def showAttributes(self,item):
'''Shows the attributes of the selected relation''' '''Shows the attributes of the selected relation'''
rel=compatibility.get_py_str(item.text()) rel=compatibility.get_py_str(item.text())
self.ui.lstAttributes.clear() self.ui.lstAttributes.clear()
for j in self.relations[rel].header.attributes: for j in self.relations[rel].header.attributes:
self.ui.lstAttributes.addItem (j) self.ui.lstAttributes.addItem (j)
def updateRelations(self): def updateRelations(self):
self.ui.lstRelations.clear() self.ui.lstRelations.clear()
for i in self.relations: for i in self.relations:
@ -156,8 +164,8 @@ class relForm(QtGui.QMainWindow):
self.ui.lstRelations.addItem(i) self.ui.lstRelations.addItem(i)
def saveRelation(self): 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) filename=compatibility.get_filename(filename)
if (len(filename)==0):#Returns if no file was selected if (len(filename)==0):#Returns if no file was selected
return return
self.selectedRelation.save(filename) self.selectedRelation.save(filename)
@ -176,36 +184,45 @@ class relForm(QtGui.QMainWindow):
def newRelation(self): def newRelation(self):
import creator import creator
result=creator.edit_relation() result=creator.edit_relation()
if result==None: if result==None:
return return
res=QtGui.QInputDialog.getText( res=QtGui.QInputDialog.getText(
self, self,
QtGui.QApplication.translate("Form", "New relation"), 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,'') QtGui.QLineEdit.Normal,'')
if res[1]==False or len(res[0])==0: if res[1]==False or len(res[0])==0:
return return
#Patch provided by Angelo 'Havoc' Puglisi #Patch provided by Angelo 'Havoc' Puglisi
name=compatibility.get_py_str(res[0]) name=compatibility.get_py_str(res[0])
if not rtypes.is_valid_relation_name(name): if not rtypes.is_valid_relation_name(name):
r=QtGui.QApplication.translate("Form", str("Wrong name for destination relation: %s." % name)) r=QtGui.QApplication.translate("Form", str("Wrong name for destination relation: %s." % name))
QtGui.QMessageBox.information(self,QtGui.QApplication.translate("Form", "Error"),r) QtGui.QMessageBox.information(self,QtGui.QApplication.translate("Form", "Error"),r)
return return
try: try:
self.relations[name]=result self.relations[name]=result
except Exception, e: except Exception, e:
print 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 return
self.updateRelations()
self.updateRelations()
def closeEvent(self, event):
self.save_settings()
event.accept()
def save_settings(self):
#self.settings.setValue("width",)
pass
def restore_settings(self):
#self.settings.value('session_name','default').toString()
pass
def showSurvey(self): def showSurvey(self):
if self.Survey==None: if self.Survey==None:
self.Survey=surveyForm.surveyForm() self.Survey=surveyForm.surveyForm()
@ -214,6 +231,7 @@ class relForm(QtGui.QMainWindow):
ui.setupUi(self.Survey) ui.setupUi(self.Survey)
self.Survey.setDefaultValues() self.Survey.setDefaultValues()
self.Survey.show() self.Survey.show()
def showAbout(self): def showAbout(self):
if self.About==None: if self.About==None:
self.About = QtGui.QDialog() self.About = QtGui.QDialog()
@ -233,35 +251,35 @@ class relForm(QtGui.QMainWindow):
#Default relation's name #Default relation's name
f=filename.split('/') #Split the full path f=filename.split('/') #Split the full path
defname=f[len(f)-1].lower() #Takes only the lowercase filename defname=f[len(f)-1].lower() #Takes only the lowercase filename
if len(defname)==0: if len(defname)==0:
return return
if (defname.endswith(".csv")): #removes the extension if (defname.endswith(".csv")): #removes the extension
defname=defname[:-4] defname=defname[:-4]
if name==None: #Prompt dialog to insert name for the relation 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) QtGui.QLineEdit.Normal,defname)
if res[1]==False or len(res[0])==0: if res[1]==False or len(res[0])==0:
return return
#Patch provided by Angelo 'Havoc' Puglisi #Patch provided by Angelo 'Havoc' Puglisi
name=compatibility.get_py_str(res[0]) name=compatibility.get_py_str(res[0])
if not rtypes.is_valid_relation_name(name): if not rtypes.is_valid_relation_name(name):
r=QtGui.QApplication.translate("Form", str("Wrong name for destination relation: %s." % name)) r=QtGui.QApplication.translate("Form", str("Wrong name for destination relation: %s." % name))
QtGui.QMessageBox.information(self,QtGui.QApplication.translate("Form", "Error"),r) QtGui.QMessageBox.information(self,QtGui.QApplication.translate("Form", "Error"),r)
return return
try: try:
self.relations[name]=relation.relation(filename) self.relations[name]=relation.relation(filename)
except Exception, e: except Exception, e:
print 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 return
self.updateRelations() self.updateRelations()
def addProduct(self): def addProduct(self):
@ -290,8 +308,7 @@ class relForm(QtGui.QMainWindow):
self.addSymbolInQuery(u"ρ") self.addSymbolInQuery(u"ρ")
def addArrow(self): def addArrow(self):
self.addSymbolInQuery(u"") self.addSymbolInQuery(u"")
def addSymbolInQuery(self,symbol): def addSymbolInQuery(self,symbol):
self.ui.txtQuery.insert(symbol) self.ui.txtQuery.insert(symbol)
self.ui.txtQuery.setFocus() self.ui.txtQuery.setFocus()