For Developers

This technical section is targeting software developers.

music_publisher

Django-Music-Publisher (DMP) is open source software for managing music metadata, registration/licencing of musical works and royalty processing.

music_publisher app is the only Django app in this project.

music_publisher.apps

Django app definition for music_publisher.

class music_publisher.apps.MusicPublisherConfig(app_name, app_module)

Bases: AppConfig

Configuration for Music Publisher app.

label

app label

Type:

str

name

app name

Type:

str

verbose_name

app verbose name

Type:

str

ready()

Validate settings when ready to prevent deployments with invalid settings.

music_publisher.societies

Create society tuple and dict.

music_publisher.societies.SOCIETIES

(tis-n, Name (Country))

Type:

tuple

music_publisher.societies.SOCIETY_DICT

{tis-n, Name (Country)}

Type:

dict

music_publisher.validators

CWR-compatibility field-level validation.

For formats that allow dashes and dots (ISWC, IPI Base), the actual format is from CWR 2.x specification: ISWC without and IPI Base with dashes.

music_publisher.validators.check_ean_digit(ean)

EAN checksum validation.

Parameters:

ean (str) – EAN

Raises:

ValidationError

music_publisher.validators.check_iswc_digit(iswc, weight)

ISWC / IPI Base checksum validation.

Parameters:
  • iswc (str) – ISWC or IPI Base #

  • weight (int) – 1 for ISWC, 2 for IPI Base #

Raises:

ValidationError

music_publisher.validators.check_ipi_digit(all_digits)

IPI Name checksum validation.

Parameters:

all_digits (str) – IPI Name #

Raises:

ValidationError

music_publisher.validators.check_isni_digit(all_digits)

ISNI checksum validation.

Parameters:

all_digits (str) – ISNI

Raises:

ValidationError

music_publisher.validators.check_dpid(dpid)

Calculate the checksum. A valid number should have a checksum of 1.

class music_publisher.validators.CWRFieldValidator(*args, **kwargs)

Bases: object

Validate fields for CWR compliance.

field

Validation service name of the field being validated

Type:

str

deconstruct()

Return a 3-tuple of class import path, positional arguments, and keyword arguments.

music_publisher.validators.validate_publisher_settings()

CWR-compliance validation for publisher settings.

music_publisher.validators.validate_settings()

CWR-compliance validation for settings.

This is used to prevent deployment with invalid settings.

music_publisher.base

Contains base (abstract) classes used in models

class music_publisher.base.NotesManager(*args, **kwargs)

Bases: Manager

Manager for objects inheriting from NotesBase.

Defers NotesBase.notes field.

get_queryset()

Defer NotesBase.notes field.

class music_publisher.base.NotesBase(*args, **kwargs)

Bases: Model

Abstract class for all classes that have notes.

notes

Notes, free internal text field

Type:

django.db.models.TextField

class music_publisher.base.DescriptionBase(*args, **kwargs)

Bases: Model

Abstract class for all classes that have publicly visible descriptions.

description

Public description

Type:

django.db.models.TextField

class music_publisher.base.TitleBase(*args, **kwargs)

Bases: Model

Abstract class for all classes that have a title.

title

Title, used in work title, alternate title, etc.

Type:

django.db.models.CharField

class music_publisher.base.PersonBase(*args, **kwargs)

Bases: Model

Base class for all classes that contain people with first and last name.

This includes writers and artists. For bands, only the last name field is used.

first_name

First Name

Type:

django.db.models.CharField

last_name

Last Name

Type:

django.db.models.CharField

class music_publisher.base.SocietyAffiliationBase(*args, **kwargs)

Bases: Model

Abstract base for all objects with CMO affiliations

pr_society

Performing Rights Society Code

Type:

django.db.models.CharField

mr_society

Mechanical Rights Society Code

Type:

django.db.models.CharField

sr_society

Sync. Rights Society Code

Type:

django.db.models.CharField

class music_publisher.base.IPIBase(*args, **kwargs)

Bases: Model

Abstract base for all objects containing IPI numbers.

ipi_base

IPI Base Number

Type:

django.db.models.CharField

ipi_name

IPI Name Number

Type:

django.db.models.CharField

_can_be_controlled

used to determine if there is enough data for a writer to be controlled.

Type:

django.db.models.BooleanField

clean_fields(*args, **kwargs)

Data cleanup, allowing various import formats to be converted into consistently formatted data.

class music_publisher.base.IPIWithGeneralAgreementBase(*args, **kwargs)

Bases: IPIBase, SocietyAffiliationBase

Abstract base for all objects with general agreements.

saan

Society-assigned agreement number, in this context it is used for general agreements, for specific agreements use models.WriterInWork.saan.

Type:

django.db.models.CharField

generally_controlled

flags if a writer is generally controlled (in all works)

Type:

django.db.models.BooleanField

publisher_fee

this field is used in calculating publishing fees

Type:

django.db.models.DecimalField

clean()

Clean the data and validate.

clean_fields(*args, **kwargs)

Data cleanup, allowing various import formats to be converted into consistently formatted data.

class music_publisher.base.AccountNumberBase(*args, **kwargs)

Bases: Model

Abstract base for all objects with an account number.

account_number

account number, used for royalty processing

Type:

django.db.models.CharField

clean_fields(*args, **kwargs)

Account Number cleanup

class music_publisher.base.ArtistBase(*args, **kwargs)

Bases: PersonBase, NotesBase, DescriptionBase

Performing artist base class.

isni

International Standard Name Id

Type:

django.db.models.CharField

clean_fields(*args, **kwargs)

ISNI cleanup

class music_publisher.base.WriterBase(*args, **kwargs)

Bases: PersonBase, IPIWithGeneralAgreementBase, NotesBase, DescriptionBase, AccountNumberBase

Base class for writers.

class music_publisher.base.LabelBase(*args, **kwargs)

Bases: NotesBase, DescriptionBase

Music Label base class.

name

Label Name

Type:

django.db.models.CharField

class music_publisher.base.LibraryBase(*args, **kwargs)

