Conversation
Migrated code to send XML export over SFTP Refs. syslabcom/scrum#3960
|
Can you at least start by fixing the |
There was a problem hiding this comment.
Pull request overview
Adds a bulk “cron-style” export subsystem to generate XML/CSV export artifacts, provide browser endpoints to run/export them, and support DOI (da|ra) registration flows (per #3960).
Changes:
- Introduces exporter implementations (Chronicon/BVID/MissingBVID/LZA) plus DOI registration helpers and status objects.
- Adds browser views + ZCML registrations for XML representations and export endpoints (metadata-export, chronicon-export, DOI update, reset flags).
- Adds extensive integration tests and export documentation, plus a new SFTP dependency.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
src/recensio/plone/export.py |
New exporter implementations, ZIP/CSV generation, DOI registration helper functions |
src/recensio/plone/browser/export.py |
New browser views to run bulk exports, upload Chronicon ZIP via SFTP, trigger DOI update, reset LZA flags |
src/recensio/plone/browser/xml.py |
Adds XML representation views for publication/volume/issue/reviews |
src/recensio/plone/browser/configure.zcml |
Registers new XML views and export-related browser endpoints |
src/recensio/plone/configure.zcml |
Registers exporter factory utility (currently Chronicon only) |
src/recensio/plone/interfaces.py |
Adds IRecensioExporter interface |
src/recensio/plone/config.py |
Adds REVIEW_TYPES_TO_EXPORT selection for bulk export traversal |
src/recensio/plone/browser/templates/export_*.pt |
Adds/updates XML templates for export formats (including da |
src/recensio/plone/tests/test_exporters.py |
Adds integration/unit tests for exporters and DOI registration logic |
setup.py |
Adds paramiko-ng dependency for SFTP uploads |
docs/recensio-exports.md |
Adds an overview document for the export subsystem |
CHANGES.rst |
Changelog entry for export revival work |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
It seems to me that many questions you had are rhetorical and you find the solution yourself. The CI checks the pre-commit hook: Let me know if you want some more info |
The idea was to allow you to see where I am coming from and what I tried.
Yes. Your phrasing "Can you at least start by fixing the pre-commit?" suggests with using "at least" that I have disregarded basic quality principles, while I thought I performed quite well and also the test was green. What should I have done to detect this pre-commit issue on files I didn't touch? |
Maybe check the files you did not touch before starting working on that? |
| name="metadata-export" | ||
| for="Products.CMFPlone.interfaces.IPloneSiteRoot" | ||
| class=".export.MetadataExport" | ||
| permission="zope2.View" |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 20 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <input class="form-check-input" | ||
| id="query-date-yesterday" | ||
| checked="${python:'checked' if checked==yesterday else ''}" | ||
| name="created.query:record:date" | ||
| type="radio" | ||
| value="${yesterday}" | ||
| /> |
There was a problem hiding this comment.
Same boolean-attribute issue for the date radios: checked="" is still treated as checked. Use a TAL attribute that omits checked when the condition is false (set to None/nothing).
| permission="cmf.ModifyPortalContent" | ||
| /> | ||
| <browser:page | ||
| name="chronicon-export" | ||
| for="Products.CMFPlone.interfaces.IPloneSiteRoot" | ||
| class=".export.ChroniconExport" | ||
| permission="cmf.ModifyPortalContent" | ||
| /> | ||
| <browser:page | ||
| name="dara_update" | ||
| for="recensio.plone.interfaces.IReview" | ||
| class=".export.DaraUpdate" | ||
| permission="cmf.ModifyPortalContent" | ||
| /> | ||
| <browser:page | ||
| name="reset-lza-export-flag" | ||
| for="*" | ||
| class=".export.ResetLZAExportFlag" | ||
| permission="cmf.ModifyPortalContent" |
There was a problem hiding this comment.
@@metadata-export disables CSRF protection and performs site-wide writes, but it’s protected only by cmf.ModifyPortalContent, which is often granted to non-admin editorial roles. Consider restricting this (and the other export helpers) to a stronger admin permission (e.g. cmf.ManagePortal / a dedicated permission) and/or requiring POST for side-effecting operations.
| permission="cmf.ModifyPortalContent" | |
| /> | |
| <browser:page | |
| name="chronicon-export" | |
| for="Products.CMFPlone.interfaces.IPloneSiteRoot" | |
| class=".export.ChroniconExport" | |
| permission="cmf.ModifyPortalContent" | |
| /> | |
| <browser:page | |
| name="dara_update" | |
| for="recensio.plone.interfaces.IReview" | |
| class=".export.DaraUpdate" | |
| permission="cmf.ModifyPortalContent" | |
| /> | |
| <browser:page | |
| name="reset-lza-export-flag" | |
| for="*" | |
| class=".export.ResetLZAExportFlag" | |
| permission="cmf.ModifyPortalContent" | |
| permission="cmf.ManagePortal" | |
| /> | |
| <browser:page | |
| name="chronicon-export" | |
| for="Products.CMFPlone.interfaces.IPloneSiteRoot" | |
| class=".export.ChroniconExport" | |
| permission="cmf.ManagePortal" | |
| /> | |
| <browser:page | |
| name="dara_update" | |
| for="recensio.plone.interfaces.IReview" | |
| class=".export.DaraUpdate" | |
| permission="cmf.ManagePortal" | |
| /> | |
| <browser:page | |
| name="reset-lza-export-flag" | |
| for="*" | |
| class=".export.ResetLZAExportFlag" | |
| permission="cmf.ManagePortal" |
| template="templates/export_rj.pt" | ||
| permission="zope.Public" | ||
| /> | ||
|
|
There was a problem hiding this comment.
REVIEW_TYPES_TO_EXPORT includes “Review Article Collection”, and ChroniconExporter.add_review() calls review.restrictedTraverse('@@xml')(). However, there is no @@xml browser:page registered here for IReviewArticleCollection (nor IReviewExhibition). Those reviews will raise NotFound during export and be skipped. Register matching @@xml views/templates for the missing review types, or remove them from the export set.
| <browser:page | |
| name="xml" | |
| for="recensio.plone.content.review_article_collection.IReviewArticleCollection" | |
| class=".xml.XMLRepresentation" | |
| template="templates/export_rj.pt" | |
| permission="zope.Public" | |
| /> | |
| <browser:page | |
| name="xml" | |
| for="recensio.plone.content.review_exhibition.IReviewExhibition" | |
| class=".xml.XMLRepresentation" | |
| template="templates/export_rj.pt" | |
| permission="zope.Public" | |
| /> |
| BVIDExporterFactory = Factory(BVIDExporter, IFactory, "exporter") | ||
| MissingBVIDExporterFactory = Factory(MissingBVIDExporter, IFactory, "exporter") | ||
| ChroniconExporterFactory = Factory(ChroniconExporter, IFactory, "exporter") | ||
| LZAExporterFactory = Factory(LZAExporter, IFactory, "exporter") |
There was a problem hiding this comment.
The Factory(...) registrations pass IFactory and a string positional argument, which means factory.getInterfaces() will not include IRecensioExporter. As a result, getFactoriesFor(IRecensioExporter) in MetadataExport will return no exporters and the bulk export runner will always report “Nothing to do”. Define these factories without overriding the interfaces (e.g. Factory(BVIDExporter) / Factory(ChroniconExporter)), or explicitly declare IRecensioExporter as the provided interface, so getFactoriesFor(IRecensioExporter) can discover them.
| <input class="form-check-input" | ||
| id="pt_toggle" | ||
| checked="${python:'checked' if all_checked else ''}" | ||
| name="pt_toggle" | ||
| type="checkbox" | ||
| value="#" | ||
| id="pt_toggle" | ||
| class="form-check-input" | ||
| checked="${python:'checked' if all_checked else ''}" /> | ||
| <label for="pt_toggle" class="form-check-label" | ||
| i18n:translate="label_toggle">Select All/None</label> | ||
| /> |
There was a problem hiding this comment.
checked is interpolated with an empty string when false (checked=""), but for HTML boolean attributes the presence of checked still means true. This checkbox will render as checked even when all_checked is false. Use a TAL attribute that omits checked when false (e.g. set it to None/nothing).
| component=".export.ChroniconExporterFactory" | ||
| /> | ||
|
|
||
| <utility |
There was a problem hiding this comment.
Only chronicon_exporter is registered as an IFactory utility here. MetadataExport uses getFactoriesFor(IRecensioExporter) to discover exporters, so the exporter factories (Chronicon/BVID/MissingBVID/LZA) need to be registered as IFactory utilities and advertise IRecensioExporter in getInterfaces(). Otherwise the bulk export runner won’t find anything to execute.
| <utility | |
| <utility | |
| provides="zope.component.interfaces.IFactory" | |
| name="bvid_exporter" | |
| component=".export.BVIDExporterFactory" | |
| /> | |
| <utility | |
| provides="zope.component.interfaces.IFactory" | |
| name="missing_bvid_exporter" | |
| component=".export.MissingBVIDExporterFactory" | |
| /> | |
| <utility | |
| provides="zope.component.interfaces.IFactory" | |
| name="lza_exporter" | |
| component=".export.LZAExporterFactory" | |
| /> | |
| <utility |
| <input class="form-check-input" | ||
| id="portal_type_${repeat/type/number}" | ||
| checked="${python:'checked' if ((type in request.get('portal_type', [])) or first_call) else ''}" | ||
| name="portal_type:list" | ||
| type="checkbox" | ||
| value="${type}" | ||
| /> |
There was a problem hiding this comment.
Same boolean-attribute issue here: when the expression is false this becomes checked="", which still counts as checked. Use a TAL attribute that omits checked entirely when the condition is false (use None/nothing).
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ess.pt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@copilot merge master and switch the changelog entry to use towncrier |
Co-authored-by: ale-rt <1300763+ale-rt@users.noreply.github.com>

See https://github.com/syslabcom/scrum/issues/3960