leadguru-jobs 0.639.0__py3-none-any.whl → 0.641.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {leadguru_jobs-0.639.0.dist-info → leadguru_jobs-0.641.0.dist-info}/METADATA +1 -1
- leadguru_jobs-0.641.0.dist-info/RECORD +109 -0
- lgt_jobs/lgt_data/models/boards/__init__.py +0 -0
- lgt_jobs/lgt_data/models/boards/board.py +39 -0
- lgt_jobs/lgt_data/models/boards/status.py +14 -0
- lgt_jobs/lgt_data/models/bots/__init__.py +0 -0
- lgt_jobs/lgt_data/models/bots/base_bot.py +44 -0
- lgt_jobs/lgt_data/models/bots/bot_info.py +33 -0
- lgt_jobs/lgt_data/models/bots/dedicated_bot.py +90 -0
- lgt_jobs/lgt_data/models/chat/__init__.py +0 -0
- lgt_jobs/lgt_data/models/chat/file.py +8 -0
- lgt_jobs/lgt_data/models/chat/grouped_messages.py +17 -0
- lgt_jobs/lgt_data/models/chat/message.py +85 -0
- lgt_jobs/lgt_data/models/chat/request.py +15 -0
- lgt_jobs/lgt_data/models/chat/scheduled_message.py +14 -0
- lgt_jobs/lgt_data/models/contacts/__init__.py +0 -0
- lgt_jobs/lgt_data/models/contacts/contact.py +22 -0
- lgt_jobs/lgt_data/models/external/__init__.py +0 -0
- lgt_jobs/lgt_data/models/external/cloud/__init__.py +0 -0
- lgt_jobs/lgt_data/models/external/cloud/file.py +18 -0
- lgt_jobs/lgt_data/models/external/discord/__init__.py +0 -0
- lgt_jobs/lgt_data/models/external/discord/user.py +45 -0
- lgt_jobs/lgt_data/models/external/slack/__init__.py +0 -0
- lgt_jobs/lgt_data/models/external/slack/timezone.py +8 -0
- lgt_jobs/lgt_data/models/external/slack/user.py +43 -0
- lgt_jobs/lgt_data/models/external/user_workspace.py +32 -0
- lgt_jobs/lgt_data/models/leads/__init__.py +0 -0
- lgt_jobs/lgt_data/models/leads/config.py +13 -0
- lgt_jobs/lgt_data/models/leads/extended_lead.py +46 -0
- lgt_jobs/lgt_data/models/leads/lead.py +48 -0
- lgt_jobs/lgt_data/models/leads/message.py +51 -0
- lgt_jobs/lgt_data/models/notifications/__init__.py +0 -0
- lgt_jobs/lgt_data/models/notifications/notification.py +248 -0
- lgt_jobs/lgt_data/models/notifications/notification_settings.py +52 -0
- lgt_jobs/lgt_data/models/people/__init__.py +0 -0
- lgt_jobs/lgt_data/models/people/people.py +75 -0
- lgt_jobs/lgt_data/models/people/profile.py +13 -0
- lgt_jobs/lgt_data/models/post/__init__.py +0 -0
- lgt_jobs/lgt_data/models/post/message.py +18 -0
- lgt_jobs/lgt_data/models/post/post.py +15 -0
- lgt_jobs/lgt_data/models/templates/__init__.py +0 -0
- lgt_jobs/lgt_data/models/templates/template.py +12 -0
- lgt_jobs/lgt_data/models/user/__init__.py +0 -0
- lgt_jobs/lgt_data/models/user/feature.py +10 -0
- lgt_jobs/lgt_data/models/user/general_settings.py +9 -0
- lgt_jobs/lgt_data/models/user/reset_password.py +7 -0
- lgt_jobs/lgt_data/models/user/subscription.py +22 -0
- lgt_jobs/lgt_data/models/user/user.py +95 -0
- lgt_jobs/lgt_data/models/user/user_follow_ups.py +20 -0
- lgt_jobs/lgt_data/models/user/user_page.py +19 -0
- lgt_jobs/lgt_data/models/user/verification.py +12 -0
- lgt_jobs/lgt_data/models/user_leads/__init__.py +0 -0
- lgt_jobs/lgt_data/models/user_leads/extended_user_lead.py +41 -0
- lgt_jobs/lgt_data/models/user_leads/user_lead.py +33 -0
- lgt_jobs/lgt_data/repositories/__init__.py +0 -0
- lgt_jobs/lgt_data/repositories/post/__init__.py +0 -0
- lgt_jobs/lgt_data/repositories/post/posts.py +28 -0
- leadguru_jobs-0.639.0.dist-info/RECORD +0 -54
- {leadguru_jobs-0.639.0.dist-info → leadguru_jobs-0.641.0.dist-info}/WHEEL +0 -0
- {leadguru_jobs-0.639.0.dist-info → leadguru_jobs-0.641.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
class UserWorkspace:
|
2
|
+
pass
|
3
|
+
|
4
|
+
def __init__(self):
|
5
|
+
super().__init__()
|
6
|
+
self.id = ''
|
7
|
+
self.name = ''
|
8
|
+
self.url = ''
|
9
|
+
self.domain = ''
|
10
|
+
self.active_users = ''
|
11
|
+
self.profile_photos = []
|
12
|
+
self.associated_user = ''
|
13
|
+
self.magic_login_url = ''
|
14
|
+
self.magic_login_code = ''
|
15
|
+
self.user_email = ''
|
16
|
+
self.user_type = ''
|
17
|
+
self.variant = ''
|
18
|
+
self.token = ''
|
19
|
+
self.icon = ''
|
20
|
+
self.two_factor_required = False
|
21
|
+
|
22
|
+
@classmethod
|
23
|
+
def from_dic(cls, dic: dict):
|
24
|
+
if not dic:
|
25
|
+
return None
|
26
|
+
|
27
|
+
model: UserWorkspace = cls()
|
28
|
+
for k, v in dic.items():
|
29
|
+
setattr(model, k, v)
|
30
|
+
|
31
|
+
model.icon = dic.get('icon_88', "")
|
32
|
+
return model
|
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from lgt_jobs.lgt_data.models.base import DictionaryModel
|
2
|
+
|
3
|
+
|
4
|
+
class BaseConfig(DictionaryModel):
|
5
|
+
def __init__(self):
|
6
|
+
self.owner = None
|
7
|
+
self.id = None
|
8
|
+
|
9
|
+
|
10
|
+
class Config(BaseConfig):
|
11
|
+
def __init__(self):
|
12
|
+
super().__init__()
|
13
|
+
self.name = None
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
3
|
+
from lgt_jobs.lgt_data.models.bots.bot_info import BotInfo
|
4
|
+
from lgt_jobs.lgt_data.models.chat.message import ChatMessage
|
5
|
+
from lgt_jobs.lgt_data.models.contacts.contact import UserContact
|
6
|
+
from lgt_jobs.lgt_data.models.leads.lead import LeadModel
|
7
|
+
from lgt_jobs.lgt_data.models.people.people import SlackMemberInformation
|
8
|
+
from lgt_jobs.lgt_data.models.user_leads.user_lead import UserLeadModel
|
9
|
+
|
10
|
+
|
11
|
+
class ExtendedLeadModel(LeadModel):
|
12
|
+
def __init__(self):
|
13
|
+
super().__init__()
|
14
|
+
self.previous_publications = []
|
15
|
+
self.last_conversation: List[ChatMessage] = []
|
16
|
+
self.contact: SlackMemberInformation | None = None
|
17
|
+
self.deleted = False
|
18
|
+
self.user_lead: UserLeadModel | None = None
|
19
|
+
self.dedicated: bool = False
|
20
|
+
self.bots: List[BotInfo] = []
|
21
|
+
self.user_contact: UserContact | None = None
|
22
|
+
self.paid: bool = False
|
23
|
+
self.hidden_by_user: bool = False
|
24
|
+
|
25
|
+
@classmethod
|
26
|
+
def from_dic(cls, dic: dict):
|
27
|
+
if not dic:
|
28
|
+
return None
|
29
|
+
|
30
|
+
result: ExtendedLeadModel | None = LeadModel.from_dic(dic)
|
31
|
+
if not result:
|
32
|
+
return None
|
33
|
+
|
34
|
+
result.contact = SlackMemberInformation.from_dic(dic.get('contact'))
|
35
|
+
result.user_contact = UserContact.from_dic(dic.get('user_contact'))
|
36
|
+
result.previous_publications = [LeadModel.from_dic(lead) for lead in dic.get('previous_publications', [])]
|
37
|
+
result.user_lead = UserLeadModel.from_dic(dic.get('user_lead'))
|
38
|
+
result.last_conversation = [ChatMessage.from_dic(message) for message in dic.get('last_conversation', [])]
|
39
|
+
result.bots = [BotInfo.from_dic(bot) for bot in dic.get('bots', [])]
|
40
|
+
return result
|
41
|
+
|
42
|
+
def to_csv(self, board_name: str) -> List[str]:
|
43
|
+
return [self.message.source, self.contact.real_name, self.contact.title, self.contact.email,
|
44
|
+
self.notes, board_name, self.status,
|
45
|
+
self.followup_date.strftime("%d.%m.%Y %H:%M") if self.followup_date else "",
|
46
|
+
self.message.message.replace('\n', ' ').strip()]
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import copy
|
2
|
+
from datetime import datetime
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from lgt_jobs.lgt_data.models.base import BaseModel
|
6
|
+
from lgt_jobs.lgt_data.models.leads.message import MessageModel
|
7
|
+
|
8
|
+
|
9
|
+
class LeadModel(BaseModel):
|
10
|
+
def __init__(self):
|
11
|
+
super().__init__()
|
12
|
+
self.status = ''
|
13
|
+
self.notes = ''
|
14
|
+
self.archived = False
|
15
|
+
self.message: Optional[MessageModel] = None
|
16
|
+
self.hidden = False
|
17
|
+
self.followup_date = None
|
18
|
+
self.score = 0
|
19
|
+
self.board_id = None
|
20
|
+
self.linkedin_urls = []
|
21
|
+
self.likes = 0
|
22
|
+
self.reactions = 0
|
23
|
+
self.replies = []
|
24
|
+
self.last_action_at: Optional[datetime] = None
|
25
|
+
self.slack_channel = None
|
26
|
+
|
27
|
+
@classmethod
|
28
|
+
def from_dic(cls, dic: dict):
|
29
|
+
if not dic:
|
30
|
+
return None
|
31
|
+
|
32
|
+
model: LeadModel = cls()
|
33
|
+
for k, v in dic.items():
|
34
|
+
setattr(model, k, v)
|
35
|
+
|
36
|
+
if dic.get('message'):
|
37
|
+
model.message = MessageModel.from_dic(dic['message'])
|
38
|
+
|
39
|
+
if not model.last_action_at:
|
40
|
+
model.last_action_at = model.created_at
|
41
|
+
|
42
|
+
return model
|
43
|
+
|
44
|
+
def to_dic(self):
|
45
|
+
result = copy.deepcopy(self.__dict__)
|
46
|
+
result["message"] = self.message.to_dic()
|
47
|
+
result['archived'] = self.archived
|
48
|
+
return result
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import copy
|
2
|
+
import json
|
3
|
+
from typing import List
|
4
|
+
|
5
|
+
from lgt_jobs.lgt_data.models.bots.base_bot import Source
|
6
|
+
from lgt_jobs.lgt_data.models.leads.config import BaseConfig
|
7
|
+
|
8
|
+
|
9
|
+
class MessageModel:
|
10
|
+
pass
|
11
|
+
|
12
|
+
def __init__(self):
|
13
|
+
self.message_id = None
|
14
|
+
self.channel_id = None
|
15
|
+
self.channel_name = None
|
16
|
+
self.message = None
|
17
|
+
self.name = None
|
18
|
+
self.sender_id = None
|
19
|
+
self.source: Source | None = None
|
20
|
+
self.companies: List[str] = list()
|
21
|
+
self.technologies: List[str] = list()
|
22
|
+
self.locations: List[str] = list()
|
23
|
+
self.configs: List[BaseConfig] = list()
|
24
|
+
self.attachments: List[dict] = []
|
25
|
+
self.timestamp = None
|
26
|
+
self.tags: List[str] = []
|
27
|
+
|
28
|
+
@classmethod
|
29
|
+
def from_dic(cls, dic: dict):
|
30
|
+
if not dic:
|
31
|
+
return None
|
32
|
+
if isinstance(dic.get('attachments'), str):
|
33
|
+
dic['attachments'] = json.loads(dic['attachments'])
|
34
|
+
|
35
|
+
model: MessageModel = cls()
|
36
|
+
for k, v in dic.items():
|
37
|
+
setattr(model, k, v)
|
38
|
+
|
39
|
+
model.source = Source.from_dic(dic.get("source"))
|
40
|
+
model.configs = [BaseConfig.from_dic(doc) for doc in dic.get("configs", [])]
|
41
|
+
return model
|
42
|
+
|
43
|
+
def to_dic(self):
|
44
|
+
result = copy.deepcopy(self.__dict__)
|
45
|
+
|
46
|
+
if result.get('source'):
|
47
|
+
result['source'] = Source.to_dic(result.get('source'))
|
48
|
+
if result.get('configs'):
|
49
|
+
result['configs'] = [BaseConfig.to_dic(config) for config in result.get('configs')]
|
50
|
+
return result
|
51
|
+
|
File without changes
|
@@ -0,0 +1,248 @@
|
|
1
|
+
from datetime import datetime, UTC, timedelta
|
2
|
+
from lgt_jobs.env import portal_url
|
3
|
+
from lgt_jobs.lgt_data.enums import NotificationType
|
4
|
+
from lgt_jobs.lgt_data.models.base import DictionaryModel
|
5
|
+
from lgt_jobs.lgt_data.models.bots.dedicated_bot import DedicatedBotModel
|
6
|
+
from lgt_jobs.lgt_data.models.post.post import Post
|
7
|
+
from lgt_jobs.lgt_data.models.user_leads.extended_user_lead import ExtendedUserLeadModel
|
8
|
+
from lgt_jobs.lgt_data.models.user_leads.user_lead import UserLeadModel
|
9
|
+
|
10
|
+
|
11
|
+
class Notification(DictionaryModel):
|
12
|
+
def __init__(self):
|
13
|
+
self.enabled: bool = True
|
14
|
+
self.type: NotificationType = NotificationType.INSTANTLY
|
15
|
+
self.day: int | None = None
|
16
|
+
self.hour: int | None = None
|
17
|
+
self.minute: int | None = None
|
18
|
+
self.last_notification: datetime | None = None
|
19
|
+
self.need_to_notify: bool = False
|
20
|
+
self.attributes: list = []
|
21
|
+
|
22
|
+
@property
|
23
|
+
def need_to_notify_now(self) -> bool:
|
24
|
+
if not self.enabled or not self.need_to_notify:
|
25
|
+
return False
|
26
|
+
|
27
|
+
now = datetime.now(UTC)
|
28
|
+
current_week_day = datetime.isoweekday(now)
|
29
|
+
if self.last_notification:
|
30
|
+
self.last_notification = self.last_notification.replace(tzinfo=UTC)
|
31
|
+
|
32
|
+
if (self.type == NotificationType.UNREAD_FOR_FEW_MINUTES
|
33
|
+
and self.last_notification and (now.minute - self.minute <= self.last_notification.minute)):
|
34
|
+
return False
|
35
|
+
|
36
|
+
if self.type == NotificationType.ONCE_A_WEEK and current_week_day != self.day:
|
37
|
+
return False
|
38
|
+
|
39
|
+
if ((self.type == NotificationType.ONCE_A_DAY or self.type == NotificationType.ONCE_A_WEEK)
|
40
|
+
and now.hour != self.hour and now.minute < self.minute):
|
41
|
+
return False
|
42
|
+
|
43
|
+
if ((self.type == NotificationType.ONCE_A_DAY or self.type == NotificationType.ONCE_A_WEEK)
|
44
|
+
and self.last_notification and self.last_notification > now - timedelta(hours=1)):
|
45
|
+
return False
|
46
|
+
|
47
|
+
return True
|
48
|
+
|
49
|
+
@staticmethod
|
50
|
+
def need_to_notify_week_before(date: datetime) -> bool:
|
51
|
+
return datetime.now(UTC) < (date + timedelta(7))
|
52
|
+
|
53
|
+
|
54
|
+
class IncomingMessageNotification(Notification):
|
55
|
+
@staticmethod
|
56
|
+
def get_button_name() -> str:
|
57
|
+
return 'View message'
|
58
|
+
|
59
|
+
@staticmethod
|
60
|
+
def get_button_url(sender_id: str, source_id: str) -> str:
|
61
|
+
return f'{portal_url}/feed?senderId={sender_id}&sourceId={source_id}'
|
62
|
+
|
63
|
+
def get_subject_text(self, users: list) -> str:
|
64
|
+
if self.type == NotificationType.INSTANTLY or self.type == NotificationType.UNREAD_FOR_FEW_MINUTES:
|
65
|
+
return 'New message from your lead'
|
66
|
+
elif len(users) > 1:
|
67
|
+
return 'Unread messages from your lead'
|
68
|
+
return 'Unread message from your lead'
|
69
|
+
|
70
|
+
def get_notification_text(self, users: list):
|
71
|
+
if self.type == NotificationType.INSTANTLY:
|
72
|
+
return f'{users[-1]} has just sent you a message.'
|
73
|
+
elif self.type == NotificationType.UNREAD_FOR_FEW_MINUTES:
|
74
|
+
return f'{users[-1]} sent you a message {self.minute} minutes ago.'
|
75
|
+
elif (self.type == NotificationType.ONCE_A_DAY or self.type == NotificationType.ONCE_A_WEEK) and users:
|
76
|
+
match len(users):
|
77
|
+
case 1:
|
78
|
+
return f'You have unread message(s) from {users[0]}.'
|
79
|
+
case 2 | 3:
|
80
|
+
return f'You have unread messages from {", ".join(users)}.'
|
81
|
+
case _:
|
82
|
+
return f'You have unread messages from {", ".join(users[:3])} and other leads.'
|
83
|
+
|
84
|
+
|
85
|
+
class InboxNotification(Notification):
|
86
|
+
|
87
|
+
@staticmethod
|
88
|
+
def get_button_name():
|
89
|
+
return 'View message request'
|
90
|
+
|
91
|
+
@staticmethod
|
92
|
+
def get_button_url():
|
93
|
+
return f'{portal_url}/feed?requests=true'
|
94
|
+
|
95
|
+
def get_subject_text(self, users: list) -> str:
|
96
|
+
if self.type == NotificationType.INSTANTLY:
|
97
|
+
return 'New message request on Leadguru'
|
98
|
+
elif len(users) > 1:
|
99
|
+
return 'Message requests on Leadguru'
|
100
|
+
return 'Message request on Leadguru'
|
101
|
+
|
102
|
+
def get_notification_text(self, users: list):
|
103
|
+
if self.type == NotificationType.INSTANTLY:
|
104
|
+
return f'{users[0]} has just sent you message request.'
|
105
|
+
elif (self.type == NotificationType.ONCE_A_DAY or self.type == NotificationType.ONCE_A_WEEK) and users:
|
106
|
+
match len(users):
|
107
|
+
case 1:
|
108
|
+
return f'You have unread message request from {users[0]}.'
|
109
|
+
case 2 | 3:
|
110
|
+
return f'You have unread message request from {", ".join(users)}.'
|
111
|
+
case _:
|
112
|
+
return f'You have unread message requests from {", ".join(users[:3])} and other leads.'
|
113
|
+
|
114
|
+
|
115
|
+
class SourceDeactivationNotification(Notification):
|
116
|
+
@staticmethod
|
117
|
+
def get_button_name() -> str:
|
118
|
+
return 'Show community'
|
119
|
+
|
120
|
+
@staticmethod
|
121
|
+
def get_button_url() -> str:
|
122
|
+
return f'{portal_url}/communities?inactive=true'
|
123
|
+
|
124
|
+
@staticmethod
|
125
|
+
def get_subject_text() -> str:
|
126
|
+
return 'Inactivation of Community on Leadguru'
|
127
|
+
|
128
|
+
def get_notification_text(self, bots: list[DedicatedBotModel]):
|
129
|
+
names = [bot.source.source_name.capitalize() for bot in bots]
|
130
|
+
if self.type == NotificationType.INSTANTLY:
|
131
|
+
return f'{bots[-1].source.source_name.capitalize()} became inactive on Leadguru.'
|
132
|
+
elif (self.type == NotificationType.ONCE_A_DAY or self.type == NotificationType.ONCE_A_WEEK) and bots:
|
133
|
+
match len(bots):
|
134
|
+
case 1:
|
135
|
+
return f'{names[-1]} became inactive on Leadguru.'
|
136
|
+
case 2 | 3:
|
137
|
+
return f'{", ".join(names)} became inactive on Leadguru.'
|
138
|
+
case _:
|
139
|
+
return f'{", ".join(names)} and other became inactive on Leadguru.'
|
140
|
+
|
141
|
+
|
142
|
+
class BulkRepliesNotification(Notification):
|
143
|
+
|
144
|
+
@staticmethod
|
145
|
+
def get_button_name() -> str:
|
146
|
+
return 'View post'
|
147
|
+
|
148
|
+
@staticmethod
|
149
|
+
def get_button_url(post: Post) -> str:
|
150
|
+
return f'{portal_url}/bulk-post/form/{post.id}'
|
151
|
+
|
152
|
+
def get_subject_text(self, post: Post) -> str:
|
153
|
+
replied_messages = [message.id for message in post.messages if message.id in self.attributes]
|
154
|
+
if len(replied_messages) > 1:
|
155
|
+
return 'New replies to your bulk post'
|
156
|
+
return 'New reply to your bulk post'
|
157
|
+
|
158
|
+
@staticmethod
|
159
|
+
def get_notification_text(post: Post):
|
160
|
+
if len(post.messages) <= 1:
|
161
|
+
source_name = post.messages[0].server_name
|
162
|
+
channel_name = post.messages[0].channel_name
|
163
|
+
return f'You have new reply in #{channel_name} from {source_name.capitalize()} to your {post.title} post.'
|
164
|
+
|
165
|
+
channels = set([message.channel_id for message in post.messages])
|
166
|
+
sources = set([message.server_id for message in post.messages])
|
167
|
+
return (f'You have new replies in {len(channels)} from {len(sources)} communities '
|
168
|
+
f'to your {post.title} post.')
|
169
|
+
|
170
|
+
|
171
|
+
class BulkReactionsNotification(Notification):
|
172
|
+
|
173
|
+
@staticmethod
|
174
|
+
def get_button_name() -> str:
|
175
|
+
return 'View post'
|
176
|
+
|
177
|
+
@staticmethod
|
178
|
+
def get_button_url(post: Post) -> str:
|
179
|
+
return f'{portal_url}/bulk-post/form/{post.id}'
|
180
|
+
|
181
|
+
@staticmethod
|
182
|
+
def get_subject_text() -> str:
|
183
|
+
return 'People are reacting to your post'
|
184
|
+
|
185
|
+
@staticmethod
|
186
|
+
def get_notification_text(post: Post):
|
187
|
+
if len(post.messages) <= 1:
|
188
|
+
source_name = post.messages[0].server_name
|
189
|
+
channel_name = post.messages[0].channel_name
|
190
|
+
return (f'You have new reaction in #{channel_name} from {source_name.capitalize()} '
|
191
|
+
f'to your {post.title} post.')
|
192
|
+
|
193
|
+
channels = set([message.channel_id for message in post.messages])
|
194
|
+
sources = set([message.server_id for message in post.messages])
|
195
|
+
return (f'You have new reactions in {len(channels)} from {len(sources)} communities '
|
196
|
+
f'to your {post.title} post.')
|
197
|
+
|
198
|
+
|
199
|
+
class FollowUpNotification(Notification):
|
200
|
+
|
201
|
+
@staticmethod
|
202
|
+
def get_button_name(actual: list[UserLeadModel]) -> str:
|
203
|
+
if len(actual) > 1:
|
204
|
+
return 'View calendar'
|
205
|
+
return 'Send message'
|
206
|
+
|
207
|
+
def get_button_url(self, actual: list[UserLeadModel]) -> str:
|
208
|
+
if len(actual) > 1:
|
209
|
+
if self.type == NotificationType.ONCE_A_DAY:
|
210
|
+
return f'{portal_url}/dashboard/calendar?view=day'
|
211
|
+
return f'{portal_url}/dashboard/calendar?view=week'
|
212
|
+
return f'{portal_url}/feed?senderId={actual[0].message.sender_id}&sourceId={actual[0].message.source.source_id}'
|
213
|
+
|
214
|
+
def get_subject_text(self, actual: list[UserLeadModel]) -> str:
|
215
|
+
subject_text = 'You have planned follow-ups' if len(actual) > 1 else 'You have planned follow-up'
|
216
|
+
|
217
|
+
if self.type == NotificationType.ONCE_A_DAY:
|
218
|
+
return f'{subject_text} for today'
|
219
|
+
elif self.type == NotificationType.ONCE_A_WEEK:
|
220
|
+
return f'{subject_text} for this week'
|
221
|
+
|
222
|
+
def get_notification_text(self, actual: list[ExtendedUserLeadModel], overdue: list[ExtendedUserLeadModel]) -> str:
|
223
|
+
notification_text = ''
|
224
|
+
names = [lead.contact.real_name for lead in actual]
|
225
|
+
if self.type == NotificationType.ONCE_A_DAY:
|
226
|
+
match len(actual):
|
227
|
+
case 1:
|
228
|
+
notification_text = f'You have planned to send follow-up today to {names[0]}.'
|
229
|
+
case 2 | 3:
|
230
|
+
notification_text = f'You have planned to send follow-up today to {", ".join(names)}.'
|
231
|
+
case _:
|
232
|
+
notification_text = (f'You have planned to send follow-up today to {", ".join(names[:3])} '
|
233
|
+
f'and {len(names) - 3} other leads.')
|
234
|
+
elif self.type == NotificationType.ONCE_A_WEEK:
|
235
|
+
match len(actual):
|
236
|
+
case 1:
|
237
|
+
notification_text = f'You have planned to send follow-up to {names[0]} this week.'
|
238
|
+
case 2 | 3:
|
239
|
+
notification_text = f'You have planned to send follow-up to {", ".join(names)} this week.'
|
240
|
+
case _:
|
241
|
+
notification_text = (f'You have planned to send follow-up to {", ".join(names[:3])} '
|
242
|
+
f'and {len(names) - 3} other leads this week.')
|
243
|
+
|
244
|
+
return f'{notification_text} Plus you have {len(overdue)} overdue follow-ups.' if overdue else notification_text
|
245
|
+
|
246
|
+
|
247
|
+
class BillingNotifications(Notification):
|
248
|
+
pass
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import copy
|
2
|
+
from lgt_jobs.lgt_data.models.base import DictionaryModel
|
3
|
+
from lgt_jobs.lgt_data.models.notifications.notification import (Notification, IncomingMessageNotification,
|
4
|
+
InboxNotification, SourceDeactivationNotification,
|
5
|
+
BillingNotifications, BulkRepliesNotification,
|
6
|
+
BulkReactionsNotification, FollowUpNotification)
|
7
|
+
|
8
|
+
|
9
|
+
class NotificationSettings(DictionaryModel):
|
10
|
+
def __init__(self):
|
11
|
+
self.incoming_messages: IncomingMessageNotification | None = None
|
12
|
+
self.inbox: InboxNotification | None = None
|
13
|
+
self.source_deactivation: SourceDeactivationNotification | None = None
|
14
|
+
self.billing: BillingNotifications | None = None
|
15
|
+
self.bulk_replies: BulkRepliesNotification | None = None
|
16
|
+
self.bulk_reactions: BulkReactionsNotification | None = None
|
17
|
+
self.follow_ups: FollowUpNotification | None = None
|
18
|
+
|
19
|
+
@classmethod
|
20
|
+
def from_dic(cls, dic: dict):
|
21
|
+
if not dic:
|
22
|
+
return None
|
23
|
+
|
24
|
+
model: NotificationSettings = cls()
|
25
|
+
model.incoming_messages = IncomingMessageNotification.from_dic(dic.get('incoming_messages'))
|
26
|
+
model.inbox = InboxNotification.from_dic(dic.get('inbox'))
|
27
|
+
model.source_deactivation = SourceDeactivationNotification.from_dic(dic.get('source_deactivation'))
|
28
|
+
model.billing = BillingNotifications.from_dic(dic.get('billing'))
|
29
|
+
model.bulk_replies = BulkRepliesNotification.from_dic(dic.get('bulk_replies'))
|
30
|
+
model.bulk_reactions = BulkReactionsNotification.from_dic(dic.get('bulk_reactions'))
|
31
|
+
model.follow_ups = FollowUpNotification.from_dic(dic.get('follow_ups'))
|
32
|
+
return model
|
33
|
+
|
34
|
+
def to_dic(self):
|
35
|
+
result = copy.deepcopy(self.__dict__)
|
36
|
+
|
37
|
+
if result.get('incoming_messages'):
|
38
|
+
result['incoming_messages'] = Notification.to_dic(result.get('incoming_messages'))
|
39
|
+
if result.get('inbox'):
|
40
|
+
result['inbox'] = Notification.to_dic(result.get('inbox'))
|
41
|
+
if result.get('source_deactivation'):
|
42
|
+
result['source_deactivation'] = Notification.to_dic(result.get('source_deactivation'))
|
43
|
+
if result.get('billing'):
|
44
|
+
result['billing'] = Notification.to_dic(result.get('billing'))
|
45
|
+
if result.get('bulk_replies'):
|
46
|
+
result['bulk_replies'] = Notification.to_dic(result.get('bulk_replies'))
|
47
|
+
if result.get('bulk_reactions'):
|
48
|
+
result['bulk_reactions'] = Notification.to_dic(result.get('bulk_reactions'))
|
49
|
+
if result.get('follow_ups'):
|
50
|
+
result['follow_ups'] = Notification.to_dic(result.get('follow_ups'))
|
51
|
+
|
52
|
+
return result
|
File without changes
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import copy
|
2
|
+
from datetime import datetime
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from lgt_jobs.lgt_data.models.base import BaseModel
|
6
|
+
from lgt_jobs.lgt_data.models.bots.base_bot import Source
|
7
|
+
from lgt_jobs.lgt_data.models.external.slack.timezone import SlackTimeZone
|
8
|
+
from lgt_jobs.lgt_data.models.people.profile import Profile
|
9
|
+
|
10
|
+
|
11
|
+
class SlackMemberInformation(BaseModel, Profile):
|
12
|
+
workspace: str
|
13
|
+
sender_id: str
|
14
|
+
images: dict
|
15
|
+
full_text: str
|
16
|
+
deleted: bool = False
|
17
|
+
is_bot: bool = False
|
18
|
+
is_app_user: bool = False
|
19
|
+
is_admin: bool = False
|
20
|
+
is_owner: bool = False
|
21
|
+
is_email_confirmed: bool = False
|
22
|
+
online: Optional[str] = None
|
23
|
+
online_updated_at: datetime = None
|
24
|
+
timezone: SlackTimeZone = None
|
25
|
+
source: Source = None
|
26
|
+
|
27
|
+
@classmethod
|
28
|
+
def from_dic(cls, dic: dict):
|
29
|
+
model: SlackMemberInformation = cls()
|
30
|
+
if not dic:
|
31
|
+
return None
|
32
|
+
|
33
|
+
for k, v in dic.items():
|
34
|
+
setattr(model, k, v)
|
35
|
+
|
36
|
+
model.online = dic.get('online', '') == "active"
|
37
|
+
model: SlackMemberInformation | None = super().from_dic(dic)
|
38
|
+
model.source = Source.from_dic(dic.get('source'))
|
39
|
+
return model
|
40
|
+
|
41
|
+
def to_dic(self):
|
42
|
+
result = copy.deepcopy(self.__dict__)
|
43
|
+
if result.get('source'):
|
44
|
+
result['source'] = Source.to_dic(result.get('source'))
|
45
|
+
return result
|
46
|
+
|
47
|
+
@staticmethod
|
48
|
+
def from_slack_response(slack_profile: dict, source: Source = None):
|
49
|
+
member_info: SlackMemberInformation = SlackMemberInformation()
|
50
|
+
member_info.source = source
|
51
|
+
member_info.sender_id = slack_profile.get("id")
|
52
|
+
member_info.display_name = slack_profile["profile"].get("display_name")
|
53
|
+
member_info.real_name = slack_profile["profile"].get("real_name")
|
54
|
+
member_info.title = slack_profile["profile"].get("title")
|
55
|
+
member_info.phone = slack_profile["profile"].get("phone")
|
56
|
+
member_info.skype = slack_profile["profile"].get("skype")
|
57
|
+
member_info.email = slack_profile["profile"].get("email")
|
58
|
+
member_info.images = {
|
59
|
+
'image_24': slack_profile.get("profile", {}).get("image_24",
|
60
|
+
'https://a.slack-edge.com/80588/img/slackbot_24.png'),
|
61
|
+
'image_32': slack_profile.get("profile", {}).get("image_32",
|
62
|
+
'https://a.slack-edge.com/80588/img/slackbot_32.png'),
|
63
|
+
'image_48': slack_profile.get("profile", {}).get("image_48",
|
64
|
+
'https://a.slack-edge.com/80588/img/slackbot_48.png'),
|
65
|
+
'image_72': slack_profile.get("profile", {}).get("image_72",
|
66
|
+
'https://a.slack-edge.com/80588/img/slackbot_72.png'),
|
67
|
+
'image_192': slack_profile.get("profile", {}).get("image_192",
|
68
|
+
'https://a.slack-edge.com/80588/img/slackbot_192.png'),
|
69
|
+
'image_512': slack_profile.get("profile", {}).get("image_512",
|
70
|
+
'https://a.slack-edge.com/80588/img/slackbot_512.png'),
|
71
|
+
|
72
|
+
}
|
73
|
+
member_info.timezone = {"tz": slack_profile.get("tz"), "tz_label": slack_profile.get("tz_label"),
|
74
|
+
"tz_offset": slack_profile.get("tz_offset")}
|
75
|
+
return member_info
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from lgt_jobs.lgt_data.models.base import DictionaryModel
|
2
|
+
|
3
|
+
|
4
|
+
class Profile(DictionaryModel):
|
5
|
+
def __init__(self):
|
6
|
+
self.title = ''
|
7
|
+
self.phone = ''
|
8
|
+
self.skype = ''
|
9
|
+
self.display_name = ''
|
10
|
+
self.real_name = ''
|
11
|
+
self.email = ''
|
12
|
+
self.photo_url = ''
|
13
|
+
self.main = False
|
File without changes
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from lgt_jobs.lgt_data.models.base import DictionaryModel
|
2
|
+
|
3
|
+
|
4
|
+
class PostMessage(DictionaryModel):
|
5
|
+
id: str
|
6
|
+
server_id: str
|
7
|
+
server_name: str
|
8
|
+
channel_id: str
|
9
|
+
channel_name: str
|
10
|
+
|
11
|
+
@classmethod
|
12
|
+
def from_dic(cls, dic: dict):
|
13
|
+
if not dic:
|
14
|
+
return None
|
15
|
+
model = cls()
|
16
|
+
for k, v in dic.items():
|
17
|
+
setattr(model, k, v)
|
18
|
+
return model
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from lgt_jobs.lgt_data.models.base import BaseModel
|
2
|
+
from lgt_jobs.lgt_data.models.post.message import PostMessage
|
3
|
+
|
4
|
+
|
5
|
+
class Post(BaseModel):
|
6
|
+
messages: list[PostMessage]
|
7
|
+
title: str
|
8
|
+
|
9
|
+
@classmethod
|
10
|
+
def from_dic(cls, dic: dict):
|
11
|
+
if not dic:
|
12
|
+
return None
|
13
|
+
model: Post | None = super().from_dic(dic)
|
14
|
+
model.messages = [PostMessage.from_dic(message) for message in dic.get('messages', [])]
|
15
|
+
return model
|
File without changes
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
from lgt_jobs.lgt_data.enums import FeaturesEnum, FeatureOptions
|
2
|
+
from lgt_jobs.lgt_data.models.base import DictionaryModel
|
3
|
+
|
4
|
+
|
5
|
+
class Feature(DictionaryModel):
|
6
|
+
display_name: str
|
7
|
+
name: FeaturesEnum
|
8
|
+
description: str | None = None
|
9
|
+
limit: int | None = None
|
10
|
+
options: FeatureOptions | None = None
|
@@ -0,0 +1,9 @@
|
|
1
|
+
from lgt_jobs.lgt_data.models.base import DictionaryModel
|
2
|
+
|
3
|
+
|
4
|
+
class GeneralSettings(DictionaryModel):
|
5
|
+
def __init__(self):
|
6
|
+
self.theme: str | None = None
|
7
|
+
self.ask_pipeline_and_status: bool = False
|
8
|
+
self.ask_follow_up: bool = False
|
9
|
+
self.dashboard_is_starting_page: bool = False
|