Completed
Pull Request — master (#138)
by
unknown
20:13
created

ArticlesField.to_xml()   B

Complexity

Conditions 2

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 2
Metric Value
cc 2
dl 0
loc 27
ccs 14
cts 14
cp 1
crap 2
rs 8.8571
1
# -*- coding: utf-8 -*-
2 10
"""
3
    wechatpy.fields
4
    ~~~~~~~~~~~~~~~~
5
6
    This module defines some useful field types for parse WeChat messages
7
8
    :copyright: (c) 2014 by messense.
9
    :license: MIT, see LICENSE for more details.
10
"""
11 10
from __future__ import absolute_import, unicode_literals
12 10
import time
13 10
from datetime import datetime
14 10
import base64
15 10
import copy
16
17 10
import six
18
19 10
from wechatpy.utils import to_text, to_binary, ObjectDict, timezone
20
21
22 10
default_timezone = timezone('Asia/Shanghai')
23
24
25 10
class FieldDescriptor(object):
26
27 10
    def __init__(self, field):
28 10
        self.field = field
29 10
        self.attr_name = field.name
30
31 10
    def __get__(self, instance, instance_type=None):
32 10
        if instance is not None:
33 10
            value = instance._data.get(self.attr_name)
34 10
            if value is None:
35 10
                value = copy.deepcopy(self.field.default)
36 10
                instance._data[self.attr_name] = value
37 10
            if isinstance(value, dict):
38 10
                value = ObjectDict(value)
39 10
            if value and not isinstance(value, (dict, list, tuple)) and \
40
                    six.callable(self.field.converter):
41 10
                value = self.field.converter(value)
42 10
            return value
43
        return self.field
44
45 10
    def __set__(self, instance, value):
46 10
        instance._data[self.attr_name] = value
47
48
49 10
class BaseField(object):
50 10
    converter = None
51
52 10
    def __init__(self, name, default=None):
53 10
        self.name = name
54 10
        self.default = default
55
56 10
    def to_xml(self, value):
57
        raise NotImplementedError()
58
59
    def __repr__(self):
60
        _repr = '{klass}({name})'.format(
61
            klass=self.__class__.__name__,
62
            name=repr(self.name)
63
        )
64
        if six.PY2:
65
            return to_binary(_repr)
66
        else:
67
            return to_text(_repr)
68
69 10
    def add_to_class(self, klass, name):
70 10
        self.klass = klass
71 10
        klass._fields[name] = self
72 10
        setattr(klass, name, FieldDescriptor(self))
73
74
75 10
class StringField(BaseField):
76
77 10
    def __to_text(self, value):
78 10
        return to_text(value)
79
80 10
    converter = __to_text
81
82 10
    def to_xml(self, value):
83 10
        value = self.converter(value)
84 10
        tpl = '<{name}><![CDATA[{value}]]></{name}>'
85 10
        return tpl.format(name=self.name, value=value)
86
87
88 10
class IntegerField(BaseField):
89 10
    converter = int
90
91 10
    def to_xml(self, value):
92 10
        value = self.converter(value) if value is not None else self.default
93 10
        tpl = '<{name}>{value}</{name}>'
94 10
        return tpl.format(name=self.name, value=value)
95
96
97 10
class DateTimeField(BaseField):
98 10
    def __converter(self, value):
99 10
        v = int(value)
100 10
        return datetime.fromtimestamp(v, tz=default_timezone)
101 10
    converter = __converter
102
103 10
    def to_xml(self, value):
104 10
        value = time.mktime(datetime.timetuple(value))
105 10
        value = int(value)
106 10
        tpl = '<{name}>{value}</{name}>'
107 10
        return tpl.format(name=self.name, value=value)
108
109
110 10
class FloatField(BaseField):
111 10
    converter = float
112
113 10
    def to_xml(self, value):
114 10
        value = self.converter(value) if value is not None else self.default
115 10
        tpl = '<{name}>{value}</{name}>'
116 10
        return tpl.format(name=self.name, value=value)
117
118
119 10
class ImageField(StringField):
120
121 10
    def to_xml(self, value):
122 10
        value = self.converter(value)
123 10
        tpl = """<Image>
124
        <MediaId><![CDATA[{value}]]></MediaId>
125
        </Image>"""
126 10
        return tpl.format(value=value)
127
128
129 10
class VoiceField(StringField):
130
131 10
    def to_xml(self, value):
132 10
        value = self.converter(value)
133 10
        tpl = """<Voice>
134
        <MediaId><![CDATA[{value}]]></MediaId>
135
        </Voice>"""
136 10
        return tpl.format(value=value)
137
138
139 10
class VideoField(StringField):
140
141 10
    def to_xml(self, value):
142 10
        media_id = self.converter(value['media_id'])
143 10
        if 'title' in value:
144 10
            title = self.converter(value['title'])
145 10
        if 'description' in value:
146 10
            description = self.converter(value['description'])
147 10
        tpl = """<Video>
148
        <MediaId><![CDATA[{media_id}]]></MediaId>
149
        <Title><![CDATA[{title}]]></Title>
150
        <Description><![CDATA[{description}]]></Description>
151
        </Video>"""
152 10
        return tpl.format(
153
            media_id=media_id,
154
            title=title,
155
            description=description
156
        )
157
158
159 10
class MusicField(StringField):
160
161 10
    def to_xml(self, value):
162 10
        thumb_media_id = self.converter(value['thumb_media_id'])
163 10
        if 'title' in value:
164 10
            title = self.converter(value['title'])
165 10
        if 'description' in value:
166 10
            description = self.converter(value['description'])
167 10
        if 'music_url' in value:
168 10
            music_url = self.converter(value['music_url'])
169 10
        if 'hq_music_url' in value:
170 10
            hq_music_url = self.converter(value['hq_music_url'])
171 10
        tpl = """<Music>
172
        <ThumbMediaId><![CDATA[{thumb_media_id}]]></ThumbMediaId>
173
        <Title><![CDATA[{title}]]></Title>
174
        <Description><![CDATA[{description}]]></Description>
175
        <MusicUrl><![CDATA[{music_url}]]></MusicUrl>
176
        <HQMusicUrl><![CDATA[{hq_music_url}]]></HQMusicUrl>
177
        </Music>"""
178 10
        return tpl.format(
179
            thumb_media_id=thumb_media_id,
180
            title=title,
181
            description=description,
182
            music_url=music_url,
183
            hq_music_url=hq_music_url
184
        )
185
186
187 10
class ArticlesField(StringField):
188
189 10
    def to_xml(self, articles):
190 10
        article_count = len(articles)
191 10
        items = []
192 10
        for article in articles:
193 10
            title = self.converter(article.get('title', ''))
194 10
            description = self.converter(article.get('description', ''))
195 10
            image = self.converter(article.get('image', ''))
196 10
            url = self.converter(article.get('url', ''))
197 10
            item_tpl = """<item>
198
            <Title><![CDATA[{title}]]></Title>
199
            <Description><![CDATA[{description}]]></Description>
200
            <PicUrl><![CDATA[{image}]]></PicUrl>
201
            <Url><![CDATA[{url}]]></Url>
202
            </item>"""
203 10
            item = item_tpl.format(
204
                title=title,
205
                description=description,
206
                image=image,
207
                url=url
208
            )
209 10
            items.append(item)
210 10
        items_str = '\n'.join(items)
211 10
        tpl = """<ArticleCount>{article_count}</ArticleCount>
212
        <Articles>{items}</Articles>"""
213 10
        return tpl.format(
214
            article_count=article_count,
215
            items=items_str
216
        )
217
218
219 10
class Base64EncodeField(StringField):
220
221 10
    def __base64_encode(self, text):
222 10
        return to_text(base64.b64encode(to_binary(text)))
223
224 10
    converter = __base64_encode
225
226
227 10
class Base64DecodeField(StringField):
228
229 10
    def __base64_decode(self, text):
230 10
        return to_text(base64.b64decode(to_binary(text)))
231
232 10
    converter = __base64_decode
233
234
235 10
class HardwareField(StringField):
236
237 10
    def to_xml(self, value=None):
238
        value = value or {'view': 'myrank', 'action': 'ranklist'}
239
        tpl = """<{name}>
240
        <MessageView><![CDATA[{view}]]></MessageView>
241
        <MessageAction><![CDATA[{action}]]></MessageAction>
242
        </{name}>"""
243
        return tpl.format(
244
            name=self.name,
245
            view=value.get('view'),
246
            action=value.get('action')
247
        )
248