def __call__(self, parser, namespace, values, option_string): super(QualityRaw, self).__call__(parser, namespace, values, option_string) session = DBSession() with open(namespace.filename, 'wb') as ofile: writer = csv.writer(ofile, dialect='excel') writer.writerow(['CR ID', 'Customer', 'Duration in days']) for cr in session.query(CustomerRequest.id, CustomerRequest.project_id, Project.customer_id, Trac.trac_name).\ outerjoin(Project, CustomerRequest.project_id == Project.id).\ outerjoin(Trac, Project.id==Trac.project_id).distinct(): tickets, Ticket, TicketCustom = tickets_for_cr(self.metadata, session, cr.trac_name, cr.id) tids = [t.id for t in tickets] if not tids: continue entries = session.query(TimeEntry)\ .filter_by(project_id=cr.project_id)\ .filter(TimeEntry.ticket.in_(tids))\ .filter(extract('year', TimeEntry.date) == namespace.year) if entries.count(): hours = timedelta_as_work_days(sum([a.hours for a in entries], timedelta())) writer.writerow([cr.id, cr.customer_id, hours])
def main(argv=sys.argv): if len(argv) != 3: usage(argv) config_uri = argv[1] svn_csv = argv[2] setup_logging(config_uri) settings = get_appsettings(config_uri, name='dashboard') engine = engine_from_config(settings, 'sa.dashboard.') DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all() with transaction.manager: session = DBSession() with open(svn_csv, 'rb') as csvfile: reader = csv.reader(csvfile) for project_id, svn_repo in reader: svn_repo = svn_repo.strip() project_id = project_id.strip() project = session.query(Project).get(project_id) app = Application(name='SVN', project_id=project_id, application_type=SVN, svn_name=svn_repo) project.add_application(app) print 'Creating svn app [%s] for project [%s]' % (svn_repo, project_id)
def __call__(self, parser, namespace, values, option_string): super(QualityTicket, self).__call__(parser, namespace, values, option_string) session = DBSession() with open(namespace.filename, 'wb') as ofile: writer = csv.writer(ofile, dialect='excel') writer.writerow(['Ticket ID', 'Customer', 'Ticket creation year', 'Ticket creation month', 'Ticket creation day', 'Ticket completion year', 'Ticket completion moneth', 'Ticket completion day', 'Ticket state', 'Ticket last owner', 'Ticket types', 'Ticket opened by customer', 'Problem nature']) for pr in session.query(Project.customer_id, Project.id, Trac.trac_name)\ .outerjoin(Trac, Project.id==Trac.project_id)\ .outerjoin(TimeEntry, TimeEntry.project_id==Project.id)\ .filter(TimeEntry.project_id==Project.id)\ .filter(extract('year', TimeEntry.date) == namespace.year).distinct(): tickets, Ticket, TicketCustom = tickets_for_cr(self.metadata, session, pr.trac_name) tickets_in_year = session.query(Ticket)\ .outerjoin(TimeEntry, and_(TimeEntry.project_id==pr.id, TimeEntry.ticket==Ticket.id))\ .filter(extract('year', TimeEntry.date) == namespace.year).distinct() for ticket in tickets_in_year: last_status = ticket.last_history('status', namespace.year) close_date = ticket.close_date all_types = set([]) for h in ticket.history: if h.field == 'type': all_types.update([h.oldvalue]) all_types.update([ticket.type]) all_types = '|'.join(all_types).encode('utf8','ignore') writer.writerow( [ticket.id, pr.customer_id, ticket.date.strftime('%Y'), ticket.date.strftime('%m'), ticket.date.strftime('%d'), close_date and close_date.strftime('%Y') or '', close_date and close_date.strftime('%m') or '', close_date and close_date.strftime('%d') or '', last_status, ticket.owner, all_types, ticket.open_by_customer and ticket.open_by_customer.unicode_value or '', ticket.issue_type and ticket.issue_type.unicode_value or ''])
def __call__(self, parser, namespace, values, option_string): super(QualityCR, self).__call__(parser, namespace, values, option_string) session = DBSession() with open(namespace.filename, 'wb') as ofile: writer = csv.writer(ofile, dialect='excel') writer.writerow(['CR ID', 'Description', 'Customer', 'CR state', 'Estimation in days', 'TE duration in days - current year', 'TE sistem/install in days - current year', 'TE duration in days - overall', 'TE sistem/install in days - overall']) crs = session.query(CustomerRequest.id, CustomerRequest.name, CustomerRequest.workflow_state, CustomerRequest.project_id, Project.customer_id).\ outerjoin(Project, CustomerRequest.project_id == Project.id).distinct() for cr in crs: all_entries = session.query(TimeEntry).filter_by(customer_request_id=cr.id) if not all_entries.count(): continue estimations = sum([a.days for a in \ session.query(Estimation.days)\ .filter_by(customer_request_id=cr.id)]) current_year_entries = session.query(TimeEntry)\ .filter_by(customer_request_id=cr.id)\ .filter(extract('year', TimeEntry.date) == namespace.year) total_hours = timedelta_as_work_days(sum([a.hours for a in all_entries], timedelta())) total_dev_hours = timedelta_as_work_days(sum([a.hours for a in all_entries.filter(or_(TimeEntry.description.ilike('%install%'), TimeEntry.description.ilike('%sistem%')))], timedelta())) current_year_hours = timedelta_as_work_days(sum([a.hours for a in current_year_entries], timedelta())) current_year_dev_hours = timedelta_as_work_days(sum([a.hours for a in current_year_entries.filter(or_(TimeEntry.description.ilike('%install%'), TimeEntry.description.ilike('%sistem%')))], timedelta())) writer.writerow([cr.id, cr.name.encode('utf8'), cr.customer_id, cr.workflow_state, estimations, current_year_hours, current_year_dev_hours, total_hours, total_dev_hours])
def on_mark_all_seen(self, data): user_id = data['user_id'] session = DBSession() unseen = session.query(Activity)\ .filter_by(user_id=user_id)\ .filter_by(seen_at=None) now = datetime.now() for a in unseen: a.seen_at = now transaction.commit() r = redis.StrictRedis() r.publish('penelope:notifications:%s' % user_id, True)
def update_time_entries(): session = DBSession() tracs = {} time_entries = session.query(TimeEntry) for tp in time_entries: trac = list(tp.project.tracs)[0] tracs.setdefault(trac.trac_name, []) if not tp.ticket: print "TimeEntry %s has no ticket" % tp.id continue tracs[trac.trac_name].append((tp.id, tp.ticket)) ticket_vs_crs = """SELECT value AS cr_id, '%(tp_id)s' AS tp_id FROM "trac_%(trac)s".ticket_custom WHERE name='customerrequest' AND ticket=%(ticket)s""" splitted_tracs = split_dict_equally(tracs) for split in splitted_tracs: queries = [] for trac_id, opts in split.items(): for opt in opts: queries.append(ticket_vs_crs % {'trac': trac_id, 'tp_id': opt[0], 'ticket': opt[1]}) sql = '\nUNION '.join(queries) sql += ';' for trac in DBSession().execute(sql).fetchall(): cr = session.query(CustomerRequest).get(trac.cr_id) if not cr: continue contract = cr.contract if not contract: continue tp = session.query(TimeEntry).get(trac.tp_id) if not tp: continue tp.contract_id = contract.id
def _get_activities_(self): session = DBSession() base_query = session.query(Activity)\ .filter_by(user_id=self.session['user_id']) count_unseen = base_query.filter_by(seen_at=None).count() activities = base_query.order_by(Activity.created_at.desc()).limit(5) response = [] for activity in activities: response.append({'message': activity.message, 'unseen': activity.unseen, 'absolute_path': activity.absolute_path, 'created_by': activity.created_by, 'created_at': pretty.date(activity.created_at)}) transaction.commit() return count_unseen, response
def unique(value, field=None): """Successful if value is unique""" msg = _('Broken ${value}', mapping={'value': value}) if not field: raise ValidationError(msg) session = DBSession() filters = {} filters[field._column_name] = value records = session.query(field.model.__class__).filter_by(**filters) if records.count() > 0 and records.one() != field.model: msg = _("${value} already exists! Field '${field_column_name}' should be unique!", mapping={'value': value, 'field_column_name': field._column_name}) raise ValidationError(msg) return value
def add_svn_to_project(application): from penelope.core.models.dashboard import Project project = DBSession.query(Project).get(application.project_id) settings = get_current_registry().settings svnenvs = settings.get('penelope.svn.envs') tracenvs = settings.get('penelope.trac.envs') trac_admin = os.path.abspath('%s/../../bin/trac-admin' % tracenvs) if not os.path.exists(svnenvs): os.mkdir(svnenvs) svnname = application.svn_name if svnname: svn_path = '%s/%s' % (svnenvs, svnname) if not os.path.exists(svn_path): raise OSError, 'The path %s doesn\'t exists!' % svn_path else: idx = '' while (not svnname): svnname = idnormalizer.normalize("%s%s" % (project.id, idx)) svn_path = '%s/%s' % (svnenvs, svnname) if os.path.exists(svn_path): idx = idx and (idx+1) or 1 svnname = None _execute(['svnadmin', '--config-dir', '/etc/subversion', 'create', svn_path]) for trac in project.tracs: tracname = str(trac.trac_name) trac_path = '%s/%s' % (tracenvs, tracname) run([trac_path, 'repository add %s %s' % (svnname, svn_path)]) run([trac_path, 'repository resync %s' % (svnname)]) run([trac_path, 'config set trac repository_sync_per_request ""']) hook_filename = '%s/hooks/post-commit' % svn_path with open(hook_filename, 'w') as hook: opts = {'trac_path': trac_path, 'trac_admin': trac_admin} hook.write(POST_COMMIT_HOOK % opts) st = os.stat(hook_filename) os.chmod(hook_filename, st.st_mode | stat.S_IEXEC) application.api_uri = 'svn://%s' % svnname application.svn_name = svnname
def __call__(self, parser, namespace, values, option_string): super(QualityProject, self).__call__(parser, namespace, values, option_string) session = DBSession() with open(namespace.filename, 'wb') as ofile: writer = csv.writer(ofile, dialect='excel') writer.writerow(['Project ID', 'Customer', 'Project creation year', 'Project creation month', 'Project creation day', 'Project completion year', 'Project completion month', 'Project completion day']) for project in session.query(Project.id, Project.completion_date, Project.customer_id, Project.creation_date)\ .outerjoin(TimeEntry, TimeEntry.project_id==Project.id)\ .filter(extract('year', TimeEntry.date) == namespace.year)\ .distinct(): writer.writerow([project.id, project.customer_id, project.creation_date and project.creation_date.strftime('%Y') or '', project.creation_date and project.creation_date.strftime('%m') or '', project.creation_date and project.creation_date.strftime('%d') or '', project.completion_date and project.completion_date.strftime('%Y') or '', project.completion_date and project.completion_date.strftime('%m') or '', project.completion_date and project.completion_date.strftime('%d') or '', ])
def __call__(self, parser, namespace, values, option_string): super(QualityOperator, self).__call__(parser, namespace, values, option_string) session = DBSession() with open(namespace.filename, 'wb') as ofile: writer = csv.writer(ofile, dialect='excel') writer.writerow(['Customer name', 'Project name', 'CR description', 'CR total estimations (days)', 'CR status', 'Contract number', 'Contract amount', 'Contract days', 'Contract end date', 'Contract status', 'User', 'Total time in CR (days)']) query = session.query( TimeEntry.customer_request_id, TimeEntry.author_id, func.sum(TimeEntry.hours).label('total_time'), CustomerRequest, Project.name.label('project'), Customer.name.label('customer'), Contract.amount.label('contract_amount'), Contract.days.label('contract_days'), Contract.contract_number, Contract.end_date.label('contract_end_date'), Contract.workflow_state.label('contract_workflow_state'), User.fullname.label('user'), )\ .options(defer(CustomerRequest.filler), defer(CustomerRequest.old_contract_name), defer(CustomerRequest.uid), defer(CustomerRequest.description), defer(CustomerRequest.project_id), defer(CustomerRequest.contract_id), )\ .join(CustomerRequest)\ .join(Project)\ .join(Customer)\ .join(Contract, and_(TimeEntry.customer_request_id==CustomerRequest.id, CustomerRequest.contract_id==Contract.id))\ .outerjoin(User, TimeEntry.author_id==User.id)\ .filter(extract('year', TimeEntry.date) == namespace.year)\ .group_by(TimeEntry.customer_request_id)\ .group_by(TimeEntry.author_id)\ .group_by(Project.name)\ .group_by(Contract.amount)\ .group_by(Contract.contract_number)\ .group_by(Contract.days)\ .group_by(Contract.end_date)\ .group_by(Contract.workflow_state)\ .group_by(CustomerRequest.name)\ .group_by(CustomerRequest.id)\ .order_by(CustomerRequest.name)\ .order_by(CustomerRequest.workflow_state)\ .group_by(Customer.name)\ .group_by(User.fullname) for row in query: writer.writerow([row.customer.encode('utf8'), row.project.encode('utf8'), row.CustomerRequest.name.encode('utf8'), row.CustomerRequest.estimation_days, row.CustomerRequest.workflow_state, row.contract_number and row.contract_number.encode('utf8') or 'N/A', row.contract_amount or 0, row.contract_days or 0, row.contract_end_date or 'N/A', row.contract_workflow_state, row.user.encode('utf8'), timedelta_as_work_days(row.total_time)])
def __call__(self, parser, namespace, values, option_string): super(QualityOurCustomerTime, self).__call__(parser, namespace, values, option_string) session = DBSession() def is_rt_user(email): return email and 'redturtle' in email or False def round_to(n, precission): correction = 0.5 if n >= 0 else -0.5 return int(n/precission+correction)*precission def elapsed_time_in_minutes(start, end): td = from_utimestamp(end) - from_utimestamp(start) total_seconds = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 return round_to((total_seconds / 3600.0), 0.5) def url(ticket): return "https://penelope.redturtle.it/trac/{0.trac}/ticket/{0.id}".format(ticket) query = """SELECT '{0}' AS trac, '{2}' AS project, '{3}' AS customer, id, time, changetime, owner, reporter, summary, type, customerrequest.value AS cr_id, exclude_stats.value AS excluded FROM "trac_{0}".ticket AS ticket LEFT OUTER JOIN "trac_{0}".ticket_custom AS customerrequest ON ticket.id=customerrequest.ticket AND customerrequest.name='customerrequest' LEFT OUTER JOIN "trac_{0}".ticket_custom AS exclude_stats ON ticket.id=exclude_stats.ticket AND exclude_stats.name='stats_exclude' WHERE status='closed' AND EXTRACT('year' FROM to_timestamp(changetime / 1000000)) = {1}""" crs = dict(session.query(CustomerRequest.id, CustomerRequest.name).all()) queries = [] for trac in session.query(Trac): queries.append(query.format(trac.trac_name, namespace.year, trac.project.name.replace("'","''"), trac.project.customer.name.replace("'","''"))) sql = '\nUNION '.join(queries) sql += ';' with open(namespace.filename, 'wb') as ofile: writer = csv.writer(ofile, dialect='excel') writer.writerow(['Customer', 'Project', 'CR ID', 'CR name', 'Ticket #', 'Ticket summary', 'Ticket type', 'Ticket URL', 'Owner', 'Is RedTurtle user', 'Elapsed time (in normal hours)', 'Excluded from stats']) for ticket in session.execute(sql).fetchall(): reporter = ticket.reporter owner = ticket.owner or reporter history = session.execute("""SELECT time, oldvalue, newvalue FROM "trac_{0}".ticket_change WHERE ticket={1} AND field='owner' ORDER BY time""".format(ticket.trac, ticket.id)).fetchall() if not history: writer.writerow([ticket.customer, ticket.project, ticket.cr_id, crs.get(ticket.cr_id), ticket.id, ticket.summary, ticket.type, url(ticket), owner, is_rt_user(owner), elapsed_time_in_minutes(ticket.time, ticket.changetime), ticket.excluded]) else: first_history = history.pop(0) owner = first_history.oldvalue or reporter last_change = first_history.time writer.writerow([ticket.customer, ticket.project, ticket.cr_id, crs.get(ticket.cr_id), ticket.id, ticket.summary, ticket.type, url(ticket), owner, is_rt_user(owner), elapsed_time_in_minutes(ticket.time, last_change), ticket.excluded]) for h in history: owner = h.oldvalue or reporter writer.writerow([ticket.customer, ticket.project, ticket.cr_id, crs.get(ticket.cr_id), ticket.id, ticket.summary, ticket.type, url(ticket), owner, is_rt_user(owner), elapsed_time_in_minutes(last_change, h.time), ticket.excluded]) last_change = h.time
def add_trac_to_project(application, smtp_enabled=True, privatecomments=True, sensitivetickets=True, batchmod=True, autocompleteusers=True, customfieldadmin=True, qafields=True, privatetickets=False, tracwysiwyg=True, attachment_max_size=10485760, milestones=[], tickets=[], project_name=u'', ): from penelope.core.models.dashboard import Project project = DBSession.query(Project).get(application.project_id) settings = get_current_registry().settings or application.settings tracenvs = settings.get('penelope.trac.envs') if not os.path.exists(tracenvs): # TODO: logging bootstrap os.mkdir(tracenvs) tracname = None idx = '' while (not tracname): tracname = idnormalizer.normalize("%s%s" % (project.id, idx)) trac_path = '%s/%s' % (tracenvs, tracname) if os.path.exists(trac_path): idx = idx and (idx+1) or 1 tracname = None trac_perm = { 'administrator': ['TRAC_ADMIN', 'EXTRA_TIMEENTRY'], 'customer': ['TICKET_CREATE', 'TICKET_MODIFY', 'TICKET_VIEW', 'WIKI_VIEW', 'XML_RPC', 'CHANGESET_VIEW', 'FILE_VIEW', 'LOG_VIEW', 'MILESTONE_VIEW', 'REPORT_VIEW', 'REPORT_SQL_VIEW', 'ROADMAP_VIEW', 'SEARCH_VIEW', 'TIMELINE_VIEW'], 'developer': ['TICKET_CREATE', 'TICKET_MODIFY', 'TICKET_VIEW', 'WIKI_VIEW', 'XML_RPC', 'WIKI_CREATE', 'WIKI_MODIFY', 'CHANGESET_VIEW', 'FILE_VIEW', 'LOG_VIEW', 'MILESTONE_VIEW', 'REPORT_VIEW', 'REPORT_SQL_VIEW', 'ROADMAP_VIEW', 'SEARCH_VIEW', 'TIMELINE_VIEW', 'REPORT_ADMIN'], 'internal_developer': ['developer', 'TRAC_ADMIN', 'TIME_ENTRY_ADD'], 'external_developer': ['developer'], 'project_manager': ['administrator', 'TIME_ENTRY_ADD'], } pordb = str(DBSession.bind.url) run([trac_path, 'initenv "%s" "%s?schema=trac_%s"' % ( tracname, pordb.replace('postgresql://', 'postgres://'), tracname)]) tracenv = Environment(trac_path) # REPORTS cnx = tracenv.get_db_cnx().cnx cursor = cnx.cursor() cursor.executemany(\ "INSERT INTO report (title, description, query) VALUES (%s, %s, %s)", get_reports(project_id=project.id)) cursor.close() cnx.commit() # remove report 2 cursor = cnx.cursor() cursor.execute("DELETE FROM report where id=2;") cursor.close() cnx.commit() # update reports cursor = cnx.cursor() cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, t.id AS ticket, summary, t.type AS type, cr.name AS CR, owner, status, time AS created, changetime AS _changetime, t.description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE status <> ''closed'' AND tc.name = ''customerrequest'' ORDER BY CAST(p.value AS int), milestone, t.type, time' where id=1;""") cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, ''Milestone ''||milestone AS __group__, t.id AS ticket, summary, t.type AS type, cr.name AS CR, owner, status, time AS created, changetime AS _changetime, t.description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE status <> ''closed'' AND tc.name = ''customerrequest'' ORDER BY (milestone IS NULL),milestone, CAST(p.value AS int), t.type, time' where id=3;""") cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, owner AS __group__, t.id AS ticket, summary, milestone, cr.name AS CR, t.type AS type, time AS created, changetime AS _changetime, t.description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE status = ''accepted'' AND tc.name = ''customerrequest'' ORDER BY owner, CAST(p.value AS int), t.type, time' where id=4;""") cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, owner AS __group__, t.id AS ticket, summary, milestone, t.type AS type, cr.name AS CR, time AS created, t.description AS _description_, changetime AS _changetime, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE status = ''accepted'' AND tc.name = ''customerrequest'' ORDER BY owner, CAST(p.value AS int), t.type, time' where id=5;""") cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, (CASE status WHEN ''accepted'' THEN ''Accepted'' ELSE ''Owned'' END) AS __group__, t.id AS ticket, summary, milestone, cr.name AS CR, t.type AS type, priority, time AS created, changetime AS _changetime, t.description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE t.status <> ''closed'' AND owner = $USER AND tc.name = ''customerrequest'' ORDER BY (status = ''accepted'') DESC, CAST(p.value AS int), milestone, t.type, time' where id=7;""") cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, (CASE owner WHEN $USER THEN ''My Tickets'' ELSE ''Active Tickets'' END) AS __group__, t.id AS ticket, summary, milestone, t.type AS type, cr.name AS CR, owner, status, time AS created, changetime AS _changetime, t.description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE status <> ''closed'' AND tc.name = ''customerrequest'' ORDER BY (COALESCE(owner, '''') = $USER) DESC, CAST(p.value AS int), milestone, t.type, time' where id=8;""") cursor.close() cnx.commit() # remove default trac's milestones, components, versions cursor = cnx.cursor() cursor.execute("DELETE FROM milestone;") cursor.close() cnx.commit() cursor = cnx.cursor() cursor.execute("DELETE FROM component;") cursor.close() cnx.commit() cursor = cnx.cursor() cursor.execute("DELETE FROM version;") cursor.close() cnx.commit() # TODO: attualmente il riferimento dal trac al progetto dashboard è il project_id, # considerando un'instalalzzione che condicide lo stesso stack wsgi, # valutare se deve essere una uri (jsnorpc, xmlrpx, ...) # o un dsn che punti al db, o ... tracenv.config.set('por-dashboard', 'project-id', application.project_id) # custom templates templates = settings.get('penelope.trac.templates') masterconfig = settings.get('penelope.trac.masterconfig') if templates: tracenv.config.set('inherit', 'templates_dir', templates) # master config if masterconfig: tracenv.config.set('inherit', 'file', masterconfig) # set name and description tracenv.config.set('project', 'name', project_name) tracenv.config.set('project', 'descr', application.name) tracenv.config.set('notification', 'smtp_enabled', smtp_enabled and 'true' or 'false') tracenv.config.set('notification', 'always_notify_owner', 'true') tracenv.config.set('notification', 'always_notify_reporter', 'true') manager_email = project.manager.email or '' tracenv.config.set('notification', 'smtp_always_cc', manager_email) # manager should always receiv CC tracenv.config.set('attachment', 'max_size', attachment_max_size) tracenv.config.set('components', 'penelope.trac.*', 'enabled') tracenv.config.set('components', 'themeengine.admin.*', 'enabled') tracenv.config.set('components', 'themeengine.api.*', 'enabled') tracenv.config.set('components', 'themeengine.web_ui.*', 'enabled') tracenv.config.set('components', 'ticketrelations.*', 'enabled') tracenv.config.set('components', 'tracopt.perm.config_perm_provider.extrapermissionsprovider', 'enabled') # All the custom permission names are converted to uppercase. # It is not possible to have lowercase and distinguish them from the standard permissions. tracenv.config.set('extra-permissions', 'extra_timeentry', 'TIME_ENTRY_ADD') tracenv.config.set('theme','theme', 'por') # ticket-custom tracenv.config.set('ticket-custom', 'customerrequest', 'select') tracenv.config.set('ticket-custom', 'customerrequest.label', 'Customer Request') tracenv.config.set('ticket-custom', 'blocking', 'text') tracenv.config.set('ticket-custom', 'blocking.label', 'Blocking') tracenv.config.set('ticket-custom', 'blockedby', 'text') tracenv.config.set('ticket-custom', 'blockedby.label', 'Blocked By') # BBB: ii valori di customerrequest vengono caricati ondemand tracenv.config.set('ticket-custom', 'customerrequest.options', '') # see ticket:80 if qafields: tracenv.config.set('ticket-custom', 'issuetype', 'select') tracenv.config.set('ticket-custom', 'issuetype.label', 'Natura del problema') tracenv.config.set('ticket-custom', 'issuetype.options', '|'.join([u"", u"sistemistica", u"funzionalità", u"design (grafica, layout...)", u"prestazioni", u"mi aspettavo che..."])) tracenv.config.set('ticket-custom', 'esogeno', 'checkbox') tracenv.config.set('ticket-custom', 'esogeno.label', 'Ticket aperto dal Cliente') tracenv.config.set('ticket-custom', 'esogeno.value', 'false') tracenv.config.set('ticket-custom', 'stats_exclude', 'checkbox') tracenv.config.set('ticket-custom', 'stats_exclude.label', 'Exclude from report stats') tracenv.config.set('ticket-custom', 'stats_exclude.value', 'false') tracenv.config.set('ticket-custom', 'fasesviluppo', 'select') tracenv.config.set('ticket-custom', 'fasesviluppo.label', 'Fase sviluppo') tracenv.config.set('ticket-custom', 'fasesviluppo.options', '|'.join([u"", u"In lavorazione", u"Per lo staging", u"In staging", u"Per la produzione", u"In produzione"])) tracenv.config.set('ticket', 'restrict_owner', 'true') tracenv.config.set('ticket-custom', 'milestone.invalid_if', '') # WORKFLOW tracenv.config.set('ticket-workflow', 'accept', 'new,reviewing -> assigned') tracenv.config.set('ticket-workflow', 'accept.operations', 'set_owner_to_self') tracenv.config.set('ticket-workflow', 'accept.permissions', 'TICKET_MODIFY') tracenv.config.set('ticket-workflow', 'leave', '* -> *') tracenv.config.set('ticket-workflow', 'leave.default', '1') tracenv.config.set('ticket-workflow', 'leave.operations', 'leave_status') tracenv.config.set('ticket-workflow', 'reassign', 'new,assigned,accepted,reopened -> assigned') tracenv.config.set('ticket-workflow', 'reassign.operations', 'set_owner') tracenv.config.set('ticket-workflow', 'reassign.permissions', 'TICKET_MODIFY') tracenv.config.set('ticket-workflow', 'reassign_reviewing', 'reviewing -> *') tracenv.config.set('ticket-workflow', 'reassign_reviewing.name', 'reassign review') tracenv.config.set('ticket-workflow', 'reassign_reviewing.operations', 'set_owner') tracenv.config.set('ticket-workflow', 'reassign_reviewing.permissions', 'TICKET_MODIFY') tracenv.config.set('ticket-workflow', 'reopen', 'closed -> reopened') tracenv.config.set('ticket-workflow', 'reopen.operations', 'del_resolution') tracenv.config.set('ticket-workflow', 'reopen.permissions', 'TRAC_ADMIN') tracenv.config.set('ticket-workflow', 'resolve', 'new,assigned,reopened,reviewing -> closed') tracenv.config.set('ticket-workflow', 'resolve.operations', 'set_resolution') tracenv.config.set('ticket-workflow', 'resolve.permissions', 'TICKET_MODIFY') tracenv.config.set('ticket-workflow', 'review', 'new,assigned,reopened -> reviewing') tracenv.config.set('ticket-workflow', 'review.operations', 'set_owner') tracenv.config.set('ticket-workflow', 'review.permissions', 'TICKET_MODIFY') tracenv.config.set('milestone-groups', 'closed', 'closed') tracenv.config.set('milestone-groups', 'closed.order', '0') tracenv.config.set('milestone-groups', 'closed.query_args', 'group=resolution') tracenv.config.set('milestone-groups', 'closed.overall_completion', 'true') tracenv.config.set('milestone-groups', 'active', '*') tracenv.config.set('milestone-groups', 'active.order', '1') tracenv.config.set('milestone-groups', 'active.css_class', 'open') tracenv.config.set('milestone-groups', 'new', 'new,reopened') tracenv.config.set('milestone-groups', 'new.order', '2') # navigation tracenv.config.set('metanav', 'logout', 'disabled') # permissions tracenv.config.set('components', 'penelope.trac.users.*', 'enabled') tracenv.config.set('trac', 'permission_store', 'PorPermissionStore') tracenv.config.set('trac', 'show_email_addresses', 'true') # xmlrpc plugin tracenv.config.set('components', 'tracrpc.api.xmlrpcsystem', 'enabled') tracenv.config.set('components', 'tracrpc.xml_rpc.xmlrpcprotocol', 'enabled') tracenv.config.set('components', 'tracrpc.web_ui.rpcweb', 'enabled') tracenv.config.set('components', 'tracrpc.ticket.*', 'enabled') # DynamicFields Plugin tracenv.config.set('components', 'dynfields.rules.*','enabled') tracenv.config.set('components', 'dynfields.web_ui.*','enabled') # User & Roles (ReadOnly !!!) # tracenv.config.set('user_manager', 'user_store', 'PorUserStore') # tracenv.config.set('user_manager', 'attribute_provider', 'PorAttributeProvider') # BatchModifyPlugin if batchmod: tracenv.config.set('components', 'batchmod.web_ui.*', 'enabled') # Traccustomfieldadmin if customfieldadmin: tracenv.config.set('components', 'customfieldadmin.*', 'enabled') # PrivateCommentsPlugin if privatecomments: tracenv.config.set('components', 'privatecomments.privatecomments.*', 'enabled') # PrivateTicketPlugin if privatetickets: tracenv.config.set('components', 'privatetickets.policy.*', 'enabled') tracenv.config.set('trac', 'permission_policies', 'PrivateTicketsPolicy, %s' % tracenv.config.get('trac', 'permission_policies')) trac_perm['customer'].append('TICKET_VIEW_SELF') # SensitiveTicketsPlugin if sensitivetickets: tracenv.config.set('components', 'sensitivetickets.*', 'enabled') tracenv.config.set('trac', 'permission_policies', 'SensitiveTicketsPolicy, %s' % tracenv.config.get('trac', 'permission_policies')) tracenv.config.set('ticket-custom', 'sensitive.show_if_group', 'administrator|developer') # utilizzato se il default e' 1, se il default e' 0 non serve # tracenv.config.set('ticket-custom', 'sensitive.clear_on_hide', 'false') # tracenv.config.set('ticket-custom', 'sensitive.has_permission', 'SENSITIVE_VIEW') tracenv.config.set('ticket-custom', 'sensitive.value', '0') trac_perm['developer'].append('SENSITIVE_VIEW') # tracwysiwyg if tracwysiwyg: tracenv.config.set('components', 'tracwysiwyg.wysiwygmodule', 'enabled') # AutoCompleteUsers if autocompleteusers: tracenv.config.set('components', 'autocompleteusers.autocompleteusers', 'enabled') tracenv.config.set('wiki', 'max_size', 1048576) tracenv.config.set('logging', 'log_file', 'trac.log') tracenv.config.set('logging', 'log_level', 'INFO') tracenv.config.set('logging', 'log_type', 'file') tracenv.config.save() # let's remove notification config - it is set by the global config tracenv.config.remove('notification', 'smtp_from') tracenv.config.remove('notification', 'smtp_from_name') tracenv.config.remove('notification', 'replyto') tracenv.config.remove('notification', 'smtp_replyto') tracenv.config.remove('notification', 'email_sender') tracenv.config.remove('notification', 'smtp_enabled') tracenv.config.remove('notification', 'smtp_host') tracenv.config.remove('notification', 'smtp_port') tracenv.config.remove('notification', 'smtp_password') tracenv.config.remove('notification', 'smtp_username') tracenv.config.remove('trac', 'repository_sync_per_request') tracenv.config.save() run([trac_path, 'upgrade --no-backup']) run([trac_path, 'permission remove anonymous *']) run([trac_path, 'permission remove authenticated *']) for role, perms in trac_perm.items(): for perm in perms: run([trac_path, "permission add %s %s" % (role, perm)]) run([trac_path, "ticket_type add verification"]) # hack to minimize config size run([trac_path, 'config set browser color_scale True']) # add properly milestones milestones.append({'title': 'Backlog', 'due_date': date.today().replace(year=date.today().year+1)}) for milestone in milestones: due = milestone['due_date'] if due: due = milestone['due_date'].strftime('%Y-%m-%d') run([trac_path, 'milestone add "%s" %s' % (milestone['title'], due)]) else: run([trac_path, 'milestone add "%s"' % milestone['title']]) tracenv = Environment(trac_path) for ticket in tickets: # in this moment the customer request has a proper id ticket['status'] = 'new' t = Ticket(tracenv) t.populate(ticket) t.insert() application.api_uri = 'trac://%s' % tracname application.trac_name = tracname
def main(argv=sys.argv): if len(argv) < 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) env = bootstrap('%s#dashboard'% config_uri) settings = env.get('registry').settings engine = engine_from_config(settings, 'sa.dashboard.') DBSession.configure(bind=engine) Base.metadata.bind = engine google_opts = argv[2:] try: google_user = google_opts.pop(0) except IndexError: google_user = raw_input('Google Spreadsheet User: '******'Google Spreadsheet ID: ') google_password = getpass.getpass("Google Spreadsheet Password: ") gc = gspread.login(google_user, google_password) sht = gc.open_by_key(spreadsheet_key) worksheet = sht.get_worksheet(0) crs = worksheet.get_all_records() contracts = {} for row in crs: if not row['titolocommessa']: continue contract_uid = '%s_%s_%s' % (row['project_name'], row['titolocommessa'], row['customer_id']) contracts.setdefault(contract_uid, {'crs': []}) contracts[contract_uid]['titolocommessa'] = row['titolocommessa'] contracts[contract_uid]['nrcontratto'] = row['nrcontratto'] contracts[contract_uid]['gg'] = row['gg'] or 0 contracts[contract_uid]['amount'] = row['amount'] or 0 contracts[contract_uid]['crs'].append(row['cr_id']) contracts[contract_uid]['stato'] = map_state(row['stato']) # now we have a structure: # contracts['ContractUID'] = {'crs': ['customer_request_id_1', # 'customer_request_id_2'], # 'gg': '12'} with transaction.manager: session = DBSession() for contract_uid, opts in contracts.items(): crs = [session.query(CustomerRequest).get(a) for a in opts['crs']] crs = [a for a in crs if a] if not crs: continue contract = crs[0].contract if not contract: contract = Contract(name=opts['titolocommessa']) contract.days = opts['gg'] contract.amount = opts['amount'] contract.contract_number = opts['nrcontratto'] contract.workflow_state = opts['stato'] for cr in crs: if not cr: continue cr.contract = contract contract.project_id = cr.project_id update_time_entries()