def test_save_version(self): ap = VersionAdminPanel(self.env) old_name = '2.0' new_name = '4.0 dev' version = Version(self.env, old_name) self.assertEqual(old_name, version.name) req = MockRequest(self.env, method='POST', args={ 'name': ' 1.0 ', 'save': True }) with self.assertRaises(ResourceExistsError) as cm: ap.render_admin_panel(req, 'ticket', 'version', old_name) self.assertIn('Version "1.0" already exists', unicode(cm.exception)) req = MockRequest(self.env, method='POST', args={ 'name': ' 4.0 \t dev ', 'save': True }) self.assertRaises(RequestDone, ap.render_admin_panel, req, 'ticket', 'version', old_name) self.assertIn('Your changes have been saved.', req.chrome['notices']) version = Version(self.env, new_name) self.assertEqual(new_name, version.name) self.assertRaises(ResourceNotFound, ap.render_admin_panel, req, 'ticket', 'version', old_name) self.assertRaises(ResourceNotFound, Version, self.env, old_name)
def process_request(self, req): version_id = req.args.get('id') version_project = req.args.get('project', '') db = self.env.get_db_cnx() # TODO: db can be removed action = req.args.get('action', 'view') try: version = Version(self.env, version_id, db) except: version = Version(self.env, None, db) version.name = version_id action = 'edit' # rather than 'new' so that it works for POST/save if req.method == 'POST': if req.args.has_key('cancel'): if version.exists: req.redirect(req.href.version(version.name)) else: req.redirect(req.href.roadmap()) elif action == 'edit': return self._do_save(req, db, version) elif action == 'delete': self._do_delete(req, version) elif action in ('new', 'edit'): return self._render_editor(req, db, version) elif action == 'delete': return self._render_confirm(req, db, version) if not version.name: req.redirect(req.href.roadmap()) return self._render_view(req, db, version)
def copy_version(source_env, dest_env, name, dest_db=None): # In case a string gets passed in if not isinstance(source_env, Environment): source_env = _open_environment(source_env) if not isinstance(dest_env, Environment): dest_env = _open_environment(dest_env) # Log message source_env.log.info('DatamoverPlugin: Moving version %s to the environment at %s', name, dest_env.path) dest_env.log.info('DatamoverPlugin: Moving version %s from the environment at %s', name, source_env.path) # Open databases source_db = source_env.get_db_cnx() source_cursor = source_db.cursor() handle_commit = True if not dest_db: dest_db, handle_commit = dest_env.get_db_cnx(), False dest_cursor = dest_db.cursor() # Remove the version from the destination try: dest_version = Version(dest_env, name, db=dest_db) dest_version.delete(db=dest_db) except TracError: pass # Copy each entry in the version table source_cursor.execute('SELECT * FROM version WHERE name=%s',(name,)) for row in source_cursor: version_data = dict(zip([d[0] for d in source_cursor.description], row)) q = make_query(version_data, 'version') dest_cursor.execute(*q) if handle_commit: dest_db.commit()
def milestone_changed(self, milestone, old_values): if not milestone.is_completed or 'completed' not in old_values \ or old_values['completed'] is not None: return m = re.match(self.pattern, milestone.name) if not m: return version_name = m.groupdict().get('version') if not version_name: return try: version = Version(self.env, version_name) if not version.time: version.time = milestone.completed version.update() self.log.info('Existing version "%s" updated with completion ' 'time from milestone "%s"', version.name, milestone.name) else: self.log.info('Version "%s" already exists. No new version ' 'created from milestone "%s"', version.name, milestone.name) except ResourceNotFound: version = Version(self.env) version.name = version_name version.time = milestone.completed version.insert() self.log.info('New version "%s" created from completed milestone ' '"%s".', version.name, milestone.name)
def test_get_ticket_fields_version_rename(self): """Cached ticket fields are updated when version is renamed.""" fields = self.ticket_system.get_ticket_fields() version_field = self._get_ticket_field('version') v2 = Version(self.env, '2.0') v2.name = '0.0' v2.update() updated_fields = self.ticket_system.get_ticket_fields() updated_version_field = self._get_ticket_field('version') self.assertNotEqual(fields, updated_fields) self.assertEqual(['2.0', '1.0'], version_field['options']) self.assertEqual(['1.0', '0.0'], updated_version_field['options'])
def _render_editor(self, req, db, version): # Suggest a default due time of 18:00 in the user's timezone default_time = datetime.now(req.tz).replace(hour=18, minute=0, second=0, microsecond=0) if default_time <= datetime.now(utc): default_time += timedelta(days=1) data = { 'version': version, 'datetime_hint': get_datetime_format_hint(), 'default_time': default_time } if version.exists: req.perm.require('MILESTONE_MODIFY') versions = [ v for v in Version.select(self.env, db=db) if v.name != version.name and 'MILESTONE_VIEW' in req.perm ] else: req.perm.require('MILESTONE_CREATE') Chrome(self.env).add_wiki_toolbars(req) return 'version_edit.html', data, None
def _versions_and_stats(self, req, filter_projects): req.perm.require('MILESTONE_VIEW') db = self.env.get_db_cnx() versions = Version.select(self.env, db) filtered_versions = [] stats = [] show = req.args.getlist('show') for version in sorted(versions, key=lambda v: self._version_time(v)): project = self.__SmpModel.get_project_version(version.name) if not filter_projects or (project and project[0] in filter_projects): if not version.time or version.time.replace( tzinfo=None) >= datetime.now() or 'completed' in show: if version.time: if version.time.replace(tzinfo=None) >= datetime.now(): version.is_due = True else: version.is_completed = True filtered_versions.append(version) tickets = get_tickets_for_any(self.env, db, 'version', version.name, 'owner') tickets = apply_ticket_permissions(self.env, req, tickets) stat = get_ticket_stats(self.stats_provider, tickets) stats.append( any_stats_data(self.env, req, stat, 'version', version.name)) return filtered_versions, stats
def process_request(self, req): version_id = req.args.get('id') req.perm('version', version_id).require('VERSION_VIEW') version = Version(self.env, version_id) action = req.args.get('action', 'view') if req.method == 'POST': if 'cancel' in req.args: if version.exists: req.redirect(req.href.version(version.name)) else: req.redirect(req.href.versions()) elif action == 'edit': return self._do_save(req, version) elif action == 'delete': self._do_delete(req, version) elif action in ('new', 'edit'): return self._render_editor(req, version) elif action == 'delete': return self._render_confirm(req, version) if not version.name: req.redirect(req.href.versions()) add_stylesheet(req, 'common/css/roadmap.css') return self._render_view(req, version)
def _versions_and_stats(self, req, filter_projects): req.perm.require('MILESTONE_VIEW') db = self.env.get_db_cnx() versions = Version.select(self.env, db) filtered_versions = [] stats = [] show = req.args.getlist('show') for version in sorted(versions, key=lambda v: self._version_time(v)): project = self.__SmpModel.get_project_version(version.name) if not filter_projects or (project and project[0] in filter_projects): if not version.time or version.time.replace(tzinfo=None) >= datetime.now() or 'completed' in show: if version.time: if version.time.replace(tzinfo=None) >= datetime.now(): version.is_due = True; else: version.is_completed = True; filtered_versions.append(version) tickets = get_tickets_for_any(self.env, db, 'version', version.name, 'owner') tickets = apply_ticket_permissions(self.env, req, tickets) stat = get_ticket_stats(self.stats_provider, tickets) stats.append(any_stats_data(self.env, req, stat, 'version', version.name)) return filtered_versions, stats
def _render_confirm(self, req, db, version): req.perm.require('MILESTONE_DELETE') version = [ v for v in Version.select(self.env, db=db) if v.name != version.name and 'MILESTONE_VIEW' in req.perm ] data = {'version': version} return 'version_delete.html', data, None
def process_admin_request(self, req, cat, page, path_info): envs = DatamoverSystem(self.env).all_environments() versions = [v.name for v in Version.select(self.env)] if req.method == 'POST': source_type = req.args.get('source') if not source_type or source_type not in ('version', 'all'): raise TracError, "Source type not specified or invalid" source = req.args.get(source_type) dest = req.args.get('destination') action = None if 'copy' in req.args.keys(): action = 'copy' elif 'move' in req.args.keys(): action = 'move' else: raise TracError, 'Action not specified or invalid' action_verb = {'copy': 'Copied', 'move': 'Moved'}[action] ver_filter = None if source_type == 'version': in_versions = req.args.getlist('version') ver_filter = lambda c: c in in_versions elif source_type == 'all': ver_filter = lambda c: True try: sel_versions = [v for v in versions if ver_filter(v)] dest_db = _open_environment(dest).get_db_cnx() for version in sel_versions: copy_version(self.env, dest, version, dest_db) dest_db.commit() if action == 'move': for version in sel_versions: Version(self.env, version).delete() req.hdf['datamover.message'] = '%s versions %s' % ( action_verb, ', '.join(sel_versions)) except TracError, e: req.hdf[ 'datamover.message'] = "An error has occured: \n" + str(e) self.log.warn(req.hdf['datamover.message'], exc_info=True)
def test_get_ticket_fields_version_update_time(self): """Cached ticket fields are updated when version release time is changed. """ fields = self.ticket_system.get_ticket_fields() version_field = self._get_ticket_field('version') v1 = Version(self.env, '1.0') v1.time = datetime_now(utc) v2 = Version(self.env, '2.0') v2.time = v1.time - timedelta(seconds=1) v1.update() v2.update() updated_fields = self.ticket_system.get_ticket_fields() updated_version_field = self._get_ticket_field('version') self.assertNotEqual(fields, updated_fields) self.assertEqual(['2.0', '1.0'], version_field['options']) self.assertEqual(['1.0', '2.0'], updated_version_field['options'])
def _render_confirm(self, req, db, version): req.perm.require('MILESTONE_DELETE') version = [v for v in Version.select(self.env, db=db) if v.name != version.name and 'MILESTONE_VIEW' in req.perm] data = { 'version': version } return 'version_delete.html', data, None
def test_create_and_update(self): version = Version(self.env) version.name = 'Test' version.insert() self.assertEqual([('Test', 0, None)], self.env.db_query( "SELECT name, time, description FROM version WHERE name='Test'")) # Use the same model object to update the version version.description = 'Some text' version.update() self.assertEqual([('Test', 0, 'Some text')], self.env.db_query( "SELECT name, time, description FROM version WHERE name='Test'"))
def test_add_version(self): name = '3.0' ap = VersionAdminPanel(self.env) req = MockRequest(self.env, method='POST', args={'name': name, 'add': True}) self.assertRaises(ResourceNotFound, Version, self.env, name) self.assertRaises(RequestDone, ap.render_admin_panel, req, 'ticket', 'version', None) version = Version(self.env, name) self.assertEqual(name, version.name)
def _find_first_version_from_list(self, possible_versions): #print('_find_first_version_from_list %s' % str(possible_versions)) ret = None for v_name in possible_versions: try: ver = Version(self.env, v_name) ret = ver.name break except ResourceNotFound: # No such component exists pass return ret
def copy_version(source_env, dest_env, name, dest_db=None): # In case a string gets passed in if not isinstance(source_env, Environment): source_env = _open_environment(source_env) if not isinstance(dest_env, Environment): dest_env = _open_environment(dest_env) # Log message source_env.log.info( 'DatamoverPlugin: Moving version %s to the environment at %s', name, dest_env.path) dest_env.log.info( 'DatamoverPlugin: Moving version %s from the environment at %s', name, source_env.path) # Open databases source_db = source_env.get_db_cnx() source_cursor = source_db.cursor() handle_commit = True if not dest_db: dest_db, handle_commit = dest_env.get_db_cnx(), False dest_cursor = dest_db.cursor() # Remove the version from the destination try: dest_version = Version(dest_env, name, db=dest_db) dest_version.delete(db=dest_db) except TracError: pass # Copy each entry in the version table source_cursor.execute('SELECT * FROM version WHERE name=%s', (name, )) for row in source_cursor: version_data = dict(zip([d[0] for d in source_cursor.description], row)) q = make_query(version_data, 'version') dest_cursor.execute(*q) if handle_commit: dest_db.commit()
def test_create_and_update(self): version = Version(self.env) version.name = 'Test' version.insert() cursor = self.db.cursor() cursor.execute("SELECT name,time,description FROM version " "WHERE name='Test'") self.assertEqual(('Test', 0, None), cursor.fetchone()) # Use the same model object to update the version version.description = 'Some text' version.update() cursor.execute("SELECT name,time,description FROM version " "WHERE name='Test'") self.assertEqual(('Test', 0, 'Some text'), cursor.fetchone())
def test_create_and_update(self): version = Version(self.env) version.name = "Test" version.insert() cursor = self.db.cursor() cursor.execute("SELECT name,time,description FROM version " "WHERE name='Test'") self.assertEqual(("Test", 0, None), cursor.fetchone()) # Use the same model object to update the version version.description = "Some text" version.update() cursor.execute("SELECT name,time,description FROM version " "WHERE name='Test'") self.assertEqual(("Test", 0, "Some text"), cursor.fetchone())
def test_add_version_with_spaces(self): name = '4.0 dev' ap = VersionAdminPanel(self.env) req = MockRequest(self.env, method='POST', args={'name': ' 4.0 \t dev ', 'add': True}) self.assertRaises(ResourceNotFound, Version, self.env, name) self.assertRaises(RequestDone, ap.render_admin_panel, req, 'ticket', 'version', None) self.assertIn('The version "4.0 dev" has been added.', req.chrome['notices']) version = Version(self.env, name) self.assertEqual(name, version.name) with self.assertRaises(ResourceExistsError) as cm: ap.render_admin_panel(req, 'ticket', 'version', None) self.assertIn('Version "4.0 dev" already exists', str(cm.exception))
def test_get_ticket_fields_version_update_time(self): """Cached ticket fields are updated when version release time is changed. """ fields = self.ticket_system.get_ticket_fields() version_field = self._get_ticket_field('version') v1 = Version(self.env, '1.0') v1.time = datetime.now(utc) v2 = Version(self.env, '2.0') v2.time = v1.time - timedelta(seconds=1) v1.update() v2.update() updated_fields = self.ticket_system.get_ticket_fields() updated_version_field = self._get_ticket_field('version') self.assertNotEqual(fields, updated_fields) self.assertEqual(['2.0', '1.0'], version_field['options']) self.assertEqual(['1.0', '2.0'], updated_version_field['options'])
def _render_link(self, context, name, label, extra=''): try: version = Version(self.env, name) except TracError: version = None # Note: the above should really not be needed, `Milestone.exists` # should simply be false if the milestone doesn't exist in the db # (related to #4130) href = context.href.version(name) if version and version.exists: resource = Resource('version', name) if 'VERSION_VIEW' in context.perm(resource): return tag.a(label, class_='version', href=href + extra) elif 'VERSION_CREATE' in context.perm('version', name): return tag.a(label, class_='missing version', href=href + extra, rel='nofollow') return tag.a(label, class_='missing version')
def process_admin_request(self, req, cat, page, path_info): envs = DatamoverSystem(self.env).all_environments() versions = [v.name for v in Version.select(self.env)] if req.method == 'POST': source_type = req.args.get('source') if not source_type or source_type not in ('version', 'all'): raise TracError, "Source type not specified or invalid" source = req.args.get(source_type) dest = req.args.get('destination') action = None if 'copy' in req.args.keys(): action = 'copy' elif 'move' in req.args.keys(): action = 'move' else: raise TracError, 'Action not specified or invalid' action_verb = {'copy':'Copied', 'move':'Moved'}[action] ver_filter = None if source_type == 'version': in_versions = req.args.getlist('version') ver_filter = lambda c: c in in_versions elif source_type == 'all': ver_filter = lambda c: True try: sel_versions = [v for v in versions if ver_filter(v)] dest_db = _open_environment(dest).get_db_cnx() for version in sel_versions: copy_version(self.env, dest, version, dest_db) dest_db.commit() if action == 'move': for version in sel_versions: Version(self.env, version).delete() req.hdf['datamover.message'] = '%s versions %s'%(action_verb, ', '.join(sel_versions)) except TracError, e: req.hdf['datamover.message'] = "An error has occured: \n"+str(e) self.log.warn(req.hdf['datamover.message'], exc_info=True)
def _render_editor(self, req, version): resource = Resource('version', version.name) data = { 'version': version, 'resource': resource, 'versions': [ver.name for ver in Version.select(self.env)], 'datetime_hint': get_datetime_format_hint(), 'version_groups': [], } if version.exists: req.perm(resource).require('VERSION_MODIFY') #versions = [m for m in Version.select(self.env) # if m.name != version.name # and 'VERSION_VIEW' in req.perm(m.resource)] else: req.perm(resource).require('VERSION_CREATE') Chrome(self.env).add_jquery_ui(req) Chrome(self.env).add_wiki_toolbars(req) add_stylesheet(req, 'common/css/roadmap.css') return 'version_edit.html', data, None
def _render_editor(self, req, db, version): # Suggest a default due time of 18:00 in the user's timezone default_time = datetime.now(req.tz).replace(hour=18, minute=0, second=0, microsecond=0) if default_time <= datetime.now(utc): default_time += timedelta(days=1) data = { 'version': version, 'datetime_hint': get_datetime_format_hint(), 'default_time': default_time } if version.exists: req.perm.require('MILESTONE_MODIFY') versions = [v for v in Version.select(self.env, db=db) if v.name != version.name and 'MILESTONE_VIEW' in req.perm] else: req.perm.require('MILESTONE_CREATE') Chrome(self.env).add_wiki_toolbars(req) return 'version_edit.html', data, None
def milestone_changed(self, milestone, old_values): if not milestone.is_completed or 'completed' not in old_values \ or old_values['completed'] is not None: return m = re.match(self.pattern, milestone.name) if not m: return version_name = m.groupdict().get('version') if not version_name: return try: version = Version(self.env, version_name) if not version.time: version.time = milestone.completed version.update() self.log.info('Existing version "%s" updated with completion ' 'time from milestone "%s"' % (version.name, milestone.name)) else: self.log.info('Version "%s" already exists. No new version ' 'created from milestone "%s"' % (version.name, milestone.name)) except ResourceNotFound: version = Version(self.env) version.name = version_name version.time = milestone.completed version.insert() self.log.info('New version "%s" created from completed milstone ' '"%s".' % (version.name, milestone.name))
def process_request(self, req): name = req.args.get('name') if not (name == 'query'): id = req.args.get('id') if name == 'ticket': ticket = Ticket(self.env, id) comm_num = 0 attachment_num = len(self.get_attachments('ticket', id)) ticket_log = self.changeLog(id) for log in ticket_log: if log[2] == 'comment' and log[4]: comm_num += 1 data = {'ticket': ticket, 'comm_num': comm_num, 'attachment_num': attachment_num} return 'bh_emb_ticket.html', data, None elif name == 'milestone': ticket_num = len(get_tickets_for_milestone(self.env, milestone=id)) attachment_num = len(self.get_attachments('milestone', id)) data = {'milestone': Milestone(self.env, id), 'product': self.env.product, 'ticket_number': ticket_num, 'attachment_number': attachment_num } return 'bh_emb_milestone.html', data, None elif name == 'products': product = Product(self.env, {'prefix': id}) ticket_num = len(self.get_tickets_for_product(self.env, id)) product_env = ProductEnvironment(self.env, product.prefix) milestone_num = len(Milestone.select(product_env)) version_num = len(Version.select(product_env)) components = component.select(product_env) component_num = 0 for c in components: component_num += 1 data = {'product': product, 'ticket_num': ticket_num, 'owner': product.owner, 'milestone_num': milestone_num, 'version_num': version_num, 'component_num': component_num} return 'bh_emb_product.html', data, None elif name == 'query': qstr = req.query_string qstr = urllib.unquote(qstr).decode('utf8') if qstr=='': qstr = 'status!=closed' qresults = self.query(req, qstr) filters = qresults[0] tickets = qresults[1] data={'tickets': tickets, 'query': qstr, 'filters': filters} return 'bh_emb_query.html', data, None else: msg = "It is not possible to embed this resource." raise ResourceNotFound((msg), ("Invalid resource"))
def test_exists(self): """ http://trac.edgewall.org/ticket/4247 """ for v in Version.select(self.env): self.assertEqual(v.exists, True)
def resource_exists(self, resource): try: Version(self.env, resource.id) return Version.exists except ResourceNotFound: return False
def _do_save(self, req, version): resource = Resource('version', version.name) if version.exists: req.perm(resource).require('VERSION_MODIFY') else: req.perm(resource).require('VERSION_CREATE') old_name = version.name new_name = req.args.get('name') version.name = new_name version.description = req.args.get('description', '') time = req.args.get('time', '') # Instead of raising one single error, check all the constraints and # let the user fix them by going back to edit mode showing the warnings warnings = [] def warn(msg): add_warning(req, msg) warnings.append(msg) # -- check the name if new_name: if new_name != old_name: # check that the version doesn't already exists # FIXME: the whole .exists business needs to be clarified # (#4130) and should behave like a WikiPage does in # this respect. try: Version(self.env, new_name) warn( _( 'Version "%(name)s" already exists, please ' 'choose another name', name=new_name)) except ResourceNotFound: pass else: warn(_('You must provide a name for the version.')) # -- check completed date if 'released' in req.args: time = user_time(req, parse_date, time, hint='datetime') \ if time else None if time and time > datetime.now(utc): warn(_("Release date may not be in the future")) else: time = None version.time = time if warnings: return self._render_editor(req, version) # -- actually save changes with self.env.db_transaction as db: if version.exists: if version.name != version._old_name: # Update tickets db( """ UPDATE milestone_version SET version=%s WHERE version=%s """, (version.name, version._old_name)) version.update() else: version.insert() req.redirect(req.href.version(version.name))
def get_fake_version(): return Version(self.env, "-1")
def test_version_release_date_displayed(self): """Version release date is shown in ticket properties.""" v1 = Version(self.env) v1.name = 'v1' v1.time = datetime_now(utc) - timedelta(weeks=2) v1.insert() v2 = Version(self.env) v2.name = 'v2' v2.insert() ticket = [ self._insert_ticket(summary='ticket 1', version='v1'), self._insert_ticket(summary='ticket 2', version='v2'), self._insert_ticket(summary='ticket 3', version='v3') ] def version_field(data): for field in data['fields']: if field['name'] == 'version': return field # Version with release data. req = MockRequest(self.env, method='GET', args={'id': ticket[0].id}) data = self.ticket_module.process_request(req)[1] self.assertIn(u'title="Released ', unicode(version_field(data)['rendered'])) # Version without release data. req = MockRequest(self.env, method='GET', args={'id': ticket[1].id}) data = self.ticket_module.process_request(req)[1] self.assertNotIn(u'title="Released ', unicode(version_field(data)['rendered'])) # Non-existent version. req = MockRequest(self.env, method='GET', args={'id': ticket[2].id}) data = self.ticket_module.process_request(req)[1] self.assertNotIn(u'title="Released ', unicode(version_field(data)['rendered']))
def _do_save(self, req, db, version): version_name = req.args.get('name') version_project = req.args.get('project') old_version_project = self.__SmpModel.get_id_project_version( version.name) if version.exists: req.perm.require('MILESTONE_MODIFY') else: req.perm.require('MILESTONE_CREATE') old_name = version.name new_name = version_name version.description = req.args.get('description', '') time = req.args.get('time', '') if time: version.time = user_time(req, parse_date, time, hint='datetime') else: version.time = None # Instead of raising one single error, check all the constraints and # let the user fix them by going back to edit mode showing the warnings warnings = [] def warn(msg): add_warning(req, msg) warnings.append(msg) # -- check the name # If the name has changed, check that the version doesn't already # exist # FIXME: the whole .exists business needs to be clarified # (#4130) and should behave like a WikiPage does in # this respect. try: new_version = Version(self.env, new_name, db) if new_version.name == old_name: pass # Creation or no name change elif new_version.name: warn( _( 'Version "%(name)s" already exists, please ' 'choose another name.', name=new_version.name)) else: warn(_('You must provide a name for the version.')) except: version.name = new_name if warnings: return self._render_editor(req, db, version) # -- actually save changes if version.exists: version.update() if old_name != version.name: self.__SmpModel.rename_version_project(old_name, version.name) if not version_project: self.__SmpModel.delete_version_project(version.name) elif not old_version_project: self.__SmpModel.insert_version_project(version.name, version_project) else: self.__SmpModel.update_version_project(version.name, version_project) else: version.insert() if version_project: self.__SmpModel.insert_version_project(version.name, version_project) add_notice(req, _('Your changes have been saved.')) req.redirect(req.href.version(version.name))