def testModel(self): session = model.SessionFactory() # Test the database is empty assert session.query(model.ArchitectureBlock.Id).count() == 0 # Add some architecture blocks and a View with model.sessionScope(session) as s: b1 = model.ArchitectureBlock(Name='blok1') b2 = model.ArchitectureBlock(Name='blok2') v1 = model.View(Name='view1') # Add a connection c1 = model.BlockConnection(theStart=b1, theEnd=b2) # Add function points fp1 = model.FunctionPoint(theDetails=c1, Name='fp1') fp2 = model.FunctionPoint(theBlock=b1, Name='fp2') # Add elements to the view br1 = model.BlockRepresentation(theDetails=b1, theView=v1) s.add(model.BlockRepresentation(theDetails=b1, theView=v1)) br2 = model.BlockRepresentation(theDetails=b2, theView=v1) fpr1 = model.FpRepresentation(theView=v1, theFp=fp1, theAnchor=br1) fpr2 = model.FpRepresentation(theView=v1, theFp=fp2, theAnchor=br2) s.add(fpr1) s.add(fpr2) assert session.query(model.FpRepresentation.Id).count() == 2 assert session.query(model.BlockRepresentation.Id).count() == 3 block1 = session.query(model.ArchitectureBlock).get(b1.Id) assert len(block1.Representations) == 2 # Delete Block two; check everything is deleted correctly with model.sessionScope(session) as s: s.query(model.ArchitectureBlock).filter(model.ArchitectureBlock.Id==b2.Id).delete() assert session.query(model.ArchitectureBlock.Id).count() == 1 self.assertEqual(session.query(model.BlockRepresentation.Id).count(), 2) self.assertEqual(session.query(model.BlockConnection.Id).count(), 0) self.assertEqual(session.query(model.FpRepresentation.Id).count(), 1) # Delete the View fp2uc = session.query(model.FpRepresentation).get(fpr2.Id) with model.sessionScope(session) as s: s.query(model.View).filter(model.View.Id==1).delete() self.assertEqual(session.query(model.BlockRepresentation.Id).count(), 0) self.assertEqual(session.query(model.FpRepresentation.Id).count(), 0) # Check that accessing ORM objects that have been deleted raises errors. try: print 'DELETED FP2UC', fp2uc self.fail('Should have raised an exception') except ObjectDeletedError: pass
def do(self, ctrl): source = self.start target = self.end # Find the connection object, or create it if it does not yet exist. # This connection is between Architecture Blocks, not their representations. with model.sessionScope(ctrl.session) as session: bc = model.BlockConnection conns = session.query(bc).\ filter(bc.Start.in_([source.Block, target.Block])).\ filter(bc.End.in_([source.Block, target.Block])).all() if conns: connection = conns[0] else: connection = model.BlockConnection(Start=source.Block, End=target.Block) session.add(connection) # We need a valid primary key, so flush the session but do not commit. session.flush() # Add the representation of the connection, between two representations of blocks. details = model.ConnectionRepresentation(Connection=connection.Id, theStart=source, theEnd=target, View=self.view) session.add(details) # Flush the database so that all references are updated. session.flush() return details
def onItemChanged(self, item, column): new_name = str(item.text(0)) if new_name != item.details.Name: item.details.Name = new_name # Ensure the changes are committed. with model.sessionScope(self.session): pass
def deleteItems(self, items): ''' Delete a set of items ''' to_remove = [] # A list of all graphic items to remove. # Use a scoped session to ensure the consistency of the database. with model.sessionScope(self.session) as session: for item in items: to_remove.append(item) # When deleting a block, remove the connections as well. # Deleting a block means only the representation is removed from the view, # so remove only the line, leave the connection in the model. details = item.details if isinstance(details, model.BlockRepresentation): block_id = details.Id for con in self.scene.connections.values(): if con.Start == block_id or con.End == block_id: to_remove.append(self.scene.connection_items[con.Id]) elif isinstance(details, model.ConnectionRepresentation): session.delete(details) # When deleting connections, check that there are no functionpoints on it. nr_fps = self.session.query(sql.func.count(model.FunctionPoint.Id)).\ filter(model.FunctionPoint.Connection==details.Id).one()[0] if nr_fps > 0: raise RuntimeError('Can not delete connection: has function points!') # Also check for annotations anchored on the item! session.delete(details) # No errors: actually delete the items from the drawing. for it in to_remove: self.scene.removeItem(it)
def dropEvent(self, event): ''' Replaces the dropEvent handler for the tree widgets. ''' # Check that the drop is from within the widget. if event.source() != self: event.ignore() return # Find out which item is dropped on. item = self.itemAt(event.pos()) # Check it is not dropped on itself if item in self.selectedItems(): event.ignore() return # Change the action from IGNORE to MOVE event.setDropAction(QtCore.Qt.MoveAction) # Get the current list of children children = None if item: children = [item.child(i) for i in range(item.childCount())] # Do the drop result = QtGui.QTreeWidget.dropEvent(self, event) # Find out which item was dropped on, and administrate the changes. with model.sessionScope(self.session_parent.getSession()): if item: new_children = [item.child(i) for i in range(item.childCount())] new_children = [ch for ch in new_children if ch not in children] parent_item = item.details.Id for ch in new_children: ch.details.Parent = parent_item else: # The dragged item has become a top-level item. # Find out which item it was from the mime data. details = self.session_parent.drop2Details(event) details.Parent = None
def executeQueries(self, queries): ''' Execute a set of queries, using a transaction. ''' session = sessionmaker(bind=self.engine)() with sessionScope(session) as s: for q in queries: s.execute(q)
def closeDetailsViewer(self): if self.details_viewer: # Automatically commit any changes from the editor with model.sessionScope(self.session): widget = self.ui.areaDetails.takeWidget() widget.close() self.ui.areaDetails.hide() self.details_viewer = None
def do(self, ctrl): with file(self.fname, 'rb') as f: data = f.read() with model.sessionScope(ctrl.session) as session: icon_name = os.path.split(self.fname)[-1] details = model.Icon(Name = icon_name, Data = data, Length = len(data)) session.add(details) return details
def do(self, ctrl): x, y = self.pos.x(), self.pos.y() with model.sessionScope(ctrl.session) as session: details = model.Annotation(View=self.view_id, x=x, y=y, AnchorPoint=self.anchor_id, Order = self.order, width=BLOCK_WIDTH, height=BLOCK_HEIGHT) session.add(details) return details
def onDeleteWorker(self, triggered=False): selection = self.ui.lstWorkers.selectedItems() if len(selection) == 0: return with model.sessionScope(self.session) as session: for item in selection: session.delete(item.details) for item in selection: index = self.ui.lstWorkers.row(item) _ = self.ui.lstWorkers.takeItem(index)
def onAddWorker(self, triggered=False): text, ok = QtGui.QInputDialog.getText(self, 'Nieuwe Werker', "Welke naam krijgt de nieuwe werker?") if not ok: return worker = model.Worker(Name=str(text)) with model.sessionScope(self.session) as session: session.add(worker) item = QtGui.QListWidgetItem(worker.Name, self.ui.lstWorkers) item.details = worker
def onEstimateDetails(self): # Commit any outstanding changes self.session.commit() with model.sessionScope(self.session) as session: project = self.ui.treeProjects.currentItem().details widget = EstimateDetails(None, project) action = QtGui.QAction('Export as CSV', self) action.triggered.connect(widget.exportCsv) result = showWidgetDialog(self, widget, [action]) if result != QtGui.QDialog.Accepted: # Rollback any changes made while editing session.rollback()
def onAssign(self, _checked): ''' Called when the user wants to assign an item to a user. ''' worker = str(self.ui.cmbWorker.currentText()) index = self.ui.tblItems.currentRow() item = self.table_contents[self.ui.tblItems][index] worker_id = self.session.query(model.Worker.Id).filter(model.Worker.Name==worker).one()[0] status = model.PlaneableStatus(Planeable=item[0].Id, AssignedTo=worker_id, Status=item[1], TimeRemaining=item[3], TimeSpent=item[4]) with model.sessionScope(self.session): self.session.add(status) # Update the views self.onWorkerChange() self.onFilterChange(False)
def addHandler(self, checked=False): ''' Add a new instance of the model class. ''' items = self.selectedItems() if len(items) == 1: # Not a top-level item: try to find the ID of the item. parent_item = items[0].details.Id else: parent_item = None details = self.model_class(Name='new item', Parent=parent_item) with model.sessionScope(self.session_parent.getSession()) as session: session.add(details) # As the session is closed, the new item is added to this tree. # Cause the item to be selected. self.session_parent.onTreeItemAdded(details)
def createDefaultStyle(session): with model.sessionScope(session): default = session.query(model.Style).filter(model.Style.Name=='Default').first() if not default: default = model.Style(Name='Default') session.add(default) default.Details = ''' background-color:white; font:Arial 12pt; halign:center; valign:center; is_gradient:True; color:#00529c; color1:white; color2:#00529c; text-is_gradient:False; icon:None; functionpoint-is_gradient:False; functionpoint-alpha:1.0; functionpoint-end:[[0,0], [-5, 5], [0, 0], [-5, -5], [0,0]]; arrow-functionpoint-offset:[-10,10]; connection-text-alpha:1.0; annotation-color2:#ffef5d; annotation-text-alpha:0.0; usecase-color2:#ff6347; usecase-text-alpha:0.0; width:3; archblock-width:1; archblock-offset:[0,0]; archblock-text-alpha:0.0; white-archblock-color2:white; dark-archblock-color2:#f2f2f2; type3-archblock-color2:#838383; time-archblock-color2:#ffc0cb; description-archblock-color2:#add8e6; party-archblock-color2:#90ee90; role-archblock-color2:#ffff80; inheritance-connection-end:[[0,0], [-10,10], [-10,-10], [0,0]]; aggregation-connection-start:[[0,0], [-10,-10], [-20,0], [-10,10], [0,0]]; association-connection-end:[[0,0],[-10,10],[0,0],[-10,-10],[0,0]]; ''' return default
def do(self, ctrl): new_details = None with model.sessionScope(ctrl.session): if isinstance(self.details, model.ArchitectureBlock): new_details = model.BlockRepresentation(Block=self.details.Id, View = self.view_id, x = self.coods.x(), y = self.coods.y(), height = BLOCK_HEIGHT, width = BLOCK_WIDTH, Order = self.order) ctrl.session.add(new_details) elif isinstance(self.details, model.View): new_details = model.UsecaseRepresentation(Parent=self.details.Id, View = self.view_id, x = self.coods.x(), y = self.coods.y(), height = BLOCK_HEIGHT, width = BLOCK_WIDTH, Order = self.order) ctrl.session.add(new_details) return new_details
def importData(data, db): table_renames = {'fptousecase': 'fprepresentation', 'fptoview':'fprepresentation', 'connection':'blockconnection'} table_ignore = ['colorpalette', 'dbaseversion'] tables = {tab.__tablename__:tab for tab in model.Base.getTables()} # Switch off the foreign keys for now model.check_fkeys = False engine = model.create_engine(db, echo=True) old_url = model.the_url model.the_url = db model.changeEngine(engine) # Clean the database model.cleanDatabase() try: session = model.SessionFactory() with model.sessionScope(session) as session: # In PostgreSQL, ensure the foreign keys are only checked at the commit, not before. if db.startswith('postgresql'): session.execute('SET CONSTRAINTS ALL DEFERRED') v = int(data['dbaseversion'][0]['Version']) if v < 6: upgradeToVersion6(data) del data['dbaseversion'] # Remove the old database version number # Add the current database version session.add(model.DbaseVersion()) # Treat the planeable and anchor items differently: these are polymorphic tables. # The base tables are not added directly but through their child tables, using the ORM poly_items = {} # Store the contents held by the polymorphic tables. These are needed later.. poly_bases = {} # Find the base for a specific table. for poly_table, poly_column in [('planeableitem', 'ItemType'), ('anchor', 'AnchorType')]: poly_items[poly_table] = {r['Id']:r for r in data[poly_table]} children = set([r[poly_column] for r in data[poly_table]]) for c in children: poly_bases[c] = poly_table # Do not add the table directly, so remove it from the list. del data[poly_table] for n1, n2 in table_renames.items(): if n1 in data: data[n2] = data[n1] for table, name in [(t, t.__tablename__) for t in model.order] + \ [(model.Base.metadata.tables['planeablexref'], 'planeablexref')]: records = data.get(name, []) if not records: continue # Start of a new table. if name in table_ignore: # Skip this table. continue if name not in tables: table = [name] else: table = tables[name] base_class = poly_bases.get(name, None) # Determine which fields are no longer used fields = records[0].keys() exclude = [f for f in fields if not hasattr(table, f)] for d in records: print 'Table:', name, 'data:', d # Exclude fields that have been removed from the database. if exclude: for e in exclude: del d[e] if base_class: # Add in the data stored in the polymorphic base table d.update(poly_items[base_class][d['Id']]) # Add the record to the database if name not in tables: # This class needs raw SQL to create. if d: ins = table.insert().values(**d) session.execute(ins) else: el = table(**d) session.add(el) if db.startswith('postgresql'): # Only update the sequence for direct children from MyBase. # This excludes all polymorphic derivatives (e.g. Requirement) that have no sequence. for table in model.MyBase.__subclasses__()[0].__subclasses__(): name = table.__tablename__ # In PostgreSQL, the sequences are not updated automatically in this case... if 'Id' in table.getFields(): # Fix the sequence number seqname = '%s_Id_seq'%name q = '''SELECT setval('"%s"', (SELECT MAX("Id") FROM %s))'''%(seqname, name) conn = engine.connect() conn.execute("commit") conn.execute(q) finally: model.the_engine = None model.SessionFactory = None model.check_fkeys = True model.the_url = old_url