diff --git a/cwr/__init__.py b/cwr/__init__.py index 7134672..a96970f 100644 --- a/cwr/__init__.py +++ b/cwr/__init__.py @@ -7,5 +7,5 @@ :license: MIT, see LICENSE for more details. """ -__version__ = '0.0.15' +__version__ = '0.0.16' __license__ = 'MIT' \ No newline at end of file diff --git a/cwr/acknowledgement.py b/cwr/acknowledgement.py index e56b633..79806f1 100644 --- a/cwr/acknowledgement.py +++ b/cwr/acknowledgement.py @@ -194,6 +194,16 @@ def __init__(self, ack, agr=None, nwr=None, rev=None, exc=None, messages=None): else: self._messages = messages + def __str__(self): + return '%s [agr=%r, nwr=%r, rev=%r, exc=%r, messages=%r]' % ( + 'Acnowledgement Transaction', self._agr, + self._nwr, self._rev, self._exc, self._messages) + + def __repr__(self): + return '(agr=%r, nwr=%r, rev=%r, exc=%r, messages=%r)' % ( + self.__class__.__name__, self._agr, + self._nwr, self._rev, self._exc, self._messages) + @property def ack(self): """ diff --git a/cwr/file.py b/cwr/file.py index 0248480..8a6995b 100644 --- a/cwr/file.py +++ b/cwr/file.py @@ -53,7 +53,7 @@ def __str__(self): def __repr__(self): return '(tag=%r, transmission=%r)' % ( - 'CWRFile', self._tag, + self.__class__.__name__, self._tag, self._transmission) @property @@ -118,10 +118,11 @@ def __str__(self): self._version) def __repr__(self): - return '(year=%s, sequence_n=%r, sender=%r, receiver=%r, version=%r)' % ('FileTag', self._year, - self._sequence_n, - self._sender, self._receiver, - self._version) + return '(year=%s, sequence_n=%r, sender=%r, receiver=%r, version=%r)' % ( + self.__class__.__name__, self._year, + self._sequence_n, + self._sender, self._receiver, + self._version) @property def year(self): diff --git a/cwr/grammar/factory/adapter.py b/cwr/grammar/factory/adapter.py index 932b186..793f7ef 100644 --- a/cwr/grammar/factory/adapter.py +++ b/cwr/grammar/factory/adapter.py @@ -9,7 +9,14 @@ """ CWR fields grammar adapters. -These classes allow the factories to create rules in an homogeneous way. +These classes allow the factories to create rules in an homogeneous way, by setting a basic interface which will wrap +around field rules, giving a basic common method through which rules can be created. + +This interface is the FieldAdapter, having only the get_field method, which will receive a series of parameters, all +of them optional, and generate a field rule from them. The concrete rule will depend on the implementation. + +Additionally, it offers the wrap_as_optional method, which allows setting a field as optional. It is meant to be used +with a field created by the adapter, so it can be overriden for specific fields. """ __author__ = 'Bernardo Martínez Garrido' @@ -19,9 +26,10 @@ class FieldAdapter(object): """ - Instances of this class generate field rules. + Interface for adapting field rules creation to the parser factory requirements. - This serves as an adapter so the different field rules use the same parameters. + This is meant to receive always the same, or similar, groups of values, and then generate a specific field rule + from them. """ __metaclass__ = ABCMeta @@ -36,7 +44,7 @@ def get_field(self, name=None, columns=None, values=None): :param name: the name of the field :param columns: number of columns :param values: allowed values for the field - :return: the rules for the field + :return: the rule for the field """ raise NotImplementedError("The get_field method is not implemented") @@ -114,6 +122,12 @@ def get_field(self, name=None, columns=None, values=None): class NumericAdapter(FieldAdapter): + """ + Creates the grammar for a Numeric (N) field, accepting only the specified number of characters. + + This version only allows integers. + """ + def __init__(self): super(NumericAdapter, self).__init__() @@ -122,6 +136,10 @@ def get_field(self, name=None, columns=None, values=None): class BooleanAdapter(FieldAdapter): + """ + Creates the grammar for a Boolean (B) field, accepting only 'Y' or 'N' + """ + def __init__(self): super(BooleanAdapter, self).__init__() @@ -130,6 +148,10 @@ def get_field(self, name=None, columns=None, values=None): class FlagAdapter(FieldAdapter): + """ + Creates the grammar for a Flag (F) field, accepting only 'Y', 'N' or 'U'. + """ + def __init__(self): super(FlagAdapter, self).__init__() @@ -138,6 +160,10 @@ def get_field(self, name=None, columns=None, values=None): class DateAdapter(FieldAdapter): + """ + Creates the grammar for a Date (D) field, accepting only numbers in a certain pattern. + """ + def __init__(self): super(DateAdapter, self).__init__() @@ -180,6 +206,10 @@ def wrap_as_optional(self, field, name, columns): class TimeAdapter(FieldAdapter): + """ + Creates the grammar for a Time (D) field, accepting only numbers in a certain pattern. + """ + def __init__(self): super(TimeAdapter, self).__init__() @@ -188,6 +218,11 @@ def get_field(self, name=None, columns=None, values=None): class DateTimeAdapter(FieldAdapter): + """ + Creates the grammar for a date and time field, which is a combination of the Date (D) and Time or Duration field (T) + . + """ + def __init__(self): super(DateTimeAdapter, self).__init__() @@ -196,6 +231,12 @@ def get_field(self, name=None, columns=None, values=None): class BlankAdapter(FieldAdapter): + """ + Creates the grammar for a blank field. + + These are for constant empty strings which should be ignored, as they are used just as fillers. + """ + def __init__(self): super(BlankAdapter, self).__init__() @@ -204,14 +245,22 @@ def get_field(self, name=None, columns=None, values=None): class LookupAdapter(FieldAdapter): + """ + Creates the grammar for a Lookup (L) field, accepting only values from a list. + """ + def __init__(self): super(LookupAdapter, self).__init__() def get_field(self, name=None, columns=None, values=None): - return basic.lookup(values, columns, name) + return basic.lookup(values, name) class ISWCAdapter(FieldAdapter): + """ + ISWC field. + """ + def __init__(self): super(ISWCAdapter, self).__init__() @@ -220,6 +269,10 @@ def get_field(self, name=None, columns=None, values=None): class IPIBaseNumberAdapter(FieldAdapter): + """ + IPI Base Number field. + """ + def __init__(self): super(IPIBaseNumberAdapter, self).__init__() @@ -228,6 +281,10 @@ def get_field(self, name=None, columns=None, values=None): class IPINameNumberAdapter(FieldAdapter): + """ + IPI Name Number field. + """ + def __init__(self): super(IPINameNumberAdapter, self).__init__() @@ -236,6 +293,11 @@ def get_field(self, name=None, columns=None, values=None): class PercentageAdapter(FieldAdapter): + """ + Creates the grammar for a Numeric (N) field storing a percentage and accepting only the specified number of + characters. + """ + def __init__(self): super(PercentageAdapter, self).__init__() @@ -249,6 +311,10 @@ def get_field(self, name=None, columns=None, values=None): class EAN13Adapter(FieldAdapter): + """ + Creates the grammar for an EAN 13 code. + """ + def __init__(self): super(EAN13Adapter, self).__init__() @@ -257,6 +323,10 @@ def get_field(self, name=None, columns=None, values=None): class ISRCAdapter(FieldAdapter): + """ + Creates the grammar for an ISRC code. + """ + def __init__(self): super(ISRCAdapter, self).__init__() @@ -265,6 +335,10 @@ def get_field(self, name=None, columns=None, values=None): class VISANAdapter(FieldAdapter): + """ + Creates the grammar for a V-ISAN code. + """ + def __init__(self): super(VISANAdapter, self).__init__() @@ -273,6 +347,10 @@ def get_field(self, name=None, columns=None, values=None): class AudioVisualKeydapter(FieldAdapter): + """ + Creates the grammar for an Audio Visual Key code. + """ + def __init__(self): super(AudioVisualKeydapter, self).__init__() @@ -281,6 +359,10 @@ def get_field(self, name=None, columns=None, values=None): class CharSetAdapter(FieldAdapter): + """ + Character set code field. + """ + def __init__(self): super(CharSetAdapter, self).__init__() @@ -289,6 +371,10 @@ def get_field(self, name=None, columns=None, values=None): class VariableAlphanumAdapter(FieldAdapter): + """ + Creates the grammar for an alphanumeric code where the size ranges between two values. + """ + def __init__(self): super(VariableAlphanumAdapter, self).__init__() @@ -302,6 +388,10 @@ def get_field(self, name=None, columns=None, values=None): class NumericFloatAdapter(FieldAdapter): + """ + Creates the grammar for a Numeric (N) field, accepting only the specified number of characters. + """ + def __init__(self): super(NumericFloatAdapter, self).__init__() diff --git a/cwr/grammar/factory/decorator.py b/cwr/grammar/factory/decorator.py index 165cfe1..b094e3f 100644 --- a/cwr/grammar/factory/decorator.py +++ b/cwr/grammar/factory/decorator.py @@ -9,17 +9,19 @@ """ -Record fields factories. +Decorators for the grammar rules. + +These serve to adapt a basic set of rules for a certain type of work. For example, for converting a group of rules +into the rule for a record. + +This works through the basic interface RuleDecorator, which will receive the rule, and any required additional data, and +return the adapted rule. """ __author__ = 'Bernardo Martínez Garrido' __license__ = 'MIT' __status__ = 'Development' -""" -Configuration classes. -""" - class RuleDecorator(object): __metaclass__ = ABCMeta diff --git a/cwr/grammar/factory/field.py b/cwr/grammar/factory/field.py index 4f03102..459f742 100644 --- a/cwr/grammar/factory/field.py +++ b/cwr/grammar/factory/field.py @@ -13,10 +13,6 @@ __license__ = 'MIT' __status__ = 'Development' -""" -Configuration classes. -""" - class FieldTerminalRuleFactory(TerminalRuleFactory): """ diff --git a/cwr/grammar/field/basic.py b/cwr/grammar/field/basic.py index 1f45bae..090b16d 100644 --- a/cwr/grammar/field/basic.py +++ b/cwr/grammar/field/basic.py @@ -125,8 +125,6 @@ def numeric(columns, name=None): This version only allows integers. - This can be a compulsory field, in which case the zero is disallowed. - :param columns: number of columns for this field :param name: name for the field :return: grammar for the integer numeric field @@ -268,7 +266,7 @@ def _check_above_value_float(string, minimum): def boolean(name=None): """ - Creates the grammar for a Boolean (F) field, accepting only 'Y' or 'N' + Creates the grammar for a Boolean (B) field, accepting only 'Y' or 'N' :param name: name for the field :return: grammar for the flag field @@ -378,8 +376,6 @@ def date(name=None): """ Creates the grammar for a Date (D) field, accepting only numbers in a certain pattern. - The field can be compulsory, in which case the empty date, composed only of zeros, is disallowed. - :param name: name for the field :return: grammar for the date field """ @@ -417,7 +413,7 @@ def date(name=None): def time(name=None): """ - Creates the grammar for a Time (D) field, accepting only numbers in a certain pattern. + Creates the grammar for a Time or Duration (T) field, accepting only numbers in a certain pattern. :param name: name for the field :return: grammar for the date field @@ -449,16 +445,12 @@ def time(name=None): """ -def lookup(values, columns=1, name=None): +def lookup(values, name=None): """ Creates the grammar for a Lookup (L) field, accepting only values from a list. - The 'columns' parameter is used only in the case the field is optional. It will be used to indicate the number - of whitespaces this field can take. - Like in the Alphanumeric field, the result will be stripped of all heading and trailing whitespaces. - :param columns: number of columns, for the case this field is left empty :param name: name for the field :return: grammar for the lookup field """ diff --git a/cwr/grammar/field/filename.py b/cwr/grammar/field/filename.py index a35b9ae..ecdb5ed 100644 --- a/cwr/grammar/field/filename.py +++ b/cwr/grammar/field/filename.py @@ -12,6 +12,15 @@ def alphanum_variable(min, max, name=None): + """ + Creates the grammar for an alphanumeric code where the size ranges between two values. + + :param min: minimum size + :param max: maximum size + :param name: name for the field + :return: grammar for an alphanumeric field of a variable size + """ + if name is None: name = 'Alphanumeric Field' diff --git a/cwr/grammar/field/record.py b/cwr/grammar/field/record.py index 318b4fb..810adc1 100644 --- a/cwr/grammar/field/record.py +++ b/cwr/grammar/field/record.py @@ -35,8 +35,7 @@ def record_type(values): :param values: allowed record type codes :return: grammar for the record type field """ - field = basic.lookup(values, columns=_config.field_size('record', 'record_type'), - name='Record Type (one of ' + str(values) + ')') + field = basic.lookup(values, name='Record Type (one of ' + str(values) + ')') return field.setResultsName('record_type') diff --git a/cwr/grammar/field/special.py b/cwr/grammar/field/special.py index f32facb..96b7739 100644 --- a/cwr/grammar/field/special.py +++ b/cwr/grammar/field/special.py @@ -450,6 +450,15 @@ def _to_avi(parsed): def date_time(name=None): + """ + Creates the grammar for a date and time field, which is a combination of the Date (D) and Time or Duration field (T) + . + + This field requires first a Date, and then a Time, without any space in between. + + :param name: name for the field + :return: grammar for a Date and Time field + """ if name is None: name = 'Date and Time Field' diff --git a/cwr/parser/decoder/cwrjson.py b/cwr/parser/decoder/cwrjson.py new file mode 100644 index 0000000..cf2f063 --- /dev/null +++ b/cwr/parser/decoder/cwrjson.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from cwr.parser.decoder.common import Decoder +from cwr.parser.decoder.dictionary import FileDictionaryDecoder +import json + +""" +Classes for decoding CWR classes from JSON dictionaries. +""" + +__author__ = 'Bernardo Martínez Garrido' +__license__ = 'MIT' +__status__ = 'Development' + + +class JSONDecoder(Decoder): + def __init__(self): + super(JSONDecoder, self).__init__() + self._dict_decoder = FileDictionaryDecoder() + + def decode(self, data): + decoded = json.loads(data) + + return self._dict_decoder.decode(decoded) \ No newline at end of file diff --git a/cwr/parser/decoder/dictionary.py b/cwr/parser/decoder/dictionary.py index 48b340c..5032da6 100644 --- a/cwr/parser/decoder/dictionary.py +++ b/cwr/parser/decoder/dictionary.py @@ -31,6 +31,52 @@ __status__ = 'Development' +class TransactionRecordDictionaryDecoder(Decoder): + def __init__(self): + super(TransactionRecordDictionaryDecoder, self).__init__() + self._decoders = {} + + self._decoders['ACK'] = AcknowledgementDictionaryDecoder() + self._decoders['AGR'] = AgreementDictionaryDecoder() + self._decoders['TER'] = AgreementTerritoryDictionaryDecoder() + self._decoders['ARI'] = AdditionalRelatedInformationDictionaryDecoder() + self._decoders['ALT'] = AlternateTitleDictionaryDecoder() + self._decoders['EWT'] = AuthoredWorkDictionaryDecoder() + self._decoders['VER'] = AuthoredWorkDictionaryDecoder() + self._decoders['COM'] = ComponentDictionaryDecoder() + self._decoders['IPA'] = InterestedPartyForAgreementDictionaryDecoder() + self._decoders['SPT'] = IPTerritoryOfControlDictionaryDecoder() + self._decoders['SWT'] = IPTerritoryOfControlDictionaryDecoder() + self._decoders['IND'] = InstrumentationDetailDictionaryDecoder() + self._decoders['INS'] = InstrumentationSummaryDictionaryDecoder() + self._decoders['MSG'] = MessageDictionaryDecoder() + self._decoders['PER'] = PerformingArtistDictionaryDecoder() + self._decoders['PWR'] = PublisherForWriterDictionaryDecoder() + self._decoders['REC'] = RecordingDetailDictionaryDecoder() + self._decoders['EXC'] = WorkDictionaryDecoder() + self._decoders['ISW'] = WorkDictionaryDecoder() + self._decoders['NWR'] = WorkDictionaryDecoder() + self._decoders['REV'] = WorkDictionaryDecoder() + self._decoders['ORN'] = WorkOriginDictionaryDecoder() + self._decoders['SWR'] = WriterRecordDictionaryDecoder() + self._decoders['OWR'] = WriterRecordDictionaryDecoder() + self._decoders['OWR'] = WriterRecordDictionaryDecoder() + self._decoders['NPA'] = NonRomanAlphabetAgreementPartyDictionaryDecoder() + self._decoders['NOW'] = NonRomanAlphabetOtherWriterDictionaryDecoder() + self._decoders['NPR'] = NonRomanAlphabetPerformanceDataDictionaryDecoder() + self._decoders['NPN'] = NonRomanAlphabetPublisherNameDictionaryDecoder() + self._decoders['NAT'] = NonRomanAlphabetTitleDictionaryDecoder() + self._decoders['NET'] = NonRomanAlphabetWorkDictionaryDecoder() + self._decoders['NCT'] = NonRomanAlphabetWorkDictionaryDecoder() + self._decoders['NVT'] = NonRomanAlphabetWorkDictionaryDecoder() + self._decoders['NWN'] = NonRomanAlphabetWriterNameDictionaryDecoder() + self._decoders['SPU'] = PublisherRecordDictionaryDecoder() + self._decoders['OPU'] = PublisherRecordDictionaryDecoder() + + def decode(self, data): + return self._decoders[data['record_type']].decode(data) + + class AcknowledgementDictionaryDecoder(Decoder): def __init__(self): super(AcknowledgementDictionaryDecoder, self).__init__() @@ -308,20 +354,82 @@ def decode(self, data): media_type=data['media_type']) +class FileDictionaryDecoder(Decoder): + def __init__(self): + super(FileDictionaryDecoder, self).__init__() + + self._tag_decoder = FileTagDictionaryDecoder() + self._transmission_decoder = TransmissionDictionaryDecoder() + + def decode(self, data): + tag = data['tag'] + if isinstance(tag, dict): + tag = self._tag_decoder.decode(tag) + + transmission = data['transmission'] + if isinstance(transmission, dict): + transmission = self._transmission_decoder.decode(transmission) + + return CWRFile(tag, transmission) + + class TransmissionDictionaryDecoder(Decoder): def __init__(self): super(TransmissionDictionaryDecoder, self).__init__() + self._header_decoder = TransmissionHeaderDictionaryDecoder() + self._trailer_decoder = TransmissionTrailerDictionaryDecoder() + self._group_decoder = GroupDictionaryDecoder() + def decode(self, data): - return Transmission(data['transmission_header'], data['transmission_trailer'], data['groups']) + header = data['header'] + if isinstance(header, dict): + header = self._header_decoder.decode(header) + + trailer = data['trailer'] + if isinstance(trailer, dict): + trailer = self._trailer_decoder.decode(trailer) + + groups = [] + if len(data['groups']) > 0: + if isinstance(data['groups'][0], dict): + for group in data['groups']: + groups.append(self._group_decoder.decode(group)) + else: + groups = data['groups'] + + return Transmission(header, trailer, groups) class GroupDictionaryDecoder(Decoder): def __init__(self): super(GroupDictionaryDecoder, self).__init__() + self._header_decoder = GroupHeaderDictionaryDecoder() + self._trailer_decoder = GroupTrailerDictionaryDecoder() + self._transaction_decoder = TransactionRecordDictionaryDecoder() + def decode(self, data): - return Group(data['group_header'], data['group_trailer'], data['transactions']) + header = data['group_header'] + if isinstance(header, dict): + header = self._header_decoder.decode(header) + + trailer = data['group_trailer'] + if isinstance(trailer, dict): + trailer = self._trailer_decoder.decode(trailer) + + transactions = [] + if len(data['transactions']) > 0: + if isinstance(data['transactions'][0][0], dict): + for transaction in data['transactions']: + transaction_records = [] + for record in transaction: + transaction_records.append(self._transaction_decoder.decode(record)) + transactions.append(transaction_records) + else: + transactions = data['transactions'] + + return Group(header, trailer, transactions) class TransmissionHeaderDictionaryDecoder(Decoder): diff --git a/cwr/parser/encoder/cwrjson.py b/cwr/parser/encoder/cwrjson.py index 69d0618..27af342 100644 --- a/cwr/parser/encoder/cwrjson.py +++ b/cwr/parser/encoder/cwrjson.py @@ -7,7 +7,7 @@ """ -Classes for encoding CWR classes into dictionaries. +Classes for encoding CWR classes into JSON dictionaries. It just consists of a single parser, the JSONEncoder, which delegates most of the work to an instance of the CWRDictionaryEncoder. @@ -40,7 +40,30 @@ def encode(self, instance): """ encoded = self._dict_encoder.encode(instance) - return json.dumps(encoded, default=_iso_handler) + try: + result = json.dumps(encoded, ensure_ascii=False, default=_iso_handler, encoding='latin1') + except TypeError: + # TODO: Is this really the best way to handle this? + # For Python 3 + result = json.dumps(encoded, ensure_ascii=False, default=_iso_handler) + + return result + + +def _unicode_handler(obj): + """ + Transforms an unicode string into a UTF-8 equivalent. + + :param obj: object to transform into it's UTF-8 equivalent + :return: the UTF-8 equivalent of the string + """ + if isinstance(obj, str): + result = obj.isoformat() + else: + raise TypeError("Unserializable object {} of type {}".format(obj, + type(obj))) + + return result def _iso_handler(obj): diff --git a/cwr/record.py b/cwr/record.py index c54aa4b..f812f29 100644 --- a/cwr/record.py +++ b/cwr/record.py @@ -103,7 +103,7 @@ def __str__(self): def __repr__(self): return '(record_type=%r, transaction_sequence_n=%r, record_sequence_n=%r)' % ( - 'TransactionRecord', self._record_type, + self.__class__.__name__, self._record_type, self._transaction_sequence_n, self._record_sequence_n) diff --git a/data_commonworks/field_config_common.yml b/data_commonworks/field_config_common.yml index 453e432..47c3fb9 100644 --- a/data_commonworks/field_config_common.yml +++ b/data_commonworks/field_config_common.yml @@ -249,6 +249,12 @@ ip_name: size: 160 name: Interested Party Name +ip_name_ext: + type: alphanum_ext + size: 160 + name: Interested Party Name + results_name: ip_name + ip_writer_first_name: type: alphanum size: 30 @@ -259,6 +265,12 @@ ip_writer_name: size: 160 name: Interested Party Writer First Name +ip_writer_name_ext: + type: alphanum_ext + size: 160 + name: Interested Party Writer First Name + results_name: ip_writer_name + ipi_base_n: type: ipi_base_n size: 13 @@ -366,6 +378,12 @@ performing_artist_first_name_long: name: Performing Artist First Name results_name: performing_artist_first_name +performing_artist_first_name_long_ext: + type: alphanum_ext + size: 160 + name: Performing Artist First Name + results_name: performing_artist_first_name + performing_artist_ipi_base_n: type: ipi_base_n size: 13 @@ -386,6 +404,12 @@ performing_artist_name: size: 160 name: Performing Artist Name +performing_artist_name_ext: + type: alphanum_ext + size: 160 + name: Performing Artist Name + results_name: performing_artist_name + personal_number: type: numeric size: 12 @@ -457,6 +481,12 @@ publisher_name_long: name: Publisher Name results_name: publisher_name +publisher_name_long_ext: + type: alphanum_ext + size: 480 + name: Publisher Name + results_name: publisher_name + publisher_sequence_n: type: numeric size: 2 @@ -578,6 +608,12 @@ title: size: 640 name: Title +title_ext: + type: alphanum_ext + size: 640 + name: Title + results_name: title + total_monetary_value: type: alphanum size: 10 @@ -682,6 +718,12 @@ writer_first_name_long: name: Writer First Name results_name: writer_first_name +writer_first_name_long_ext: + type: alphanum_ext + size: 160 + name: Writer First Name + results_name: writer_first_name + writer_ip_n: type: alphanum size: 9 @@ -698,11 +740,23 @@ writer_last_name_long: name: Writer Last Name results_name: writer_last_name +writer_last_name_long_ext: + type: alphanum_ext + size: 160 + name: Writer Last Name + results_name: writer_last_name + writer_name: type: alphanum size: 160 name: Writer Name +writer_name_ext: + type: alphanum_ext + size: 160 + name: Writer Name + results_name: writer_name + writer_unknown: type: flag size: 1 diff --git a/data_commonworks/record_config_common.yml b/data_commonworks/record_config_common.yml index 1335935..83c847e 100644 --- a/data_commonworks/record_config_common.yml +++ b/data_commonworks/record_config_common.yml @@ -314,9 +314,9 @@ nra_agreement_party: - rule_type: field id: ip_n - rule_type: field - id: ip_name + id: ip_name_ext - rule_type: field - id: ip_writer_name + id: ip_writer_name_ext - rule_type: field id: language_code @@ -328,9 +328,9 @@ nra_other_writer: - group_type: sequence rules: - rule_type: field - id: writer_name + id: writer_name_ext - rule_type: field - id: writer_first_name_long + id: writer_first_name_long_ext - rule_type: field id: language_code - rule_type: field @@ -344,9 +344,9 @@ nra_performance_data: - group_type: sequence rules: - rule_type: field - id: performing_artist_name + id: performing_artist_name_ext - rule_type: field - id: performing_artist_first_name_long + id: performing_artist_first_name_long_ext - rule_type: field id: performing_artist_ipi_name_n - rule_type: field @@ -370,7 +370,7 @@ nra_publisher_name: - rule_type: field id: ip_n - rule_type: field - id: publisher_name_long + id: publisher_name_long_ext - rule_type: field id: language_code @@ -382,7 +382,7 @@ nra_title: - group_type: sequence rules: - rule_type: field - id: title + id: title_ext - rule_type: field id: title_type - rule_type: field @@ -399,7 +399,7 @@ nra_work: - group_type: sequence rules: - rule_type: field - id: title + id: title_ext - rule_type: field id: language_code @@ -413,9 +413,9 @@ nra_writer_name: - rule_type: field id: ip_n - rule_type: field - id: writer_last_name_long + id: writer_last_name_long_ext - rule_type: field - id: writer_first_name_long + id: writer_first_name_long_ext - rule_type: field id: language_code @@ -630,6 +630,7 @@ transmission_header: id: transmission_date - rule_type: field id: character_set + results_name: header transmission_trailer: id: transmission_trailer @@ -644,6 +645,7 @@ transmission_trailer: id: transaction_count - rule_type: field id: record_count + results_name: trailer work: id: work diff --git a/tests/grammar/factory/record/test_nat.py b/tests/grammar/factory/record/test_nat.py index 9755643..5a4c70f 100644 --- a/tests/grammar/factory/record/test_nat.py +++ b/tests/grammar/factory/record/test_nat.py @@ -39,4 +39,21 @@ def test_valid_full(self): self.assertEqual(23, result.record_sequence_n) self.assertEqual('THE TITLE', result.title) self.assertEqual('AT', result.title_type) + self.assertEqual('ES', result.language_code) + + def test_extended_character(self): + """ + Tests that IPA grammar decodes correctly formatted record prefixes. + + This test contains all the optional fields. + """ + record = 'NAT0000123400000023THE TITLE \xc6\x8f ATES' + + result = self.grammar.parseString(record)[0] + + self.assertEqual('NAT', result.record_type) + self.assertEqual(1234, result.transaction_sequence_n) + self.assertEqual(23, result.record_sequence_n) + self.assertEqual('THE TITLE \xc6\x8f', result.title) + self.assertEqual('AT', result.title_type) self.assertEqual('ES', result.language_code) \ No newline at end of file diff --git a/tests/grammar/factory/record/test_now.py b/tests/grammar/factory/record/test_now.py index 6fd1c0f..993c867 100644 --- a/tests/grammar/factory/record/test_now.py +++ b/tests/grammar/factory/record/test_now.py @@ -40,4 +40,22 @@ def test_valid_full(self): self.assertEqual('NAME', result.writer_name) self.assertEqual('FIRST NAME', result.writer_first_name) self.assertEqual('ES', result.language_code) + self.assertEqual(1, result.position) + + def test_extended_character(self): + """ + Tests that IPA grammar decodes correctly formatted record prefixes. + + This test contains all the optional fields. + """ + record = 'NOW0000123400000023NAME \xc6\x8f FIRST NAME \xc6\x8f ES1' + + result = self.grammar.parseString(record)[0] + + self.assertEqual('NOW', result.record_type) + self.assertEqual(1234, result.transaction_sequence_n) + self.assertEqual(23, result.record_sequence_n) + self.assertEqual('NAME \xc6\x8f', result.writer_name) + self.assertEqual('FIRST NAME \xc6\x8f', result.writer_first_name) + self.assertEqual('ES', result.language_code) self.assertEqual(1, result.position) \ No newline at end of file diff --git a/tests/grammar/factory/record/test_npa.py b/tests/grammar/factory/record/test_npa.py index af1e88c..e457eae 100644 --- a/tests/grammar/factory/record/test_npa.py +++ b/tests/grammar/factory/record/test_npa.py @@ -58,4 +58,22 @@ def test_valid_min(self): self.assertEqual('000000000', result.ip_n) self.assertEqual('PARTY NAME', result.ip_name) self.assertEqual('PARTY WRITER NAME', result.ip_writer_name) + self.assertEqual(None, result.language_code) + + def test_extended_character(self): + """ + Tests that IPA grammar decodes correctly formatted record prefixes. + + This test contains none of the optional fields. + """ + record = 'NPA0000123400000023000000000PARTY NAME \xc6\x8f PARTY WRITER NAME \xc6\x8f ' + + result = self.grammar.parseString(record)[0] + + self.assertEqual('NPA', result.record_type) + self.assertEqual(1234, result.transaction_sequence_n) + self.assertEqual(23, result.record_sequence_n) + self.assertEqual('000000000', result.ip_n) + self.assertEqual('PARTY NAME \xc6\x8f', result.ip_name) + self.assertEqual('PARTY WRITER NAME \xc6\x8f', result.ip_writer_name) self.assertEqual(None, result.language_code) \ No newline at end of file diff --git a/tests/grammar/factory/record/test_npn.py b/tests/grammar/factory/record/test_npn.py index 4a0670e..af57d0b 100644 --- a/tests/grammar/factory/record/test_npn.py +++ b/tests/grammar/factory/record/test_npn.py @@ -40,4 +40,22 @@ def test_valid_full(self): self.assertEqual(12, result.publisher_sequence_n) self.assertEqual('A12345678', result.ip_n) self.assertEqual('THE NAME', result.publisher_name) + self.assertEqual('ES', result.language_code) + + def test_extended_character(self): + """ + Tests that IPA grammar decodes correctly formatted record prefixes. + + This test contains all the optional fields. + """ + record = 'NPN000012340000002312A12345678THE NAME \xc6\x8f ES' + + result = self.grammar.parseString(record)[0] + + self.assertEqual('NPN', result.record_type) + self.assertEqual(1234, result.transaction_sequence_n) + self.assertEqual(23, result.record_sequence_n) + self.assertEqual(12, result.publisher_sequence_n) + self.assertEqual('A12345678', result.ip_n) + self.assertEqual('THE NAME \xc6\x8f', result.publisher_name) self.assertEqual('ES', result.language_code) \ No newline at end of file diff --git a/tests/grammar/factory/record/test_npr.py b/tests/grammar/factory/record/test_npr.py index ef72622..1efd3ca 100644 --- a/tests/grammar/factory/record/test_npr.py +++ b/tests/grammar/factory/record/test_npr.py @@ -41,4 +41,27 @@ def test_valid_full(self): self.assertEqual(7, result.performing_artist_ipi_base_n.check_digit) self.assertEqual('ES', result.language_code) self.assertEqual('EN', result.performance_language) + self.assertEqual('CAN', result.performance_dialect) + + def test_extended_character(self): + """ + Tests that IPA grammar decodes correctly formatted record prefixes. + + This test contains all the optional fields. + """ + record = 'NPR0000123400000023NAME \xc6\x8f FIRST NAME \xc6\x8f 00014107338I-000000229-7ESENCAN' + + result = self.grammar.parseString(record)[0] + + self.assertEqual('NPR', result.record_type) + self.assertEqual(1234, result.transaction_sequence_n) + self.assertEqual(23, result.record_sequence_n) + self.assertEqual('NAME \xc6\x8f', result.performing_artist_name) + self.assertEqual('FIRST NAME \xc6\x8f', result.performing_artist_first_name) + self.assertEqual(14107338, result.performing_artist_ipi_name_n) + self.assertEqual('I', result.performing_artist_ipi_base_n.header) + self.assertEqual(229, result.performing_artist_ipi_base_n.id_code) + self.assertEqual(7, result.performing_artist_ipi_base_n.check_digit) + self.assertEqual('ES', result.language_code) + self.assertEqual('EN', result.performance_language) self.assertEqual('CAN', result.performance_dialect) \ No newline at end of file diff --git a/tests/grammar/factory/record/test_nra_work.py b/tests/grammar/factory/record/test_nra_work.py index 7a9dccd..77b13a4 100644 --- a/tests/grammar/factory/record/test_nra_work.py +++ b/tests/grammar/factory/record/test_nra_work.py @@ -37,4 +37,20 @@ def test_valid_full(self): self.assertEqual(1234, result.transaction_sequence_n) self.assertEqual(23, result.record_sequence_n) self.assertEqual('THE TITLE', result.title) + self.assertEqual('ES', result.language_code) + + def test_extended_character(self): + """ + Tests that NWN grammar decodes correctly formatted record prefixes. + + This test contains all the optional fields. + """ + record = 'NCT0000123400000023THE TITLE \xc6\x8f ES' + + result = self.grammar.parseString(record)[0] + + self.assertEqual('NCT', result.record_type) + self.assertEqual(1234, result.transaction_sequence_n) + self.assertEqual(23, result.record_sequence_n) + self.assertEqual('THE TITLE \xc6\x8f', result.title) self.assertEqual('ES', result.language_code) \ No newline at end of file diff --git a/tests/grammar/factory/record/test_nwn.py b/tests/grammar/factory/record/test_nwn.py index 4085921..649d0be 100644 --- a/tests/grammar/factory/record/test_nwn.py +++ b/tests/grammar/factory/record/test_nwn.py @@ -39,4 +39,22 @@ def test_valid_full(self): self.assertEqual('A12345678', result.ip_n) self.assertEqual('LAST NAME', result.writer_last_name) self.assertEqual('FIRST NAME', result.writer_first_name) + self.assertEqual('ES', result.language_code) + + def test_extended_character(self): + """ + Tests that NWN grammar decodes correctly formatted record prefixes. + + This test contains all the optional fields. + """ + record = 'NWN0000123400000023A12345678LAST NAME \xc6\x8f FIRST NAME \xc6\x8f ES' + + result = self.grammar.parseString(record)[0] + + self.assertEqual('NWN', result.record_type) + self.assertEqual(1234, result.transaction_sequence_n) + self.assertEqual(23, result.record_sequence_n) + self.assertEqual('A12345678', result.ip_n) + self.assertEqual('LAST NAME \xc6\x8f', result.writer_last_name) + self.assertEqual('FIRST NAME \xc6\x8f', result.writer_first_name) self.assertEqual('ES', result.language_code) \ No newline at end of file diff --git a/tests/grammar/field/test_lookup.py b/tests/grammar/field/test_lookup.py index 05d07fe..55d0b34 100644 --- a/tests/grammar/field/test_lookup.py +++ b/tests/grammar/field/test_lookup.py @@ -21,7 +21,7 @@ def test_name_default(self): """ Tests that the default field name is correct for optional fields. """ - field = basic.lookup(['AB1', 'CD2', 'EF3'], columns=3) + field = basic.lookup(['AB1', 'CD2', 'EF3']) self.assertEqual('Lookup Field', field.name) @@ -30,7 +30,7 @@ def test_name_set(self): Tests that the given field name is set correctly for optional fields. """ name = "Field Name" - field = basic.lookup(['AB1', 'CD2', 'EF3'], columns=3, name=name) + field = basic.lookup(['AB1', 'CD2', 'EF3'], name=name) self.assertEqual(name, field.name) @@ -38,8 +38,8 @@ def test_name_set_no_changes(self): """ Tests that the field name does not change for creating a new one """ - field1 = basic.lookup(['AB1', 'CD2', 'EF3'], columns=3, name='field1') - field2 = basic.lookup(['AB1', 'CD2', 'EF3'], columns=3, name='field2') + field1 = basic.lookup(['AB1', 'CD2', 'EF3'], name='field1') + field2 = basic.lookup(['AB1', 'CD2', 'EF3'], name='field2') self.assertEqual('field1', field1.name) self.assertEqual('field2', field2.name) @@ -51,7 +51,7 @@ class TestLookupValid(unittest.TestCase): """ def setUp(self): - self.lookup = basic.lookup(['AB1', 'CD2', 'EF3'], columns=3) + self.lookup = basic.lookup(['AB1', 'CD2', 'EF3']) def test_valid(self): """ @@ -63,7 +63,7 @@ def test_valid(self): class TestLookupExceptionCompulsory(unittest.TestCase): def setUp(self): - self.lookup = basic.lookup(['AB1', 'CD2', 'EF3'], columns=3) + self.lookup = basic.lookup(['AB1', 'CD2', 'EF3']) def test_invalid(self): """ diff --git a/tests/parser/dictionary/decoder/control/test_group.py b/tests/parser/dictionary/decoder/control/test_group.py new file mode 100644 index 0000000..edd7287 --- /dev/null +++ b/tests/parser/dictionary/decoder/control/test_group.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- + +import unittest +import datetime + +from cwr.parser.decoder.dictionary import GroupDictionaryDecoder + + +""" +Group from dictionary encoding tests. + +The following cases are tested: +""" + +__author__ = 'Bernardo Martínez Garrido' +__license__ = 'MIT' +__version__ = '0.0.0' +__status__ = 'Development' + + +class TestGroupDictionaryEncoding(unittest.TestCase): + def setUp(self): + self._decoder = GroupDictionaryDecoder() + + def test_encoded(self): + trailer = {} + trailer['record_count'] = 20 + trailer['record_type'] = 'GRT' + trailer['group_id'] = 3 + trailer['transaction_count'] = 15 + + header = {} + header['record_type'] = 'GRH' + header['version_number'] = '02.10' + header['group_id'] = 3 + header['batch_request_id'] = 15 + header['transaction_type'] = 'AGR' + + transactions = [[{'original_group_id': 4, 'creation_date_time': datetime.date(2003, 2, 15), + 'original_transaction_type': 'AGR', 'record_sequence_n': 15, 'record_type': 'ACK', + 'creation_title': 'TITLE', 'original_transaction_sequence_n': 5, 'transaction_status': 'AS', + 'recipient_creation_n': 'B124', 'submitter_creation_n': 'A123', 'transaction_sequence_n': 3, + 'processing_date': datetime.date(2003, 2, 16)}, + {'original_record_sequence_n': 124, 'validation_n': 'AB3', 'message_record_type': 'AGR', + 'message_text': 'THE MESSAGE', 'record_sequence_n': 15, 'record_type': 'MSG', + 'message_level': 'F', 'message_type': 'G', 'transaction_sequence_n': 3}, + {'original_record_sequence_n': 124, 'validation_n': 'AB3', 'message_record_type': 'AGR', + 'message_text': 'THE MESSAGE', 'record_sequence_n': 15, 'record_type': 'MSG', + 'message_level': 'F', 'message_type': 'G', 'transaction_sequence_n': 3}, + {'sales_manufacture_clause': 'M', 'date_of_signature': datetime.date(2003, 2, 17), + 'prior_royalty_start_date': datetime.date(2003, 2, 19), 'advance_given': True, + 'retention_end_date': datetime.date(2003, 2, 18), 'international_standard_code': 'DFG135', + 'prior_royalty_status': 'D', 'agreement_end_date': datetime.date(2003, 2, 16), + 'record_type': 'AGR', 'shares_change': True, 'post_term_collection_status': 'D', + 'agreement_type': 'OS', 'submitter_agreement_n': 'AB12', + 'society_assigned_agreement_n': 'DF35', 'record_sequence_n': 15, + 'agreement_start_date': datetime.date(2003, 2, 15), 'transaction_sequence_n': 3, + 'post_term_collection_end_date': datetime.date(2003, 2, 20), 'number_of_works': 12}]] + + dict = {'group_trailer': trailer, 'transactions': transactions, 'group_header': header} + + record = self._decoder.decode(dict) + + self.assertEqual('GRH', record.group_header.record_type) + self.assertEqual('GRT', record.group_trailer.record_type) + + self.assertEqual('ACK', record.transactions[0][0].record_type) \ No newline at end of file diff --git a/tests/parser/dictionary/decoder/control/test_transmission.py b/tests/parser/dictionary/decoder/control/test_transmission.py new file mode 100644 index 0000000..66a71d3 --- /dev/null +++ b/tests/parser/dictionary/decoder/control/test_transmission.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- + +import unittest +import datetime + +from cwr.parser.decoder.dictionary import TransmissionDictionaryDecoder + + +""" +Dictionary to Message decoding tests. + +The following cases are tested: +""" + +__author__ = 'Bernardo Martínez Garrido' +__license__ = 'MIT' +__version__ = '0.0.0' +__status__ = 'Development' + + +class TestTransmissionDecoder(unittest.TestCase): + def setUp(self): + self._decoder = TransmissionDictionaryDecoder() + + def test_encoded(self): + header = {} + header['record_type'] = 'HDR' + header['sender_id'] = 'SND123' + header['sender_name'] = 'THE SENDER' + header['sender_type'] = 'SO' + header['creation_date_time'] = datetime.datetime.strptime('20030216', '%Y%m%d').date() + header['transmission_date'] = datetime.datetime.strptime('20030217', '%Y%m%d').date() + header['edi_standard'] = '01.10' + header['character_set'] = 'ASCII' + + trailer = {} + trailer['record_type'] = 'TRL' + trailer['group_count'] = 11 + trailer['transaction_count'] = 22 + trailer['record_count'] = 33 + + trailer_group = {} + trailer_group['record_count'] = 20 + trailer_group['record_type'] = 'GRT' + trailer_group['group_id'] = 3 + trailer_group['transaction_count'] = 15 + + header_group = {} + header_group['record_type'] = 'GRH' + header_group['version_number'] = '02.10' + header_group['group_id'] = 3 + header_group['batch_request_id'] = 15 + header_group['transaction_type'] = 'AGR' + + transactions = [[{'original_group_id': 4, 'creation_date_time': datetime.date(2003, 2, 15), + 'original_transaction_type': 'AGR', 'record_sequence_n': 15, 'record_type': 'ACK', + 'creation_title': 'TITLE', 'original_transaction_sequence_n': 5, 'transaction_status': 'AS', + 'recipient_creation_n': 'B124', 'submitter_creation_n': 'A123', 'transaction_sequence_n': 3, + 'processing_date': datetime.date(2003, 2, 16)}, + {'original_record_sequence_n': 124, 'validation_n': 'AB3', 'message_record_type': 'AGR', + 'message_text': 'THE MESSAGE', 'record_sequence_n': 15, 'record_type': 'MSG', + 'message_level': 'F', 'message_type': 'G', 'transaction_sequence_n': 3}, + {'original_record_sequence_n': 124, 'validation_n': 'AB3', 'message_record_type': 'AGR', + 'message_text': 'THE MESSAGE', 'record_sequence_n': 15, 'record_type': 'MSG', + 'message_level': 'F', 'message_type': 'G', 'transaction_sequence_n': 3}, + {'sales_manufacture_clause': 'M', 'date_of_signature': datetime.date(2003, 2, 17), + 'prior_royalty_start_date': datetime.date(2003, 2, 19), 'advance_given': True, + 'retention_end_date': datetime.date(2003, 2, 18), 'international_standard_code': 'DFG135', + 'prior_royalty_status': 'D', 'agreement_end_date': datetime.date(2003, 2, 16), + 'record_type': 'AGR', 'shares_change': True, 'post_term_collection_status': 'D', + 'agreement_type': 'OS', 'submitter_agreement_n': 'AB12', + 'society_assigned_agreement_n': 'DF35', 'record_sequence_n': 15, + 'agreement_start_date': datetime.date(2003, 2, 15), 'transaction_sequence_n': 3, + 'post_term_collection_end_date': datetime.date(2003, 2, 20), 'number_of_works': 12}]] + + groups = [{'group_header': header_group, 'transactions': transactions, 'group_trailer': trailer_group}] + + transmission = {'header': header, 'groups': groups, 'trailer': trailer} + + record = self._decoder.decode(transmission) + + self.assertEqual('HDR', record.header.record_type) + self.assertEqual('TRL', record.trailer.record_type) + + self.assertEqual(1, len(record.groups)) + + group = record.groups[0] + + self.assertEqual('GRH', group.group_header.record_type) + self.assertEqual('GRT', group.group_trailer.record_type) + + self.assertEqual('ACK', group.transactions[0][0].record_type) \ No newline at end of file diff --git a/tests/parser/dictionary/decoder/file/__init__.py b/tests/parser/dictionary/decoder/file/__init__.py new file mode 100644 index 0000000..7e44f26 --- /dev/null +++ b/tests/parser/dictionary/decoder/file/__init__.py @@ -0,0 +1 @@ +__author__ = 'Bernardo' diff --git a/tests/parser/dictionary/decoder/file/test_file.py b/tests/parser/dictionary/decoder/file/test_file.py new file mode 100644 index 0000000..1867ec0 --- /dev/null +++ b/tests/parser/dictionary/decoder/file/test_file.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- + +import unittest +import datetime + +from cwr.parser.decoder.dictionary import FileDictionaryDecoder + + +""" +Group Header to dictionary encoding tests. + +The following cases are tested: +""" + +__author__ = 'Bernardo Martínez Garrido' +__license__ = 'MIT' +__version__ = '0.0.0' +__status__ = 'Development' + + +class TestFileDictionaryEncoding(unittest.TestCase): + def setUp(self): + self._decoder = FileDictionaryDecoder() + + def test_encoded(self): + tag = {} + tag['year'] = 2015 + tag['sequence_n'] = 123 + tag['sender'] = 'SND' + tag['receiver'] = 'RCV' + tag['version'] = 2.1 + + transmission = _get_transmission() + + file = {} + file['tag'] = tag + file['transmission'] = transmission + + data = self._decoder.decode(file) + + tag = data.tag + + self.assertEqual(2015, tag.year) + self.assertEqual(123, tag.sequence_n) + self.assertEqual('SND', tag.sender) + self.assertEqual('RCV', tag.receiver) + self.assertEqual(2.1, tag.version) + + record = data.transmission + + self.assertEqual('HDR', record.header.record_type) + self.assertEqual('TRL', record.trailer.record_type) + + self.assertEqual(1, len(record.groups)) + + group = record.groups[0] + + self.assertEqual('GRH', group.group_header.record_type) + self.assertEqual('GRT', group.group_trailer.record_type) + + self.assertEqual('ACK', group.transactions[0][0].record_type) + + +def _get_transmission(): + header = {} + header['record_type'] = 'HDR' + header['sender_id'] = 'SND123' + header['sender_name'] = 'THE SENDER' + header['sender_type'] = 'SO' + header['creation_date_time'] = datetime.datetime.strptime('20030216', '%Y%m%d').date() + header['transmission_date'] = datetime.datetime.strptime('20030217', '%Y%m%d').date() + header['edi_standard'] = '01.10' + header['character_set'] = 'ASCII' + + trailer = {} + trailer['record_type'] = 'TRL' + trailer['group_count'] = 11 + trailer['transaction_count'] = 22 + trailer['record_count'] = 33 + + trailer_group = {} + trailer_group['record_count'] = 20 + trailer_group['record_type'] = 'GRT' + trailer_group['group_id'] = 3 + trailer_group['transaction_count'] = 15 + + header_group = {} + header_group['record_type'] = 'GRH' + header_group['version_number'] = '02.10' + header_group['group_id'] = 3 + header_group['batch_request_id'] = 15 + header_group['transaction_type'] = 'AGR' + + transactions = [[{'original_group_id': 4, 'creation_date_time': datetime.date(2003, 2, 15), + 'original_transaction_type': 'AGR', 'record_sequence_n': 15, 'record_type': 'ACK', + 'creation_title': 'TITLE', 'original_transaction_sequence_n': 5, 'transaction_status': 'AS', + 'recipient_creation_n': 'B124', 'submitter_creation_n': 'A123', 'transaction_sequence_n': 3, + 'processing_date': datetime.date(2003, 2, 16)}, + {'original_record_sequence_n': 124, 'validation_n': 'AB3', 'message_record_type': 'AGR', + 'message_text': 'THE MESSAGE', 'record_sequence_n': 15, 'record_type': 'MSG', + 'message_level': 'F', 'message_type': 'G', 'transaction_sequence_n': 3}, + {'original_record_sequence_n': 124, 'validation_n': 'AB3', 'message_record_type': 'AGR', + 'message_text': 'THE MESSAGE', 'record_sequence_n': 15, 'record_type': 'MSG', + 'message_level': 'F', 'message_type': 'G', 'transaction_sequence_n': 3}, + {'sales_manufacture_clause': 'M', 'date_of_signature': datetime.date(2003, 2, 17), + 'prior_royalty_start_date': datetime.date(2003, 2, 19), 'advance_given': True, + 'retention_end_date': datetime.date(2003, 2, 18), 'international_standard_code': 'DFG135', + 'prior_royalty_status': 'D', 'agreement_end_date': datetime.date(2003, 2, 16), + 'record_type': 'AGR', 'shares_change': True, 'post_term_collection_status': 'D', + 'agreement_type': 'OS', 'submitter_agreement_n': 'AB12', + 'society_assigned_agreement_n': 'DF35', 'record_sequence_n': 15, + 'agreement_start_date': datetime.date(2003, 2, 15), 'transaction_sequence_n': 3, + 'post_term_collection_end_date': datetime.date(2003, 2, 20), 'number_of_works': 12}]] + + groups = [{'group_header': header_group, 'transactions': transactions, 'group_trailer': trailer_group}] + + transmission = {'header': header, 'groups': groups, 'trailer': trailer} + + return transmission \ No newline at end of file diff --git a/tests/parser/dictionary/decoder/file/test_file_tag.py b/tests/parser/dictionary/decoder/file/test_file_tag.py new file mode 100644 index 0000000..6e3ddf3 --- /dev/null +++ b/tests/parser/dictionary/decoder/file/test_file_tag.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +import unittest + +from cwr.parser.decoder.dictionary import FileTagDictionaryDecoder + + +""" +Group Header to dictionary encoding tests. + +The following cases are tested: +""" + +__author__ = 'Bernardo Martínez Garrido' +__license__ = 'MIT' +__version__ = '0.0.0' +__status__ = 'Development' + + +class TestFileTagDictionaryDecoding(unittest.TestCase): + def setUp(self): + self._decoder = FileTagDictionaryDecoder() + + def test_encoded(self): + data = {} + + data['year'] = 2015 + data['sequence_n'] = 123 + data['sender'] = 'SND' + data['receiver'] = 'RCV' + data['version'] = 2.1 + + tag = self._decoder.decode(data) + + self.assertEqual(2015, tag.year) + self.assertEqual(123, tag.sequence_n) + self.assertEqual('SND', tag.sender) + self.assertEqual('RCV', tag.receiver) + self.assertEqual(2.1, tag.version) \ No newline at end of file diff --git a/tests/parser/dictionary/encoder/control/test_group.py b/tests/parser/dictionary/encoder/control/test_group.py index 940211a..b449e71 100644 --- a/tests/parser/dictionary/encoder/control/test_group.py +++ b/tests/parser/dictionary/encoder/control/test_group.py @@ -84,7 +84,7 @@ def _get_message(self): message_record_type='AGR') def _get_agreement(self): - return AgreementRecord(record_type='ACK', + return AgreementRecord(record_type='AGR', transaction_sequence_n=3, record_sequence_n=15, submitter_agreement_n='AB12', diff --git a/tests/parser/dictionary/encoder/file/test_file_tag.py b/tests/parser/dictionary/encoder/file/test_file_tag.py index fe0fd74..2198b57 100644 --- a/tests/parser/dictionary/encoder/file/test_file_tag.py +++ b/tests/parser/dictionary/encoder/file/test_file_tag.py @@ -18,7 +18,7 @@ __status__ = 'Development' -class TestFileDictionaryEncoding(unittest.TestCase): +class TestFileTagDictionaryEncoding(unittest.TestCase): def setUp(self): self._encoder = CWRDictionaryEncoder() diff --git a/tests/parser/json/encoder/record/test_alternate_title.py b/tests/parser/json/encoder/record/test_alternate_title.py index 230a7c9..e6fac39 100644 --- a/tests/parser/json/encoder/record/test_alternate_title.py +++ b/tests/parser/json/encoder/record/test_alternate_title.py @@ -40,4 +40,24 @@ def test_encoded(self): self.assertEqual(15, encoded['record_sequence_n']) self.assertEqual('ALTERNATE', encoded['alternate_title']) self.assertEqual('FT', encoded['title_type']) + self.assertEqual('ES', encoded['language_code']) + + def test_extended_character(self): + data = AlternateTitleRecord(record_type='SWR', + transaction_sequence_n=3, + record_sequence_n=15, + alternate_title='PA\xc6\x8f', + title_type='FT', + language_code='ES') + + encoded = self._encoder.encode(data) + + encoded = json.loads(encoded) + + self.assertEqual('SWR', encoded['record_type']) + self.assertEqual(3, encoded['transaction_sequence_n']) + self.assertEqual(15, encoded['record_sequence_n']) + # TODO: Fix this test check for Python 2 and 3 + # self.assertEqual(unicode('PA\xc6\x8f', 'latin1'), encoded['alternate_title']) + self.assertEqual('FT', encoded['title_type']) self.assertEqual('ES', encoded['language_code']) \ No newline at end of file