diff --git a/themis/search/configure.zcml b/themis/search/configure.zcml index b6b7838..8250a23 100644 --- a/themis/search/configure.zcml +++ b/themis/search/configure.zcml @@ -20,6 +20,14 @@ permission="zope2.View" template="mail.pt"/> + + + + + + + + + +

Recherche de courrier

+ +
+
+ + +
+
+ Nombre de résultats : +
+ +
+
+ + + + + + + +
+
+ + +
+
+
+ + + + + + + + + diff --git a/themis/search/doc.py b/themis/search/doc.py index 9c98e9c..ec2d845 100644 --- a/themis/search/doc.py +++ b/themis/search/doc.py @@ -1,5 +1,182 @@ +from five import grok +from zope import interface, schema, component +from zope.interface import implements from Products.Five import BrowserView +from Products.CMFCore.utils import getToolByName +from zope.schema.interfaces import IContextSourceBinder +from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm +from z3c.form import form, field, button, widget +from z3c.form.ptcompat import ViewPageTemplateFile +from z3c.form.browser.radio import RadioWidget +from z3c.form.browser.checkbox import CheckBoxWidget +from z3c.form.browser.text import TextWidget +from z3c.form.interfaces import ITextWidget +import z3c.form.interfaces + +from themis.fields.vocabs import get_terms_for_persons +import tabellio.config.utils +from themis.fields import Commission + +from interfaces import MessageFactory as _ + +def FieldRadioboxesWidget(field, request): + return widget.FieldWidget(field, RadioWidget(request)) + +def FieldCheckboxWidget(field, request): + return widget.FieldWidget(field, CheckBoxWidget(request)) + +@grok.provider(IContextSourceBinder) +def possible_categories(context): + catalog = getToolByName(context, 'portal_catalog') + possible_categories = catalog.uniqueValuesFor('docCategory') + terms = [] + for category in possible_categories: + if not category: + continue + category_id = category.encode('ascii', 'replace') + terms.append(SimpleVocabulary.createTerm(category_id, category_id, category)) + terms.sort(cmp_term) + return SimpleVocabulary(terms) + + +def cmp_term(x, y): + from plone.i18n.normalizer.fr import normalizer + return cmp(normalizer.normalize(x.title), normalizer.normalize(y.title)) + +@grok.provider(IContextSourceBinder) +def possible_topics(context): + catalog = getToolByName(context, 'portal_catalog') + topics = tabellio.config.utils.get_topics_dict() + terms = [] + for key, value in sorted(topics.items()): + topic_id = key + topic_str = value[0] + terms.append(SimpleVocabulary.createTerm(topic_id, topic_id, topic_str)) + return SimpleVocabulary(terms) + + +@grok.provider(IContextSourceBinder) +def possible_sessions(context): + terms = [] + for term in tabellio.config.utils.get_legisl_and_sessions(): + term_id = term.encode('ascii', 'replace') + terms.append(SimpleVocabulary.createTerm(term_id, term_id, term)) + return SimpleVocabulary(terms) + +@grok.provider(IContextSourceBinder) +def possible_status(context): + return SimpleVocabulary([ + SimpleVocabulary.createTerm('all', 'all', _(u'All')), + SimpleVocabulary.createTerm('private', 'private', _(u'Private')), + SimpleVocabulary.createTerm('pending', 'pending', _(u'Pending')), + SimpleVocabulary.createTerm('published', 'published', _(u'Public'))]) + +@grok.provider(IContextSourceBinder) +def possible_commission_status(context): + portal = getToolByName(context, 'portal_url').getPortalObject() + fti = getattr(portal.portal_types, 'ProjetD') + return fti.lookupSchema().get('etat_en_commission').vocabulary + + +class IDocSearchForm(interface.Interface): + text = schema.TextLine(title=_(u'Text'), required=False) + number = schema.TextLine(title=_(u'Document Number'), required=False) + category = schema.Choice(title=_(u'Category'), required=False, + source=possible_categories) + topics = schema.List(title=_(u'Topics'), required=False, + value_type=schema.Choice(required=False, + source=possible_topics)); + person = schema.TextLine(title=_(u'Person'), required=False) + session = schema.Choice(title=_(u'Legislature / Session'), required=False, + source=possible_sessions) + meeting_date_min = schema.Date(title=_(u'Meeting Date (minimum)'), required=False) + meeting_date_max = schema.Date(title=_(u'Meeting Date (maximum)'), required=False) + status = schema.Choice(title=_(u'Status'), required=True, default='all', + source=possible_status) + commission = Commission(title=_(u'Commission'), required=False) + commission_status = schema.Choice(title=_(u'Commission Status'), + required=False, source=possible_commission_status) + +class DocSearchForm(form.Form): + fields = field.Fields(IDocSearchForm) + fields['topics'].widgetFactory = FieldCheckboxWidget + #fields['person'].widgetFactory = FieldPersonWidget + ignoreContext = True + template = ViewPageTemplateFile('view_form.pt') + + @button.buttonAndHandler(_(u'Submit')) + def handleApply(self, action): + pass class SearchView(BrowserView): - pass + batch_macros = ViewPageTemplateFile('batch_macros.pt') + + def form(self): + f = DocSearchForm(self.context, self.request) + f.update() + return f.render() + + def search_results(self): + f = DocSearchForm(self.context, self.request) + f.update() + + if not self.request.form.get('form.buttons.submit'): + return [] + data, errors = f.extractData() + kw = {} + + if data.get('meeting_date_min') and data.get('meeting_date_max'): + kw['docMeetingDate'] = {'query': [data.get('meeting_date_min'), + data.get('meeting_date_max')], + 'range': 'minmax'} + elif data.get('meeting_date_min'): + kw['docMeetingDate'] = {'query': data.get('meeting_date_min'), 'range': 'min'} + elif data.get('meeting_date_max'): + kw['docMeetingDate'] = {'query': data.get('meeting_date_max'), 'range': 'max'} + + if data.get('text'): + kw['docSearchableText'] = data.get('text') + if data.get('number'): + kw['docNumber'] = data.get('number') + if data.get('category'): + kw['docCategory'] = possible_categories(self.context).by_token.get(data.get('category')).title + else: + portal = getToolByName(self.context, 'portal_url').getPortalObject() + kw['portal_type'] = [] + for typeid in portal.portal_types.objectIds(): + if '(D)' in getattr(portal.portal_types, typeid).Title(): + kw['portal_type'].append(typeid) + if data.get('person'): + kw['docPersonsFuzzy'] = data.get('person') + + if data.get('session'): + kw['session'] = tabellio.config.utils.get_list_of_sessions(data.get('session')) + + if data.get('topics'): + kw['docTopics'] = data.get('topics') + + if data.get('commission'): + kw['docCommissions'] = data.get('commission') + + if data.get('commission_status'): + kw['docCommissionState'] = data.get('commission_status') + + if data.get('status') and data.get('status') != 'all': + kw['review_state'] = data.get('status') + + kw['sort_on'] = 'created' + kw['sort_order'] = 'descending' + print 'kw:', kw + catalog = getToolByName(self.context, 'portal_catalog') + return catalog(**kw) + + def get_batchlinkparams(self): + d = dict() + for key in self.request.form: + d[key] = self.request.form[key] + if type(d[key]) is str: + d[key] = unicode(d[key], 'utf-8').encode('utf-8') + elif type(d[key]) is unicode: + d[key] = d[key].encode('utf-8') + return d diff --git a/themis/search/indexer.py b/themis/search/indexer.py index 311c331..010121c 100644 --- a/themis/search/indexer.py +++ b/themis/search/indexer.py @@ -8,7 +8,7 @@ from ZODB.POSException import ConflictError from themis.fields.vocabs import ContactsSource @indexer(plone.dexterity.interfaces.IDexterityItem) -def dateIndexer(obj): +def mailDateIndexer(obj): if obj.portal_type not in ('courrier_entrant', 'courrier_sortant'): return None for attr in ('date_reelle_courrier', 'date_reception'): @@ -18,20 +18,36 @@ def dateIndexer(obj): return DateTime(getattr(obj, attr).isoformat()) return None -grok.global_adapter(dateIndexer, name="mailDate") - +grok.global_adapter(mailDateIndexer, name="mailDate") @indexer(plone.dexterity.interfaces.IDexterityItem) -def mail_dynamic_searchable_text_indexer(obj): - """Dynamic searchable text indexer. - """ - if obj.portal_type not in ('courrier_entrant', 'courrier_sortant'): +def docDateIndexer(obj): + if not '(D)' in obj.Type(): return None + for attr in ('date_document_imprime', 'date_du_document', + 'date_de_publication', 'date_du_rapport'): + if not hasattr(obj, attr): + continue + if getattr(obj, attr): + return DateTime(getattr(obj, attr).isoformat()) + return None +grok.global_adapter(docDateIndexer, name="docDate") - data = obj.fichier - if not data or data.getSize() == 0: - return obj.title +@indexer(plone.dexterity.interfaces.IDexterityItem) +def docMeetingDateIndexer(obj): + if not '(D)' in obj.Type(): + return None + for attr in ('date_seance', 'date_de_la_commission', + 'date_seance_ou_commission'): + if not hasattr(obj, attr): + continue + if getattr(obj, attr): + return DateTime(getattr(obj, attr).isoformat()) + return None +grok.global_adapter(docMeetingDateIndexer, name="docMeetingDate") + +def get_data_to_index(obj, data): # if there is no path to text/plain, do nothing transforms = getToolByName(obj, 'portal_transforms') if not transforms._findPath(data.contentType, 'text/plain'): @@ -54,11 +70,47 @@ def mail_dynamic_searchable_text_indexer(obj): except UnicodeDecodeError: # ok, forget it data = '' + return data - return obj.title + ' ' + data + +@indexer(plone.dexterity.interfaces.IDexterityItem) +def mail_dynamic_searchable_text_indexer(obj): + """Dynamic searchable text indexer. + """ + if obj.portal_type not in ('courrier_entrant', 'courrier_sortant'): + return None + + data = obj.fichier + if not data or data.getSize() == 0: + return obj.title + + return obj.title + ' ' + get_data_to_index(obj, data) grok.global_adapter(mail_dynamic_searchable_text_indexer, name='MailSearchableText') + +@indexer(plone.dexterity.interfaces.IDexterityItem) +def doc_dynamic_searchable_text_indexer(obj): + """Dynamic searchable text indexer. + """ + if not '(D)' in obj.Type(): + return None + + data = None + for attr in ('fichier' 'document_imprime'): + if not hasattr(obj, attr): + continue + if getattr(obj, attr): + data = getattr(obj, attr) + break + if not data or data.getSize() == 0: + return obj.title + + return obj.title + ' ' + get_data_to_index(obj, data) + +grok.global_adapter(doc_dynamic_searchable_text_indexer, name='DocSearchableText') + + @indexer(plone.dexterity.interfaces.IDexterityItem) def contactIndexer(obj): if obj.portal_type not in ('courrier_entrant', 'courrier_sortant'): @@ -99,3 +151,89 @@ def contactFuzzyIndexer(obj): grok.global_adapter(contactFuzzyIndexer, name="MailContactFuzzy") + +@indexer(plone.dexterity.interfaces.IDexterityItem) +def docNumberIndexer(obj): + if not '(D)' in obj.Type(): + return None + for attr in ('numero_biq', 'numero_document', 'numero_bqr'): + if not hasattr(obj, attr): + continue + if getattr(obj, attr): + return getattr(obj, attr) + return None +grok.global_adapter(docNumberIndexer, name="docNumber") + + +@indexer(plone.dexterity.interfaces.IDexterityItem) +def docCommissionsIndexer(obj): + if not '(D)' in obj.Type(): + return None + l = [] + for attr in ('commissions', 'commission_qui_examine', 'commission', + 'examine_en', 'commisions__examine'): + if not hasattr(obj, attr): + continue + t = getattr(obj, attr) + if t: + if type(t) is list: + l.extend(t) + elif type(t) in (unicode, str): + l.append(t) + if not l: + return None + return l +grok.global_adapter(docCommissionsIndexer, name="docCommissions") + + +@indexer(plone.dexterity.interfaces.IDexterityItem) +def docCategoryIndexer(obj): + if not '(D)' in obj.Type(): + return None + category = [obj.Type().replace('(D)', '').strip()] + if hasattr(obj, 'type_de_projet') and getattr(obj, 'type_de_projet'): + s = getattr(obj, 'type_de_projet') + if type(s) is list: + category.extend(s) + elif s: + category.append(s) + if hasattr(obj, 'type_de_proposition') and getattr(obj, 'type_de_proposition'): + s = getattr(obj, 'type_de_proposition') + if type(s) is list: + category.extend(s) + elif s: + category.append(s) + return category +grok.global_adapter(docCategoryIndexer, name="docCategory") + +@indexer(plone.dexterity.interfaces.IDexterityItem) +def personsFuzzyIndexer(obj): + if not '(D)' in obj.Type(): + return None + persons = [] + src = ContactsSource() + for attr in ('auteur', 'auteurs', 'rapporteurs', 'orateurs_seance', + 'orateurs', 'orateurs_en_commission', 'orateurs__en_seanceprop', + 'orateurs_rapportcom', 'orateurs_seance_reponse_orale', + 'ministres_concernes'): + if not hasattr(obj, attr): + continue + value = getattr(obj, attr) + if not value: + continue + if type(value) is not list: + value = [value] + for item in value: + if ':' in item: + try: + r = src.fastGetTitleByToken(obj, item) + except KeyError: + continue + if not type(r) is unicode: + r = unicode(r, 'utf-8') + persons.append(r) + else: + persons.append(item) + return ' '.join(persons) + +grok.global_adapter(personsFuzzyIndexer, name='DocPersonsFuzzy') diff --git a/themis/search/profiles/default/catalog.xml b/themis/search/profiles/default/catalog.xml index 39d0075..6f2ae68 100644 --- a/themis/search/profiles/default/catalog.xml +++ b/themis/search/profiles/default/catalog.xml @@ -30,5 +30,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +