Skip to content

Commit

Permalink
22291 Furnishings Job - Add logic to generate XML for stage 2 (exclud…
Browse files Browse the repository at this point in the history
…e EPs) (#2895)

* 22291(Initial work) - Furnishings Job - Add logic to generate XML for stage 2 (exclude EPs)

Signed-off-by: Hongjing Chen <[email protected]>

* leave date & volume blank

Signed-off-by: Hongjing Chen <[email protected]>

---------

Signed-off-by: Hongjing Chen <[email protected]>
  • Loading branch information
chenhongjing authored Aug 8, 2024
1 parent 6eded47 commit 220ebb8
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 11 deletions.
27 changes: 27 additions & 0 deletions jobs/furnishings/src/furnishings-templates/gazette-notice.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<public_notice>
<volume></volume>
<date></date>

{% for furnishing in furnishings.values() -%}
<filing_desc title="{{ furnishing['title'] }}" id="">
<category>{{ furnishing['category'] }}</category>
<subcategory>{{ furnishing['subcategory'] }}</subcategory>
<corp_class>{{ furnishing['corp_class'] }}</corp_class>
<description>{{ furnishing['description'] }}</description>
<filings>
{% for f in furnishing['items'] -%}
<filing_detail>
<effective_date>{{ effective_date }}</effective_date>
{% if f.business_identifier.startswith('A') -%}
<corp_num>{{ f.business_identifier }}</corp_num>
{%- else -%}
<corp_num>{{ f.business_identifier[2:] }}</corp_num>
{%- endif %}
<text>{{ f.business_name }}</text>
</filing_detail>
{%- endfor %}
</filings>
</filing_desc>
{%- endfor %}
</public_notice>
2 changes: 2 additions & 0 deletions jobs/furnishings/src/furnishings/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ class _Config: # pylint: disable=too-few-public-methods
DEBUG = False

SECOND_NOTICE_DELAY = int(os.getenv('SECOND_NOTICE_DELAY', '5'))
LEGISLATIVE_TIMEZONE = os.getenv('LEGISLATIVE_TIMEZONE', 'America/Vancouver')
TEMPLATE_PATH = os.getenv('TEMPLATE_PATH', 'src/furnishings-templates')


class DevConfig(_Config): # pylint: disable=too-few-public-methods
Expand Down
121 changes: 121 additions & 0 deletions jobs/furnishings/src/furnishings/stage_processors/post_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Copyright © 2024 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Furnishings job processing rules after stage runs of involuntary dissolution."""
from datetime import datetime
from pathlib import Path
from typing import Final

from flask import Flask, current_app
from jinja2 import Template
from legal_api.models import Furnishing, FurnishingGroup, XmlPayload
from legal_api.utils.legislation_datetime import LegislationDatetime


XML_DATE_FORMAT: Final = '%B %-d, %Y'


class PostProcessor:
"""Processor after stage run of furnishings job."""

def __init__(self, app, furnishings_dict):
"""Create post process helper instance."""
self._app = app
self._furnishings_dict = furnishings_dict
self._xml_data = {}
self._processed_date = LegislationDatetime.now()

def _set_meta_info(self):
"""Set meta information for XML file."""
# we leave date and volume in XML blank
self._xml_data['effective_date'] = self._processed_date.strftime(XML_DATE_FORMAT)

def _format_furnishings(self):
"""Format furnishing details presented in XML file."""
self._xml_data['furnishings'] = {}
for name, furnishings in self._furnishings_dict.items():
self._xml_data['furnishings'][name] = XmlMeta.get_info_by_name(name)
self._xml_data['furnishings'][name]['items'] = sorted(furnishings, key=lambda f: f.business_name)

@staticmethod
def _build_xml_data(xml_data):
"""Build XML payload."""
template = Path(
f'{current_app.config.get("TEMPLATE_PATH")}/gazette-notice.xml'
).read_text()
jinja_template = Template(template, autoescape=True)

return jinja_template.render(xml_data)

@staticmethod
def _save_xml_payload(payload):
"""Save XML payload."""
xml_payload = XmlPayload(payload=payload)
xml_payload.save()
furnishing_group = FurnishingGroup(xml_payload_id=xml_payload.id)
furnishing_group.save()
return furnishing_group, xml_payload

def _update_furnishings_status(self, furnishing_group_id):
"""Update furnishing entries after processing."""
for furnishings in self._furnishings_dict.values():
for furnishing in furnishings:
furnishing.furnishing_group_id = furnishing_group_id
furnishing.status = Furnishing.FurnishingStatus.PROCESSED
furnishing.processed_date = datetime.utcnow()
furnishing.last_modified = datetime.utcnow()
furnishing.save()

def process(self):
"""Postprocess to generate and upload file to external resources(BC Laws)."""
self._format_furnishings()
self._set_meta_info()
payload = self._build_xml_data(self._xml_data)
furnishing_group, _ = self._save_xml_payload(payload)
# TODO: SFTP to BC Laws

# mark furnishing records processed
self._update_furnishings_status(furnishing_group.id)


class XmlMeta:
"""Helper class to maintain the XML meta information."""

furnishings = {
Furnishing.FurnishingName.INTENT_TO_DISSOLVE: {
'title': 'Intent to Dissolve (B.C.)',
'category': 'INTENT TO DISSOLVE',
'subcategory': 'B.C.',
'corp_class': 'BC Company(s)',
'description': (
'The Registrar of Companies hereby gives notice that the following companies may, '
'at any time after the expiration of one month from the date of publication of this notice, '
'unless cause is shown to the contrary, '
'be dissolved under section 422 of the Business Corporations Act.'
)
},
}

@staticmethod
def get_info_by_name(name: Furnishing.FurnishingName) -> dict:
"""Return the furnishing category information as per furnishing name."""
return XmlMeta.furnishings[name]


def process(app: Flask, furnishings_dict: dict):
"""Run postprocess after stage run to upload files to external resources."""
try:
processor = PostProcessor(app, furnishings_dict)
processor.process()
except Exception as err:
app.logger.error(err)
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Furnishings job procssing rules for stage one of involuntary dissolution."""
"""Furnishings job processing rules for stage one of involuntary dissolution."""
import uuid
from datetime import datetime

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Furnishings job procssing rules for stage three of involuntary dissolution."""
"""Furnishings job processing rules for stage three of involuntary dissolution."""
from datetime import datetime

from flask import Flask
Expand Down
14 changes: 8 additions & 6 deletions jobs/furnishings/src/furnishings/stage_processors/stage_two.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Furnishings job procssing rules for stage two of involuntary dissolution."""
"""Furnishings job processing rules for stage two of involuntary dissolution."""
from datetime import datetime

from flask import Flask
from legal_api.models import Batch, BatchProcessing, Business, Furnishing, db
from sqlalchemy import exists, not_


def process(app: Flask):
def process(app: Flask, xml_furnishings: dict):
"""Run process to manage and track notifications for dissolution stage two process."""
try:
furnishing_subquery = exists().where(
Expand All @@ -40,7 +40,7 @@ def process(app: Flask):
.filter(not_(furnishing_subquery))
).all()

furnishing_group_id = Furnishing.get_next_furnishing_group_id()
bc_furnishings = []

for batch_processing in batch_processings:
business = batch_processing.business
Expand All @@ -58,12 +58,14 @@ def process(app: Flask):
created_date=datetime.utcnow(),
last_modified=datetime.utcnow(),
status=Furnishing.FurnishingStatus.QUEUED,
furnishing_group_id=furnishing_group_id,
business_name=business.legal_name
)
new_furnishing.save()
# TODO: create data files and SFTPing to BC Laws
# TODO: mark furnishings entry processed