Bases: Model

Music Library base class.

name

Library Name

Type:

django.db.models.CharField

class music_publisher.base.ReleaseBase(*args, **kwargs)

Bases: DescriptionBase

Music Release base class

cd_identifier

CD Identifier, used when origin is library

Type:

django.db.models.CharField

library

Library Name

Type:

django.db.models.CharField

release_date

Date of the release

Type:

django.db.models.DateField

ean

EAN code

Type:

django.db.models.CharField

release_label

Label Name

Type:

django.db.models.CharField

release_title

Title of the release

Type:

django.db.models.CharField

music_publisher.models

Concrete models.

They mostly inherit from classes in base.

class music_publisher.models.Artist(*args, **kwargs)

Bases: ArtistBase

Performing artist.

get_dict()

Get the object in an internal dictionary format

Returns:

internal dict format

Return type:

dict

property artist_id

Artist identifier

Returns:

Artist ID

Return type:

str

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.Label(*args, **kwargs)

Bases: LabelBase

Music Label.

property label_id

Label identifier

Returns:

Label ID

Return type:

str

get_dict()

Get the object in an internal dictionary format

Returns:

internal dict format

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.Library(*args, **kwargs)

Bases: LibraryBase

Music Library.

property library_id

Library identifier

Returns:

Library ID

Return type:

str

get_dict()

Get the object in an internal dictionary format

Returns:

internal dict format

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.Release(*args, **kwargs)

Bases: ReleaseBase

Music Release (album / other product)

library

Foreign key to models.Library

Type:

django.db.models.ForeignKey

release_label

Foreign key to models.Label

Type:

django.db.models.ForeignKey

recordings

M2M to models.Recording through models.Track

Type:

django.db.models.ManyToManyField

property release_id

Release identifier.

Returns:

Release ID

Return type:

str

get_dict(with_tracks=False)

Get the object in an internal dictionary format

Parameters:

with_tracks (bool) – add track data to the output

Returns:

internal dict format

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.LibraryReleaseManager(*args, **kwargs)

Bases: Manager

Manager for a proxy class models.LibraryRelease

get_queryset()

Return only library releases

Returns:

Queryset with instances of models.LibraryRelease

Return type:

django.db.models.query.QuerySet

get_dict(qs)

Get the object in an internal dictionary format

Parameters:

qs (django.db.models.query.QuerySet)

Returns:

internal dict format

Return type:

dict

class music_publisher.models.LibraryRelease(*args, **kwargs)

Bases: Release

Proxy class for Library Releases (AKA Library CDs)

objects

Database Manager

Type:

LibraryReleaseManager

clean()

Make sure that release title is required if one of the other “non-library” fields is present.

Raises:

ValidationError – If not compliant.

get_origin_dict()

Get the object in an internal dictionary format.

This is used for work origin, not release data.

Returns:

internal dict format

Return type:

dict

exception DoesNotExist

Bases: DoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.CommercialReleaseManager(*args, **kwargs)

Bases: Manager

Manager for a proxy class models.CommercialRelease

get_queryset()

Return only commercial releases

Returns:

Queryset with instances of models.CommercialRelease

Return type:

django.db.models.query.QuerySet

get_dict(qs)

Get the object in an internal dictionary format

Parameters:

qs (django.db.models.query.QuerySet)

Returns:

internal dict format

Return type:

dict

class music_publisher.models.CommercialRelease(*args, **kwargs)

Bases: Release

Proxy class for Commercial Releases

objects

Database Manager

Type:

CommercialReleaseManager

exception DoesNotExist

Bases: DoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.PlaylistManager(*args, **kwargs)

Bases: Manager

Manager for a proxy class models.Playlist

get_queryset()

Return only commercial releases

Returns:

Queryset with instances of models.CommercialRelease

Return type:

django.db.models.query.QuerySet

get_dict(qs)

Get the object in an internal dictionary format

Parameters:

qs (django.db.models.query.QuerySet)

Returns:

internal dict format

Return type:

dict

class music_publisher.models.Playlist(*args, **kwargs)

Bases: Release

Proxy class for Playlists

objects

Database Manager

Type:

CommercialReleaseManager

clean(*args, **kwargs)

Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.

exception DoesNotExist

Bases: DoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.Writer(*args, **kwargs)

Bases: WriterBase

Writers.

original_publishing_agreement

Foreign key to models.OriginalPublishingAgreement

Type:

django.db.models.ForeignKey

clean(*args, **kwargs)

Check if writer who is controlled still has enough data.

property writer_id

Writer ID for CWR

Returns:

formatted writer ID

Return type:

str

get_dict()

Create a data structure that can be serialized as JSON.

Returns:

JSON-serializable data structure

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.WorkManager(*args, **kwargs)

Bases: Manager

Manager for class models.Work

get_queryset()

Get an optimized queryset.

Returns:

Queryset with instances of models.Work

Return type:

django.db.models.query.QuerySet

get_dict_items(qs)

Yield dictionary items for works from the queryset

Parameters:

qs (django.db.models.query import QuerySet)

Returns:

dictionary with works

Return type:

dict

get_dict(qs)

Return a dictionary with works from the queryset

Parameters:

qs (django.db.models.query import QuerySet)

Returns:

dictionary with works

Return type:

dict

class music_publisher.models.Work(*args, **kwargs)

Bases: TitleBase

Concrete class, with references to foreign objects.

_work_id

permanent work id, either imported or fixed when exports are created

Type:

django.db.models.CharField

iswc

ISWC

Type:

django.db.models.CharField

original_title

title of the original work, implies modified work

Type:

django.db.models.CharField

release_label

Foreign key to models.LibraryRelease

Type:

django.db.models.ForeignKey

last_change

when the last change was made to this object or any of the child objects, basically used in filtering

Type:

django.db.models.DateTimeField

artists

Artists performing the work

Type:

django.db.models.ManyToManyField

writers

Writers who created the work

Type:

django.db.models.ManyToManyField

objects

Database Manager

Type:

WorkManager

property work_id

