Changeset 13804
- Timestamp:
- 6 Apr 2016, 07:42:09 (9 years ago)
- Location:
- main/waeup.ikoba/trunk
- Files:
-
- 3 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
main/waeup.ikoba/trunk/CHANGES.txt
r13803 r13804 4 4 0.2.dev0 (unreleased) 5 5 ===================== 6 7 * Rearrange datacenter upload page. Provide modal windows to view all 8 processors and sources and vocabularies. 6 9 7 10 * Enable temporary suspension of officer accounts. Plugins must be -
main/waeup.ikoba/trunk/layout/theme.html
r12193 r13804 328 328 </div> 329 329 <div>...content that spans the whole page.</div> 330 <br><br> 331 332 <h3>Accordions</h3> 333 <div class="panel-group" id="accordion" role="tablist" 334 aria-multiselectable="true"> 335 <div class="panel panel-default"> 336 <div class="panel-heading" role="tab" id="headingTwo"> 337 <h4 class="panel-title"> 338 <a class="collapsed" data-toggle="collapse" 339 data-parent="#accordion" href="#collapseTwo" 340 aria-expanded="false" aria-controls="collapseTwo"> 341 Collapsible Group 342 </a> 343 </h4> 344 </div> 345 <div id="collapseTwo" class="panel-collapse collapse" 346 role="tabpanel" aria-labelledby="headingTwo"> 347 <div class="panel-body"> 348 Anim pariatur cliche reprehenderit, enim eiusmod high life 349 terry richardson ad squid. 3 wolf moon officia aute, non 350 skateboard dolor brunch. Food truck quinoa laborum eiusmod. 351 </div> 352 </div> 353 </div> 354 </div> 355 330 356 <br><br> 331 357 <h3>A Sample Data Table</h3> -
main/waeup.ikoba/trunk/src/waeup/ikoba/browser/pages.py
r13803 r13804 55 55 ILocalRolesAssignable, DuplicationError, IConfigurationContainer, 56 56 IJobManager, 57 IPasswordValidator, IContactForm, IIkobaUtils, ICSVExporter) 57 IPasswordValidator, IContactForm, IIkobaUtils, ICSVExporter, 58 CurrencySource) 58 59 from waeup.ikoba.permissions import ( 59 60 get_users_with_local_roles, get_all_roles, get_all_users, 60 61 get_users_with_role) 61 62 from waeup.ikoba.customers.vocabularies import GenderSource 62 63 from waeup.ikoba.authentication import LocalRoleSetEvent 63 64 from waeup.ikoba.utils.helpers import get_user_account, check_csv_charset 64 65 from waeup.ikoba.mandates.mandate import PasswordMandate 65 66 from waeup.ikoba.datacenter import DataCenterFile 67 from waeup.ikoba.customers.utils import ICustomersUtils 68 from waeup.ikoba.documents.utils import IDocumentsUtils 66 69 67 70 FORBIDDEN_CHARACTERS = (160,) … … 245 248 return True 246 249 return False 250 251 def getImporters(context): 252 importers = getAllUtilitiesRegisteredFor(IBatchProcessor) 253 importer_props = [] 254 for x in importers: 255 # Skip User Processor if user isn't allowed to manage users. 256 if x.util_name == 'userprocessor' and not checkPermission( 257 'waeup.manageUsers', context): 258 continue 259 iface_fields = schema.getFields(x.iface) 260 available_fields = [] 261 for key in iface_fields.keys(): 262 iface_fields[key] = (iface_fields[key].__class__.__name__, 263 iface_fields[key].required) 264 for value in x.available_fields: 265 available_fields.append( 266 dict(f_name=value, 267 f_type=iface_fields.get(value, (None, False))[0], 268 f_required=iface_fields.get(value, (None, False))[1] 269 ) 270 ) 271 available_fields = sorted(available_fields, key=lambda k: k[ 272 'f_name']) 273 importer_props.append( 274 dict(title=x.name, name=x.util_name, fields=available_fields)) 275 return sorted(importer_props, key=lambda k: k['title']) 247 276 248 277 class LocalRoleAssignmentUtilityView(object): … … 496 525 return html 497 526 527 class SourcesOverview(grok.View): 528 grok.context(ICompany) 529 grok.name('sourcesoverview') 530 grok.require('waeup.Public') 531 532 def _set_con_cats(self): 533 con_cats = getUtility(IIkobaUtils).CON_CATS_DICT 534 self.con_cats = con_cats.items() 535 return 536 537 def _set_payment_categories(self): 538 payment_categories = getUtility(IIkobaUtils).PAYMENT_CATEGORIES 539 self.payment_categories = payment_categories.items() 540 return 541 542 def _set_customer_doctypes(self): 543 customer_doctypes = getUtility(ICustomersUtils).DOCTYPES_DICT 544 self.customer_doctypes = customer_doctypes.items() 545 return 546 547 def _set_contypes(self): 548 contypes = getUtility(ICustomersUtils).CONTYPES_DICT 549 self.contypes = contypes.items() 550 return 551 552 def _set_doctypes(self): 553 doctypes = getUtility(IDocumentsUtils).DOCTYPES_DICT 554 self.doctypes = doctypes.items() 555 return 556 557 def _set_customer_states(self): 558 customer_states = getUtility(ICustomersUtils).TRANSLATED_CUSTOMER_STATES 559 self.customer_states = customer_states.items() 560 return 561 562 def _set_contract_states(self): 563 contract_states = getUtility(ICustomersUtils).TRANSLATED_CONTRACT_STATES 564 self.contract_states = contract_states.items() 565 return 566 567 def _set_customer_document_states(self): 568 customer_document_states = getUtility( 569 ICustomersUtils).TRANSLATED_DOCUMENT_STATES 570 self.customer_document_states = customer_document_states.items() 571 return 572 573 def _set_document_states(self): 574 document_states = getUtility( 575 IDocumentsUtils).TRANSLATED_DOCUMENT_STATES 576 self.document_states = document_states.items() 577 return 578 579 def _set_currencies(self): 580 currenciessource = CurrencySource().factory 581 self.currencies = [] 582 for code in currenciessource.getValues(): 583 title = currenciessource.getTitle(code) 584 self.currencies.append((code, title)) 585 return 586 587 def _set_sex(self): 588 gendersource = GenderSource().factory 589 self.sex = [] 590 for sex in gendersource.getValues(): 591 title = gendersource.getTitle(sex) 592 self.sex.append((sex, title)) 593 return 594 595 def update(self): 596 self._set_con_cats() 597 self._set_payment_categories() 598 self._set_customer_doctypes() 599 self._set_contypes() 600 self._set_doctypes() 601 self._set_currencies() 602 self._set_sex() 603 self._set_customer_states() 604 self._set_contract_states() 605 self._set_customer_document_states() 606 self._set_document_states() 607 608 class SourcesOverviewPage(IkobaPage, SourcesOverview): 609 grok.name('sources') 610 grok.require('waeup.Public') 611 label = _(u'Sources & Vocabularies') 612 pnav = 0 613 614 class ProcessorsOverview(grok.View): 615 grok.context(ICompany) 616 grok.name('processorsoverview') 617 grok.require('waeup.Public') 618 619 def getImporters(self): 620 return getImporters(self.context) 621 622 class ProcessorsOverviewPage(IkobaPage, ProcessorsOverview): 623 grok.name('processors') 624 grok.require('waeup.Public') 625 label = _(u'Available Processors (Importers)') 626 pnav = 0 627 628 class SkeletonDownloadView(UtilityView, grok.View): 629 grok.context(ICompany) 630 grok.name('skeleton') 631 grok.require('waeup.Public') 632 633 def update(self, processorname=None): 634 self.processorname = self.request.form['name'] 635 self.filename = ('%s_000.csv' % 636 self.processorname.replace('processor','import')) 637 return 638 639 def render(self): 640 #ob_class = self.__implemented__.__name__.replace('waeup.kofa.','') 641 #self.context.logger.info( 642 # '%s - skeleton downloaded: %s' % (ob_class, self.filename)) 643 self.response.setHeader( 644 'Content-Type', 'text/csv; charset=UTF-8') 645 self.response.setHeader( 646 'Content-Disposition:', 'attachment; filename="%s' % self.filename) 647 processor = getUtility(IBatchProcessor, name=self.processorname) 648 csv_data = processor.get_csv_skeleton() 649 return csv_data 650 498 651 class AdministrationPage(IkobaPage): 499 652 """ The administration overview page. … … 997 1150 998 1151 def getImporters(self): 999 importers = getAllUtilitiesRegisteredFor(IBatchProcessor) 1000 ikoba_utils = getUtility(IIkobaUtils) 1001 importer_props = [] 1002 for x in importers: 1003 if not x.util_name in ikoba_utils.BATCH_PROCESSOR_NAMES: 1004 continue 1005 # Skip User Processor if user isn't allowed to manage users. 1006 if x.util_name == 'userprocessor' and not checkPermission( 1007 'waeup.manageUsers', self.context): 1008 continue 1009 iface_fields = schema.getFields(x.iface) 1010 available_fields = [] 1011 for key in iface_fields.keys(): 1012 iface_fields[key] = (iface_fields[key].__class__.__name__, 1013 iface_fields[key].required) 1014 for value in x.available_fields: 1015 available_fields.append( 1016 dict(f_name=value, 1017 f_type=iface_fields.get(value, (None, False))[0], 1018 f_required=iface_fields.get(value, (None, False))[1] 1019 ) 1020 ) 1021 available_fields = sorted(available_fields, key=lambda k: k['f_name']) 1022 importer_props.append( 1023 dict(title=x.name, name=x.util_name, fields=available_fields)) 1024 return sorted(importer_props, key=lambda k: k['title']) 1152 return getImporters(self.context) 1025 1153 1026 1154 class FileDownloadView(UtilityView, grok.View): … … 1044 1172 fullpath = os.path.join(self.context.storage, self.filename) 1045 1173 return open(fullpath, 'rb').read() 1046 1047 class SkeletonDownloadView(UtilityView, grok.View):1048 grok.context(IDataCenter)1049 grok.name('skeleton')1050 grok.require('waeup.manageDataCenter')1051 1052 def update(self, processorname=None):1053 self.processorname = self.request.form['name']1054 self.filename = ('%s_000.csv' %1055 self.processorname.replace('processor','import'))1056 return1057 1058 def render(self):1059 #ob_class = self.__implemented__.__name__.replace('waeup.ikoba.','')1060 #self.context.logger.info(1061 # '%s - skeleton downloaded: %s' % (ob_class, self.filename))1062 self.response.setHeader(1063 'Content-Type', 'text/csv; charset=UTF-8')1064 self.response.setHeader(1065 'Content-Disposition:', 'attachment; filename="%s' % self.filename)1066 processor = getUtility(IBatchProcessor, name=self.processorname)1067 csv_data = processor.get_csv_skeleton()1068 return csv_data1069 1174 1070 1175 class DatacenterImportStep1(IkobaPage): -
main/waeup.ikoba/trunk/src/waeup/ikoba/browser/templates/datacenteruploadpage.pt
r11949 r13804 1 <p i18n:translate=""> 2 Before uploading a file check that your file header corresponds 3 with header format of the selected processor. All available headers 4 are listed below. 5 After file upload click the 'Process data' button and proceed up to 6 import step 3. Verify that the data format 7 meets all the import criteria and requirements of the processor. 8 </p> 9 1 <span i18n:translate=""> 2 Before uploading a file, check that your file header corresponds 3 with the header format of the selected processor: 4 </span> 5 <!-- Processors Modal --> 6 <button i18n:translate="" class="btn btn-info btn-xs" 7 data-toggle="modal" data-target="#ProcessorsModal" 8 href="../processorsoverview"> 9 View available processors (importers) 10 </button> 11 <div class="modal fade" id="ProcessorsModal" tabindex="-1" 12 role="dialog" aria-labelledby="ProcessorsModalLabel" aria-hidden="true"> 13 <div class="modal-dialog"> 14 <div class="modal-content"> 15 </div> 16 </div> 17 </div> 18 <br /><br /> 19 <span i18n:translate=""> 20 Many importer fields are of type 'Choice' which means only definied keywords 21 are allowed: 22 </span> 23 <!-- Sources Modal --> 24 <button i18n:translate="" class="btn btn-info btn-xs" 25 data-toggle="modal" data-target="#SourcesModal" 26 href="../sourcesoverview"> 27 View choices 28 </button> 29 <div class="modal fade" id="SourcesModal" tabindex="-1" 30 role="dialog" aria-labelledby="SourcesModalLabel" aria-hidden="true"> 31 <div class="modal-dialog"> 32 <div class="modal-content"> 33 </div> 34 </div> 35 </div> 36 <br /> 10 37 <form method="post" enctype="multipart/form-data"> 11 38 <br /> … … 16 43 </td> 17 44 <td> 18 19 20 45 <div class="input-group half"> 46 <div class="input-group-btn"> 47 <div class="btn btn-default btn-file"> 21 48 Select… 22 49 <input type="file" name="uploadfile:file" /> 23 50 </div> 24 25 51 </div> 52 <input type="text" class="form-control" readonly> 26 53 </div> 27 54 </td> … … 59 86 tal:attributes="value view/cancel_button"/> 60 87 </form> 61 <br /> 62 <p i18n:translate=""> 63 Import managers will be automatically informed by email after file upload. 64 There is no need for assigning tickets or sending emails anymore. 65 The data will be imported according to the information given. 66 </p> 88 67 89 68 90 <br /><br /> 69 91 70 <h2 i18n:translate="">Available Processors (Importers)</h2> 71 72 <table i18n:domain="waeup.ikoba" class="table table-condensed"> 73 <thead> 74 <tr> 75 <th i18n:translate="">Processor</th> 76 <th i18n:translate="">Required Schema Fields</th> 77 <th i18n:translate="">Optional Schema Fields</th> 78 <th i18n:translate="">Non-Schema Fields</th> 79 </tr> 80 </thead> 81 <tr tal:repeat="importer view/getImporters"> 82 <td> 83 <span tal:content="importer/title">TITLE</span><br /><br /> 84 <a i18n:translate="" class="btn btn-primary btn-xs" 85 tal:attributes="href python: 'skeleton?name=' + importer['name']"> 86 Download CSV Skeleton File 87 </a> 88 </td> 89 <td nowrap> 90 <span tal:repeat="field importer/fields"> 91 <tal:schemafield condition="field/f_type"> 92 <tal:required condition="field/f_required"> 93 <span tal:replace="field/f_name"></span> 94 (<span tal:replace="field/f_type"></span>) 95 <br /> 96 </tal:required> 97 </tal:schemafield> 98 </span> 99 </td> 100 <td nowrap> 101 <span tal:repeat="field importer/fields"> 102 <tal:schemafield condition="field/f_type"> 103 <tal:notrequired condition="not:field/f_required"> 104 <span tal:replace="field/f_name"></span> 105 (<span tal:replace="field/f_type"></span>) 106 <br /> 107 </tal:notrequired> 108 </tal:schemafield> 109 </span> 110 </td> 111 <td> 112 <span tal:repeat="field importer/fields"> 113 <tal:extrafield condition="not:field/f_type"> 114 <span tal:replace="field/f_name"></span> 115 <br /> 116 </tal:extrafield> 117 </span> 118 </td> 119 120 </tr> 121 </table> 92 <div i18n:translate=""> 93 After file upload click the 'Process data' button and proceed up to 94 import step 3. Verify that the data format 95 meets all the import criteria and requirements of the processor. 96 </div> 97 <br /> 98 <div i18n:translate=""> 99 Import managers will be automatically informed by email after file upload. 100 </div> -
main/waeup.ikoba/trunk/src/waeup/ikoba/browser/tests/test_browser.py
r13803 r13804 214 214 'This account has been suspended.' in self.browser.contents) 215 215 return 216 217 def test_sources_overview(self): 218 self.browser.open('http://localhost/app/sources') 219 self.assertEqual(self.browser.headers['Status'], '200 Ok') 220 self.assertTrue('id="headingCustomerStates"' in self.browser.contents) 221 222 def test_processors_overview(self): 223 self.browser.open('http://localhost/app/processors') 224 self.assertEqual(self.browser.headers['Status'], '200 Ok') 225 self.browser.getLink(url='skeleton?name=productprocessor').click() 226 self.assertEqual(self.browser.headers['Status'], '200 Ok') 227 self.assertEqual(self.browser.headers['Content-Type'], 228 'text/csv; charset=UTF-8') 229 self.assertEqual(self.browser.contents, 230 'contract_category,contract_title,description,' 231 'options,product_id,terms_and_conditions,title,' 232 'valid_from,valid_to\r\n') 233 return -
main/waeup.ikoba/trunk/src/waeup/ikoba/browser/viewlets.py
r12525 r13804 57 57 grok.name('widgets') 58 58 59 class SourcesLeft(grok.ViewletManager): 60 grok.name('sources_left') 61 62 class SourcesRight(grok.ViewletManager): 63 grok.name('sources_right') 64 59 65 60 66 # … … 601 607 super(SubobjectLister, self).update() 602 608 return 609 610 class ConCatsSource(grok.Viewlet): 611 """Avalable contract categories for the sources overview page. 612 """ 613 grok.order(1) 614 grok.viewletmanager(SourcesLeft) 615 grok.require('waeup.Public') 616 grok.template('source') 617 column = 'left' 618 name = 'ConCats' 619 source_name = 'con_cats' 620 title = _('Contract Categories') 621 622 def heading(self): 623 return 'heading%s' % self.name 624 625 def accordion(self): 626 return '#accordion-%s' % self.column 627 628 def collapse(self): 629 return 'collapse%s' % self.name 630 631 def hash_collapse(self): 632 return '#collapse%s' % self.name 633 634 def source(self): 635 return getattr(self.view, self.source_name) 636 637 class PaymentCatsSource(ConCatsSource): 638 """Avalable payment categories for the sources overview page. 639 """ 640 grok.order(2) 641 name = 'PaymentCats' 642 source_name = 'payment_categories' 643 title = _('Payment Categories') 644 645 class PaymentCatsSource(ConCatsSource): 646 """Avalable payment categories for the sources overview page. 647 """ 648 grok.order(2) 649 name = 'PaymentCats' 650 source_name = 'payment_categories' 651 title = _('Payment Categories') 652 653 class CustomerDocTypesSource(ConCatsSource): 654 """Avalable customer document types for the sources overview page. 655 """ 656 grok.order(3) 657 name = 'CustomerDocTypes' 658 source_name = 'customer_doctypes' 659 title = _('Customer Document Types') 660 661 class ConTypesSource(ConCatsSource): 662 """Avalable contract types for the sources overview page. 663 """ 664 grok.order(4) 665 name = 'ConTypes' 666 source_name = 'contypes' 667 title = _('Contract Types') 668 669 class DocTypesSource(ConCatsSource): 670 """Avalable document types for the sources overview page. 671 """ 672 grok.order(5) 673 name = 'DocTypes' 674 source_name = 'doctypes' 675 title = _('Document Types') 676 677 class SexSource(ConCatsSource): 678 """Avalable sex values for the sources overview page. 679 """ 680 grok.order(6) 681 name = 'Sex' 682 source_name = 'sex' 683 title = _('Sex') 684 685 class CurrencySource(ConCatsSource): 686 """Avalable currencies for the sources overview page. 687 """ 688 grok.order(7) 689 name = 'Currencies' 690 source_name = 'currencies' 691 title = _('Currencies') 692 693 class CustomerStatesSource(ConCatsSource): 694 """Avalable customer sates for the sources overview page. 695 """ 696 grok.order(1) 697 grok.viewletmanager(SourcesRight) 698 column = 'right' 699 name = 'CustomerStates' 700 source_name = 'customer_states' 701 title = _('Customer Registration States') 702 703 class ContractStatesSource(ConCatsSource): 704 """Avalable contract sates for the sources overview page. 705 """ 706 grok.order(2) 707 grok.viewletmanager(SourcesRight) 708 column = 'right' 709 name = 'ContractStates' 710 source_name = 'contract_states' 711 title = _('Contract Workflow States') 712 713 class CustomerDocumentStatesSource(ConCatsSource): 714 """Avalable customer document sates for the sources overview page. 715 """ 716 grok.order(3) 717 grok.viewletmanager(SourcesRight) 718 column = 'right' 719 name = 'CustomerDocumentStates' 720 source_name = 'customer_document_states' 721 title = _('Customer Document Workflow States') 722 723 class DocumentsDocumentStatesSource(ConCatsSource): 724 """Avalable document sates for the sources overview page. 725 """ 726 grok.order(4) 727 grok.viewletmanager(SourcesRight) 728 column = 'right' 729 name = 'DocumentStates' 730 source_name = 'document_states' 731 title = _('Document Workflow States')
Note: See TracChangeset for help on using the changeset viewer.