if business != Business.LegalTypes.EXTRA_PRO_A.value:
bc_furnishings.append(new_furnishing)

xml_furnishings[Furnishing.FurnishingName.INTENT_TO_DISSOLVE] = bc_furnishings

except Exception as err:
app.logger.error(err)
8 changes: 6 additions & 2 deletions jobs/furnishings/src/furnishings/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from sentry_sdk.integrations.logging import LoggingIntegration

from furnishings.config import get_named_config # pylint: disable=import-error
from furnishings.stage_processors import stage_one, stage_three, stage_two
from furnishings.stage_processors import post_processor, stage_one, stage_three, stage_two
from furnishings.utils.logging import setup_logging # pylint: disable=import-error


Expand Down Expand Up @@ -114,9 +114,13 @@ async def run(application: Flask, qsm: QueueService): # pylint: disable=redefin
)
return

xml_furnishings_dict = {}

if stage_1_valid:
await stage_one.process(application, qsm)
if stage_2_valid:
stage_two.process(application)
stage_two.process(application, xml_furnishings_dict)
if stage_3_valid:
stage_three.process(application)

post_processor.process(application, xml_furnishings_dict)
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Copyright © 2024 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from legal_api.models import BatchProcessing, Furnishing, FurnishingGroup, XmlPayload