Create Work ID used in registrations.

Returns:

Internal Work ID

Return type:

str

is_modification()

Check if the work is a modification.

Returns:

True if modification, False if original

Return type:

bool

clean_fields(*args, **kwargs)

Deal with various ways ISWC is written.

static get_publisher_dict()

Create data structure for the publisher.

Returns:

JSON-serializable data structure

Return type:

dict

get_dict(with_recordings=True)

Create a data structure that can be serialized as JSON.

Normalize the structure if required.

Returns:

JSON-serializable data structure

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.AlternateTitle(*args, **kwargs)

Bases: TitleBase

Concrete class for alternate titles.

work

Foreign key to Work model

Type:

django.db.models.ForeignKey

suffix

implies that the title should be appended to the work title

Type:

django.db.models.BooleanField

get_dict()

Create a data structure that can be serialized as JSON.

Returns:

JSON-serializable data structure

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.ArtistInWork(*args, **kwargs)

Bases: Model

Artist performing the work (live in CWR 3).

artist

FK to Artist

Type:

django.db.models.ForeignKey

work

FK to Work

Type:

django.db.models.ForeignKey

get_dict()
Returns:

taken from models.Artist.get_dict()

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.WriterInWork(*args, **kwargs)

Bases: Model

Writers who created this work.

At least one writer in work must be controlled. Sum of relative shares must be (roughly) 100%. Capacity is limited to roles for original writers.

work

FK to Work

Type:

django.db.models.ForeignKey

writer

FK to Writer

Type:

django.db.models.ForeignKey

saan

Society-assigned agreement number between the writer and the original publisher, please note that this field is for SPECIFIC agreements, for a general agreement, use base.IPIBase.saan

Type:

django.db.models.CharField

controlled

A complete mistery field

Type:

django.db.models.BooleanField

relative_share

Initial split among writers, prior to publishing

Type:

django.db.models.DecimalField

capacity

Role of the writer in this work

Type:

django.db.models.CharField

publisher_fee

Percentage of royalties kept by publisher

Type:

django.db.models.DecimalField

clean_fields(*args, **kwargs)

Turn SAAN into uppercase.

Parameters:
  • *args – passing through

  • **kwargs – passing through

Returns:

SAAN in uppercase

Return type:

str

clean()

Make sure that controlled writers have all the required data.

Also check that writers that are not controlled do not have data that can not apply to them.

get_agreement_dict()

Get agreement dictionary for this writer in work.

get_dict()

Create a data structure that can be serialized as JSON.

Returns:

JSON-serializable data structure

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.Recording(*args, **kwargs)

Bases: Model

Recording.

release_date

Recording Release Date

Type:

django.db.models.DateField

duration

Recording Duration

Type:

django.db.models.TimeField

isrc

International Standard Recording Code

Type:

django.db.models.CharField

record_label

Record Label

Type:

django.db.models.CharField

clean_fields(*args, **kwargs)

ISRC cleaning, just removing dots and dashes.

Parameters:
  • *args – may be used in upstream

  • **kwargs – may be used in upstream

Returns:

return from django.db.models.Model.clean_fields()

property complete_recording_title

Return complete recording title.

Returns:

str

property complete_version_title

Return complete version title.

Returns:

str

property title

Generate title from various fields.

property recording_id

Create Recording ID used in registrations

Returns:

Internal Recording ID

Return type:

str

get_dict(with_releases=False, with_work=True)

Create a data structure that can be serialized as JSON.

Parameters:
  • with_releases (bool) – add releases data (through tracks)

  • with_work (bool) – add work data

Returns:

JSON-serializable data structure

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.Track(*args, **kwargs)

Bases: Model

Track, a recording on a release.

recording

Recording

Type:

django.db.models.ForeignKey

release

Release

Type:

django.db.models.ForeignKey

cut_number

Cut Number

Type:

django.db.models.PositiveSmallIntegerField

get_dict()

Create a data structure that can be serialized as JSON.

Returns:

JSON-serializable data structure

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.DeferCwrManager(*args, **kwargs)

Bases: Manager

Manager for CWR Exports and ACK Imports.

Defers CWRExport.cwr and AckImport.cwr fields.

get_queryset()

Return a new QuerySet object. Subclasses can override this method to customize the behavior of the Manager.

class music_publisher.models.CWRExport(*args, **kwargs)

Bases: Model

Export in CWR format.

Common Works Registration format is a standard format for registration of musical works world-wide. Exports are available in CWR 2.1 revision 8 and CWR 3.0 (experimental).

nwr_rev

choice field where user can select which version and type of CWR it is

Type:

django.db.models.CharField

cwr

contents of CWR file

Type:

django.db.models.TextField

year

2-digit year format

Type:

django.db.models.CharField

num_in_year

CWR sequential number in a year

Type:

django.db.models.PositiveSmallIntegerField

works

included works

Type:

django.db.models.ManyToManyField

description

internal note

Type:

django.db.models.CharField

property version

Return CWR version.

property filename

Return CWR file name.

Returns:

CWR file name

Return type:

str

property filename3

Return proper CWR 3.x filename.

Format is: CWYYnnnnSUB_REP_VM - m - r.EXT

Returns:

CWR file name

Return type:

str

property filename2

Return proper CWR 2.x filename.

Returns:

CWR file name

Return type:

str

get_record(key, record)

Create CWR record (row) from the key and dict.

Parameters:
  • key (str) – type of record

  • record (dict) – field values

Returns:

CWR record (row)

Return type:

str

get_transaction_record(key, record)

Create CWR transaction record (row) from the key and dict.

This methods adds transaction and record sequences.

Parameters:
  • key (str) – type of record

  • record (dict) – field values

Returns:

CWR record (row)

Return type:

str

yield_iswc_request_lines(works)

Yield lines for an ISR (ISWC request) in CWR 3.x

yield_publisher_lines(publisher, controlled_relative_share)

Yield SPU/SPT lines.

Parameters:
  • publisher (dict) – dictionary with publisher data

  • controlled_relative_share (Decimal) – sum of manuscript shares for controlled writers

