| 289 | | # Load in a summary of the client's tickets |
|---|
| 290 | | sql = ("""\ |
|---|
| 291 | | SELECT t.id, t.summary, t.description, t.status, t.milestone, |
|---|
| 292 | | m.due, m.completed, m.description AS mdesc, |
|---|
| 293 | | tcust2.value AS estimatedhours |
|---|
| 294 | | FROM ticket_custom AS tcust |
|---|
| 295 | | INNER JOIN ticket AS t ON tcust.ticket=t.id |
|---|
| 296 | | LEFT JOIN ticket_custom AS tcust2 ON t.id=tcust2.ticket AND tcust2.name='estimatedhours' |
|---|
| 297 | | LEFT JOIN milestone m ON t.milestone=m.name |
|---|
| 298 | | WHERE tcust.name = 'client' |
|---|
| 299 | | AND tcust.value = %s |
|---|
| 300 | | AND t.milestone IN ( |
|---|
| 301 | | SELECT DISTINCT st.milestone |
|---|
| 302 | | FROM ticket_custom AS stcust |
|---|
| 303 | | INNER JOIN ticket AS st ON stcust.ticket=st.id |
|---|
| 304 | | INNER JOIN milestone AS sm ON st.milestone=sm.name |
|---|
| 305 | | WHERE stcust.name = tcust.name |
|---|
| 306 | | AND stcust.value = tcust.value |
|---|
| 307 | | AND st.status != 'closed' |
|---|
| 308 | | AND sm.due > 0) |
|---|
| 309 | | """) |
|---|
| 310 | | cur2 = db.cursor() |
|---|
| 311 | | cur2.execute(sql, (name,)) |
|---|
| 312 | | xsummary = etree.SubElement(xml, 'summary') |
|---|
| 313 | | for tid, summary, description, status, milestone, due, completed, mdescription, estimatedhours in cur2: |
|---|
| 314 | | have_data = True |
|---|
| 315 | | if milestone: |
|---|
| 316 | | if not milestones.has_key(milestone): |
|---|
| 317 | | xmilestone = etree.SubElement(xmilestones, 'milestone') |
|---|
| 318 | | etree.SubElement(xmilestone, 'name').text = milestone |
|---|
| 319 | | etree.SubElement(xmilestone, 'duetimestamp').text = str(due) |
|---|
| 320 | | etree.SubElement(xmilestone, 'due').text = myformat_date(due) |
|---|
| 321 | | if completed: |
|---|
| 322 | | etree.SubElement(xmilestone, 'completed').text = myformat_date(completed) |
|---|
| 323 | | if mdescription: |
|---|
| 324 | | xmilestone.append(etree.XML('<description>%s</description>' % wiki_to_html(extract_client_text(mdescription), self.env, self.req))) |
|---|
| 325 | | else: |
|---|
| 326 | | etree.SubElement(xmilestone, 'description').text = '' |
|---|
| 327 | | # Store for use |
|---|
| 328 | | milestones[milestone] = { 'hours': 0, 'xml': xmilestone } |
|---|
| 329 | | |
|---|
| 330 | | # Add hours to create a total. |
|---|
| 331 | | if estimatedhours: |
|---|
| 332 | | milestones[milestone]['hours'] += float(estimatedhours) |
|---|
| 333 | | |
|---|
| 334 | | if options.debug: |
|---|
| 335 | | print " Summarising ticket #%s" % tid |
|---|
| 336 | | ticket = etree.SubElement(xsummary, 'ticket') |
|---|
| 337 | | etree.SubElement(ticket, 'id').text = str(tid) |
|---|
| 338 | | etree.SubElement(ticket, 'summary').text = summary |
|---|
| 339 | | ticket.append(etree.XML('<description>%s</description>' % wiki_to_html(extract_client_text(description), self.env, self.req))) |
|---|
| 340 | | etree.SubElement(ticket, 'status').text = status |
|---|
| 341 | | etree.SubElement(ticket, 'milestone').text = milestone |
|---|
| 342 | | # For conveneince, put the date here too (keeps the XSLTs simpler) |
|---|
| 343 | | etree.SubElement(ticket, 'due').text = myformat_date(due) |
|---|
| 344 | | if estimatedhours: |
|---|
| 345 | | etree.SubElement(ticket, 'estimatedhours').text = myformat_hours(estimatedhours) |
|---|
| 346 | | |
|---|
| 347 | | elif 'changes' == field: |
|---|
| 348 | | # Load in any changes that have happend |
|---|
| 349 | | sql = ("""\ |
|---|
| 350 | | SELECT t.id, t.summary, t.description, t.status, t.resolution, t.milestone, m.due, |
|---|
| 351 | | tchng.field, tchng.oldvalue, tchng.newvalue |
|---|
| 352 | | FROM ticket_custom tcust |
|---|
| 353 | | INNER JOIN ticket AS t ON tcust.ticket=t.id |
|---|
| 354 | | INNER JOIN ticket_change AS tchng ON t.id=tchng.ticket |
|---|
| 355 | | LEFT JOIN milestone AS m ON t.milestone=m.name |
|---|
| 356 | | WHERE tcust.name = 'client' |
|---|
| 357 | | AND tcust.value = %s |
|---|
| 358 | | AND tchng.field IN ('comment', 'status', 'resolution', 'milestone') |
|---|
| 359 | | AND tchng.time >= %s |
|---|
| 360 | | AND tchng.time < %s |
|---|
| 361 | | AND t.milestone IN ( |
|---|
| 362 | | SELECT DISTINCT st.milestone |
|---|
| 363 | | FROM ticket_custom AS stcust |
|---|
| 364 | | INNER JOIN ticket AS st ON stcust.ticket=st.id |
|---|
| 365 | | INNER JOIN milestone AS sm ON st.milestone=sm.name |
|---|
| 366 | | WHERE stcust.name = tcust.name |
|---|
| 367 | | AND stcust.value = tcust.value |
|---|
| 368 | | AND sm.due > 0) |
|---|
| 369 | | ORDER BY t.time |
|---|
| 370 | | """) |
|---|
| 371 | | cur2 = db.cursor() |
|---|
| 372 | | cur2.execute(sql, (name, lastupdate, now)) |
|---|
| 373 | | changes = etree.SubElement(xml, 'changes') |
|---|
| 374 | | lasttid = 0 |
|---|
| 375 | | for tid, summary, description, status, resolution, milestone, due, cgfield, oldvalue, newvalue in cur2: |
|---|
| 376 | | text = '' |
|---|
| 377 | | if 'status' == cgfield: |
|---|
| 378 | | text = 'Status changed from "%s" to "%s"' % (oldvalue, newvalue) |
|---|
| 379 | | elif 'milestone' == cgfield: |
|---|
| 380 | | text = 'Milestone changed from "%s" to "%s" - please check for revised delivery date.' % (oldvalue, newvalue) |
|---|
| 381 | | elif 'resolution' == cgfield: |
|---|
| 382 | | if oldvalue and not newvalue: |
|---|
| 383 | | text = 'Resolution removed' |
|---|
| 384 | | elif not oldvalue and newvalue: |
|---|
| 385 | | text = 'Resolution set to "%s"' % (newvalue) |
|---|
| 386 | | else: |
|---|
| 387 | | text = 'Resolution changed from "%s" to "%s"' % (oldvalue, newvalue) |
|---|
| 388 | | elif 'comment' == cgfield: |
|---|
| 389 | | # Todo - extract... |
|---|
| 390 | | text = extract_client_text(newvalue).strip() |
|---|
| 391 | | if '' == text: |
|---|
| 392 | | # No comments for the client here so ignore it. |
|---|
| 393 | | continue |
|---|
| 394 | | text = "''Comment for your information:''[[BR]][[BR]]" + text |
|---|
| 395 | | else: |
|---|
| 396 | | # Client should not know any more than this |
|---|
| 397 | | continue |
|---|
| 398 | | |
|---|
| 399 | | if options.debug: |
|---|
| 400 | | print " Change notification (%s) for ticket #%s" % (cgfield, tid) |
|---|
| 401 | | have_data = True |
|---|
| 402 | | if lasttid != tid: |
|---|
| 403 | | ticket = etree.SubElement(changes, 'ticket') |
|---|
| 404 | | etree.SubElement(ticket, 'id').text = str(tid) |
|---|
| 405 | | etree.SubElement(ticket, 'summary').text = summary |
|---|
| 406 | | ticket.append(etree.XML('<description>%s</description>' % wiki_to_html(extract_client_text(description), self.env, self.req))) |
|---|
| 407 | | etree.SubElement(ticket, 'status').text = status |
|---|
| 408 | | etree.SubElement(ticket, 'resolution').text = resolution |
|---|
| 409 | | etree.SubElement(ticket, 'milestone').text = milestone |
|---|
| 410 | | etree.SubElement(ticket, 'due').text = myformat_date(due) |
|---|
| 411 | | changelog = etree.SubElement(ticket, 'changelog') |
|---|
| 412 | | |
|---|
| 413 | | detail = etree.XML('<detail>%s</detail>' % wiki_to_html(text, self.env, self.req)) |
|---|
| 414 | | detail.set('field', cgfield) |
|---|
| 415 | | if oldvalue: |
|---|
| 416 | | detail.set('oldvalue', oldvalue) |
|---|
| 417 | | if newvalue: |
|---|
| 418 | | detail.set('newvalue', newvalue) |
|---|
| 419 | | changelog.append(detail) |
|---|
| 420 | | lasttid = tid |
|---|
| 421 | | |
|---|
| 422 | | # Put the total hours into the milestone info |
|---|
| 423 | | for milestone in milestones: |
|---|
| 424 | | etree.SubElement(milestones[milestone]['xml'], 'estimatedhours').text = myformat_hours(milestones[milestone]['hours']) |
|---|
| 425 | | |
|---|
| 426 | | if options.debug: |
|---|
| 427 | | file = open('/tmp/send-client-email.xml', 'w') |
|---|
| 428 | | file.write(etree.tostring(xml, pretty_print=True)) |
|---|
| 429 | | file.close() |
|---|
| 430 | | print " Wrote XML to /tmp/send-client-email.xml" |
|---|
| 431 | | |
|---|
| 432 | | if not have_data: |
|---|
| | 278 | from clients.milestonesummary import ClientMilestoneSummary |
|---|
| | 279 | summariser = ClientMilestoneSummary(self.env, self.req) |
|---|
| | 280 | else: |
|---|
| | 281 | from clients.ticketchanges import ClientTicketChanges |
|---|
| | 282 | summariser = ClientTicketChanges(self.env, self.req) |
|---|
| | 283 | |
|---|
| | 284 | xml = summariser.get_summary(name, lastupdate, now) |
|---|
| | 285 | if xml is None: |
|---|