from furnishings.stage_processors.post_processor import PostProcessor, process

from .. import factory_batch, factory_batch_processing, factory_business, factory_furnishing


def helper_create_furnishings(identifiers: list):
"""Test helper to create furnishings for post processing."""
furnishings = []
for identifier in identifiers:
business = factory_business(identifier=identifier)
batch = factory_batch()
factory_batch_processing(
batch_id=batch.id,
business_id=business.id,
identifier=business.identifier,
step=BatchProcessing.BatchProcessingStep.WARNING_LEVEL_2
)
furnishing = factory_furnishing(
batch_id=batch.id,
business_id=business.id,
identifier=business.identifier,
furnishing_name=Furnishing.FurnishingName.INTENT_TO_DISSOLVE,
furnishing_type=Furnishing.FurnishingType.GAZETTE,
business_name=business.legal_name
)
furnishings.append(furnishing)
return furnishings


def test_process(app, session):
"""Assert that FurnishingGroup and XmlPayload entry are created correctly."""
furnishings = helper_create_furnishings(['BC1234567'])
furnishing_dict = {
Furnishing.FurnishingName.INTENT_TO_DISSOLVE: furnishings
}
process(app, furnishing_dict)

furnishing = furnishings[0]
assert furnishing.status == Furnishing.FurnishingStatus.PROCESSED

furnishing_group_id = furnishing.furnishing_group_id
assert furnishing_group_id
furnishing_group = FurnishingGroup.find_by_id(furnishing_group_id)
assert furnishing_group

xml_payload_id = furnishing_group.xml_payload_id
assert xml_payload_id
xml_payload = XmlPayload.find_by_id(xml_payload_id)
assert xml_payload
assert xml_payload.payload


def test_processor_format_furnishings(app, session):
"""Assert that furnishing details are formated/sorted correctly."""
furnishings = helper_create_furnishings(['BC7654321', 'BC1234567'])
name = Furnishing.FurnishingName.INTENT_TO_DISSOLVE
furnishing_dict = {
name: furnishings
}

processor = PostProcessor(app, furnishing_dict)
processor._format_furnishings()

assert processor._xml_data
assert processor._xml_data['furnishings'][name]['items']
furnishing_items = processor._xml_data['furnishings'][name]['items']
assert furnishing_items[0] == furnishings[1]
assert furnishing_items[1] == furnishings[0]
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def test_process_create_furnishings(app, session, test_name, entity_type, step,
business_name=business.legal_name
)

process(app)
process(app, {})

furnishings = Furnishing.find_by(business_id=business.id)
if new_entry:
Expand Down

0 comments on commit 220ebb8

Please sign in to comment.