Yields:

str – CWR record (row/line)

yield_registration_lines(works)

Yield lines for CWR registrations (WRK in 3.x, NWR and REV in 2.x)

Parameters:

works (list) – list of work dicts

Yields:

str – CWR record (row/line)

get_party_lines(work)

Yield SPU, SPT, OPU, SWR, SWT, OPT and PWR lines

Parameters:

work – musical work

Yields:

str – CWR record (row/line)

get_other_lines(work)

Yield ALT and subsequent lines

Parameters:

work – musical work

Yields:

str – CWR record (row/line)

get_header()

Construct CWR HDR record.

yield_lines(works)

Yield CWR transaction records (rows/lines) for works

Parameters:

works (query) – models.Work query

Yields:

str – CWR record (row/line)

static chunked(items, size)

Yield chunks from items with at most size items.

should_create_synchronously()

Return whether this export should be generated on save.

create_cwr_files(publisher_code=None)

Create one or more CWR files from this export request.

Large requests are split into multiple CWRExport objects, each with at most OPTION_CWR_WORKS_PER_FILE works. If this export already fits into one file, it is generated directly and returned as the only item.

Parameters:

publisher_code (str) – override publisher code for generation

Returns:

generated CWR exports

Return type:

list[CWRExport]

create_cwr(publisher_code=None, generate=True)

Create CWR and save.

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.WorkAcknowledgement(*args, **kwargs)

Bases: Model

Acknowledgement of work registration.

date

Acknowledgement date

Type:

django.db.models.DateField

remote_work_id

Remote work ID

Type:

django.db.models.CharField

society_code

3-digit society code

Type:

django.db.models.CharField

status

2-letter status code

Type:

django.db.models.CharField

TRANSACTION_STATUS_CHOICES

choices for status

Type:

tuple

work

FK to Work

Type:

django.db.models.ForeignKey

get_dict()

Return dictionary with external work IDs.

Returns:

JSON-serializable data structure

Return type:

dict

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.ACKImport(*args, **kwargs)

Bases: Model

CWR acknowledgement file import.

filename

Description

Type:

django.db.models.CharField

society_code

3-digit society code, please note that choices is not set.

Type:

models.CharField

society_name

Society name, used if society code is missing.

Type:

models.CharField

date

Acknowledgement date

Type:

django.db.models.DateField

report

Basically a log

Type:

django.db.models.CharField

cwr

contents of CWR file

Type:

django.db.models.TextField

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

class music_publisher.models.DataImport(*args, **kwargs)

Bases: Model

Importing basic work data from a CSV file.

This class just acts as log, the actual logic is in data_import.

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

music_publisher.models.smart_str_conversion(value)

Convert to Title Case only if UPPER CASE.

music_publisher.models.change_case(sender, instance, **kwargs)

Change case of CharFields from music_publisher.

music_publisher.cwr_templates

Django old_templates for CWR generation.

music_publisher.cwr_templates.TEMPLATES_21

Record old_templates for CWR 2.1

Type:

dict

music_publisher.cwr_templates.TEMPLATES_22

Record old_templates for CWR 2.2, based on 2.1

Type:

dict

music_publisher.cwr_templates.TEMPLATES_30

Record old_templates for CWR 3.0

Type:

dict

music_publisher.cwr_templates.TEMPLATES_31

Record old_templates for CWR 3.1, based on 3.0

Type:

dict

music_publisher.templatetags

Template tags for music_publisher

music_publisher.templatetags.cwr_filters

Filters used in parsing of CWR files.

music_publisher.templatetags.cwr_filters.perc(value)

Display shares as human-readable string.

music_publisher.templatetags.cwr_filters.soc_name(value)

Display society name

music_publisher.templatetags.cwr_filters.capacity(value)

Display writer capacity/role

music_publisher.templatetags.cwr_filters.agreement_type(value)

Display publishing agreement type

music_publisher.templatetags.cwr_filters.status(value)

Display acknowledgement status

music_publisher.templatetags.cwr_filters.flag(value)

Display flag value

music_publisher.templatetags.cwr_filters.orimod(value)

Display original or modification

music_publisher.templatetags.cwr_filters.terr(value)

Display territory

music_publisher.templatetags.cwr_filters.ie(value)

Display Included / Excluded

music_publisher.templatetags.cwr_filters.role(value)

Display publisher role/capacity

music_publisher.templatetags.cwr_filters.title_type(value)

Display alternative title type

music_publisher.templatetags.cwr_generators

Filters used in generation of CWR files.

music_publisher.templatetags.cwr_generators.rjust(value, length)

Format general numeric fields.

music_publisher.templatetags.cwr_generators.ljust(value, length)

Format general alphanumeric fields.

music_publisher.templatetags.cwr_generators.soc(value)

Format society fields.

music_publisher.templatetags.cwr_generators.cwrshare(value)

Get CWR-compatible output for share fields

music_publisher.templatetags.dmp_dashboard

Filter used in DMP dashboard.

music_publisher.templatetags.dmp_dashboard.yield_sections(model_dict, sections)

Convert model dictionary according to section structure

music_publisher.templatetags.dmp_dashboard.dmp_model_groups(model_list)

Return groups of models.

music_publisher.forms

Forms and formsets.

class music_publisher.forms.LibraryReleaseForm(*args, **kwargs)

Bases: ModelForm

Custom form for models.LibraryRelease.

property media

Return all media required to render the widgets on this form.

class music_publisher.forms.PlaylistForm(*args, **kwargs)

Bases: ModelForm

Custom form for models.LibraryRelease.

property media

Return all media required to render the widgets on this form.

class music_publisher.forms.AlternateTitleFormSet(data=None, files=None, instance=None, save_as_new=False, prefix=None, queryset=None, **kwargs)

Bases: BaseInlineFormSet

Formset for AlternateTitleInline.

clean()
Performs these checks:

if suffix is used, then validates the total length

Returns:

None

Raises:

ValidationError

class music_publisher.forms.WorkForm(*args, **kwargs)

