diff --git a/client/aap/mission_report/controllers/MissionReportController.js b/client/aap/mission_report/controllers/MissionReportController.js index 03cd92059..ea5e90469 100644 --- a/client/aap/mission_report/controllers/MissionReportController.js +++ b/client/aap/mission_report/controllers/MissionReportController.js @@ -5,7 +5,6 @@ MissionReportController.$inject = [ 'savedReports', 'notify', 'lodash', - 'searchReport', '$q', 'missionReportChart', 'reportConfigs', @@ -19,7 +18,6 @@ MissionReportController.$inject = [ * @requires savedReports * @requires notify * @requires lodash - * @requires searchReport * @requires $q * @requires missionReportChart * @requires reportConfigs @@ -30,7 +28,6 @@ export function MissionReportController( savedReports, notify, _, - searchReport, $q, missionReportChart, reportConfigs diff --git a/client/aap/sms_report/controllers/SMSReportController.js b/client/aap/sms_report/controllers/SMSReportController.js index c25ccf528..c6229df5f 100644 --- a/client/aap/sms_report/controllers/SMSReportController.js +++ b/client/aap/sms_report/controllers/SMSReportController.js @@ -2,19 +2,18 @@ import {getErrorMessage, getUtcOffsetInMinutes} from 'superdesk-analytics/client import {DATE_FILTERS} from 'superdesk-analytics/client/search/common'; import {CHART_FIELDS, CHART_TYPES} from 'superdesk-analytics/client/charts/directives/ChartOptions'; import {SDChart} from 'superdesk-analytics/client/charts/SDChart'; +import {searchReportService} from 'superdesk-analytics/client/search/services/SearchReport'; +import {appConfig} from 'superdesk-core/scripts/appConfig'; SMSReportController.$inject = [ '$scope', 'savedReports', 'chartConfig', 'lodash', - 'searchReport', 'moment', 'notify', 'gettext', '$q', - 'config', - 'deployConfig', 'reportConfigs', ]; @@ -24,13 +23,10 @@ export function SMSReportController( savedReports, chartConfig, _, - searchReport, moment, notify, gettext, $q, - config, - deployConfig, reportConfigs ) { const reportName = 'sms_report'; @@ -57,7 +53,7 @@ export function SMSReportController( }; this.initDefaultParams = () => { - $scope.item_states = searchReport.filterItemStates( + $scope.item_states = searchReportService.filterItemStates( ['published', 'killed', 'corrected', 'recalled'] ); @@ -79,8 +75,8 @@ export function SMSReportController( filter: DATE_FILTERS.RANGE, start: moment() .subtract(30, 'days') - .format(config.model.dateformat), - end: moment().format(config.model.dateformat), + .format(appConfig.model.dateformat), + end: moment().format(appConfig.model.dateformat), }, must: {}, must_not: {}, @@ -206,8 +202,7 @@ export function SMSReportController( // Any data after the daylight savings change will be 1 hour out const utcOffset = getUtcOffsetInMinutes( report['start'], - config.defaultTimezone, - moment + appConfig.defaultTimezone ); const chart = new SDChart.Chart({ @@ -215,7 +210,7 @@ export function SMSReportController( chartType: 'highcharts', title: $scope.generateTitle(), subtitle: $scope.generateSubtitle(), - startOfWeek: deployConfig.getSync('start_of_week', 0), + startOfWeek: appConfig.start_of_week || appConfig.startingDay || 0, timezoneOffset: utcOffset, useUTC: false, fullHeight: true, diff --git a/client/index.js b/client/index.js new file mode 100644 index 000000000..c83ae241d --- /dev/null +++ b/client/index.js @@ -0,0 +1,12 @@ +import {startApp} from 'superdesk-core/scripts/index'; +import planningExtension from 'superdesk-planning/client/planning-extension/dist/extension'; +import './aap/index.js'; + +setTimeout(() => { + startApp( + [planningExtension], + {}, + ); +}); + +export default angular.module('main.superdesk', []); \ No newline at end of file diff --git a/client/package.json b/client/package.json index 1cd86b5d0..01783ca63 100644 --- a/client/package.json +++ b/client/package.json @@ -2,8 +2,8 @@ "name": "superdesk", "license": "GPL-3.0", "dependencies": { - "superdesk-core": "superdesk/superdesk-client-core#1.31", - "superdesk-planning": "superdesk/superdesk-planning#master", - "superdesk-analytics": "superdesk/superdesk-analytics#master" + "superdesk-core": "1.33.5", + "superdesk-planning": "1.33.1", + "superdesk-analytics": "1.33.4" } } diff --git a/client/superdesk.config.js b/client/superdesk.config.js index 65b029ec2..f0e487dcc 100644 --- a/client/superdesk.config.js +++ b/client/superdesk.config.js @@ -1,7 +1,7 @@ -module.exports = function() { +module.exports = function(grunt) { return { apps: ['superdesk-planning', 'superdesk.analytics', 'aap.apps'], - importApps: ['superdesk-planning', 'superdesk-analytics', '../aap'], + importApps: ['superdesk-planning', 'superdesk-analytics', '../index.js'], defaultRoute: '/workspace', workspace: { ingest: 1, @@ -19,15 +19,6 @@ module.exports = function() { change_profile: 1 }, - editor: { - toolbar: false, - embeds: false, - paste: { - forcePlainText: true, - cleanPastedHTML: false - } - }, - features: { elasticHighlight: 1, swimlane: {defaultNumberOfColumns: 4}, @@ -45,6 +36,11 @@ module.exports = function() { editorInlineComments: false, editorSuggestions: false, validatePointOfInterestForImages: true, + customAuthoringTopbar: { + toDesk: false, + publish: false, + publishAndContinue: false, + } }, activity: { diff --git a/server/Procfile b/server/Procfile index 495471da0..8a1592a85 100644 --- a/server/Procfile +++ b/server/Procfile @@ -4,6 +4,7 @@ work: celery -A worker -Q default worker expiry: celery -A worker -Q expiry worker legal: celery -A worker -Q legal worker publish: celery -A worker -Q publish worker +ingest: celery -A worker -Q ingest worker beat: celery -A worker beat --pid= capi: gunicorn -b 0.0.0.0:5400 -c gunicorn_config.py content_api.wsgi highcharts: python3 -u -m analytics.reports.highcharts_server diff --git a/server/aap/commands/fulfill_image_assignments.py b/server/aap/commands/fulfill_image_assignments.py index 2c6906d5e..569c55fd4 100644 --- a/server/aap/commands/fulfill_image_assignments.py +++ b/server/aap/commands/fulfill_image_assignments.py @@ -13,6 +13,7 @@ from superdesk.utc import utcnow, utc_to_local from datetime import timedelta from planning.common import ASSIGNMENT_WORKFLOW_STATE +from planning.assignments.assignments_history import ASSIGNMENT_HISTORY_ACTIONS from eve.utils import ParsedRequest from flask import json from flask import current_app as app @@ -20,6 +21,9 @@ from lxml import etree from superdesk.celery_task_utils import get_lock_id from superdesk.lock import lock, unlock +from copy import deepcopy +from eve.utils import config +from bson import ObjectId logger = logging.getLogger(__name__) @@ -110,8 +114,8 @@ def _get_dc_images_by_field(self, archive, value): logger.error('DC login failed') retries += 1 if retries == 3: + logger.error('Failed to get image by field from DC {}'.format(id)) return None - logger.error('Failed to get image by field from DC {}'.format(id)) return etree.fromstring(response.content) def _get_outstanding_photo_assignments(self): @@ -120,10 +124,43 @@ def _get_outstanding_photo_assignments(self): :return: The Id of any sheduled picure assignments """ service = superdesk.get_resource_service('assignments') - assignments = service.find(where={"assigned_to.state": ASSIGNMENT_WORKFLOW_STATE.ASSIGNED, - "planning.g2_content_type": "picture", - "planning.scheduled": {"$gte": utcnow() - timedelta(days=2)}, - "lock_user": None}) + query = { + 'query': { + 'bool': { + 'must': [ + { + 'terms': { + 'assigned_to.state': [ + ASSIGNMENT_WORKFLOW_STATE.ASSIGNED, + ASSIGNMENT_WORKFLOW_STATE.IN_PROGRESS + ] + } + }, + { + 'term': { + 'planning.g2_content_type': 'picture' + } + }, + { + 'range': { + 'planning.scheduled': { + 'gte': 'now-2d' + } + } + } + ], + 'must_not': { + 'exists': { + 'field': 'lock_user' + } + } + } + } + } + req = ParsedRequest() + req.args = {'source': json.dumps(query)} + + assignments = service.get(req=req, lookup=None) if assignments.count() > 0: logger.warning('Found {} outstanding assignments in planning'.format(assignments.count())) else: @@ -183,28 +220,80 @@ def _get_image_modifier(self, assignment): # return the assignor user or the assignor desk, if nothing else available return {'proxy_user': assigned_to.get('assignor_user', assigned_to.get('assignor_desk'))} - def _check_in_progress(self, assignments): + def _extract_user(self, image): + """ + Given an image dossier extract the user that modified it + :param image: + :return: + """ + dossier = image.find('./dc_rest_docs/dc_rest_doc/dcdossier') + if dossier is not None: + modified_by = dossier.attrib.get('modified_by') + if modified_by is not None and not modified_by == 'system': + user_service = superdesk.get_resource_service('users') + user = user_service.find_one(req=None, username=modified_by) + if user: + return user + return None + + def _check_in_progress(self, assignments, complete): """ Check the AAP Image Pool for any assignments that may be in progress :param assignments: :return: """ in_progress_assignments = list() + in_list = set() for assignment in assignments: - # look in the AAP Image pool - dc_images = self._get_dc_images_by_field('aapimage', assignment.get('_id')) - if dc_images: - count = dc_images.find('./doc_count') - if count and int(count.text) > 0: - in_progress_assignments.append(assignment) + # If we have marked the assignment complete ignore it + if assignment.get('_id') in complete: + continue + if assignment.get('assigned_to', {}).get('state') == ASSIGNMENT_WORKFLOW_STATE.ASSIGNED: + # look in the AAP Image pool + dc_images = self._get_dc_images_by_field('aapimage', assignment.get('_id')) + if dc_images is not None: + count = dc_images.find('./doc_count') + if count is not None and int(count.text) > 0: + logger.info('Found in progress items in aapimage for {}'.format(assignment.get('_id'))) + user = self._extract_user(dc_images) + in_list.add(assignment.get('_id')) + in_progress_assignments.append({'assignment': assignment, 'user': user}) + continue + # look in the picedit pool + dc_images = self._get_dc_images_by_field('picedit', assignment.get('_id')) + if dc_images is not None: + count = dc_images.find('./doc_count') + if count is not None and int(count.text) > 0: + logger.info('Found in progress items in picedit for {}'.format(assignment.get('_id'))) + user = self._extract_user(dc_images) + if assignment.get('_id') not in in_list: + in_list.add(assignment.get('_id')) + in_progress_assignments.append({'assignment': assignment, 'user': user}) + return in_progress_assignments def _mark_as_in_progress(self, assignments): """ - TODO + Set the assignment state for that passed assignments to in progress :param assignments: :return: """ - pass + service = superdesk.get_resource_service('assignments') + for assignment in assignments: + logger.warning('Marking assignment {} in progress'.format(assignment.get('assignment').get('_id'))) + assignment.get('assignment')[config.ID_FIELD] = ObjectId(assignment.get('assignment')[config.ID_FIELD]) + assigned_to = assignment.get('assignment').get('assigned_to') + updates = {'assigned_to': deepcopy(assigned_to)} + updates['assigned_to']['state'] = ASSIGNMENT_WORKFLOW_STATE.IN_PROGRESS + try: + service.patch(assignment.get('assignment')[config.ID_FIELD], updates) + if assignment.get('user') is None: + updates['proxy_user'] = assigned_to.get('assignor_user', assigned_to.get('assignor_desk')) + else: + updates['proxy_user'] = assignment.get('user').get(config.ID_FIELD) + superdesk.get_resource_service('assignments_history').on_item_updated( + updates, assignment.get('assignment'), ASSIGNMENT_HISTORY_ACTIONS.START_WORKING) + except Exception as ex: + logger.exception(ex) def _mark_as_complete(self, assignments): """ @@ -216,8 +305,8 @@ def _mark_as_complete(self, assignments): for assignment in assignments: user = self._get_image_modifier(assignment) try: - logger.info('Marking assignment {} as complete'.format(assignment.get('assignment').get('_id'))) - service.patch(assignment.get('assignment').get('_id'), user) + logger.warning('Marking assignment {} as complete'.format(assignment.get('assignment').get('_id'))) + service.patch(ObjectId(assignment.get('assignment').get('_id')), user) except Exception as ex: logger.exception(ex) @@ -230,15 +319,19 @@ def run(self, ): return # Get a list of the outstanding photo assignments - assignments = self._get_outstanding_photo_assignments() + assignments = list(self._get_outstanding_photo_assignments()) # query for any images available from the image site API with those assigment id's completed_assignments = self._check_complete(assignments) self._mark_as_complete(completed_assignments) - # assignments.rewind() - # in_progress_assignments = self._check_in_progress(assignments) + complete = [c.get('assignment').get('_id') for c in completed_assignments] + + # check if any of the outstanding assignments are in either the picedit or aapimage pools + in_progress_assignments = self._check_in_progress(assignments, complete) + + self._mark_as_in_progress(in_progress_assignments) unlock(lock_name) logger.info('Finished fulfilling assignments') diff --git a/server/aap/io/data/aap_subject_codes.json b/server/aap/io/data/aap_subject_codes.json index 98c5581ef..11ecc48d2 100644 --- a/server/aap/io/data/aap_subject_codes.json +++ b/server/aap/io/data/aap_subject_codes.json @@ -74,6 +74,7 @@ "15072008":"69 kg - wrestling", "15072009":"63 kg - wrestling", "15077008":"freestyle - flying disc", + "15008001": "NBA", "01028000":"history - arts, culture and entertainment", "04002001":"biotechnology - chemicals", "04007003":"food - consumer goods", @@ -81,4 +82,4 @@ "04013002":"food - process industry", "04016034":"privatisation - company information", "08003000":"people - human interest" -} \ No newline at end of file +} diff --git a/server/aap/io/feed_parsers/aap_sportsfixtures.py b/server/aap/io/feed_parsers/aap_sportsfixtures.py index f9a820ff4..e43cba9ba 100644 --- a/server/aap/io/feed_parsers/aap_sportsfixtures.py +++ b/server/aap/io/feed_parsers/aap_sportsfixtures.py @@ -396,7 +396,7 @@ def _set_location(self, item, location_string): location['original_source'] = 'AAP Sports Results' location['position'] = {'longitude': stadium.point.longitude, 'latitude': stadium.point.latitude, 'altitude': stadium.point.altitude} - localities = [l for l in self.localityHierarchy if stadium.raw.get('address', {}).get(l)] + localities = [local for local in self.localityHierarchy if stadium.raw.get('address', {}).get(local)] areas = [a for a in self.areaHierarchy if stadium.raw.get('address', {}).get(a)] line = stadium.raw.get('address', {}).get('house_number', '') line = stadium.raw.get('address', {}).get('road', '') if line == '' else \ diff --git a/server/aap/macros/aap_currency_base.py b/server/aap/macros/aap_currency_base.py index b12850ed5..478775f53 100644 --- a/server/aap/macros/aap_currency_base.py +++ b/server/aap/macros/aap_currency_base.py @@ -145,7 +145,7 @@ def format_output(original, converted, suffix, src_currency): if suffix: return '{} ({} {})'.format(original, converted, suffix) else: - return '{} ({})'.format(original, converted, suffix) + return '{} ({})'.format(original, converted) def do_conversion(item, rate, currency, search_param, match_index, value_index, suffix_index, src_currency=None): diff --git a/server/aap/macros/accept_assignment.py b/server/aap/macros/accept_assignment.py new file mode 100644 index 000000000..6058337fc --- /dev/null +++ b/server/aap/macros/accept_assignment.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8; -*- +# +# This file is part of Superdesk. +# +# Copyright 2013, 2014 Sourcefabric z.u. and contributors. +# +# For the full copyright and license information, please see the +# AUTHORS and LICENSE files distributed with this source code, or +# at https://www.sourcefabric.org/superdesk/license +import logging +import re +import superdesk + + +logger = logging.getLogger(__name__) + + +def accept_assignment(item, **kwargs): + """ + Look for evidence that the item is an assignment acceptance and try to extract an assignment id and assignee id + Mark the assignment as accepted. + + :param item: + :param kwargs: + :return: + """ + regex = r"Assignment ([a-f0-9]{24}) has been accepted by (.*) ([a-f0-9]{24})." + + found = re.search(regex, item.get('body_html')) + if found and len(found.groups()) == 3: + assignment = found.group(1) + assignee = found.group(3) + + assignment_service = superdesk.get_resource_service('assignments') + assignment_service.accept_assignment(assignment, assignee) + + return item + + +name = 'accept assignment' +label = 'Accept Assignment' +callback = accept_assignment +access_type = 'backend' +action_type = 'direct' diff --git a/server/aap/macros/accept_assignment_test.py b/server/aap/macros/accept_assignment_test.py new file mode 100644 index 000000000..a2de6ba49 --- /dev/null +++ b/server/aap/macros/accept_assignment_test.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8; -*- +# +# This file is part of Superdesk. +# +# Copyright 2013, 2014 Sourcefabric z.u. and contributors. +# +# For the full copyright and license information, please see the +# AUTHORS and LICENSE files distributed with this source code, or +# at https://www.sourcefabric.org/superdesk/license + +from unittests import AAPTestCase +from .accept_assignment import accept_assignment +from bson import ObjectId +from superdesk.utc import utcnow + + +class AcceptAssignmentTestCase(AAPTestCase): + def setUp(self): + self.app.data.insert('assignments', [ + { + "_id": ObjectId("5e4f140db2825eac7796fbb4"), + "original_creator": "57bcfc5d1d41c82e8401dcc0", + "priority": 2, + "planning": { + "scheduled": utcnow(), + "slugline": "Thursday test 2", + "genre": None, + "g2_content_type": "picture" + }, + "type": "assignment", + "description_text": None, + "planning_item": "urn:newsml:localhost:2020-01-09T13:38:55.038020:74bd445f-fb28-4135-b4a1-2143b5308a1c", + "coverage_item": "urn:newsml:localhost:2020-01-09T13:38:56.294369:a275ef88-b67c-44f8-a850-775b6e8c6bc5", + "assigned_to": { + "assignor_desk": "57bcfc5d1d41c82e8401dcc0", + "assigned_date_desk": "2020-01-09T02:38:56.000Z", + "contact": None, + "user": "5b0d58ea1d41c8a4552121f8", + "desk": "54e68fcd1024542de76d6643", + "state": "assigned", + "coverage_provider": { + "qcode": "stringer", + "name": "Stringer", + "contact_type": "stringer" + } + } + } + ]) + self.app.data.insert('users', [{ + '_id': ObjectId('5b0d58ea1d41c8a4552121f8'), + 'name': 'user1', + 'username': 'user1', + 'user_type': 'administrator', + 'email': 'user1@a.com.au', + 'byline': 'User 1' + }]) + + def test_post(self): + item = {'body_html': '
This email is being sent to an unmonitored (system) addres' + 's. If you wish to discuss the details of the assignment further, you should' + ' contact a member of the team through other channels.
Assignment 5e4f140' + 'db2825eac7796fbb4 has been accepted by vnarayanaswamy@aap.com.au 5b0d58ea1d41c8a4552121f8.
'} + accept_assignment(item) + assignment = self.app.data.find_one('assignments', None, _id=ObjectId("5e4f140db2825eac7796fbb4")) + self.assertTrue(assignment.get('accepted')) diff --git a/server/aap/macros/currency_aud_to_nzd_test.py b/server/aap/macros/currency_aud_to_nzd_test.py index a67bf7c5b..25493d8c8 100644 --- a/server/aap/macros/currency_aud_to_nzd_test.py +++ b/server/aap/macros/currency_aud_to_nzd_test.py @@ -8,7 +8,7 @@ # AUTHORS and LICENSE files distributed with this source code, or # at https://www.sourcefabric.org/superdesk/license -from .currency_test_base import CurrencyTestClass +from .currency_test_base_test import CurrencyTestClass from .currency_aud_to_nzd import aud_to_nzd diff --git a/server/aap/macros/currency_chf_to_aud_test.py b/server/aap/macros/currency_chf_to_aud_test.py index 30a4a9cd2..cd41dae97 100644 --- a/server/aap/macros/currency_chf_to_aud_test.py +++ b/server/aap/macros/currency_chf_to_aud_test.py @@ -9,7 +9,7 @@ # at https://www.sourcefabric.org/superdesk/license from .currency_chf_to_aud import chf_to_aud -from .currency_test_base import CurrencyTestClass +from .currency_test_base_test import CurrencyTestClass class CurrencyTestCase(CurrencyTestClass): diff --git a/server/aap/macros/currency_cny_to_aud_test.py b/server/aap/macros/currency_cny_to_aud_test.py index 6910c6e3c..f15b7b650 100644 --- a/server/aap/macros/currency_cny_to_aud_test.py +++ b/server/aap/macros/currency_cny_to_aud_test.py @@ -8,7 +8,7 @@ # AUTHORS and LICENSE files distributed with this source code, or # at https://www.sourcefabric.org/superdesk/license -from .currency_test_base import CurrencyTestClass +from .currency_test_base_test import CurrencyTestClass from .currency_cny_to_aud import yuan_to_aud diff --git a/server/aap/macros/currency_euro_to_aud_test.py b/server/aap/macros/currency_euro_to_aud_test.py index 67045a393..18a8b0035 100644 --- a/server/aap/macros/currency_euro_to_aud_test.py +++ b/server/aap/macros/currency_euro_to_aud_test.py @@ -8,7 +8,7 @@ # AUTHORS and LICENSE files distributed with this source code, or # at https://www.sourcefabric.org/superdesk/license -from .currency_test_base import CurrencyTestClass +from .currency_test_base_test import CurrencyTestClass from .currency_euro_to_aud import euro_to_aud diff --git a/server/aap/macros/currency_gbp_to_aud_test.py b/server/aap/macros/currency_gbp_to_aud_test.py index 9bc09c7fc..6d0ceff24 100644 --- a/server/aap/macros/currency_gbp_to_aud_test.py +++ b/server/aap/macros/currency_gbp_to_aud_test.py @@ -8,7 +8,7 @@ # AUTHORS and LICENSE files distributed with this source code, or # at https://www.sourcefabric.org/superdesk/license -from .currency_test_base import CurrencyTestClass +from .currency_test_base_test import CurrencyTestClass from .currency_gbp_to_aud import gbp_to_aud diff --git a/server/aap/macros/currency_gbp_to_nzd_test.py b/server/aap/macros/currency_gbp_to_nzd_test.py index d2c36e984..226ed5ba6 100644 --- a/server/aap/macros/currency_gbp_to_nzd_test.py +++ b/server/aap/macros/currency_gbp_to_nzd_test.py @@ -8,7 +8,7 @@ # AUTHORS and LICENSE files distributed with this source code, or # at https://www.sourcefabric.org/superdesk/license -from .currency_test_base import CurrencyTestClass +from .currency_test_base_test import CurrencyTestClass from .currency_gbp_to_nzd import gbp_to_nzd diff --git a/server/aap/macros/currency_jpy_to_aud_test.py b/server/aap/macros/currency_jpy_to_aud_test.py index 3e786ae6d..b763ee3e1 100644 --- a/server/aap/macros/currency_jpy_to_aud_test.py +++ b/server/aap/macros/currency_jpy_to_aud_test.py @@ -8,7 +8,7 @@ # AUTHORS and LICENSE files distributed with this source code, or # at https://www.sourcefabric.org/superdesk/license -from .currency_test_base import CurrencyTestClass +from .currency_test_base_test import CurrencyTestClass from .currency_jpy_to_aud import yen_to_aud diff --git a/server/aap/macros/currency_nzd_to_aud_test.py b/server/aap/macros/currency_nzd_to_aud_test.py index 69889d269..ff76f8281 100644 --- a/server/aap/macros/currency_nzd_to_aud_test.py +++ b/server/aap/macros/currency_nzd_to_aud_test.py @@ -9,7 +9,7 @@ # at https://www.sourcefabric.org/superdesk/license from .currency_nzd_to_aud import nzd_to_aud -from .currency_test_base import CurrencyTestClass +from .currency_test_base_test import CurrencyTestClass class CurrencyTestCase(CurrencyTestClass): diff --git a/server/aap/macros/currency_test_base.py b/server/aap/macros/currency_test_base_test.py similarity index 100% rename from server/aap/macros/currency_test_base.py rename to server/aap/macros/currency_test_base_test.py diff --git a/server/aap/macros/currency_usd_to_aud_test.py b/server/aap/macros/currency_usd_to_aud_test.py index b0b923760..35365e923 100644 --- a/server/aap/macros/currency_usd_to_aud_test.py +++ b/server/aap/macros/currency_usd_to_aud_test.py @@ -8,7 +8,7 @@ # AUTHORS and LICENSE files distributed with this source code, or # at https://www.sourcefabric.org/superdesk/license -from .currency_test_base import CurrencyTestClass +from .currency_test_base_test import CurrencyTestClass from .currency_usd_to_aud import usd_to_aud diff --git a/server/aap/macros/currency_usd_to_nzd_test.py b/server/aap/macros/currency_usd_to_nzd_test.py index a7e61dd4b..9aba1a234 100644 --- a/server/aap/macros/currency_usd_to_nzd_test.py +++ b/server/aap/macros/currency_usd_to_nzd_test.py @@ -9,7 +9,7 @@ # at https://www.sourcefabric.org/superdesk/license from .currency_usd_to_nzd import usd_to_nzd -from .currency_test_base import CurrencyTestClass +from .currency_test_base_test import CurrencyTestClass class CurrencyTestCase(CurrencyTestClass): diff --git a/server/aap/macros/golf_collation.py b/server/aap/macros/golf_collation.py index 686ad984c..10969eae5 100644 --- a/server/aap/macros/golf_collation.py +++ b/server/aap/macros/golf_collation.py @@ -158,7 +158,8 @@ def get_result_items(location, desk_id, stage_ids, midnight_utc): # If no match is found then it is assumed that a collated story of all regions is to be produced. collated_grouped = True - items = list(get_result_items(location, copytakers_desk.get('_id'), stage_ids, midnight_utc)) + items = sorted(list(get_result_items(location, copytakers_desk.get('_id'), stage_ids, midnight_utc)), + key=lambda s: s.get('slugline', '').lower()) body = '' if collated_grouped: # keep a set of the golf links that have been include so as not to include them multiple times @@ -166,16 +167,20 @@ def get_result_items(location, desk_id, stage_ids, midnight_utc): for region in state_region.get('regions'): body += '

' + region.get('name') + '

' for i in items: - for l in region.get('links'): - if l.lower().startswith(i.get('slugline', '').lower()) and l not in include_links: + for link in region.get('links'): + if link.lower().startswith(i.get('slugline', '').lower())\ + and i.get('slugline', '').lower() not in include_links: body += i.get('body_html') - include_links.add(l) + include_links.add(i.get('slugline', '').lower()) else: + include_links = set() for i in items: if links: - for l in links: - if l.lower().startswith(i.get('slugline', '').lower()): + for link in links: + if link.lower().startswith(i.get('slugline', '').lower())\ + and i.get('slugline', '').lower() not in include_links: body += i.get('body_html') + include_links.add(i.get('slugline', '').lower()) else: body += i.get('body_html') diff --git a/server/aap/macros/golf_collation_test.py b/server/aap/macros/golf_collation_test.py index f81877bda..312142454 100644 --- a/server/aap/macros/golf_collation_test.py +++ b/server/aap/macros/golf_collation_test.py @@ -116,6 +116,18 @@ def setUp(self): "state": "submitted", "versioncreated": utcnow() - timedelta(hours=25), "body_html": "

LINKS A: somebody something

" + }, + {"place": [{"qcode": "SA"}], + "task": { + "desk": ObjectId("5e1e9474d70421b46535ebe6"), + "stage": ObjectId("5e1e9474d70421b46535ebe4") + }, + "headline": "Exclude duplicate", + "slugline": "Echung", + "subject": [{"qcode": "15027000"}], + "state": "submitted", + "versioncreated": utcnow() - timedelta(hours=5), + "body_html": "

ECHUNGA: somebody something

" } ]) self.app.data.insert('desks', [ @@ -145,4 +157,4 @@ def testSACollatedGolf(self): self.assertTrue(1) self.assertEqual(item.get('body_html'), '

Metropolitan

ECHUNGA: somebody something

' '

GAWLER: somebody something

Country

' - '

Country

') + '

South East

PENOLA: somebody something

') diff --git a/server/aap/macros/golf_collation_v2.py b/server/aap/macros/golf_collation_v2.py new file mode 100644 index 000000000..734fb9995 --- /dev/null +++ b/server/aap/macros/golf_collation_v2.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8; -*- +# +# This file is part of Superdesk. +# +# Copyright 2013, 2014 Sourcefabric z.u. and contributors. +# +# For the full copyright and license information, please see the +# AUTHORS and LICENSE files distributed with this source code, or +# at https://www.sourcefabric.org/superdesk/license + +from superdesk import get_resource_service +import logging +from eve.utils import ParsedRequest +import json +from datetime import datetime +import pytz +from flask import current_app as app +from apps.prepopulate.app_initialize import get_filepath + +logger = logging.getLogger(__name__) + + +def golf_collation(item, **kwargs): + """ + Collates a number of Golf results into a single story. + It uses the location of the input item to filter the included stories. + It expects the name of the golf course (links) to be in the slugline + Stories will be included based on the order of the slugline + If grouping result into regions it expect the region name to be in the anpa_take_key of the input item + :param item: + :param kwargs: + :return: + """ + + def get_desk(): + """ + Search for a desk on the system with the name "Copytakers" + :return: + """ + logger.info('Fetching the ObjectID for the desk "Copytakers".') + query = {'name': 'Copytakers'} + req = ParsedRequest() + req.where = json.dumps(query) + + desk_service = get_resource_service('desks') + desk_item = list(desk_service.get_from_mongo(req=req, lookup=None)) + if not desk_item: + raise('Failed to find the a desk called "Copytakers".') + + desk_id = desk_item[0]['_id'] + logger.info('ObjectID for the desk Copytakers is {}.'.format(desk_id)) + return desk_item[0] + + def get_hold_stages(desk_id): + """ + Get any stages on the passed desk that have the word Hold in their name + :param desk_id: + :return: + """ + lookup = {'$and': [{'name': {'$regex': 'Hold', '$options': 'i'}}, {'desk': str(desk_id)}]} + stages = get_resource_service('stages').get(req=None, lookup=lookup) + return stages + + def get_result_items(location, desk_id, stage_ids, midnight_utc): + """ + Need to find all stories the need to be collated + The subject should be golf + The place should match that of the story the macro is being run against + The slugline should not start with 'Golf Results' (output story will have this slugline) + The story should be updated/created since midnight + Should be on the copy takers desk maybe hold stage? + Not spiked + Not already a collated story + :param location: + :param desk_id: + :param stage_ids: + :param midnight_utc: + :return: + """ + query = { + "query": { + "filtered": { + "filter": { + "bool": { + "must": [ + {"term": {"place.qcode": location.get("qcode")}}, + {"term": {"subject.qcode": "15027000"}}, + {"term": {"task.desk": str(desk_id)}}, + {"terms": {"task.stage": stage_ids}}, + { + "range": { + "versioncreated": { + "gte": midnight_utc + } + } + } + ], + "must_not": [ + {"term": {"state": "spiked"}}, + {"query": { + "match_phrase_prefix": { + "slugline": "Golf Results" + } + }} + ] + } + } + } + }, + "sort": [{"slugline": "asc"}], + "size": 200 + } + + req = ParsedRequest() + repos = 'archive' + req.args = {'source': json.dumps(query), 'repo': repos} + return get_resource_service('search').get(req=req, lookup=None) + + if 'place' not in item or len(item.get('place')) != 1: + raise Exception('The story you''re running the macro on must have a single place defined') + location = item.get('place')[0] + + # Read the file that groups golf courses into regions + path = get_filepath('golf_links.json') + try: + with path.open('r') as f: + regions = json.load(f) + except Exception as ex: + logger.error('Exception loading golf_links.json : {}'.format(ex)) + + copytakers_desk = get_desk() + + # Attempt to get the hold stages for the Copytakers desk + stages = get_hold_stages(copytakers_desk.get('_id')) + stage_ids = [str(s.get('_id')) for s in stages] + if len(stage_ids) == 0: + raise Exception('No hold stages found on desk "{}"'.format(copytakers_desk.get('name'))) + + # Get the local midnight in UTC + midnight_utc = datetime.now(pytz.timezone(app.config['DEFAULT_TIMEZONE']))\ + .replace(hour=0, minute=0, second=0, microsecond=0).astimezone(pytz.utc).isoformat()[:19] + 'z' + + # List of golf courses to include, if grouping by region + links = None + # A flag that indicates if all regions are to be included + collated_grouped = False + + # Get any any entry from the golf links file for the state defined in the location of the item story + state_regions = [s for s in regions.get('states') if s.get('state') == location.get('qcode')] + if len(state_regions): + state_region = state_regions[0] + # Match the value in the take key to any region in the links file + region = [r for r in state_region.get('regions') if + item.get('anpa_take_key', '') and r.get('name', '').lower() == item.get('anpa_take_key', '').lower()] + if len(region): + links = region[0].get('links', []) + else: + # If no match is found then it is assumed that a collated story of all regions is to be produced. + collated_grouped = True + + items = sorted(list(get_result_items(location, copytakers_desk.get('_id'), stage_ids, midnight_utc)), + key=lambda s: s.get('slugline', '').lower()) + body = '' + if collated_grouped: + for region in state_region.get('regions'): + body += '

' + region.get('name') + '

' + for i in items: + if len(list(filter(lambda x: x.lower().startswith(i.get('slugline', '').lower()), + region.get('links')))) > 0: + body += i.get('body_html') + else: + for i in items: + if links: + if len(list(filter(lambda x: x.lower().startswith(i.get('slugline', '').lower()), links))) > 0: + body += i.get('body_html') + else: + body += i.get('body_html') + + if not links: + dayname = datetime.now(pytz.timezone(app.config['DEFAULT_TIMEZONE'])).strftime('%A') + item['anpa_take_key'] = location.get('state', '') + ' ' + dayname + + item['body_html'] = body + item['slugline'] = 'Golf Results' + + return item + + +name = 'Golf collation V2' +label = 'Golf collation V2' +callback = golf_collation +access_type = 'frontend' +action_type = 'direct' +group = 'Copytakers' diff --git a/server/aap/macros/preserve_format.py b/server/aap/macros/preserve_format.py index a577f3790..80e550a61 100644 --- a/server/aap/macros/preserve_format.py +++ b/server/aap/macros/preserve_format.py @@ -33,7 +33,7 @@ def sanitize_tags(item): br.tail = '\n' + br.tail if br.tail else '\n' etree.strip_elements(parsed, 'br', with_tail=False) - for tag in parsed.xpath('/html/div/child::*'): + for tag in parsed.xpath('/div/child::*'): format_text_content(tag) item['body_html'] = '
{}
'.format(html.escape(''.join(parsed.itertext()))) diff --git a/server/aap/macros/strip_paragraphs.py b/server/aap/macros/strip_paragraphs.py index 3ac4e5bba..34589ecc5 100644 --- a/server/aap/macros/strip_paragraphs.py +++ b/server/aap/macros/strip_paragraphs.py @@ -30,7 +30,7 @@ def format_text_content(tag): parsed = parse_html(content, content='html') - for tag in parsed.xpath('/html/div/child::*'): + for tag in parsed.xpath('/div/child::*'): format_text_content(tag) item['body_html'] = '

{}

'.format(html.escape(''.join(parsed.itertext()))).replace('__##NBSP##__', ' ') diff --git a/server/aap/macros/victorian_harness_racing.py b/server/aap/macros/victorian_harness_racing.py index e412fcc63..32433e32b 100644 --- a/server/aap/macros/victorian_harness_racing.py +++ b/server/aap/macros/victorian_harness_racing.py @@ -50,7 +50,7 @@ def race_number_to_words(race): except KeyError: return str(n) - content = item.get('body_html', '') + content = item.get('body_html', '').replace(' ', ' ') comment_item = { "anpa_category": [ { @@ -80,14 +80,17 @@ def race_number_to_words(race): ITEM_TYPE: CONTENT_TYPE.TEXT } selections_item = deepcopy(comment_item) + # copy the genre of the item that we are oprerting on + if 'genre' in item: + selections_item['genre'] = deepcopy(item['genre']) parsed = parse_html(content, content='html') - for tag in parsed.xpath('/html/div/child::*'): + for tag in parsed.xpath('/div/child::*'): if tag.tag == 'p': - if tag.text.startswith('VENUE: '): + if tag.text and tag.text.startswith('VENUE: '): venue = tag.text.replace('VENUE: ', '') - elif tag.text.startswith('DATE: '): + elif tag.text and tag.text.startswith('DATE: '): try: meeting_date = datetime.strptime(tag.text.replace('DATE: ', '').replace(' ', ''), '%d/%m/%y') except Exception: @@ -106,13 +109,13 @@ def race_number_to_words(race): comment_item['anpa_take_key'] = meeting_date.strftime('%A') comment_item['headline'] = venue + ' Trot Comment ' + meeting_date.strftime('%A') comment_item['firstcreated'] = utcnow() - set_dateline(comment_item, 'Melbourne', 'AAP') + set_dateline(comment_item, 'Melbourne', '') selections_item['slugline'] = venue + ' Selections' selections_item['anpa_take_key'] = meeting_date.strftime('%A') selections_item['headline'] = venue + ' Trot Selections ' + meeting_date.strftime('%A') selections_item['firstcreated'] = utcnow() - set_dateline(selections_item, 'Melbourne', 'AAP') + set_dateline(selections_item, 'Melbourne', '') selections_item['body_html'] = '

{} Selections for {}\'s {} trots.-

'.format( selections_item.get('dateline').get('text'), meeting_date.strftime('%A'), venue) @@ -120,8 +123,8 @@ def race_number_to_words(race): break regex = r"Race ([1-9][0-9]|[1-9]):" - for tag in parsed.xpath('/html/div/child::*'): - if tag.tag == 'p': + for tag in parsed.xpath('/div/child::*'): + if tag.tag == 'p' and tag.text: m = re.match(regex, tag.text) if m: selections_item['body_html'] += '

{} '.format(tag.text) @@ -133,13 +136,13 @@ def race_number_to_words(race): # get rid of the trailing one sels = re.sub(r'(, $|,$)', ' ', sels) selections_item['body_html'] += '{}

'.format(sels) - selections_item['body_html'] += '

AAP SELECTIONS

' + selections_item['body_html'] += '

MEDIALITY RACING SELECTIONS

' comment_item['body_html'] = '' overview = '' regex = r"Race ([1-9][0-9]|[1-9]):" - for tag in parsed.xpath('/html/div/child::*'): - if tag.tag == 'p': + for tag in parsed.xpath('/div/child::*'): + if tag.tag == 'p' and tag.text: m = re.match(regex, tag.text) if m: comment_item['body_html'] += '

Race {}:

'.format(race_number_to_words(tag.text)) @@ -154,7 +157,7 @@ def race_number_to_words(race): for i, j in substitution_map.items(): comment_item['body_html'] = comment_item['body_html'].replace(i, j) - comment_item['body_html'] += '

AAP COMMENT

' + comment_item['body_html'] += '

MEDIALITY RACING COMMENT

' service = get_resource_service('archive') selections_item['task'] = item.get('task') diff --git a/server/aap/publish/formatters/aap_apple_news_formatter.py b/server/aap/publish/formatters/aap_apple_news_formatter.py index 4cb59e6a1..d934f0473 100644 --- a/server/aap/publish/formatters/aap_apple_news_formatter.py +++ b/server/aap/publish/formatters/aap_apple_news_formatter.py @@ -540,7 +540,7 @@ def _parse_content(self, article): references_found = False statement_elements = [] - for top_level_tag in parsed_content.xpath('/html/div/child::*'): + for top_level_tag in parsed_content.xpath('/div/child::*'): tag_text = format_text_content(top_level_tag).strip() if not tag_text: continue diff --git a/server/aap/publish/formatters/aap_bulletinbuilder_formatter.py b/server/aap/publish/formatters/aap_bulletinbuilder_formatter.py index 222a0d229..d50327aa1 100644 --- a/server/aap/publish/formatters/aap_bulletinbuilder_formatter.py +++ b/server/aap/publish/formatters/aap_bulletinbuilder_formatter.py @@ -23,6 +23,7 @@ from copy import deepcopy import json from superdesk.etree import parse_html, etree +from superdesk.macros.extract_html import extract_html_macro class AAPBulletinBuilderFormatter(Formatter): @@ -41,6 +42,8 @@ def format(self, article, subscriber, codes=None): try: formatted_article = deepcopy(article) + formatted_article = extract_html_macro(formatted_article) + pub_seq_num = superdesk.get_resource_service('subscribers').generate_sequence_number(subscriber) body_html = to_ascii(self.append_body_footer(formatted_article)).strip('\r\n') @@ -123,7 +126,7 @@ def get_text_content(self, content): etree.strip_elements(parsed, 'br', with_tail=False) text = '' - for top_level_tag in parsed.xpath('/html/div/child::*'): + for top_level_tag in parsed.xpath('/div/child::*'): text += self.format_text_content(top_level_tag) return re.sub(' +', ' ', text) @@ -179,7 +182,7 @@ def format_associated_item(self, item): if not item.get(ASSOCIATIONS): return - for assoc, value in item.get(ASSOCIATIONS).items(): + for _assoc, value in item.get(ASSOCIATIONS).items(): if not value or value.get(ITEM_TYPE) not in {CONTENT_TYPE.AUDIO, CONTENT_TYPE.VIDEO, CONTENT_TYPE.GRAPHIC, CONTENT_TYPE.PICTURE}: continue diff --git a/server/aap/publish/formatters/aap_bulletinbuilder_formatter_tests.py b/server/aap/publish/formatters/aap_bulletinbuilder_formatter_tests.py index 452b1a9d9..a1c1bceaf 100644 --- a/server/aap/publish/formatters/aap_bulletinbuilder_formatter_tests.py +++ b/server/aap/publish/formatters/aap_bulletinbuilder_formatter_tests.py @@ -92,7 +92,7 @@ def test_strip_html(self): '
test
') } - body_text = ('The story body line 1 Line 2\r\n\r\n' + body_text = ('The story body line 1\r\n\r\nLine 2\r\n\r\n' 'abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi' ' abcdefghi abcdefghi abcdefghi more\r\n\r\n' 'test\r\n\r\n') @@ -120,7 +120,7 @@ def test_strip_html_case1(self): '
test
') } - body_text = ('The story body line 1 Line 2\r\n\r\n' + body_text = ('The story body line 1\r\n\r\nLine 2\r\n\r\n' 'abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi' ' abcdefghi abcdefghi abcdefghi more\r\n\r\n' 'test\r\n\r\n') @@ -143,7 +143,7 @@ def test_strip_html_case2(self): '

This is test.



') } - body_text = ('This is third take.\r\n\r\n' + body_text = ('This is third\r\n\r\ntake.\r\n\r\n' 'Correction in the third take.\r\n\r\n' 'This is test.\r\n\r\n') @@ -165,8 +165,8 @@ def test_strip_html_with_linebreak(self): '

This is test.



') } - body_text = ('This is third take.\r\n\r\n' - 'Correction in the third take.\r\n\r\n' + body_text = ('This is\r\n\r\nthird\r\n\r\ntake.\r\n\r\n' + 'Correction\r\n\r\nin the third take.\r\n\r\n' 'This is test.\r\n\r\n') subscriber = self.app.data.find('subscribers', None, None)[0] diff --git a/server/aap/publish/formatters/aap_ipnews_formatter.py b/server/aap/publish/formatters/aap_ipnews_formatter.py index 186688e95..70ecd73e2 100644 --- a/server/aap/publish/formatters/aap_ipnews_formatter.py +++ b/server/aap/publish/formatters/aap_ipnews_formatter.py @@ -107,7 +107,7 @@ def get_wrapped_text_content(self, content): br.tail = '\r\n' + br.tail if br.tail else '\r\n' etree.strip_elements(parsed, 'br', with_tail=False) - for tag in parsed.xpath('/html/div/child::*'): + for tag in parsed.xpath('/div/child::*'): ptext = '' for x in tag.itertext(): ptext += x diff --git a/server/aap/publish/formatters/aap_ipnews_formatter_test.py b/server/aap/publish/formatters/aap_ipnews_formatter_test.py index bf5bbef68..18aa3dbbe 100644 --- a/server/aap/publish/formatters/aap_ipnews_formatter_test.py +++ b/server/aap/publish/formatters/aap_ipnews_formatter_test.py @@ -836,7 +836,7 @@ def testHeadlineEscape(self): article = { '_id': '3', 'source': 'AAP', - 'anpa_category': [{'qcode': 's'}], + 'anpa_category': [{'qcode': 'a'}], 'headline': 'Arrested man ‘punched me in nose’: officer', 'byline': 'joe', 'slugline': 'slugline', diff --git a/server/aap/publish/formatters/aap_newscentre_formatter.py b/server/aap/publish/formatters/aap_newscentre_formatter.py index 266d04833..ec1887c1e 100644 --- a/server/aap/publish/formatters/aap_newscentre_formatter.py +++ b/server/aap/publish/formatters/aap_newscentre_formatter.py @@ -98,7 +98,7 @@ def get_text_content(self, content): br.tail = '\r\n' + br.tail if br.tail else '\r\n' etree.strip_elements(parsed, 'br', with_tail=False) - for tag in parsed.xpath('/html/div/child::*'): + for tag in parsed.xpath('/div/child::*'): ptext = '' for x in tag.itertext(): ptext += x diff --git a/server/aap/publish/formatters/aap_newsroom_ninjs_formatter.py b/server/aap/publish/formatters/aap_newsroom_ninjs_formatter.py index 104eb5e89..4e52c8c9b 100644 --- a/server/aap/publish/formatters/aap_newsroom_ninjs_formatter.py +++ b/server/aap/publish/formatters/aap_newsroom_ninjs_formatter.py @@ -30,7 +30,7 @@ def replacement(match_object): # so return the whole match. return match_object.group(0) else: - return '{0}'.format(html.unescape(url), url) + return '{1}'.format(html.unescape(url), url) return re.sub(self.URL_REGEX, replacement, tag_text) diff --git a/server/aap/publish/formatters/aap_newsroom_ninjs_formatter_test.py b/server/aap/publish/formatters/aap_newsroom_ninjs_formatter_test.py index 0416c19d0..efc287ec6 100644 --- a/server/aap/publish/formatters/aap_newsroom_ninjs_formatter_test.py +++ b/server/aap/publish/formatters/aap_newsroom_ninjs_formatter_test.py @@ -199,8 +199,8 @@ def test_link_expansion(self): '192&item=urn:newsml:localhost:2019-07-10T14:24:14.653510:f7554544-88b1-4263-9d46-db' '4424541eff&action=edit"' ' target="_blank">http://127.0.0.1:9000/#/workspace/monitoring?assignment=5cb5' - '7b165f627d6b2c55a192&item=urn:newsml:localhost:2019-07-10T14:24:14.653510:f7554544-8' - '8b1-4263-9d46-db4424541eff&action=edit ' + '7b165f627d6b2c55a192&item=urn:newsml:localhost:2019-07-10T14:24:14.653510:f75545' + '44-88b1-4263-9d46-db4424541eff&action=edit ' 'AFTER

Instagram ( https://c212.net/c/link/?t=0&l=en&o=2519' - '051-1&h=802772405&u=https%3A%2F%2Fc212.net%2Fc%2Flink%2F%3Ft%3D0%26l%3Den%26o%3D2434' + ' target="_blank">https://c212.net/c/link/?t=0&l=en&o=2519' + '051-1&h=802772405&u=https%3A%2F%2Fc212.net%2Fc%2F' + 'link%2F%3Ft%3D0%26l%3Den%26o%3D2434' '488-1%26h%3D3410101566%26u%3Dhttps%253A%252F%252Furldefense.proofpoint.com%252Fv2%25' '2Furl%253Fu%253Dhttps-3A__www.instagram.com_automobilityla_%2526d%253DDwMGaQ%2526c%2' '53D9wxE0DgWbPxd1HCzjwN8Eaww1--ViDajIU4RXCxgSXE%2526r%253DTsighQ5d9Vys_pgjwOXtbe-L7j' 'q0CiKHyITp_PSm_7w%2526m%253D4NT5PB8zM8WgWXWL9xj-7rCog61nziXpkuqaIM3yrvM%2526s%253DD' - 'hXARHgccri7aqVjW4fNJ33toDz3OKMKBPQzLVKKlyM%2526e%253D%26a%3DInstagram&a=Instagram' + 'hXARHgccri7aqVjW4fNJ33toDz3OKMKBPQzLVKKlyM%2526e%253D%26a%3DInstagram&a=Instagram' ' ' ')

', 'byline': 'Article Byline', diff --git a/server/aap/publish/formatters/aap_ninjs_formatter_test.py b/server/aap/publish/formatters/aap_ninjs_formatter_test.py index fc560b294..9790e0378 100644 --- a/server/aap/publish/formatters/aap_ninjs_formatter_test.py +++ b/server/aap/publish/formatters/aap_ninjs_formatter_test.py @@ -388,7 +388,6 @@ def test_associated_media_formatter(self): "CropBottom": 3712, "mimetype": "image/jpeg", "CropRight": 5568, - "CropTop": 0, "height": 600, "CropLeft": 636, "width": 800, @@ -427,9 +426,7 @@ def test_associated_media_formatter(self): "CropBottom": 3143, "mimetype": "image/jpeg", "CropRight": 5568, - "CropTop": 0, "height": 720, - "CropLeft": 0, "width": 1280, "media": "59a769f81d41c88f16818b2c", "poi": { diff --git a/server/aap/publish/formatters/aap_nitf_formatter.py b/server/aap/publish/formatters/aap_nitf_formatter.py index 052af7b30..3a196df31 100644 --- a/server/aap/publish/formatters/aap_nitf_formatter.py +++ b/server/aap/publish/formatters/aap_nitf_formatter.py @@ -93,7 +93,7 @@ def map_html_to_xml(self, element, html): html = html.replace('\n', ' ') html = re.sub(r'\s\s+', ' ', html) parsed = parse_html(html, content='html') - for tag in parsed.xpath('/html/div/child::*'): + for tag in parsed.xpath('/div/child::*'): p = etree.Element('p') p.text = to_ascii(get_text(to_string(tag, method='html'), content='html')) element.append(p) diff --git a/server/aap/publish/formatters/anpa_formatter.py b/server/aap/publish/formatters/anpa_formatter.py index 56f194775..a343ac7a2 100644 --- a/server/aap/publish/formatters/anpa_formatter.py +++ b/server/aap/publish/formatters/anpa_formatter.py @@ -152,7 +152,7 @@ def get_text_content(self, content): br.tail = '\r\n' + br.tail if br.tail else '\r\n' etree.strip_elements(parsed, 'br', with_tail=False) - for tag in parsed.xpath('/html/div/child::*'): + for tag in parsed.xpath('/div/child::*'): if tag.tag not in ('br') and tag.text is not None and tag.text.strip() != '': tag.text = ' ' + re.sub(' +', ' ', re.sub('(? {% endif %} {% endif %} -Assignment ID: {{ assignment_id }}
-Details: Click to see Assignment - Details (Internal only)
+{% if recepient and system_reciepient %} + {% set mail_addr = recepient.get('email') or recepient.get('contact_email')[0] %} + {% set body = 'This email is being sent to an unmonitored (system) address. If you wish to discuss the details of the assignment further, you should contact a member of the team through other channels. +%0D%0AAssignment {} has been accepted by {} {}.'.format(assignment_id, mail_addr, recepient._id) %} + Accept Assignment +{% endif %} diff --git a/server/templates/assignment_details_email.txt b/server/templates/assignment_details_email.txt index 2949f40ab..2529b86a0 100644 --- a/server/templates/assignment_details_email.txt +++ b/server/templates/assignment_details_email.txt @@ -63,4 +63,3 @@ Slugline: {{ slugline }} {% endif %} {% endif %} -Assignment ID: {{ assignment_id }} diff --git a/server/templates/assignment_details_internal_email.html b/server/templates/assignment_details_internal_email.html new file mode 100644 index 000000000..bc4b63cdb --- /dev/null +++ b/server/templates/assignment_details_internal_email.html @@ -0,0 +1,4 @@ +{% include "assignment_details_email.html" %} +
Assignment ID: {{ assignment_id }}
+Details: Click to see Assignment + Details (Internal only)
diff --git a/server/templates/assignment_details_internal_email.txt b/server/templates/assignment_details_internal_email.txt new file mode 100644 index 000000000..90fc0f073 --- /dev/null +++ b/server/templates/assignment_details_internal_email.txt @@ -0,0 +1,3 @@ +{% include "assignment_details_email.txt" %} + +Assignment ID: {{ assignment_id }}