Giter VIP home page Giter VIP logo

mosromgr's Introduction

mosromgr

Python library for managing MOS running orders. Pronounced mos-ro-manager.

https://mosromgr.readthedocs.io/en/latest/_images/mos.jpg

The library provides functionality for classifying MOS file types, processing and inspecting MOS message files, as well as merging MOS files into a running order, and providing a "completed" programme including all additions and changes made between the first message (roCreate) and the last (roDelete).

This can be used as a library, using the utilities provided in the mosromgr module, and the command line command mosromgr can be used to process either a directory of MOS files, or a folder within an S3 bucket.

This library was developed by the BBC News Labs team.

Usage

Command line

Inspect a MOS file:

$ mosromgr inspect -f 53783448-roStoryReplace.mos.xml
53783448-roStoryReplace.mos.xml: StoryReplace
REPLACE STORY: OM_5.765650;OM_5.765654,5.765650.7 WITH:
    STORY: OM_5.765650;OM_5.765654,5.765650.7

Merge all MOS files in directory newsnight and save in FINAL.xml:

$ mosromgr merge -f newsnight/* -o FINAL.xml

Library

Load a roCreate file and view its stories:

from mosromgr.mostypes import RunningOrder

ro = RunningOrder.from_file('roCreate.mos.xml')

for story in ro.stories:
    print(story.slug)

Merge a single roStorySend into a roCreate and output the file to a new file:

from mosromgr.mostypes import RunningOrder, StorySend

ro = RunningOrder.from_file('roCreate.mos.xml')
ss = StorySend.from_file('roStorySend.mos.xml')

ro += ss

with open('final.mos.xml', 'w') as f:
    f.write(str(ro))

If you're automating this process you won't necessarily know which MOS Type to use, so you can construct an object from the base class MosFile which will automatically classify your file:

>>> from mosromgr.mostypes import MosFile
>>> mf1 = MosFile.from_file('roCreate.mos.xml')
>>> mf1
<RunningOrder 1000>
>>> mf2 = MosFile.from_file('roStorySend.mos.xml')
>>> mf2
<StorySend 1001>

Using MosCollection will sort and classify multiple MOS types of all given files, allowing you to process a collection of MOS files within a complete or partially complete programme:

from mosromgr.moscollection import MosCollection

mos_files = ['roCreate.mos.xml', 'roStorySend.mos.xml', 'roDelete.mos.xml']
mc = MosCollection.from_files(mos_files)

mc.merge()
with open('final.mos.xml', 'w') as f:
    f.write(str(mc))

Documentation

Comprehensive documentation is provided at https://mosromgr.readthedocs.io/

The documentation follows the Diátaxis system, so is split between four modes of documentation: tutorials, how-to guides, technical reference and explanation.

Issues and questions

Questions can be asked on the discussion board, and issues can be raised on the issue tracker.

Contributing

Source code can be found on GitHub at github.com/bbc/mosromgr.

Contributions are welcome. Please refer to the contributing guidelines.

Contributors

Licence

Licensed under the Apache License, Version 2.0.

Contact

To get in touch with the maintainers, please contact the BBC News Labs team: [email protected]

https://mosromgr.readthedocs.io/en/latest/_images/bbcnewslabs.png

mosromgr's People

Contributors

bennuttall avatar bevand10 avatar lannem avatar mattsbbc avatar owentourlamain avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mosromgr's Issues

tests won't run

$ make install 
pip install .
Defaulting to user installation because normal site-packages is not writeable
Processing /home/bevand10/Documents/bbc/git/mosromgr
Requirement already satisfied: boto3 in /usr/lib/python3/dist-packages (from mosromgr==0.6.0) (1.9.253)
Requirement already satisfied: setuptools in /usr/lib/python3/dist-packages (from mosromgr==0.6.0) (45.2.0)
Collecting xmltodict
  Downloading xmltodict-0.12.0-py2.py3-none-any.whl (9.2 kB)
Building wheels for collected packages: mosromgr
  Building wheel for mosromgr (setup.py) ... done
  Created wheel for mosromgr: filename=mosromgr-0.6.0-py3-none-any.whl size=16892 sha256=609fbc9f3f6c69b8365b7d23baae3ceb7b204a0a257d508245cd83c53ec1626a
  Stored in directory: /tmp/pip-ephem-wheel-cache-x1a_8cvz/wheels/1d/62/19/967c460f562f03f765b2c1a5e4f06647b60e231664f360046d
Successfully built mosromgr
Installing collected packages: xmltodict, mosromgr
Successfully installed mosromgr-0.6.0 xmltodict-0.12.0
$
$
$
$ make test 
pylint -E mosromgr
coverage run --rcfile coverage.cfg -m pytest -v tests
make: coverage: Command not found
make: *** [Makefile:39: test] Error 127
$

Maintaining <storyBody>

to <story><item>...</item></story>

Decide if we should depart from the mos protocol convention in roStorySend where the following is used:

<roStorySend>
 <storyBody>
  <p>
  <p>
  <storyItem></storyItem>
  <p>
  <storyItem></storyItem>
 </storyBody>
</roStorySend>

We currently flatten to to <story><item>...</item></story>

By flattening, we depart from the mos convention for stories, and also make final.xml less easy to comprehend from a structural perspective, where <p> and <storyItem> are truly children of <storyBody>, not peers of, for example, <storySlug>.

DRAFT: Implement same-origin filter

Given

  • In some working scenarios, MOS servers are configured to emit main and reserve path traffic.
  • In these circumstances, MOS messages, when collated by receiving MOS devices (that can have main and buddy servers defined), the main path and reserve path messages end up saved side-by-side in the same directory
  • mosromgr does not work correctly when processing a directory of mixed main and reserve MOS messages received for the same running order

When
mosromgr loads MOS messages from a directory

Then
A filtering process should be applied to messages to skip those arriving via the buddy NCS server

AC/s

  • That MOSROMGR will correctly order stories on a client configured with main and buddy NCS servers

BBC use of main and buddy
Main and buddy NCS configurations are common across the BBC. See this recent example in Cardiff where Autoscript clients are configured with main and buddy server connections.
The FIP servers in News are configured with main and buddy connections.

Developer notes
Messages that emit from main and buddy servers feature a handy way to identify buddy messages.

A typical main message contains the following basic data:

<mos>
  <mosID>mosid.w1.bbc.mos</mosID>
  <ncsID>ncsid.w1.bbc.mos</ncsID>
  <roCreate>
  ...
  </roCreate>
</mos>

A typical buddy message contains the following basic data:

<mos>
  <mosID>mosid.w1.bbc.mos</mosID>
  <ncsID>mosid.salford.bbc.mos</ncsID>
  <roCreate>
  ...
  </roCreate>
</mos>

The tell-tail sign of a buddy message is a consistent difference between the domain_name values for used in the mosID vs ncsID elements in the message.

To

Implement `roElementStat`

Given
That the BBC have requested OpenMedia/CGI to implement <roElementStat element="STORY"> messages here

Then
We need to ensure that this library can support the message they are expecting.

ACs

  • That when a <roElementStat type="STORY"> messages appears it is processed.

Developer Notes
A typical message is of the form:

<mos>
   <mosID>aircache.newscenter.com</mosID>
   <ncsID>ncs.newscenter.com</ncsID>
   <messageID>506702</messageID>
   <roElementStat element = "STORY">
      <roID>5PM</roID>
      <storyID>HOTEL FIRE </storyID>
      <status>PLAY</status>
      <time>1999-04-11T14:13:53</time>
  </roElementStat>
</mos>

This example is taken directly from the MOS Protocol specification: https://mosprotocol.com/wp-content/MOS-Protocol-Documents/MOS_Protocol_Version_2.8.5_Final.htm#_3.8.2_roElementStat_-_Status%20of%20a%20S

The message will generally have two recognised values for <status> (these are the ones the BBC have asked CGI to implement):

  • PLAY - indicates that this story was "started" (transmitted) at the <time> given.
  • STOP - indicates that this story was "finished" (no longer the active story) at the <time> given.

Effect of receiving one of these messages

  • That an attribute be added to the STORY structure that retains the PLAY and STOP times. The suggested element to add to the <story> element within FINAL.XML is either:
<story>
...
  <roElementStatPLAY messageID="506702"/>1999-04-11T14:13:53</roElementStatPLAY>
  <roElementStatSTOP messageID="506705"/>1999-04-11T14:15:21</roElementStatSTOP>
...
</story>

or probably better, the elements could be added to the more generic <mosExternalMetadata> STORY child element as follows:

...
<story>
...
  <mosExternalMetadata>
    <mosPayload>
      .....
      <roElementStatPLAY messageID="506702"/>1999-04-11T14:13:53</roElementStatPLAY>
      <roElementStatSTOP messageID="506705"/>1999-04-11T14:15:21</roElementStatSTOP>
    </mosPayload>
  </mosExternalMetadata>
</story>
...

There are other potential values for the <status> value in the incoming MOS message - these include:

  • EXECUTE
  • PAUSE
  • SIGNAL

and could all be covered by the above suggested construct.

BBC: Database considerations
These additional time values are actuals so should probably be stored separately against each story - i.e. we shouldn't overwrite the 'estimated' story duration we build by adding item and script duration times. These should augment those estimates, and if/when available, be used in down-stream processing systems (e.g. creating super-stories JSON/OTIO documents, SMP markers, PIPS chapters etc, etc).

Handling absence of STOP times
Though this is not what we have asked CGI to implement, should our database only possess a series of PLAY times, then we should consider creating "fake" STOP times as follows:

<roElementStatSTOP origin="calculated">1999-04-11T14:13:53</roElementStatSTOP>

roElementStat for other element types
In the MOS spec, roElementStat> messages could arrive for other types as follows:

  • RO - applies to the running order. roID will be supplied to 'locate' the message.
  • ITEM - applies to an item within a story. itemID will be supplied to 'locate' the message.

Whichever approach is taken to store and 'export' the actual timing data provided by <roElementStat>, for full coverage of the MOS spec, the behaviour should be equally applied to RO and ITEM, as well as STORY.

<storyItem> vs <item>

for item in ss_tag.find('storyBody').findall('storyItem'):

Decide /quickly/ if we should be departing from the mos protocol spec here, before we get too many (any) customers that rely on the schema of this document.

Story ordering

We've noticed stories occasionally ending up in the wrong order, and we've found one example of a root cause - but there could be more similar instances as it's about how edge cases are handled.

The bug we've found lies in EAStoryMove.merge(), specifically on line #1705. Essentially it boils down to the case when a story is moved to the bottom - as this message doesn't include a story id to move the story below. The logic for working out where to place the story counts the number of story tags, and inserts the story at that position - but this counting doesn't account for any non-story tags at the same level.

The same bug probably exists in other places, such as EAItemMove.

I can add a test which will demonstrate this bug, and it should be easy to fix, but we should really expand the tests to include all edge cases.

Test for EAStoryMove merge: https://github.com/bbc/mosromgr/blob/main/tests/mostypes/test_mostypes.py#L299 so we need to add a test for a case where the story is being moved to the bottom.

Generalise RunningOrderControl class

Currently the RunningOrderControl class assumes it is one type (the one we use for MOSART timing) but there are others which are different - it might need to be a whole set of subclasses like ElementAction. Additional types not essential but we should not stabilise the API with RunningOrderControl that only works with one type of file.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.