Bases: ModelForm

Custom form for models.Work.

Calculate values for readonly field version_type.

property media

Return all media required to render the widgets on this form.

class music_publisher.forms.ACKImportForm(data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=<class 'django.forms.utils.ErrorList'>, label_suffix=None, empty_permitted=False, instance=None, use_required_attribute=None, renderer=None)

Bases: ModelForm

Form used for CWR acknowledgement imports.

acknowledgement_file

Field for file upload

Type:

FileField

clean()

Perform usual clean, then process the file, returning the content field as if it was the TextField.

property media

Return all media required to render the widgets on this form.

class music_publisher.forms.WriterInWorkFormSet(data=None, files=None, instance=None, save_as_new=False, prefix=None, queryset=None, **kwargs)

Bases: BaseInlineFormSet

Formset for WriterInWorkInline.

clean()
Performs these checks:

at least one writer must be controlled, at least one writer music be Composer or Composer&Lyricist sum of relative shares must be ~100% for original works, only allowed capacities may be used for modified works, at least one writer must have an extended capacity

Returns:

None

Raises:

ValidationError

class music_publisher.forms.DataImportForm(data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=<class 'django.forms.utils.ErrorList'>, label_suffix=None, empty_permitted=False, instance=None, use_required_attribute=None, renderer=None)

Bases: ModelForm

Form used for data imports.

data_file

Field for file upload

Type:

FileField

clean()

This is the actual import process, if all goes well, the report is saved.

Raises:

ValidationError

property media

Return all media required to render the widgets on this form.

music_publisher.admin

Main interface for music_publisher.

All views are here, except for royalty_calculation.

class music_publisher.admin.ImageWidget(attrs=None)

Bases: ClearableFileInput

class music_publisher.admin.AudioPlayerWidget(attrs=None)

Bases: ClearableFileInput

class music_publisher.admin.MusicPublisherAdmin(model, admin_site)

Bases: ModelAdmin

Parent class to all admin classes.

class music_publisher.admin.ArtistInWorkInline(parent_model, admin_site)

Bases: TabularInline

Inline interface for models.ArtistInWork.

model

alias of ArtistInWork

class music_publisher.admin.RecordingInline(parent_model, admin_site)

Bases: StackedInline

Inline interface for models.Recording, used in WorkAdmin.

get_fieldsets(request, obj=None)

Hook for specifying fieldsets.

model

alias of Recording

class music_publisher.admin.ArtistAdmin(model, admin_site)

Bases: MusicPublisherAdmin

Admin interface for models.Artist.

get_fieldsets(request, obj=None)

Hook for specifying fieldsets.

last_or_band(obj)

Placeholder for models.Artist.last_name.

actions = None
save_model(request, obj, form, *args, **kwargs)

Save, then update last_change of the works whose CWR registration changes due to this change.

get_queryset(request)

Optimized queryset for changelist view.

work_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

recording_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

class music_publisher.admin.LabelAdmin(model, admin_site)

Bases: MusicPublisherAdmin

Admin interface for models.Label.

actions = None
get_fieldsets(request, obj=None)

Hook for specifying fieldsets.

get_queryset(request)

Optimized queryset for changelist view.

commercialrelease_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

libraryrelease_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

recording_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

save_model(request, obj, form, *args, **kwargs)

Save, then update last_change of the corresponding works.

class music_publisher.admin.LibraryAdmin(model, admin_site)

Bases: MusicPublisherAdmin

Admin interface for models.Library.

actions = None
get_queryset(request)

Optimized queryset for changelist view.

libraryrelease_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

work_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

save_model(request, obj, form, *args, **kwargs)

Save, then update last_change of the corresponding works.

class music_publisher.admin.TrackInline(parent_model, admin_site)

Bases: TabularInline

Inline interface for models.Track, used in LibraryReleaseAdmin and CommercialReleaseAdmin.

model

alias of Track

class music_publisher.admin.PlaylistTrackInline(parent_model, admin_site)

Bases: TrackInline

class music_publisher.admin.ReleaseAdmin(model, admin_site)

Bases: MusicPublisherAdmin

Admin interface for models.Release.

actions = None
has_module_permission(request)

Return False

has_add_permission(request)

Return False

has_change_permission(request, obj=None)

Return False

has_delete_permission(request, obj=None)

Return False

class music_publisher.admin.LibraryReleaseAdmin(model, admin_site)

Bases: MusicPublisherAdmin

Admin interface for models.LibraryRelease.

form

alias of LibraryReleaseForm

get_fieldsets(request, obj=None)

Hook for specifying fieldsets.

get_inline_instances(request, obj=None)

Limit inlines in popups.

save_model(request, obj, form, *args, **kwargs)

Save, then update last_change of the corresponding works.

get_queryset(request)

Optimized queryset for changelist view.

work_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

track_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

create_json(request, qs)

Batch action that downloads a JSON file containing library releases.

Returns:

JSON file with selected works

Return type:

JsonResponse

get_actions(request)

Custom action disabling the default delete_selected.

class music_publisher.admin.PlaylistAdmin(model, admin_site)

Bases: MusicPublisherAdmin

Admin interface for models.Playlist.

form

alias of PlaylistForm

get_inline_instances(request, obj=None)

Limit inlines in popups.

get_queryset(request)

Optimized queryset for changelist view.

track_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

class music_publisher.admin.CommercialReleaseAdmin(model, admin_site)

Bases: MusicPublisherAdmin

Admin interface for models.CommercialRelease.

get_fieldsets(request, obj=None)

Hook for specifying fieldsets.

get_inline_instances(request, obj=None)

Limit inlines in popups.

get_queryset(request)

Optimized queryset for changelist view.

track_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

create_json(request, qs)

Batch action that downloads a JSON file containing commercial releases.

Returns:

JSON file with selected commercial releases

Return type:

JsonResponse

get_actions(request)

Custom action disabling the default delete_selected.

class music_publisher.admin.WriterAdmin(model, admin_site)

Bases: MusicPublisherAdmin

