-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcision.py
153 lines (130 loc) · 6.02 KB
/
cision.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import requests
from requests import Response
from functools import lru_cache
from datetime import datetime
# TODO: Handle constants
class ImmutableDict(dict):
def __setitem__(self, key, value):
raise TypeError("%r object does not support item assignment" % type(self).__name__)
def __delitem__(self, key):
raise TypeError("%r object does not support item deletion" % type(self).__name__)
def __getattribute__(self, attribute):
if attribute in ('clear', 'update', 'pop', 'popitem', 'setdefault'):
raise AttributeError("%r object has no attribute %r" % (type(self).__name__, attribute))
return dict.__getattribute__(self, attribute)
def __hash__(self):
return hash(tuple(sorted(self.iteritems())))
def fromkeys(self, S, v):
return type(self)(dict(self).fromkeys(S, v))
class ImmutableStr(str):
def __setitem__(self, key, value):
raise TypeError("%r object STRING __setitem__ does not support item assignment" % type(self).__name__)
def __set__(self, key, value):
raise TypeError("%r object STRING __set__ does not support item assignment" % type(self).__name__)
def __delitem__(self, key):
raise TypeError("%r object does not support item deletion" % type(self).__name__)
class ImmutableInt(int):
def __setitem__(self, key, value):
raise TypeError("%r object INT __setitem__ does not support item assignment" % type(self).__name__)
def __set__(self, key, value):
raise TypeError("%r object INT __set__ does not support item assignment" % type(self).__name__)
def __delitem__(self, key):
raise TypeError("%r object INT does not support item deletion" % type(self).__name__)
class CisionService:
id: int
page_size: int = 50
page_index: int = 1
items_per_page: int = 0
language: str = None
types: list = []
categories: list = []
keywords: list = []
regulatory: bool = None
must_have_media: bool = False
query_page: str = 'cb_page'
query_id: str = 'cb_id'
CISION_FEED_URL = 'https://publish.ne.cision.com/papi/NewsFeed/{id}'
CISION_RELEASE_URL = 'http://publish.ne.cision.com/papi/Release/{id}'
DEFAULT_TYPES = ('PRM',)
DEFAULT_PAGE_SIZE = 50
DEFAULT_PAGE_INDEX = 1
DEFAULT_ITEMS_PER_PAGE = 0
VERSION = '1.0.0'
def __init__(self, options: dict):
categories = [c.lower() for c in options.get('categories', [])]
keywords = [k.lower() for k in options.get('keywords', [])]
types = [t.upper() for t in options.get('types', self.DEFAULT_TYPES)]
self.id = options.get('id')
self.page_size = options.get('page_size', self.DEFAULT_PAGE_SIZE)
self.page_index = options.get('page_index', self.DEFAULT_PAGE_INDEX)
self.items_per_page = options.get('items_per_page', self.DEFAULT_ITEMS_PER_PAGE)
self.language = options.get('language', None)
self.types = types
self.categories = categories
self.keywords = keywords
self.regulatory = options.get('regulatory', None)
self.must_have_media = options.get('must_have_media', False)
self.query_page = options.get('query_page', 'cb_page')
self.query_id = options.get('query_id', 'cb_id')
@lru_cache
def get_feed(self) -> list:
params = {
'PageSize': self.page_size,
'PageIndex': self.page_index,
'DetailLevel': 'detail',
'Regulatory': None if self.regulatory == None else f'{self.regulatory}'.lower()
}
response = self.__fetch(self.CISION_FEED_URL.format(id=self.id), params={
k: v for k, v in params.items() if v
})
return self.__handle_feed_response(response.json()) if response.status_code in [200, 201] else []
@lru_cache
def get_feed_item(self, id: str) -> dict:
"""Returns feed item based on its encrypted id.
Arguments:
id -- EncryptedId of the release
"""
response = self.__fetch(self.CISION_RELEASE_URL.format(id=id))
return response.json().get('Release') if response.status_code in [200, 201] else []
@staticmethod
def __transform_items(items: list) -> list:
return [{
'EncryptedId': it.get('EncryptedId'),
'Title': it.get('Title'),
'Intro': it.get('Intro'),
'Body': it.get('Body'),
'Images': it.get('Images'),
'PublishDate': datetime.strptime(it.get('PublishDate')[0:it.get('PublishDate').index('T')], '%Y-%m-%d').date(),
'Files': it.get('Files'),
} for it in items]
@staticmethod
def __fetch(url: str, params: dict = {}) -> Response:
return requests.get(url, params, headers={
b'User-Agent': 'cisionpy/{0}'.format(CisionService.VERSION),
b'Accept-Encoding': 'gzip, deflate',
b'Accept': 'application/json'
})
def __handle_feed_response(self, content: dict) -> dict:
items = content.get('Releases')
for item in items[:]:
if self.must_have_media and not len(item.get('Images')):
print('removing based on media')
items.remove(item)
continue
if item.get('InformationType') not in self.types:
print('removing based on type', item.get('InformationType'))
items.remove(item)
continue
if self.language and item.get('LanguageCode') != self.language:
print('removing basd on language')
items.remove(item)
continue
if len(self.categories) and not bool([c for c in item.get('Categories') if c.get('Name').lower() in self.categories]):
print('removing based on category')
items.remove(item)
continue
if len(self.keywords) and not bool([k for k in item.get('Keywords') if k.lower() in self.keywords]):
print('removing based on keyword')
items.remove(item)
continue
return self.__transform_items(items=items)