Ignore:
Timestamp:
22 Dec 2012, 09:16:05 (12 years ago)
Author:
Henrik Bettermann
Message:

Combine ExportCSVPage and ExportJobContainerOverview? as well as ExportCSVView and ExportJobContainerDownload?. Adjust pagetemplates. Do not remove jobs after download. Display jobs of all users on ExportCSVPage.

One test still fails because we do not have a logger for VirtualExportJobContainers?.

Location:
main/waeup.kofa/trunk/src/waeup/kofa
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py

    r9806 r9822  
    194194    return
    195195
     196def doll_up(view, user=None):
     197    """Doll up export jobs for displaying in table.
     198    """
     199    job_entries = view.context.get_running_export_jobs(user)
     200    job_manager = getUtility(IJobManager)
     201    entries = []
     202    for job_id, exporter_name, user_id in job_entries:
     203        job = job_manager.get(job_id)
     204        exporter = getUtility(ICSVExporter, name=exporter_name)
     205        exporter_title = getattr(exporter, 'title', 'Unknown')
     206        args = ', '.join([str(x) for x in job.kwargs.values()])
     207        descr = '%s <br> (%s)' % (exporter_title, args)
     208        status = job.finished and 'ready' or 'running'
     209        status = job.failed and 'FAILED' or status
     210        start_time = getattr(job, 'begin_after', None)
     211        if start_time:
     212            start_time = start_time.astimezone(
     213                getUtility(
     214                    IKofaUtils).tzinfo).strftime("%Y-%m-%d %H:%M:%S %Z")
     215        download_url = view.url(view.context, 'download_export',
     216                                data=dict(job_id=job_id))
     217        new_entry = dict(
     218            job=job_id,
     219            descr=descr,
     220            creator=user_id,
     221            status=status,
     222            start_time=start_time,
     223            download_url=download_url,
     224            show_download_button = (job.finished and not job.failed),
     225            show_refresh_button = not job.finished,
     226            show_discard_button = job.finished,)
     227        entries.append(new_entry)
     228    return entries
     229
    196230#
    197231# Login/logout and language switch pages...
     
    15051539    pnav = 0
    15061540    export_button = _(u'Create CSV file')
    1507     _running_exports = None
    15081541
    15091542    def getExporters(self):
     
    15161549        return sorted(title_name_tuples)
    15171550
    1518     def job_finished(self, status):
    1519         return status == 'completed'
    1520 
    1521     def getRunningExports(self):
    1522         """Returns running exports as list of tuples.
    1523 
    1524         Only exports triggered by the current user (identified by
    1525         principal.id) are returned.
    1526 
    1527         Each tuple has the form (<STATUS>, <STATUS_TITLE>, <EXPORTER_NAME>).
    1528 
    1529         ``STATUS``:
    1530            the status as machine readable string (something like
    1531            ``'completed'``)
    1532 
    1533         ``STATUS_TITLE``:
    1534            status of export as translated string.
    1535 
    1536         ``EXPORTER_NAME``:
    1537            string representing the exporter title used when triggering
    1538            the export job.
    1539         """
    1540         if self._running_exports is None:
    1541             self._running_exports = self._getRunningExports()
    1542         return self._running_exports
    1543 
    1544     def _getRunningExports(self):
    1545         result = self.context.get_export_jobs_status(self.user_id)
    1546         return result
    1547 
    1548     def update(self, export=None, start_export=None, exporter=None,
    1549                discard=None, download=None):
    1550         self.user_id = self.request.principal.id
    1551         if discard:
    1552             myjobs = self.context.get_running_export_jobs(self.user_id)
    1553             for entry in myjobs:
    1554                 self.context.delete_export_entry(entry)
    1555                 self.flash(_('Discarded export result'))
    1556             return
    1557         if download:
    1558             myjobs = self.context.get_running_export_jobs(self.user_id)
    1559             if not len(myjobs):
    1560                 self.flash(_('This export was already discarded.'))
    1561                 return
    1562             job_id = myjobs[0][0]
    1563             self.redirect(
    1564                 self.url(self.context, 'export.csv') + '?job_id=%s' % job_id)
    1565             return
    1566         if None in (start_export, exporter):
    1567             return
    1568         job_id = self.context.start_export_job(
    1569             exporter, self.request.principal.id)
    1570         self.redirect(self.url(self.context, 'export'))
     1551    def update(self, CREATE=None, DISCARD=None, exporter=None, job_id=None):
     1552        if CREATE:
     1553            job_id = self.context.start_export_job(
     1554                exporter, self.request.principal.id)
     1555        if DISCARD and job_id:
     1556            entry = self.context.entry_from_job_id(job_id)
     1557            self.context.delete_export_entry(entry)
     1558            self.flash(_('Discarded export') + ' %s' % job_id)
     1559        self.entries = doll_up(self, user=None)
    15711560        return
    15721561
    15731562class ExportCSVView(grok.View):
    15741563    grok.context(IDataCenter)
    1575     grok.name('export.csv')
     1564    grok.name('download_export')
    15761565    grok.require('waeup.manageDataCenter')
    15771566
    15781567    def render(self, job_id=None):
    1579 
    15801568        manager = getUtility(IJobManager)
    15811569        job = manager.get(job_id)
     
    15971585            'Content-Disposition', 'attachment; filename="%s' % filename)
    15981586        # remove job and running_exports entry from context
    1599         self.context.delete_export_entry(
    1600             self.context.entry_from_job_id(job_id))
     1587        #self.context.delete_export_entry(
     1588        #    self.context.entry_from_job_id(job_id))
    16011589        ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
    16021590        self.context.logger.info('%s - exported: %s' % (ob_class, filename))
  • main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/datacenterexportpage.pt

    r9821 r9822  
    1 <div i18n:domain="waeup.kofa" class="row">
    2   <div class="span8 columns">
    3     <div class="span8 columns">
    4       <p i18n:translate="">
    5         Here you can create CSV files from parts of portal data.
    6       </p>
    7       <p i18n:translate="">
    8         Please pick the type of objects you want to export from the
    9         selection below.
    10       </p>
    11       <p i18n:translate="">
    12         The file will be generated and then be made available for you
    13         to download. You must download or discard any existing export
    14         file before creating a new one.
    15       </p>
    16     </div>
    17     <form  method="POST">
    18       <fieldset>
    19         <label for="running_exports"
    20           tal:condition="view/getRunningExports">Running Export:
    21         </label>
     1<p i18n:translate="">
     2  Here you can create CSV files from parts of portal data.
     3  Please pick the type of objects you want to export from the
     4  selection below. The file will be generated and then be
     5  made available for you to download in the table below.
     6</p>
    227
     8<br />
    239
    24         <div class="input span6">
    25           <div tal:repeat="items view/getRunningExports">
    26             Data type:
    27             <span tal:content="python: items[2]">exporter
    28             </span><br />
    29               Status:
    30             <span tal:content="python: items[1]">status
    31             </span>
    32             <br /><br /><br />
    33             <span tal:condition="not: python: view.job_finished(items[0])">
    34               <img src="" tal:attributes="src static/ajax-loader.gif"
    35                 alt="Loading..."  class="spinner" />
    36               <input type="submit" name="reload" value="Reload"
    37                 class="btn primary" />
    38             </span>
    39             <div>
    40               <span tal:condition="python: view.job_finished(items[0])">
    41                 <input type="submit" name="download" value="Download"
    42                   class="btn primary" />
    43                 <input type="submit" name="discard" value="Discard"
    44                   class="btn secondary" />
    45               </span>
    46             </div>
    47           </div>
    48           <div>
    49             <tal:loading_bar content="structure provider:loadingbar" />
    50           </div>
    51         </div>
     10<form  method="POST">
     11  <label for="exporter">
     12    Exporter (Data Type):
     13  </label>
     14  <div class="input">
     15    <select name="exporter">
     16      <span tal:repeat="items view/getExporters" tal:omit-tag="">
     17        <option tal:define="name python: items[1]; title python: items[0]"
     18          tal:attributes="value name">
     19        <span tal:replace="title">TITLE
     20        </span>
     21        </option>
     22      </span>
     23    </select>
     24  </div>
     25  <br />
     26  <div class="input">
     27    <input i18n:translate="" type="submit" class="btn primary"
     28      name="CREATE" tal:attributes="value view/export_button" />
     29  </div>
     30</form>
    5231
     32<br /><br />
    5333
    54         <div class="clearfix" tal:condition="not: view/getRunningExports">
    55           <label for="exporter">Data Type:
    56           </label>
    57           <div class="input">
    58             <select name="exporter">
    59               <span tal:repeat="items view/getExporters" tal:omit-tag="">
    60                 <option tal:define="name python: items[1]; title python: items[0]"
    61                   tal:attributes="value name">
    62                 <span tal:replace="title">TITLE
    63                 </span>
    64                 </option>
    65               </span>
    66             </select>
    67             <span class="help-inline" i18n:translate="">
    68               Type of objects to export
    69             </span>
    70           </div>
    71         </div>
    72 
    73 
    74         <div class="input" tal:condition="not: view/getRunningExports">
    75           <input i18n:translate="" type="submit" class="btn primary"
    76             name="start_export" tal:attributes="value view/export_button" />
    77         </div>
    78       </fieldset>
    79     </form>
    80   </div>
    81 </div>
     34<table i18n:domain="waeup.kofa">
     35  <thead>
     36    <tr>
     37      <th i18n:translate="">Export Number</th>
     38      <th i18n:translate="">Description</th>
     39      <th i18n:translate="">Creator</th>
     40      <th i18n:translate="">Creation Date</th>
     41      <th i18n:translate="">Status</th>
     42      <th>&nbsp;</th>
     43    </tr>
     44  </thead>
     45  <tbody>
     46    <tr tal:repeat="job view/entries">
     47      <td>
     48        <span tal:replace="job/job">12</span>
     49      </td>
     50      <td>
     51        <span tal:replace="structure job/descr">DESCRIPTION</span>
     52      </td>
     53      <td>
     54        <span tal:replace="job/creator">CREATOR</span>
     55      </td>
     56      <td nowrap>
     57        <span tal:replace="job/start_time">DATETIME</span>
     58      </td>
     59      <td>
     60        <span tal:replace="job/status">STATUS</span>
     61      </td>
     62      <td nowrap>
     63        <a href="" class="btn" i18n:translate=""
     64           tal:condition="job/show_refresh_button">
     65          <img tal:attributes="src static/actionicon_reload.png" />
     66          Reload
     67        </a>
     68        <a href="" class="btn primary"
     69               tal:attributes="href job/download_url"
     70               tal:condition="job/show_download_button">
     71          Download</a>
     72        <form method="POST">
     73          <input type="hidden" name="job_id"
     74                 tal:attributes="value job/job" />
     75          <input type="submit" class="btn secondary"
     76                 name="DISCARD" value="Discard"
     77                 tal:condition="job/show_discard_button" />
     78        </form>
     79      </td>
     80    </tr>
     81  </tbody>
     82</table>
  • main/waeup.kofa/trunk/src/waeup/kofa/browser/tests/test_browser.py

    r9701 r9822  
    200200        # while the export file is created, we get a reload button
    201201        # (or a loading bar if javascript is enabled)...
    202         self.browser.getControl("Reload").click()
     202        self.assertTrue('Reload' in self.browser.contents)
    203203        # ...which is displayed as long as the job is not finished.
    204204        # When the job is finished and we reload the page...
    205205        job_id = self.wait_for_export_job_completed()
    206         try:
    207             self.browser.getControl("Reload").click()
    208         except LookupError:
    209             # if the job completed very fast, we will get the download
    210             # link immediately
    211             pass
     206        self.browser.open(self.datacenter_path + '/export')
     207        self.assertFalse('Reload' in self.browser.contents)
    212208        # ...we can download the result
    213         self.browser.getControl("Download").click()
     209        self.browser.getLink("Download").click()
    214210        self.assertEqual(self.browser.headers['content-type'],
    215211                         'text/csv; charset=UTF-8')
     
    221217
    222218        # after download, the job and the result file are removed
    223         manager = getUtility(IJobManager)
    224         result = manager.get(job_id)
    225         self.assertEqual(result, None)
    226         self.assertEqual(self.stored_in_datacenter(job_id), False)
     219        #manager = getUtility(IJobManager)
     220        #result = manager.get(job_id)
     221        #self.assertEqual(result, None)
     222        #self.assertEqual(self.stored_in_datacenter(job_id), False)
    227223        logfile = os.path.join(
    228224            self.app['datacenter'].storage, 'logs', 'datacenter.log')
     
    259255        self.browser.getControl("Create CSV file").click()
    260256        job_id = self.wait_for_export_job_completed()
    261         try:
    262             self.browser.getControl("Reload").click()
    263         except LookupError:
    264             pass
    265         self.browser.getControl("Download").click()
     257        self.browser.open(self.datacenter_path + '/@@export')
     258        self.browser.getLink("Download").click()
    266259        self.assertEqual(self.browser.headers['content-type'],
    267260                         'text/csv; charset=UTF-8')
     
    291284        self.browser.open(self.datacenter_path + '/@@export')
    292285        self.browser.getControl("Discard").click()
    293         self.assertTrue('Discarded export result' in self.browser.contents)
     286        self.assertTrue('Discarded export' in self.browser.contents)
    294287        return
    295288
  • main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py

    r9819 r9822  
    3737    KofaForm, NullValidator)
    3838from waeup.kofa.browser.breadcrumbs import Breadcrumb
    39 from waeup.kofa.browser.pages import ContactAdminForm
     39from waeup.kofa.browser.pages import ContactAdminForm, ExportCSVView, doll_up
    4040from waeup.kofa.browser.resources import (
    4141    datepicker, datatable, tabs, warning, toggleall)
     
    26612661    pnav = 1
    26622662
    2663     def doll_up(self):
    2664         job_entries = self.context.get_running_export_jobs(
    2665             self.request.principal.id)
    2666         job_manager = getUtility(IJobManager)
    2667         entries = []
    2668         for job_id, exporter_name, user_id in job_entries:
    2669             job = job_manager.get(job_id)
    2670             exporter = getUtility(ICSVExporter, name=exporter_name)
    2671             exporter_title = getattr(exporter, 'title', 'Unknown')
    2672             args = ', '.join([str(x) for x in job.kwargs.values()])
    2673             descr = '%s (%s)' % (exporter_title, args)
    2674             status = job.finished and 'ready' or 'running'
    2675             status = job.failed and 'FAILED' or status
    2676             start_time = getattr(job, 'begin_after', None)
    2677             if start_time:
    2678                 start_time = start_time.astimezone(
    2679                     getUtility(
    2680                         IKofaUtils).tzinfo).strftime("%Y-%m-%d %H:%M:%S %Z")
    2681             download_url = self.url(self.context, 'download',
    2682                                     data=dict(job_id=job_id))
    2683             new_entry = dict(
    2684                 job=job_id,
    2685                 descr=descr,
    2686                 creator=user_id,
    2687                 status=status,
    2688                 start_time=start_time,
    2689                 download_url=download_url,
    2690                 show_download_button = (job.finished and not job.failed),
    2691                 show_refresh_button = not job.finished,
    2692                 show_discard_button = job.finished,)
    2693             entries.append(new_entry)
    2694         self.entries = entries
    2695         pass
    2696 
    26972663    def update(self, CREATE=None, DISCARD=None, job_id=None):
    26982664        if CREATE:
     
    27032669            self.context.delete_export_entry(entry)
    27042670            self.flash(_('Discarded export') + ' %s' % job_id)
    2705         self.doll_up()
     2671        self.entries = doll_up(self, user=self.request.principal.id)
    27062672        return
    27072673
     
    27712737        return
    27722738
    2773 class ExportJobContainerDownload(grok.View):
     2739class ExportJobContainerDownload(ExportCSVView):
    27742740    """Page that configures a students export job.
    27752741    """
    27762742    grok.context(VirtualExportJobContainer)
    27772743    grok.require('waeup.showStudents')
    2778     grok.name('download')
    2779 
    2780     def update(self, job_id=None):
    2781         self.job_id=job_id
    2782         return
    2783 
    2784     def render(self):
    2785         job = getUtility(IJobManager).get(self.job_id)
    2786         self.response.setHeader(
    2787             'Content-Type', 'text/csv; charset=UTF-8')
    2788         self.response.setHeader(
    2789             'Content-Disposition:', 'attachment; filename="%s' % (
    2790                 'students.csv',))
    2791         return open(job.result, 'rb')
  • main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/exportjobsindex.pt

    r9821 r9822  
    1616      </td>
    1717      <td>
    18         <span tal:replace="job/descr">DESCRIPTION</span>
     18        <span tal:replace="structure job/descr">DESCRIPTION</span>
    1919      </td>
    2020      <td>
     
    2828      </td>
    2929      <td nowrap>
     30        <a href="" class="btn" i18n:translate=""
     31           tal:condition="job/show_refresh_button">
     32          <img tal:attributes="src static/actionicon_reload.png" />
     33          Reload
     34        </a>
     35        <a href="" class="btn primary"
     36               tal:attributes="href job/download_url"
     37               tal:condition="job/show_download_button">
     38          Download</a>
    3039        <form method="POST">
    3140          <input type="hidden" name="job_id"
    3241                 tal:attributes="value job/job" />
    33           <a href="" class="btn primary"
    34                  tal:attributes="href job/download_url"
    35                  tal:condition="job/show_download_button">
    36             Download</a>
    3742          <input type="submit" class="btn secondary"
    3843                 name="DISCARD" value="Discard"
    3944                 tal:condition="job/show_discard_button" />
    40           <a href="" class="btn" i18n:translate=""
    41              tal:condition="job/show_refresh_button">
    42             <img tal:attributes="src static/actionicon_reload.png" />
    43             Reload
    44           </a>
    4545        </form>
    4646      </td>
Note: See TracChangeset for help on using the changeset viewer.