Interface for models.Writer.

get_fieldsets(request, obj=None)

Return the fieldsets.

Depending on settings, MR and PR affiliations may not be needed. See WriterAdmin.get_society_list()

actions = None
static get_society_list()

List which society fields are required.

Mechanical and Sync affiliation is not required if writers don’t collect any of it, which is the most usual case.

save_model(request, obj, form, *args, **kwargs)

Perform normal save_model, then update last_change of all connected works.

get_queryset(request)

Optimized queryset for changelist view.

work_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

class music_publisher.admin.AlternateTitleInline(parent_model, admin_site)

Bases: TabularInline

Inline interface for models.AlternateTitle.

model

alias of AlternateTitle

formset

alias of AlternateTitleFormSet

complete_alt_title(obj)

Return the complete title, see models.AlternateTitle.__str__()

class music_publisher.admin.WriterInWorkInline(parent_model, admin_site)

Bases: TabularInline

Inline interface for models.WriterInWork.

model

alias of WriterInWork

formset

alias of WriterInWorkFormSet

class music_publisher.admin.WorkAcknowledgementInline(parent_model, admin_site)

Bases: TabularInline

Inline interface for models.WorkAcknowledgement, used in WorkAdmin.

Note that normal users should only have a ‘view’ permission.

model

alias of WorkAcknowledgement

class music_publisher.admin.WorkAdmin(model, admin_site)

Bases: MusicPublisherAdmin

Admin interface for models.Work.

This is by far the most important part of the interface.

actions

batch actions used: create_cwr(), create_json()

Type:

tuple

inlines

inlines used in change view: AlternateTitleInline, WriterInWorkInline, RecordingInline, ArtistInWorkInline, WorkAcknowledgementInline,

Type:

tuple

form

alias of WorkForm

writer_last_names(obj)

This is a standard way how writers are shown in other apps.

percentage_controlled(obj)

Controlled percentage (sum of relative shares for controlled writers)

Please note that writers in work are already included in the queryset for other reasons, so no overhead except summing.

work_id(obj)

Return models.Work.work_id, make it sortable.

cwr_export_count(obj)

Return the count of CWR exports with the link to the filtered changelist view for CWRExportAdmin.

recording_count(obj)

Return the count of CWR exports with the link to the filtered changelist view for CWRExportAdmin.

get_queryset(request)

Optimized queryset for changelist view.

class InCWRListFilter(request, params, model, model_admin)

Bases: SimpleListFilter

Custom list filter if work is included in any of CWR files.

lookups(request, model_admin)

Simple Yes/No filter

queryset(request, queryset)

Filter if in any of CWR files.

class ACKSocietyListFilter(request, params, model, model_admin)

Bases: SimpleListFilter

Custom list filter of societies from ACK files.

lookups(request, model_admin)

Simple Yes/No filter

queryset(request, queryset)

Filter on society sending ACKs.

class ACKStatusListFilter(request, params, model, model_admin)

Bases: SimpleListFilter

Custom list filter on ACK status.

lookups(request, model_admin)

Simple Yes/No filter

queryset(request, qs)

Filter on ACK status.

class HasISWCListFilter(request, params, model, model_admin)

Bases: SimpleListFilter

Custom list filter on the presence of ISWC.

lookups(request, model_admin)

Simple Yes/No filter

queryset(request, queryset)

Filter on presence of iswc.

class HasRecordingListFilter(request, params, model, model_admin)

Bases: SimpleListFilter

Custom list filter on the presence of recordings.

lookups(request, model_admin)

Simple Yes/No filter

queryset(request, queryset)

Filter on presence of models.Recording.

get_search_results(request, queryset, search_term)

Deal with the situation term is work ID.

save_model(request, obj, form, *args, **kwargs)

Set last_change if the work form has changed.

save_formset(request, form, formset, change)

Set last_change for the work if any of the inline forms has changed.

create_cwr(request, qs)

Batch action that redirects to the add view for CWRExportAdmin with selected works.

create_json(request, qs)

Batch action that downloads a JSON file containing selected works.

Returns:

JSON file with selected works

Return type:

JsonResponse

get_labels_for_csv(works, repeating_column_nr=0, simple=False)

Return the list of labels for the CSV file.

get_rows_for_csv(works)

Return rows for the CSV file, including the header.

create_csv(request, qs)

Batch action that downloads a CSV file containing selected works.

Returns:

JSON file with selected works

Return type:

JsonResponse

get_actions(request)

Custom action disabling the default delete_selected.

get_inline_instances(request, obj=None)

Limit inlines in popups.

class music_publisher.admin.RecordingAdmin(model, admin_site)

Bases: MusicPublisherAdmin

Admin interface for models.Recording.

actions = None
class HasISRCListFilter(request, params, model, model_admin)

Bases: SimpleListFilter

Custom list filter on the presence of ISRC.

lookups(request, model_admin)

Simple Yes/No filter

queryset(request, queryset)

Filter on presence of iswc.

class HasAudioFilter(request, params, model, model_admin)

Bases: SimpleListFilter

Custom list filter on the presence of audio file.

lookups(request, model_admin)

Simple Yes/No filter

queryset(request, queryset)

Filter on presence of iswc.

get_fieldsets(request, obj=None)

Hook for specifying fieldsets.

get_queryset(request)

Optimized query regarding work name

recording_id(obj)

Return models.Recording.recording_id, make it sortable.

title(obj)

Return the recording title, which is not the necessarily the title field.

Link to the work the recording is based on.

Link to the recording artist.

Link to the recording label.

class music_publisher.admin.CWRExportAdmin(model, admin_site)

Bases: ModelAdmin

Admin interface for models.CWRExport.

actions = None
work_count(obj)

Return the work count from the database field, or count them. (dealing with legacy)

get_preview(obj)

Get CWR preview.

If you are using highlighing, then override this method.

Link to the CWR preview.

Link for downloading or creating CWR file.

get_queryset(request)

Optimized query with count of works in the export.

get_readonly_fields(request, obj=None)

