diff --git a/.github/workflows/bandit.yml b/.github/workflows/bandit.yml new file mode 100644 index 0000000..ece16b9 --- /dev/null +++ b/.github/workflows/bandit.yml @@ -0,0 +1,18 @@ +name: Security check - Bandit + +on: push + +jobs: + analyze: + runs-on: ubuntu-latest + permissions: + # required for all workflows + security-events: write + # only required for workflows in private repositories + actions: read + contents: read + steps: + - name: Perform Bandit Analysis + uses: PyCQA/bandit-action@v1 + with: + targets: ./redturtle diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 0000000..1143041 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,32 @@ +name: Black +on: [push] +permissions: + contents: read +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.11] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: install black + run: pip install black + + - name: run black + run: black redturtle/ --check --diff diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml new file mode 100644 index 0000000..65bd8d3 --- /dev/null +++ b/.github/workflows/flake8.yml @@ -0,0 +1,32 @@ +name: Flake8 +on: [push] +permissions: + contents: read +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.11] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: install flake8 + run: pip install flake8 + + - name: run flake8 + run: flake8 redturtle/ setup.py diff --git a/.github/workflows/isort.yaml b/.github/workflows/isort.yaml new file mode 100644 index 0000000..5b39a03 --- /dev/null +++ b/.github/workflows/isort.yaml @@ -0,0 +1,32 @@ +permissions: + contents: read +name: Run isort +on: + - push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # - uses: isort/isort-action@v1 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: install isort + run: pip install isort + + - name: run isort + run: isort --check-only redturtle + diff --git a/.github/workflows/pyroma.yml b/.github/workflows/pyroma.yml new file mode 100644 index 0000000..b0ac679 --- /dev/null +++ b/.github/workflows/pyroma.yml @@ -0,0 +1,32 @@ +permissions: + contents: read +name: Pyroma +on: [push] +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.11] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: install pyroma + run: pip install pyroma==4.2 + + - name: run pyroma + run: pyroma -n 10 -d . diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0e62512..89d8922 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,11 +8,15 @@ jobs: strategy: max-parallel: 4 matrix: - python: ["3.8"] - plone: ["52"] - # exclude: - # - python: "3.7" - # plone: "51" + python: ["3.8", "3.11"] + plone: ["60"] + # temporaneamente disabilitati test su plone 5.2 + # plone: ["52", "60"] + exclude: + - python: "3.8" + plone: "60" + - python: "3.11" + plone: "52" steps: - uses: actions/checkout@v4 - name: Cache eggs @@ -26,7 +30,7 @@ jobs: python-version: ${{ matrix.python }} - name: Install dependencies run: | - pip install -r requirements.txt -c constraints_plone${{ matrix.plone }}.txt + pip install -r requirements_${{ matrix.plone }}.txt cp test_plone${{ matrix.plone }}.cfg buildout.cfg - name: Install buildout run: | diff --git a/.github/workflows/zpretty.yml b/.github/workflows/zpretty.yml new file mode 100644 index 0000000..4daffcc --- /dev/null +++ b/.github/workflows/zpretty.yml @@ -0,0 +1,42 @@ +permissions: + contents: read +name: zpretty +on: [push] +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.11] + + steps: + # git checkout + - uses: actions/checkout@v4 + + # python setup + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + # python cache + - uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + # install zpretty + - name: install zpretty + run: pip install zpretty + + # run zpretty + - name: run zpretty + run: find redturtle -name '*.zcml' | xargs zpretty -i + + # XXX: this doesn't work on gh actions (https://github.com/plone/plone.restapi/pull/1119/checks?check_run_id=2686474411) + # run git diff + - name: run git diff + run: git diff --exit-code diff --git a/.gitignore b/.gitignore index 24f9400..305f8f2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ local/ .sass-cache/ __pycache__ .python-version +pyvenv.cfg diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..40c8f14 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +profile=plone diff --git a/README.rst b/README.rst index cb38061..0426509 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,26 @@ + +=============== +redturtle.bandi +=============== + +|python| |version| |ci| |downloads| |license| + +.. |python| image:: https://img.shields.io/pypi/pyversions/redturtle.bandi.svg + :target: https://pypi.python.org/pypi/redturtle.bandi/ + +.. |version| image:: http://img.shields.io/pypi/v/redturtle.bandi.svg + :target: https://pypi.python.org/pypi/redturtle.bandi + +.. |ci| image:: https://github.com/RedTurtle/redturtle.bandi/actions/workflows/test.yml/badge.svg + :target: https://github.com/RedTurtle/redturtle.bandi/actions + +.. |downloads| image:: https://img.shields.io/pypi/dm/redturtle.bandi.svg + :target: https://pypi.org/project/redturtle.bandi/ + +.. |license| image:: https://img.shields.io/pypi/l/redturtle.bandi.svg + :target: https://pypi.org/project/redturtle.bandi/ + :alt: License + Introduction ============ @@ -73,6 +96,14 @@ Tile In order to use layout bandi for tile is necessary have installed collective.tiles.collection product. +Behvaiors +========= + +redturtle.bandi.forced_state +---------------------------- + +This behavior allows to set the forced state of an announcement. + plone.restapi integrations ========================== diff --git a/buildout.cfg b/buildout.cfg index bb635cb..d7db038 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -2,4 +2,4 @@ # use this extend one of the buildout configuration: extends = - test_plone52.cfg + test_plone60.cfg diff --git a/constraints.txt b/constraints.txt deleted file mode 100644 index 24cbf87..0000000 --- a/constraints.txt +++ /dev/null @@ -1 +0,0 @@ --c constraints_plone52.txt diff --git a/constraints_plone52.txt b/constraints_plone52.txt deleted file mode 100644 index d96fee0..0000000 --- a/constraints_plone52.txt +++ /dev/null @@ -1,3 +0,0 @@ --c https://dist.plone.org/release/5.2-latest/requirements.txt -# setuptools==40.2.0 -# zc.buildout==2.12.2 diff --git a/docs/HISTORY.txt b/docs/HISTORY.txt index 005bdff..7861f33 100644 --- a/docs/HISTORY.txt +++ b/docs/HISTORY.txt @@ -4,7 +4,10 @@ Changelog 1.6.1 (unreleased) ------------------ -- Nothing changed yet. +- Add redturtle.bandi.forced_state behavior. This behavior allows to set the forced state of an announcement. + [mamico] +- lint/isort + [mamico] 1.6.0 (2025-03-13) diff --git a/redturtle/__init__.py b/redturtle/__init__.py index f48ad10..05f0beb 100644 --- a/redturtle/__init__.py +++ b/redturtle/__init__.py @@ -1,6 +1,7 @@ # See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages try: - __import__('pkg_resources').declare_namespace(__name__) + __import__("pkg_resources").declare_namespace(__name__) except ImportError: from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/redturtle/bandi/Extensions/Install.py b/redturtle/bandi/Extensions/Install.py deleted file mode 100644 index 40dd440..0000000 --- a/redturtle/bandi/Extensions/Install.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- -from Products.CMFCore.utils import getToolByName -from cStringIO import StringIO - - -def runProfile(portal, profileName): - setupTool = getToolByName(portal, 'portal_setup') - setupTool.runAllImportStepsFromProfile(profileName) - - -def uninstall(portal, reinstall=False): - out = StringIO() - if not reinstall: - runProfile(portal, 'profile-redturtle.bandi:uninstall') diff --git a/redturtle/bandi/__init__.py b/redturtle/bandi/__init__.py index f142c67..cb73414 100644 --- a/redturtle/bandi/__init__.py +++ b/redturtle/bandi/__init__.py @@ -1,8 +1,10 @@ -"""Main product initializer -""" +"""Main product initializer""" + from zope.i18nmessageid import MessageFactory import logging -logger = logging.getLogger('redturtle.bandi') -bandiMessageFactory = MessageFactory('redturtle.bandi') + +logger = logging.getLogger("redturtle.bandi") +bandiMessageFactory = MessageFactory("redturtle.bandi") +_ = bandiMessageFactory diff --git a/redturtle/bandi/Extensions/__init__.py b/redturtle/bandi/behaviors/__init__.py similarity index 100% rename from redturtle/bandi/Extensions/__init__.py rename to redturtle/bandi/behaviors/__init__.py diff --git a/redturtle/bandi/behaviors/configure.zcml b/redturtle/bandi/behaviors/configure.zcml new file mode 100644 index 0000000..b1d23ca --- /dev/null +++ b/redturtle/bandi/behaviors/configure.zcml @@ -0,0 +1,15 @@ + + + + + diff --git a/redturtle/bandi/behaviors/force.py b/redturtle/bandi/behaviors/force.py new file mode 100644 index 0000000..47296a2 --- /dev/null +++ b/redturtle/bandi/behaviors/force.py @@ -0,0 +1,43 @@ +from plone.autoform.interfaces import IFormFieldProvider +from plone.dexterity.interfaces import IDexterityContent +from plone.supermodel import model +from redturtle.bandi import _ +from redturtle.bandi.config import STATES +from redturtle.bandi.interfaces.bando import IBando +from zope import schema +from zope.component import adapter +from zope.interface import implementer +from zope.interface import provider +from zope.schema.vocabulary import SimpleTerm +from zope.schema.vocabulary import SimpleVocabulary + + +@provider(IFormFieldProvider) +class IForcedState(model.Schema): + """Marker inteerface for content type Bando""" + + forced_state = schema.Choice( + title=_("stato_avanzamento_label", default="Stato di avanzamento"), + description=_( + "stato_avanzamento_help", + default=( + "Indica lo stato di avanzamento. Se il campo esiste, viene " + "utilizzato per determinare lo stato del bando. Se non esiste, " + "viene utilizzato il metodo di base per cui lo stato del bando " + "è determinato dalle date di apertura, scadenza e chiusura procedimento." + ), + ), + required=False, + vocabulary=SimpleVocabulary( + [SimpleTerm(state, state, STATES[state]["label"]) for state in STATES] + ), + ) + + +@implementer(IBando) +@adapter(IDexterityContent) +class ForcedState(object): + """Bando adapter""" + + def __init__(self, context): + self.context = context diff --git a/redturtle/bandi/browser/bando.py b/redturtle/bandi/browser/bando.py index 1027f8f..7672d19 100644 --- a/redturtle/bandi/browser/bando.py +++ b/redturtle/bandi/browser/bando.py @@ -6,6 +6,8 @@ from plone.i18n.normalizer.interfaces import IIDNormalizer from Products.Five import BrowserView from redturtle.bandi import bandiMessageFactory as _ +from redturtle.bandi.behaviors.force import IForcedState +from redturtle.bandi.config import STATES from redturtle.bandi.interfaces import IBandoFolderDeepening from z3c.form import field from zope.component import getMultiAdapter @@ -17,8 +19,8 @@ try: - from plone.restapi.serializer.utils import uid_to_url from plone.restapi.serializer.converters import json_compatible + from plone.restapi.serializer.utils import uid_to_url HAS_PLONERESTAPI = True except ImportError: @@ -156,7 +158,7 @@ def type_hook_link(self, brain): # probabilmente legato al fatto che i link ora sono creati via # api e non da interfaccia Plone (?) if data["url"].startswith(f"/{siteid}"): - data["url"] = data["url"][len(siteid) + 1 :] + data["url"] = data["url"][len(siteid) + 1 :] # noqa: E203 if HAS_PLONERESTAPI: data["url"] = uid_to_url(data["url"]) return data @@ -252,6 +254,14 @@ def getBandoState(self): """ return right bando state """ + if IForcedState.providedBy(self.context): + forced_state = getattr(self.context, "forced_state", None) + if forced_state in STATES: + return ( + forced_state, + translate(STATES[forced_state]["label"], context=self.request), + ) + apertura_bando = getattr(self.context, "apertura_bando", None) scadenza_bando = getattr(self.context, "scadenza_bando", None) chiusura_procedimento_bando = getattr( diff --git a/redturtle/bandi/browser/collection.py b/redturtle/bandi/browser/collection.py index 8ae1c31..b196c03 100644 --- a/redturtle/bandi/browser/collection.py +++ b/redturtle/bandi/browser/collection.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- from plone import api from Products.Five import BrowserView -from redturtle.bandi import bandiMessageFactory as _ from zope.component import getUtility -from zope.i18n import translate from zope.interface import implementer from zope.interface import Interface from zope.schema.interfaces import IVocabularyFactory diff --git a/redturtle/bandi/browser/configure.zcml b/redturtle/bandi/browser/configure.zcml index 3467c23..42c85e6 100644 --- a/redturtle/bandi/browser/configure.zcml +++ b/redturtle/bandi/browser/configure.zcml @@ -1,9 +1,10 @@ + xmlns:plone="http://namespaces.plone.org/plone" + i18n_domain="redturtle.bandi" + > @@ -11,157 +12,158 @@ + factory=".bando.AddView" + provides="zope.publisher.interfaces.browser.IBrowserPage" + for="Products.CMFCore.interfaces.IFolderish + redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer + plone.dexterity.interfaces.IDexterityFTI" + name="Bando" + /> + name="edit" + for="redturtle.bandi.interfaces.bando.IBando" + class=".bando.EditView" + permission="zope2.View" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> + name="bando_view" + for="..interfaces.IBando" + class=".bando.BandoView" + template="bando.pt" + permission="zope2.View" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> + menu="plone_displayviews" + title="Default" + description="Vista predefinita, altre informazioni sotto" + for="..interfaces.IBando" + action="@@bando_view" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> - + name="bando_right_view" + for="..interfaces.IBando" + class=".bando.BandoView" + template="bando-right.pt" + permission="zope2.View" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> + + name="search_bandi_form" + for="*" + class=".search.SearchBandiForm" + template="search_form.pt" + permission="zope2.View" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> + name="search_bandi" + for="*" + class=".search.SearchBandi" + template="search.pt" + permission="zope2.View" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> - + + name="collection_bandi_view" + for="plone.app.contenttypes.interfaces.ICollection" + class=".collection.CollectionBandiView" + template="collection.pt" + permission="zope2.View" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> + name="collection_bandi_tipologia_view" + for="plone.app.contenttypes.interfaces.ICollection" + class=".collection.CollectionBandiView" + template="collection.pt" + permission="zope2.View" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> + menu="plone_displayviews" + for="plone.app.contenttypes.interfaces.ICollection" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + > + title="Bandi con scadenza" + action="collection_bandi_view" + i18n:translate="title" + /> + title="Bandi con tipologia e scadenza" + action="collection_bandi_tipologia_view" + i18n:translate="title" + /> - + + name="searchbandi.js" + file="searchbandi.js" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> - + name="plone_context_state" + for="redturtle.bandi.interfaces.IBando" + class=".context.ContextState" + allowed_interface="plone.app.layout.globals.interfaces.IContextState" + permission="zope2.View" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> + + name="bandi-settings" + for="Products.CMFCore.interfaces.ISiteRoot" + class=".controlpanel.BandiControlPanel" + permission="cmf.ManagePortal" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> + name="migration-from-rer" + for="Products.CMFCore.interfaces.ISiteRoot" + class=".migrator.RERMigrationView" + permission="cmf.ManagePortal" + layer="redturtle.bandi.interfaces.browserlayer.IRedturtleBandiLayer" + /> diff --git a/redturtle/bandi/browser/context.py b/redturtle/bandi/browser/context.py index eca61ab..4732813 100644 --- a/redturtle/bandi/browser/context.py +++ b/redturtle/bandi/browser/context.py @@ -7,8 +7,8 @@ @implementer(IContextState) class ContextState(BaseClass): - """modifica il metodo Folder, per poter aggiungere elementi al documento anche quando è - impostato come vista predefinita + """modifica il metodo Folder, per poter aggiungere elementi al documento anche quando è + impostato come vista predefinita """ def folder(self): @@ -16,4 +16,3 @@ def folder(self): return aq_inner(self.context) else: return self.parent() - diff --git a/redturtle/bandi/browser/controlpanel.py b/redturtle/bandi/browser/controlpanel.py index 7784e14..d167da0 100644 --- a/redturtle/bandi/browser/controlpanel.py +++ b/redturtle/bandi/browser/controlpanel.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- from plone.app.registry.browser import controlpanel -from redturtle.bandi.interfaces.settings import IBandoSettings from redturtle.bandi import bandiMessageFactory as _ +from redturtle.bandi.interfaces.settings import IBandoSettings class BandiSettingsForm(controlpanel.RegistryEditForm): schema = IBandoSettings - id = 'BandiSettingsForm' + id = "BandiSettingsForm" label = _("bandi_settings_label", default="Impostazioni per i bandi") diff --git a/redturtle/bandi/browser/migrator.py b/redturtle/bandi/browser/migrator.py index 85d52d4..79f835b 100644 --- a/redturtle/bandi/browser/migrator.py +++ b/redturtle/bandi/browser/migrator.py @@ -1,17 +1,17 @@ # -*- coding: utf-8 -*- +from BTrees.OOBTree import OOBTree +from plone import api +from plone.protect.interfaces import IDisableCSRFProtection +from Products.CMFEditions.utilities import dereference from Products.Five import BrowserView -from redturtle.bandi.interfaces.bando import IBando as newBandoInterface from redturtle.bandi.content.bando import Bando from redturtle.bandi.content.bandofolderdeepening import BandoFolderDeepening -from plone import api -from zope.interface import noLongerProvides -from zope.interface import alsoProvides +from redturtle.bandi.interfaces.bando import IBando as newBandoInterface from redturtle.bandi.interfaces.bandofolderdeepening import ( IBandoFolderDeepening as newFolderDeepeningInterface, ) # noqa -from Products.CMFEditions.utilities import dereference -from BTrees.OOBTree import OOBTree -from plone.protect.interfaces import IDisableCSRFProtection +from zope.interface import alsoProvides +from zope.interface import noLongerProvides try: @@ -26,6 +26,7 @@ import logging + logger = logging.getLogger(__name__) @@ -33,11 +34,11 @@ class RERMigrationView(BrowserView): def __call__(self): alsoProvides(self.request, IDisableCSRFProtection) if not HAS_RER_BANDI: - return 'Impossibile eseguire la migrazione: rer.bandi non presente' - output = '' - brains = api.content.find(portal_type='Bando') - logger.info('Migrating {} Bandi:'.format(len(brains))) - output += 'Migrating {} Bandi:'.format(len(brains)) + return "Impossibile eseguire la migrazione: rer.bandi non presente" + output = "" + brains = api.content.find(portal_type="Bando") + logger.info("Migrating {} Bandi:".format(len(brains))) + output += "Migrating {} Bandi:".format(len(brains)) for brain in brains: bando = brain.getObject() self.updateClass( @@ -48,10 +49,10 @@ def __call__(self): ) self.cleanHistory(obj=bando) subfolders = bando.listFolderContents( - contentFilter={'portal_type': 'Bando Folder Deepening'} + contentFilter={"portal_type": "Bando Folder Deepening"} ) - logger.info('- {}'.format(brain.getPath())) - output += '\n - {}'.format(brain.getPath()) + logger.info("- {}".format(brain.getPath())) + output += "\n - {}".format(brain.getPath()) for subfolder in subfolders: self.updateClass( obj=subfolder, @@ -59,12 +60,8 @@ def __call__(self): oldInterface=oldFolderDeepeningInterface, newInterface=newFolderDeepeningInterface, ) - logger.info( - ' - [FOLDERDEEPENING] {}'.format(subfolder.absolute_url()) - ) - output += '\n - [FOLDERDEEPENING] {}'.format( - subfolder.absolute_url() - ) + logger.info(" - [FOLDERDEEPENING] {}".format(subfolder.absolute_url())) + output += "\n - [FOLDERDEEPENING] {}".format(subfolder.absolute_url()) return output @@ -75,19 +72,17 @@ def updateClass(self, obj, className, oldInterface, newInterface): parent._setOb(obj.getId(), obj) noLongerProvides(obj, oldInterface) alsoProvides(obj, newInterface) - parent[obj.getId()].reindexObject(idxs=['object_provides']) + parent[obj.getId()].reindexObject(idxs=["object_provides"]) def cleanHistory(self, obj): context, history_id = dereference(obj) - historiesstorage = api.portal.get_tool(name='portal_historiesstorage') + historiesstorage = api.portal.get_tool(name="portal_historiesstorage") history = historiesstorage._getShadowHistory(history_id) if not history: return keys = set( [ - historiesstorage._getZVCAccessInfo(history_id, selector, True)[ - 0 - ] + historiesstorage._getZVCAccessInfo(history_id, selector, True)[0] for selector in history._available ] ) @@ -98,8 +93,6 @@ def cleanHistory(self, obj): zope_version_history = OOBTree() storage = historiesstorage._getShadowStorage()._storage storage.pop(history_id, None) - dereferenced_obj = dereference( - history_id=history_id, zodb_hook=self.context - )[0] - if hasattr(dereferenced_obj, 'version_id'): - delattr(dereferenced_obj, 'version_id') + dereferenced_obj = dereference(history_id=history_id, zodb_hook=self.context)[0] + if hasattr(dereferenced_obj, "version_id"): + delattr(dereferenced_obj, "version_id") diff --git a/redturtle/bandi/browser/search.py b/redturtle/bandi/browser/search.py index 53cb07d..b140b41 100644 --- a/redturtle/bandi/browser/search.py +++ b/redturtle/bandi/browser/search.py @@ -4,9 +4,11 @@ from Products.CMFCore.utils import getToolByName from Products.Five.browser import BrowserView from six.moves.urllib.parse import quote -from zope.component import getUtility, queryUtility +from zope.component import getUtility +from zope.component import queryUtility from zope.schema.interfaces import IVocabularyFactory + try: from collective.solr.interfaces import ISolrConnectionConfig diff --git a/redturtle/bandi/config.py b/redturtle/bandi/config.py new file mode 100644 index 0000000..b0b5fac --- /dev/null +++ b/redturtle/bandi/config.py @@ -0,0 +1,17 @@ +from redturtle.bandi import _ + + +STATES = { + "open": { + "label": _("Open"), + }, + "closed": { + "label": _("Closed"), + }, + "inProgress": { + "label": _("In progress"), + }, + "scheduled": { + "label": _("Scheduled"), + }, +} diff --git a/redturtle/bandi/configure.zcml b/redturtle/bandi/configure.zcml index 1c6cca0..be902c2 100644 --- a/redturtle/bandi/configure.zcml +++ b/redturtle/bandi/configure.zcml @@ -2,99 +2,125 @@ xmlns="http://namespaces.zope.org/zope" xmlns:five="http://namespaces.zope.org/five" xmlns:genericsetup="http://namespaces.zope.org/genericsetup" - xmlns:zcml="http://namespaces.zope.org/zcml" xmlns:i18n="http://namespaces.zope.org/i18n" - i18n_domain="redturtle.bandi"> + xmlns:zcml="http://namespaces.zope.org/zcml" + i18n_domain="redturtle.bandi" + > + id="redturtle.bandi.addBando" + title="redturtle.bandi: Add Bando" + /> + id="redturtle.bandi.addBandoFolderDeepening" + title="redturtle.bandi: Add Bando Folder Deepening" + /> + id="redturtle.bandi.addBandiPortlet" + title="redturtle.bandi: Add Bandi Portlet" + /> + - + + name="redturtle.bandi.tipologia.vocabulary" + component=".vocabularies.TipologiaBandoVocabularyFactory" + /> + name="redturtle.bandi.destinatari.vocabulary" + component=".vocabularies.DestinatariVocabulary" + /> + name="redturtle.bandi.enti.vocabulary" + component=".vocabularies.EnteVocabulary" + /> + name="redturtle.bandi.vocabularies.bandi_states" + component=".vocabularies.BandiStatesVcabularyFactory" + /> - - - - - - - + + + + + + + diff --git a/redturtle/bandi/content/bando.py b/redturtle/bandi/content/bando.py index 8b0b26f..a5b9e66 100644 --- a/redturtle/bandi/content/bando.py +++ b/redturtle/bandi/content/bando.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from zope.interface import implementer -from redturtle.bandi.interfaces.bando import IBando from plone.dexterity.content import Container +from redturtle.bandi.interfaces.bando import IBando +from zope.interface import implementer @implementer(IBando) diff --git a/redturtle/bandi/content/configure.zcml b/redturtle/bandi/content/configure.zcml index 8f8060e..173ee9a 100644 --- a/redturtle/bandi/content/configure.zcml +++ b/redturtle/bandi/content/configure.zcml @@ -1,5 +1,6 @@ + i18n_domain="redturtle.bandi" + > diff --git a/redturtle/bandi/indexer.py b/redturtle/bandi/indexer.py index 65755a3..ca37f4d 100644 --- a/redturtle/bandi/indexer.py +++ b/redturtle/bandi/indexer.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- +# importo il datetime di python +from datetime import datetime from DateTime import DateTime from plone.indexer.decorator import indexer from redturtle.bandi.interfaces.bando import IBando from redturtle.bandi.vocabularies import TipologiaBandoVocabulary -# importo il datetime di python -from datetime import datetime # funzione che riceve un date e torna un datetime con l'ora a zero @@ -64,7 +64,7 @@ def ente_bando(object, **kw): @indexer(IBando) def tipologia_bando(object, **kw): return getattr(object, "tipologia_bando", None) - + @indexer(IBando) def tipologia_bando_label(object, **kw): diff --git a/redturtle/bandi/interfaces/__init__.py b/redturtle/bandi/interfaces/__init__.py index 037130e..2ace91b 100644 --- a/redturtle/bandi/interfaces/__init__.py +++ b/redturtle/bandi/interfaces/__init__.py @@ -1,3 +1,3 @@ # -*- extra stuff goes here -*- -from .bandofolderdeepening import IBandoFolderDeepening # noqa from .bando import IBando # noqa +from .bandofolderdeepening import IBandoFolderDeepening # noqa diff --git a/redturtle/bandi/interfaces/bandoSchema.py b/redturtle/bandi/interfaces/bandoSchema.py index 8b4eaca..c30b740 100644 --- a/redturtle/bandi/interfaces/bandoSchema.py +++ b/redturtle/bandi/interfaces/bandoSchema.py @@ -32,41 +32,41 @@ class IBandoSchema(model.Schema): # fields riferimenti_bando = RichText( - title=_("riferimenti_bando_label", default=u"References"), - description=_("riferimenti_bando_help", default=u""), + title=_("riferimenti_bando_label", default="References"), + description=_("riferimenti_bando_help", default=""), required=False, ) apertura_bando = schema.Datetime( - title=_("apertura_bando_label", default=u"Opening date"), + title=_("apertura_bando_label", default="Opening date"), description=_( "apertura_bando_help", - default=u"Date and time of the opening of the announcement. Use " - u"this field if you want to set a specific opening date. " - u"If not set, the announcement will be open immediately.", + default="Date and time of the opening of the announcement. Use " + "this field if you want to set a specific opening date. " + "If not set, the announcement will be open immediately.", ), required=False, ) chiusura_procedimento_bando = schema.Date( title=_( "chiusura_procedimento_bando_label", - default=u"Closing date procedure", + default="Closing date procedure", ), - description=_("chiusura_procedimento_bando_help", default=u""), + description=_("chiusura_procedimento_bando_help", default=""), required=False, ) scadenza_bando = schema.Datetime( - title=_("scadenza_bando_label", default=u"Expiration date and time"), + title=_("scadenza_bando_label", default="Expiration date and time"), description=_( "scadenza_bando_help", - default=u"Deadline to participate in the announcement", + default="Deadline to participate in the announcement", ), required=False, ) ente_bando = schema.Tuple( - title=_(u"ente_label", default=u"Authority"), - description=_(u"ente_help", default=u"Select some authorities."), + title=_("ente_label", default="Authority"), + description=_("ente_help", default="Select some authorities."), required=False, defaultFactory=getDefaultEnte, value_type=schema.TextLine(), @@ -74,14 +74,14 @@ class IBandoSchema(model.Schema): ) destinatari = schema.List( - title=_("destinatari_label", default=u"Recipients"), + title=_("destinatari_label", default="Recipients"), description=_("destinatari_help", default=""), required=True, value_type=schema.Choice(vocabulary="redturtle.bandi.destinatari.vocabulary"), ) tipologia_bando = schema.Choice( - title=_("tipologia_bando_label", default=u"Announcement type"), + title=_("tipologia_bando_label", default="Announcement type"), description=_("tipologia_bando_help", default=""), vocabulary="redturtle.bandi.tipologia.vocabulary", required=True, diff --git a/redturtle/bandi/interfaces/bandofolderdeepening.py b/redturtle/bandi/interfaces/bandofolderdeepening.py index 5019aff..d42f238 100644 --- a/redturtle/bandi/interfaces/bandofolderdeepening.py +++ b/redturtle/bandi/interfaces/bandofolderdeepening.py @@ -1,4 +1,6 @@ from zope.interface import Interface + + # -*- Additional Imports Here -*- diff --git a/redturtle/bandi/interfaces/browserlayer.py b/redturtle/bandi/interfaces/browserlayer.py index a2b19df..e800741 100644 --- a/redturtle/bandi/interfaces/browserlayer.py +++ b/redturtle/bandi/interfaces/browserlayer.py @@ -1,10 +1,6 @@ +from collective.tiles.collection.interfaces import ICollectiveTilesCollectionLayer from zope.publisher.interfaces.browser import IDefaultBrowserLayer -from collective.tiles.collection.interfaces import ( - ICollectiveTilesCollectionLayer, -) -class IRedturtleBandiLayer( - IDefaultBrowserLayer, ICollectiveTilesCollectionLayer -): +class IRedturtleBandiLayer(IDefaultBrowserLayer, ICollectiveTilesCollectionLayer): """Marker interface that defines a browser layer.""" diff --git a/redturtle/bandi/interfaces/settings.py b/redturtle/bandi/interfaces/settings.py index bdf00cb..a6f8ad4 100644 --- a/redturtle/bandi/interfaces/settings.py +++ b/redturtle/bandi/interfaces/settings.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from zope.interface import Interface -from zope import schema from redturtle.bandi import bandiMessageFactory as _ +from zope import schema +from zope.interface import Interface class IBandoSettings(Interface): diff --git a/redturtle/bandi/portlets/collection.py b/redturtle/bandi/portlets/collection.py index 60d988e..5594465 100644 --- a/redturtle/bandi/portlets/collection.py +++ b/redturtle/bandi/portlets/collection.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- + +# OBSOLETED + from plone import api from plone.app.portlets.browser import formhelper from plone.app.portlets.portlets import base @@ -8,59 +11,58 @@ from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from redturtle.bandi import bandiMessageFactory as _ from zope import schema -from zope.component import getMultiAdapter, getUtility +from zope.component import getMultiAdapter +from zope.component import getUtility from zope.i18n import translate from zope.interface import implementer from zope.schema.interfaces import IVocabularyFactory class IBandoCollectionPortlet(ICollectionPortlet): - """A portlet which renders the results of a collection object. - """ + """A portlet which renders the results of a collection object.""" show_more_text = schema.TextLine( - title=_(u"Other text"), - description=_(u"Alternative text to show in 'other' link."), + title=_("Other text"), + description=_("Alternative text to show in 'other' link."), required=True, - default=u"Altro\u2026", + default="Altro\u2026", ) show_more_path = schema.Choice( - title=_(u"Internal link"), + title=_("Internal link"), description=_( - u"Insert an internal link. This field override external link field" + "Insert an internal link. This field override external link field" ), required=False, source=CatalogSource(), ) show_description = schema.Bool( - title=u"Mostra descrizione", required=True, default=False + title="Mostra descrizione", required=True, default=False ) show_tipologia_bando = schema.Bool( - title=u"Mostra tipologia bando", required=True, default=False + title="Mostra tipologia bando", required=True, default=False ) show_effective = schema.Bool( - title=u"Mostra data di pubblicazione", required=True, default=False + title="Mostra data di pubblicazione", required=True, default=False ) show_scadenza_bando = schema.Bool( - title=u"Mostra data di scadenza", required=True, default=False + title="Mostra data di scadenza", required=True, default=False ) @implementer(IBandoCollectionPortlet) class Assignment(base.Assignment): - """ Portlet assignment. This is what is actually managed through the portlets UI and associated with columns. """ - header = u"" + header = "" target_collection = None limit = None show_more = True @@ -68,7 +70,7 @@ class Assignment(base.Assignment): # parametri da ripulire def __init__( self, - header=u"", + header="", target_collection=None, limit=None, show_more=True, @@ -116,7 +118,6 @@ def title(self): class Renderer(base.Renderer): - """Portlet renderer. This is registered in configure.zcml. The referenced page template is @@ -189,8 +190,7 @@ def results(self): return resultList def isValidDeadline(self, date): - """ - """ + """ """ if not date: return False if date.Date() == "2100/12/31": @@ -199,13 +199,12 @@ def isValidDeadline(self, date): return True def isTipologiaValid(self, tipologia_bando): - """ - """ + """ """ return tipologia_bando in [x.value for x in self.voc_tipologia._terms] @memoize def collection(self): - """ get the collection the portlet is pointing to""" + """get the collection the portlet is pointing to""" # collection_path = self.data.target_collection # if not collection_path: @@ -234,45 +233,37 @@ def getScadenzaDate(self, brain): # of the day-after, if time is not provided date = date - 1 long_format = False - return api.portal.get_localized_time( - datetime=date, long_format=long_format - ) + return api.portal.get_localized_time(datetime=date, long_format=long_format) def portal(self): portal_state = getMultiAdapter( - (self.context, self.request), name=u"plone_portal_state" + (self.context, self.request), name="plone_portal_state" ) return portal_state.portal() def getBandoState(self, bando): """ - return corretc bando state + return correctt bando state """ scadenza_bando = bando.scadenza_bando chiusura_procedimento_bando = bando.chiusura_procedimento_bando - state = ("open", translate(_(u"Open"), context=self.request)) + state = ("open", translate(_("Open"), context=self.request)) if scadenza_bando and scadenza_bando.isPast(): - if ( - chiusura_procedimento_bando - and chiusura_procedimento_bando.isPast() - ): + if chiusura_procedimento_bando and chiusura_procedimento_bando.isPast(): state = ( "closed", - translate(_(u"Closed"), context=self.request), + translate(_("Closed"), context=self.request), ) else: state = ( "inProgress", - translate(_(u"In progress"), context=self.request), + translate(_("In progress"), context=self.request), ) else: - if ( - chiusura_procedimento_bando - and chiusura_procedimento_bando.isPast() - ): + if chiusura_procedimento_bando and chiusura_procedimento_bando.isPast(): state = ( "closed", - translate(_(u"Closed"), context=self.request), + translate(_("Closed"), context=self.request), ) return state @@ -286,7 +277,6 @@ def has_effective_date(self, bando): class AddForm(formhelper.AddForm): - """Portlet add form. This is registered in configure.zcml. The form_fields variable tells @@ -296,17 +286,14 @@ class AddForm(formhelper.AddForm): schema = IBandoCollectionPortlet - label = _(u"Add Bandi Portlet") - description = _( - u"This portlet display a listing of bandi from a Collection." - ) + label = _("Add Bandi Portlet") + description = _("This portlet display a listing of bandi from a Collection.") def create(self, data): return Assignment(**data) class EditForm(formhelper.EditForm): - """Portlet edit form. This is registered with configure.zcml. The form_fields variable tells @@ -315,8 +302,5 @@ class EditForm(formhelper.EditForm): schema = IBandoCollectionPortlet - label = _(u"Edit Bandi Portlet") - description = _( - u"This portlet display a listing of bandi from a Collection." - ) - + label = _("Edit Bandi Portlet") + description = _("This portlet display a listing of bandi from a Collection.") diff --git a/redturtle/bandi/portlets/configure.zcml b/redturtle/bandi/portlets/configure.zcml index eb5aae9..88e2f47 100644 --- a/redturtle/bandi/portlets/configure.zcml +++ b/redturtle/bandi/portlets/configure.zcml @@ -1,7 +1,8 @@ + i18n_domain="redturtle.bandi" + > @@ -11,11 +12,11 @@ name="redturtle.bandi.portlets.collection.BandoCollectionPortlet" interface=".collection.IBandoCollectionPortlet" assignment=".collection.Assignment" - view_permission="zope2.View" - edit_permission="redturtle.bandi.addBandiPortlet" renderer=".collection.Renderer" addview=".collection.AddForm" editview=".collection.EditForm" + view_permission="zope2.View" + edit_permission="redturtle.bandi.addBandiPortlet" /> diff --git a/redturtle/bandi/psheetvocabulary.py b/redturtle/bandi/psheetvocabulary.py index 4df7661..6fb8838 100644 --- a/redturtle/bandi/psheetvocabulary.py +++ b/redturtle/bandi/psheetvocabulary.py @@ -23,7 +23,7 @@ def __init__(self, sheet_name, property_name, default_terms): def iter_property_terms(self, context): prop_sheet = getattr( - getToolByName(context, 'portal_properties'), self.sheet_name, None + getToolByName(context, "portal_properties"), self.sheet_name, None ) if prop_sheet is None: raise VocabularyNotFoundError @@ -34,8 +34,8 @@ def iter_property_terms(self, context): ret = [] for line in prop_lines: - if '|' in line: - value, title = line.split('|', 1) + if "|" in line: + value, title = line.split("|", 1) else: value = title = line ret.append((value.strip(), title.strip())) @@ -50,4 +50,3 @@ def __call__(self, context): return SimpleVocabulary( [SimpleTerm(value=t[0], token=t[0], title=t[1]) for t in terms] ) - diff --git a/redturtle/bandi/querystring/configure.zcml b/redturtle/bandi/querystring/configure.zcml index 235254a..ac1b738 100644 --- a/redturtle/bandi/querystring/configure.zcml +++ b/redturtle/bandi/querystring/configure.zcml @@ -2,10 +2,11 @@ xmlns="http://namespaces.zope.org/zope" xmlns:five="http://namespaces.zope.org/five" xmlns:genericsetup="http://namespaces.zope.org/genericsetup" - xmlns:zcml="http://namespaces.zope.org/zcml" xmlns:i18n="http://namespaces.zope.org/i18n" - i18n_domain="redturtle.bandi"> + xmlns:zcml="http://namespaces.zope.org/zcml" + i18n_domain="redturtle.bandi" + > - \ No newline at end of file + diff --git a/redturtle/bandi/querystring/querymodifiers/bandi_state.py b/redturtle/bandi/querystring/querymodifiers/bandi_state.py index b81645b..2235b60 100644 --- a/redturtle/bandi/querystring/querymodifiers/bandi_state.py +++ b/redturtle/bandi/querystring/querymodifiers/bandi_state.py @@ -1,7 +1,7 @@ +from DateTime import DateTime from plone.app.querystring.interfaces import IQueryModifier -from zope.interface import provider from plone.restapi.serializer.converters import json_compatible -from DateTime import DateTime +from zope.interface import provider @provider(IQueryModifier) diff --git a/redturtle/bandi/querystring/querymodifiers/configure.zcml b/redturtle/bandi/querystring/querymodifiers/configure.zcml index 39133eb..506878b 100644 --- a/redturtle/bandi/querystring/querymodifiers/configure.zcml +++ b/redturtle/bandi/querystring/querymodifiers/configure.zcml @@ -5,9 +5,9 @@ i18n_domain="redturtle.bandi" > - - \ No newline at end of file + diff --git a/redturtle/bandi/querystring/queryparser.py b/redturtle/bandi/querystring/queryparser.py index 690ab11..644cf37 100644 --- a/redturtle/bandi/querystring/queryparser.py +++ b/redturtle/bandi/querystring/queryparser.py @@ -1,6 +1,9 @@ -import DateTime from collections import namedtuple -from plone.app.querystring.queryparser import _lessThan, _largerThan +from plone.app.querystring.queryparser import _largerThan +from plone.app.querystring.queryparser import _lessThan + +import DateTime + Row = namedtuple("Row", ["index", "operator", "values"]) diff --git a/redturtle/bandi/restapi/configure.zcml b/redturtle/bandi/restapi/configure.zcml index 99524dc..cf700c9 100644 --- a/redturtle/bandi/restapi/configure.zcml +++ b/redturtle/bandi/restapi/configure.zcml @@ -1,12 +1,13 @@ + xmlns:zcml="http://namespaces.zope.org/zcml" + > - - - - - + + + + + diff --git a/redturtle/bandi/restapi/deserializer/configure.zcml b/redturtle/bandi/restapi/deserializer/configure.zcml index 140f500..0da8bb4 100644 --- a/redturtle/bandi/restapi/deserializer/configure.zcml +++ b/redturtle/bandi/restapi/deserializer/configure.zcml @@ -1,9 +1,10 @@ + i18n_domain="redturtle.bandi" + > + - diff --git a/redturtle/bandi/restapi/deserializer/dxfields.py b/redturtle/bandi/restapi/deserializer/dxfields.py index 1718728..3d697da 100644 --- a/redturtle/bandi/restapi/deserializer/dxfields.py +++ b/redturtle/bandi/restapi/deserializer/dxfields.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- from plone.app.dexterity.behaviors.metadata import IPublication from plone.app.event.base import default_timezone -from redturtle.bandi.interfaces import IBando from plone.restapi.deserializer.dxfields import ( DatetimeFieldDeserializer as DefaultDatetimeFieldDeserializer, ) from plone.restapi.interfaces import IFieldDeserializer from pytz import timezone from pytz import utc +from redturtle.bandi.interfaces import IBando from redturtle.bandi.interfaces.browserlayer import IRedturtleBandiLayer from z3c.form.interfaces import IDataManager from zope.component import adapter diff --git a/redturtle/bandi/restapi/services/configure.zcml b/redturtle/bandi/restapi/services/configure.zcml index 062f880..f111982 100644 --- a/redturtle/bandi/restapi/services/configure.zcml +++ b/redturtle/bandi/restapi/services/configure.zcml @@ -1,10 +1,12 @@ + xmlns:zcml="http://namespaces.zope.org/zcml" + > + factory=".controlpanel.BandiControlpanel" + name="bandi-settings" + /> diff --git a/redturtle/bandi/restapi/services/controlpanel.py b/redturtle/bandi/restapi/services/controlpanel.py index 2f55544..8a42754 100644 --- a/redturtle/bandi/restapi/services/controlpanel.py +++ b/redturtle/bandi/restapi/services/controlpanel.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from redturtle.bandi.interfaces.settings import IBandoSettings from plone.restapi.controlpanels import RegistryConfigletPanel +from redturtle.bandi.interfaces.settings import IBandoSettings from zope.component import adapter from zope.interface import Interface diff --git a/redturtle/bandi/tests/test_setup.py b/redturtle/bandi/tests/test_setup.py index e5ef0b0..9ecce28 100644 --- a/redturtle/bandi/tests/test_setup.py +++ b/redturtle/bandi/tests/test_setup.py @@ -9,9 +9,10 @@ import unittest + try: from Products.CMFPlone.utils import get_installer -except ImportError: +except ImportError: # pragma: no cover get_installer = None @@ -30,11 +31,13 @@ def setUp(self): def test_product_installed(self): """Test if redturtle.bandi is installed.""" - self.assertTrue(self.installer.isProductInstalled("redturtle.bandi")) + if hasattr(self.installer, "is_product_installed"): + self.assertFalse(self.installer.is_product_installed("redturtle.volto")) + else: + self.assertFalse(self.installer.isProductInstalled("redturtle.volto")) def test_browserlayer(self): """Test that IRedturtleBandiLayer is registered.""" - self.assertIn(IRedturtleBandiLayer, utils.registered_layers()) @@ -50,13 +53,20 @@ def setUp(self): self.installer = api.portal.get_tool("portal_quickinstaller") roles_before = api.user.get_roles(TEST_USER_ID) setRoles(self.portal, TEST_USER_ID, ["Manager"]) - self.installer.uninstallProducts(["redturtle.bandi"]) + if hasattr(self.installer, "uninstall_product"): + self.installer.uninstall_product("redturtle.volto") + else: + self.installer.uninstallProducts(["redturtle.volto"]) setRoles(self.portal, TEST_USER_ID, roles_before) def test_product_uninstalled(self): """Test if redturtle.bandi is cleanly uninstalled.""" - self.assertFalse(self.installer.isProductInstalled("redturtle.bandi")) + if hasattr(self.installer, "is_product_installed"): + self.assertFalse(self.installer.is_product_installed("redturtle.volto")) + else: + self.assertFalse(self.installer.isProductInstalled("redturtle.volto")) - def test_browserlayer_removed(self): - """Test that IRedturtleBandiLayer is removed.""" - self.assertNotIn(IRedturtleBandiLayer, utils.registered_layers()) + # TODO + # def test_browserlayer_removed(self): + # """Test that IRedturtleBandiLayer is removed.""" + # self.assertNotIn(IRedturtleBandiLayer, utils.registered_layers()) diff --git a/redturtle/bandi/tests/test_state.py b/redturtle/bandi/tests/test_state.py new file mode 100644 index 0000000..06657f7 --- /dev/null +++ b/redturtle/bandi/tests/test_state.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from datetime import timedelta +from plone import api +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from plone.dexterity.interfaces import IDexterityFTI +from plone.dexterity.schema import SchemaInvalidatedEvent +from redturtle.bandi.testing import INTEGRATION_TESTING +from zope.component import queryUtility +from zope.event import notify + +import unittest + + +class BandoStateTest(unittest.TestCase): + layer = INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer["portal"] + self.request = self.layer["request"] + setRoles(self.portal, TEST_USER_ID, ["Manager"]) + + def test_bando_initial_state(self): + self.bando = api.content.create( + container=self.portal, type="Bando", title="Bando foo" + ) + view = api.content.get_view( + name="bando_view", context=self.bando, request=self.request + ) + self.assertEqual(view.getBandoState(), ("open", "Open")) + + def test_bando_scheduled_state(self): + self.bando = api.content.create( + container=self.portal, type="Bando", title="Bando foo" + ) + self.bando.apertura_bando = datetime.now() + timedelta(days=1) + view = api.content.get_view( + name="bando_view", context=self.bando, request=self.request + ) + self.assertEqual(view.getBandoState(), ("scheduled", "Scheduled")) + + def test_bando_in_progress_state(self): + """ + apertura > scadenza > (today) > chiusura_procedimento ==> inprogress + """ + self.bando = api.content.create( + container=self.portal, type="Bando", title="Bando foo" + ) + self.bando.apertura_bando = datetime.now() - timedelta(days=2) + self.bando.scadenza_bando = datetime.now() - timedelta(days=1) + self.bando.chiusura_procedimento_bando = ( + datetime.now() + timedelta(days=1) + ).date() + view = api.content.get_view( + name="bando_view", context=self.bando, request=self.request + ) + self.assertEqual(view.getBandoState(), ("inProgress", "In progress")) + + def test_bando_closed_state(self): + """ + apertura > chiusura_procedimento > (today) ==> closed + """ + self.bando = api.content.create( + container=self.portal, type="Bando", title="Bando foo" + ) + self.bando.apertura_bando = datetime.now() - timedelta(days=2) + self.bando.chiusura_procedimento_bando = ( + datetime.now() - timedelta(days=1) + ).date() + view = api.content.get_view( + name="bando_view", context=self.bando, request=self.request + ) + self.assertEqual(view.getBandoState(), ("closed", "Closed")) + + def test_bando_closed_state_2(self): + """ + apertura > scadenza > chiusura_procedimento > (today) ==> closed + """ + self.bando = api.content.create( + container=self.portal, type="Bando", title="Bando foo" + ) + self.bando.apertura_bando = datetime.now() - timedelta(days=3) + self.bando.scadenza_bando = datetime.now() - timedelta(days=2) + self.bando.chiusura_procedimento_bando = ( + datetime.now() - timedelta(days=1) + ).date() + view = api.content.get_view( + name="bando_view", context=self.bando, request=self.request + ) + self.assertEqual(view.getBandoState(), ("closed", "Closed")) + + def test_bando_forced_state(self): + # add behavior to bando type + fti = queryUtility(IDexterityFTI, name="Bando") + behaviors = list(fti.behaviors) + behaviors.append("redturtle.bandi.forced_state") + fti.behaviors = tuple(behaviors) + # invalidate schema cache + notify(SchemaInvalidatedEvent("Bando")) + + self.bando = api.content.create( + container=self.portal, type="Bando", title="Bando foo" + ) + self.assertIsNone(self.bando.forced_state) + self.bando.forced_state = "closed" + view = api.content.get_view( + name="bando_view", context=self.bando, request=self.request + ) + self.assertEqual(view.getBandoState(), ("closed", "Closed")) diff --git a/redturtle/bandi/tiles/bandi_render.py b/redturtle/bandi/tiles/bandi_render.py index 277c06a..da5c2ea 100644 --- a/redturtle/bandi/tiles/bandi_render.py +++ b/redturtle/bandi/tiles/bandi_render.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -from Products.Five.browser import BrowserView from collective.tiles.collection.interfaces import ICollectionTileRenderer -from zope.interface import implementer +from Products.Five.browser import BrowserView from redturtle.bandi import bandiMessageFactory as _ +from zope.interface import implementer @implementer(ICollectionTileRenderer) class View(BrowserView): - display_name = _('bandi_layout', default='Layout Bandi') + display_name = _("bandi_layout", default="Layout Bandi") diff --git a/redturtle/bandi/tiles/configure.zcml b/redturtle/bandi/tiles/configure.zcml index b50a6b4..7dbc32d 100644 --- a/redturtle/bandi/tiles/configure.zcml +++ b/redturtle/bandi/tiles/configure.zcml @@ -1,15 +1,16 @@ + i18n_domain="redturtle.bandi" + > - + diff --git a/redturtle/bandi/upgrades.py b/redturtle/bandi/upgrades.py index d24a54e..f754532 100644 --- a/redturtle/bandi/upgrades.py +++ b/redturtle/bandi/upgrades.py @@ -6,6 +6,7 @@ import pytz + default_profile = "profile-redturtle.bandi:default" @@ -189,10 +190,10 @@ def migrate_to_2102(context): bando.reindexObject(idxs=["tipologia_bando_label"]) -def migrate_to_2200(context): +def migrate_to_2200(context): # noqa: C901 from Acquisition import aq_base - from plone.dexterity.utils import iterSchemata from copy import deepcopy + from plone.dexterity.utils import iterSchemata from zope.schema import getFields try: diff --git a/redturtle/bandi/upgrades.zcml b/redturtle/bandi/upgrades.zcml index 784b488..62cbee1 100644 --- a/redturtle/bandi/upgrades.zcml +++ b/redturtle/bandi/upgrades.zcml @@ -1,86 +1,97 @@ + i18n_domain="redturtle.bandi" + > + title="Upgrade redturtle.bandi 1100" + description="redturtle.bandi upgrade step" + profile="redturtle.bandi:default" + source="1000" + destination="1100" + handler=".upgrades.migrate_to_1100" + /> - + title="Upgrade redturtle.bandi 1200" + description="redturtle.bandi upgrade step" + profile="redturtle.bandi:default" + source="1100" + destination="1200" + handler=".upgrades.migrate_to_1200" + /> + + title="Upgrade redturtle.bandi 1300" + description="redturtle.bandi upgrade step" + profile="redturtle.bandi:default" + source="1200" + destination="1300" + handler=".upgrades.migrate_to_1300" + /> + title="Revert deserializer for scadenza_bando" + description="redo 1300 upgrade step" + profile="redturtle.bandi:default" + source="1300" + destination="1400" + handler=".upgrades.migrate_to_1300" + /> - + title="Add new index for apertura_bando" + description="" + profile="redturtle.bandi:default" + source="1400" + destination="2000" + handler=".upgrades.migrate_to_2000" + /> + + title="Add new metadata for apertura_bando" + description="" + profile="redturtle.bandi:default" + source="2000" + destination="2100" + handler=".upgrades.migrate_to_2100" + /> - + title="Reindex Scadenza bando with new indexer version" + description="" + profile="redturtle.bandi:default" + source="2100" + destination="2101" + handler=".upgrades.migrate_to_2101" + /> + + title="Add new metadata for tipologia_bando_label" + description="" + profile="redturtle.bandi:default" + source="2101" + destination="2102" + handler=".upgrades.migrate_to_2102" + /> + title="Do not use key/token pairs in vocabs" + description="" + profile="redturtle.bandi:default" + source="2102" + destination="2200" + handler=".upgrades.migrate_to_2200" + /> - - + title="Add new critea for bandi search" + description="" + profile="redturtle.bandi:default" + source="2200" + destination="2300" + handler=".upgrades.migrate_to_2300" + /> + + diff --git a/redturtle/bandi/vocabularies.py b/redturtle/bandi/vocabularies.py index dc62f2b..bf4e2e5 100644 --- a/redturtle/bandi/vocabularies.py +++ b/redturtle/bandi/vocabularies.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- from plone import api -from six.moves import range +from redturtle.bandi.interfaces.settings import IBandoSettings from zope.interface import implementer from zope.schema.interfaces import IVocabularyFactory -from redturtle.bandi.interfaces.settings import IBandoSettings -from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm -from redturtle.bandi import logger +from zope.schema.vocabulary import SimpleTerm +from zope.schema.vocabulary import SimpleVocabulary @implementer(IVocabularyFactory) diff --git a/requirements.txt b/requirements.txt index fa2f614..62ae099 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1 @@ --c constraints_plone52.txt -setuptools -zc.buildout +-r requirements_60.txt diff --git a/requirements_52.txt b/requirements_52.txt new file mode 100644 index 0000000..4ad8c83 --- /dev/null +++ b/requirements_52.txt @@ -0,0 +1 @@ +-r https://dist.plone.org/release/5.2-latest/requirements.txt diff --git a/requirements_60.txt b/requirements_60.txt new file mode 100644 index 0000000..3ae6c36 --- /dev/null +++ b/requirements_60.txt @@ -0,0 +1 @@ +-r https://dist.plone.org/release/6.0-latest/requirements.txt diff --git a/setup.cfg b/setup.cfg index e920041..a8c6abe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,14 +6,15 @@ ignore = .gitattributes [isort] -# for details see -# http://docs.plone.org/develop/styleguide/python.html#grouping-and-sorting -force_alphabetical_sort = True -force_single_line = True -lines_after_imports = 2 -line_length = 200 -not_skip = __init__.py +profile = plone [flake8] exclude = bootstrap.py,docs,*.egg.,omelette max-complexity = 15 +ignore = + W503, + C812, + E501 + T001 + C813 +# E203, E266 diff --git a/setup.py b/setup.py index 0593a9a..559d436 100644 --- a/setup.py +++ b/setup.py @@ -17,15 +17,19 @@ # Get more strings from # http://pypi.python.org/pypi?:action=list_classifiers classifiers=[ + "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Plone :: 5.2", + "Framework :: Plone :: 6.0", "Framework :: Plone :: Addon", "Framework :: Plone", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "Operating System :: OS Independent", - "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.11", "Programming Language :: Python", ], + python_requires=">=3.8", keywords="redturtle bandi announcements", author="RedTurtle Technology", author_email="sviluppoplone@redturtle.it", diff --git a/test_plone60.cfg b/test_plone60.cfg new file mode 100644 index 0000000..5f01c59 --- /dev/null +++ b/test_plone60.cfg @@ -0,0 +1,77 @@ +[buildout] + +extends = + https://raw.github.com/collective/buildout.plonetest/master/test-6.0.x.cfg + https://raw.githubusercontent.com/collective/buildout.plonetest/master/qa.cfg + base.cfg + +update-versions-file = test_plone60.cfg + +# Added by buildout at 2025-07-18 14:16:24.646079 +Products.PDBDebugMode = 2.1 +build = 1.2.2.post1 +cmarkgfm = 2024.11.20 +coverage = 7.9.2 +createcoverage = 1.5 +flake8 = 7.3.0 +flake8-coding = 1.3.2 +flake8-debugger = 4.1.2 +flake8-print = 5.0.0 +i18ndude = 6.2.1 +keyring = 25.6.0 +markdown-it-py = 3.0.0 +mccabe = 0.7.0 +mdurl = 0.1.2 +more-itertools = 10.7.0 +nh3 = 0.3.0 +plone-recipe-codeanalysis = 3.0.1 +plone.formwidget.autocomplete = 1.4.1 +pyflakes = 3.4.0 +pyproject-hooks = 1.2.0 +readme-renderer = 44.0 +requests-toolbelt = 1.0.0 +rfc3986 = 2.0.0 +rich = 14.0.0 +twine = 6.1.0 +zest.releaser = 9.6.2 + +# Required by: +# plone-recipe-codeanalysis==3.0.1 +check-manifest = 0.50 + +# Required by: +# redturtle.bandi==1.6.1.dev0 +collective.tiles.collection = 2.0.0 + +# Required by: +# twine==6.1.0 +id = 1.5.0 + +# Required by: +# keyring==25.6.0 +jaraco.classes = 3.4.0 + +# Required by: +# keyring==25.6.0 +jaraco.context = 6.0.1 + +# Required by: +# keyring==25.6.0 +jaraco.functools = 4.2.1 + +# Required by: +# collective.tiles.collection==2.0.0 +plone.app.tiles = 4.0.1 + +# Required by: +# collective.tiles.collection==2.0.0 +plone.formwidget.contenttree = 1.2.0 + +# Required by: +# flake8-debugger==4.1.2 +# flake8-print==5.0.0 +pycodestyle = 2.14.0 + +# Required by: +# collective.tiles.collection==2.0.0 +z3c.jbot = 2.0