Read-only fields differ if CWR has been completed.

get_fields(request, obj=None)

Shown fields differ if CWR has been completed.

has_add_permission(request)

Return false if CWR delivery code is not present.

has_delete_permission(request, obj=None)

If CWR has been created, it can no longer be deleted, as it may have been sent. This may change once the delivery is automated.

has_change_permission(request, obj=None)

If object exists, it can only be edited in changelist.

get_form(request, obj=None, **kwargs)

Set initial values for work IDs.

add_view(request, form_url='', extra_context=None, work_ids=None)

Added work_ids as default for wizard from WorkAdmin.create_cwr().

change_view(request, object_id, form_url='', extra_context=None)

Normal change view with sub-views defined by GET parameters:

Parameters:
  • preview – that returns the preview of CWR file,

  • download – that downloads the CWR file,

  • create_cwr – that creates the CWR file.

save_model() passes the main object, which is needed to fetch CWR from the external service, but only after related objects are saved.

class music_publisher.admin.AdminWithReport(model, admin_site)

Bases: ModelAdmin

The parent class for all admin classes with a report field.

print_report(obj)

Mark report as HTML-safe.

class music_publisher.admin.ACKImportAdmin(model, admin_site)

Bases: AdminWithReport

Admin interface for models.ACKImport.

get_form(request, obj=None, **kwargs)

Returns a custom form for new objects, default one for changes.

get_fields(request, obj=None)

Return different fields for add vs change.

process(request, ack_import, file_content, import_iswcs=False)

Create appropriate WorkAcknowledgement objects, without duplicates.

Big part of this code should be moved to the model, left here because messaging is simpler.

save_model(request, obj, form, change)

Custom save_model, it ignores changes, validates the form for new instances, if valid, it processes the file and, upon success, calls super().save_model.

has_add_permission(request)

Return false if CWR delivery code is not present.

has_delete_permission(request, obj=None, *args, **kwargs)

Deleting ACK imports is a really bad idea.

has_change_permission(request, obj=None)

Deleting this would make no sense, since the data is processed.

get_preview(obj)

Get CWR preview.

If you are using highlighing, then override this method.

Link to CWR ACK preview.

change_view(request, object_id, form_url='', extra_context=None)

Normal change view with a sub-view defined by GET parameters:

Parameters:

preview – that returns the preview of CWR file.

class music_publisher.admin.DataImportAdmin(model, admin_site)

Bases: AdminWithReport

Data import from CSV files.

Only the interface is here, the whole logic is in data_import.

form

alias of DataImportForm

get_fields(request, obj=None)

Return different fields for add vs change.

has_delete_permission(request, obj=None, *args, **kwargs)

Deleting data imports is a really bad idea.

has_change_permission(request, obj=None)

Deleting this would make no sense, since the data is processed.

get_form(request, obj=None, change=False, **kwargs)

Return a Form class for use in the admin add view. This is used by add_view and change_view.

save_model(request, obj, form, change)

Custom save_model, it ignores changes, validates the form for new instances, if valid, it processes the file and, upon success, calls super().save_model.

music_publisher.data_import

All the code related to importing data from external files.

Currently, only works (with writers, artists, library data and ISRCs) are imported. (ISRCs will be used for importing recording data the in future.)

music_publisher.royalty_calculation

This module is about processing royalty statements.

It processes files in the request-response cycle, not in background workers. Therefore, focus is on speed. Nothing is written to the database, and SELECTs are optimised and performed in one batch.

music_publisher.royalty_calculation.get_id_sources()

Yield choices, fixed and societies.

music_publisher.royalty_calculation.get_right_types()

Yield fixed options.

They will be extended with columns in JS and prior to validation.

class music_publisher.royalty_calculation.RoyaltyCalculationForm(*args, **kwargs)

Bases: Form

The form for royalty calculations.

is_valid()

Append additional choices to various fields, prior to the actual validation.

property media

Return all media required to render the widgets on this form.

class music_publisher.royalty_calculation.RoyaltyCalculation(form)

Bases: object

The process of royalty calculation.

property filename

Return the filename of the output file.

property fieldnames

Return the list of field names in the output file.

get_work_ids()

Find work unambiguous work identifiers.

Returns:

set of work identifier from the file

get_work_queryset(work_ids)

Return the appropriate queryset based on work ID source and ids.

Returns:

queryset with models.WriterInWork objects. query_id has the matched field value.

generate_works_dict(qs)

Generate the works cache.

Returns:

dict (works) of lists (writerinwork) of dicts

generate_writer_dict()

Generate the writers cache. :returns: dict (writer) of dicts

get_works_and_writers()

Get work and writer data.

Extract all work IDs, then perform the queries and put them in dictionaries. When the actual file processing happens, no further queries are required.

process_row(row)

Process one incoming row, yield multiple output rows.

property out_file_path

This method creates the output file and outputs the temporary path.

Note that the process happens is several passes.

class music_publisher.royalty_calculation.RoyaltyCalculationView(**kwargs)

Bases: PermissionRequiredMixin, FormView

The view for royalty calculations.

form_class

alias of RoyaltyCalculationForm

render_to_response(context, **response_kwargs)

Prepare the context, required since we use admin template.

dispatch(request, *args, **kwargs)

Royalty processing works only with TemporaryFileUploadHandler.

form_valid(form)

This is where the magic happens.

music_publisher.tests

Tests for music_publisher.

The folder includes these files:

  • CW200001DMP_000.V21 - CWR 2.1 registration file

  • CW200002DMP_0000_V3-0-0.SUB - CWR 3.0 registration file

  • CW200003DMP_0000_V3-0-0.ISR - CWR3.0 ISWC request file

  • CW200001052_DMP.V21 - CWR 2.1 acknowledgement file

  • dataimport.csv - used for data imports

  • royaltystatement.csv - CSV royalty statement

  • royaltystatement_200k_rows.csv - CSV royalty statement with 200.000 rows, used for load testing.

Actual tests are in music_publisher.tests.tests.

music_publisher.tests.tests

Tests for music_publisher.

This software has almost full test coverage. The only exceptions are instances of Exception being caught during data imports. (User idiocy is boundless.)

Most of the tests are functional end-to-end tests. While they test that code works, they don’t always test that it works as expected.

Still, it must be noted that exports are tested against provided templates (made in a different software, not using the same code beyond Python standard library).

More precise tests would be better.

music_publisher.tests.tests.get_data_from_response(response)

Helper for extracting data from HTTP response in a way that can be fed back into POST that works with Django Admin.

class music_publisher.tests.tests.DataImportTest(methodName='runTest')

Bases: TestCase

Functional test for data import from CSV files.

classmethod setUpClass()

Hook method for setting up class fixture before running tests in the class.

test_log()

Test logging during import.

test_unknown_key_exceptions()

Test exceptions not tested in functional tests.

class music_publisher.tests.tests.AdminTest(methodName='runTest')

Bases: TestCase

Functional tests on the interface, and several related unit tests.

Note that tests build one atop another, simulating typical work flow.

classmethod create_original_work()

Create original work, three writers, one controlled, with recording, alternate titles, included in a commercial release.

classmethod create_modified_work()

Create modified work, original writer plus arranger, with recording, alternate titles.

classmethod create_copublished_work()

Create work, two writers, one co-published

classmethod create_duplicate_work()

Create work, two writers, one co-published, duplicate.

classmethod create_writers()

Create four writers with different properties.

classmethod create_cwr2_export()

Create a NWR and a REV CWR2 Export.

classmethod create_cwr3_export()

Create a WRK and an ISR CWR3 Export.

classmethod create_work_acknowledgements()

Create work acknowledgements.

classmethod setUpClass()

Class setup.

Creating users. Creating instances of classes of less importance:

  • label,

  • library,

  • artist,

  • releases,

then calling the methods above.

test_strings()

Test __str__ methods for created objects.

test_unknown_user()

Several fast test to make sure that an unregistered user is blind.

test_super_user()

Testing index for superuser covers all the cases.

test_super_user_with_files()

Testing index for superuser covers all the cases.

test_staff_user()

Test that a staff user can access some urls.

Please note that most of the work is in other tests.

test_staff_user_with_files()

Testing index for superuser covers all the cases.

test_cwr_previews()

Test that CWR preview works.

test_cwr_downloads()

Test that the CWR file can be downloaded.

test_json()

Test that JSON export works.

test_cwr_nwr()

Test that small CWR export is generated synchronously.

test_large_cwr_is_not_generated_synchronously()

CWR exports with 5,000 or more works are saved without CWR text.

Pending CWR exports show Create CWR instead of Download.

Create CWR link generates CWR for a pending export.

test_large_cwr_is_split_into_multiple_exports()

Large CWR requests are split into files of up to 10,000 works.

test_large_cwr_split_keeps_single_file_for_10000_works()

Exactly 10,000 works still produce one CWR file.

test_csv()

Test that CSV export works.

test_label_change()

Test that models.Label objects can be edited.

test_library_change()

Test that models.Library objects can be edited.

test_library_change_2()

Test that models.Library objects can be edited.

test_artist_change()

Test that models.Artist objects can be edited.

test_commercialrelease_change()

Test that models.CommercialRelease can be edited.

test_libraryrelease_change()

Test that models.LibraryRelease can be edited.

test_audit_user()

Test that audit user can see, but not change things.

test_generally_controlled_not_controlled()

Test that a controlled flag must be set for a writer who is generally controlled.

test_missing_capacity()

Test that if controlled flag is set, the capacity must be set as well.

test_controlled_but_no_writer()

Test that a line without a writer can not have controlled set.

test_controlled_but_missing_data()

The requirements for a controlled writer are higher, make sure they are obeyed when setting a writer as controlled.

test_writer_switch()

Just replace one writer with another, just to test last change

test_not_controlled_extra_saan()

SAAN can not be set if a writer is not controlled.

test_not_controlled_extra_fee()

Publisher fee can not be set if a writer is not controlled.

test_bad_alt_title()

Test that alternate title can not have disallowed characters.

test_unallowed_capacity()

Some capacieties are allowed only in modifications.

test_extended_capacity()

At least one of the additional capacieties must be set for modifications.

test_none_controlled()

At least one Writer in Work line must be set as controlled.

test_wrong_sum_of_shares()

Sum of shares must be (roughly) 100%

test_altitle_sufix_too_long()

A suffix plus the base title plus one space in between must be 60 characters or less.

test_ack_import_and_work_filters()

Test acknowledgement import and then filters on the change view, as well as other related views.

These tests must be together, ack import is used in filters.

test_data_import_and_royalty_calculations()

Test data import, ack import and royalty calculations.

This is the normal process, work data is entered, then the registration follows and then it can be processed in royalty statements.

This test also includes load testing, 200.000 rows must be imported in under 10-15 seconds, performed 4 times with different algos and ID types.

test_bad_data_import()

Test bad data import.

test_recording_filters()

Test Work changelist filters.

Test Work search.

test_simple_save()

Test saving changed Work form.

test_create_cwr_wizard()

Test if CWR creation action works as it should.

test_create_cwr_wizard_no_publisher_code()

Publisher code is required for CWR generation, it must fail if attempted otherwise.

class music_publisher.tests.tests.CWRTemplatesTest(methodName='runTest')

Bases: SimpleTestCase

A test related to CWR Templates.

test_templates()

Test CWR 2.1, 2.2 and 3.0 generation with empty values.

class music_publisher.tests.tests.ValidatorsTest(methodName='runTest')

Bases: TestCase

Test all validators.

Note that validators are also validating settings.

class music_publisher.tests.tests.ModelsSimpleTest(methodName='runTest')

Bases: TransactionTestCase

These tests are modifying objects directly.

test_work()

A complex test where a complete Work objects with all related objects is created.

class music_publisher.tests.tests.OtherFunctionalTest(methodName='runTest')

Bases: SimpleTestCase

These tests are testing things not tested otherwise.