leadguru-jobs 0.638.0__py3-none-any.whl → 0.640.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.
Files changed (74) hide show
  1. {leadguru_jobs-0.638.0.dist-info → leadguru_jobs-0.640.0.dist-info}/METADATA +1 -1
  2. leadguru_jobs-0.640.0.dist-info/RECORD +109 -0
  3. lgt_jobs/jobs/bot_stats_update.py +2 -10
  4. lgt_jobs/jobs/chat_history.py +3 -6
  5. lgt_jobs/jobs/connect_sources.py +4 -1
  6. lgt_jobs/jobs/inbox_leads.py +6 -2
  7. lgt_jobs/jobs/load_slack_people.py +2 -1
  8. lgt_jobs/jobs/send_slack_message.py +1 -1
  9. lgt_jobs/jobs/update_slack_profile.py +2 -1
  10. lgt_jobs/jobs/workspace_connect.py +3 -1
  11. lgt_jobs/lgt_data/model.py +0 -1136
  12. lgt_jobs/lgt_data/models/base.py +29 -0
  13. lgt_jobs/lgt_data/models/boards/__init__.py +0 -0
  14. lgt_jobs/lgt_data/models/boards/board.py +39 -0
  15. lgt_jobs/lgt_data/models/boards/status.py +14 -0
  16. lgt_jobs/lgt_data/models/bots/__init__.py +0 -0
  17. lgt_jobs/lgt_data/models/bots/base_bot.py +44 -0
  18. lgt_jobs/lgt_data/models/bots/bot_info.py +33 -0
  19. lgt_jobs/lgt_data/models/bots/dedicated_bot.py +90 -0
  20. lgt_jobs/lgt_data/models/chat/__init__.py +0 -0
  21. lgt_jobs/lgt_data/models/chat/file.py +8 -0
  22. lgt_jobs/lgt_data/models/chat/grouped_messages.py +17 -0
  23. lgt_jobs/lgt_data/models/chat/message.py +85 -0
  24. lgt_jobs/lgt_data/models/{message_request.py → chat/request.py} +2 -1
  25. lgt_jobs/lgt_data/models/chat/scheduled_message.py +14 -0
  26. lgt_jobs/lgt_data/models/contacts/__init__.py +0 -0
  27. lgt_jobs/lgt_data/models/contacts/contact.py +22 -0
  28. lgt_jobs/lgt_data/models/external/__init__.py +0 -0
  29. lgt_jobs/lgt_data/models/external/cloud/__init__.py +0 -0
  30. lgt_jobs/lgt_data/models/external/cloud/file.py +18 -0
  31. lgt_jobs/lgt_data/models/external/discord/__init__.py +0 -0
  32. lgt_jobs/lgt_data/models/external/discord/user.py +45 -0
  33. lgt_jobs/lgt_data/models/external/slack/__init__.py +0 -0
  34. lgt_jobs/lgt_data/models/external/slack/timezone.py +8 -0
  35. lgt_jobs/lgt_data/models/external/slack/user.py +43 -0
  36. lgt_jobs/lgt_data/models/external/user_workspace.py +32 -0
  37. lgt_jobs/lgt_data/models/leads/__init__.py +0 -0
  38. lgt_jobs/lgt_data/models/leads/config.py +13 -0
  39. lgt_jobs/lgt_data/models/leads/extended_lead.py +46 -0
  40. lgt_jobs/lgt_data/models/leads/lead.py +48 -0
  41. lgt_jobs/lgt_data/models/leads/message.py +51 -0
  42. lgt_jobs/lgt_data/models/notifications/__init__.py +0 -0
  43. lgt_jobs/lgt_data/models/notifications/notification.py +248 -0
  44. lgt_jobs/lgt_data/models/notifications/notification_settings.py +52 -0
  45. lgt_jobs/lgt_data/models/people/__init__.py +0 -0
  46. lgt_jobs/lgt_data/models/people/people.py +75 -0
  47. lgt_jobs/lgt_data/models/people/profile.py +13 -0
  48. lgt_jobs/lgt_data/models/post/__init__.py +0 -0
  49. lgt_jobs/lgt_data/models/post/message.py +18 -0
  50. lgt_jobs/lgt_data/models/post/post.py +15 -0
  51. lgt_jobs/lgt_data/models/templates/__init__.py +0 -0
  52. lgt_jobs/lgt_data/models/templates/template.py +12 -0
  53. lgt_jobs/lgt_data/models/user/__init__.py +0 -0
  54. lgt_jobs/lgt_data/models/user/feature.py +10 -0
  55. lgt_jobs/lgt_data/models/user/general_settings.py +9 -0
  56. lgt_jobs/lgt_data/models/user/reset_password.py +7 -0
  57. lgt_jobs/lgt_data/models/user/subscription.py +22 -0
  58. lgt_jobs/lgt_data/models/user/user.py +95 -0
  59. lgt_jobs/lgt_data/models/user/user_follow_ups.py +20 -0
  60. lgt_jobs/lgt_data/models/user/user_page.py +19 -0
  61. lgt_jobs/lgt_data/models/user/verification.py +12 -0
  62. lgt_jobs/lgt_data/models/user_leads/__init__.py +0 -0
  63. lgt_jobs/lgt_data/models/user_leads/extended_user_lead.py +41 -0
  64. lgt_jobs/lgt_data/models/user_leads/user_lead.py +33 -0
  65. lgt_jobs/lgt_data/mongo_repository.py +225 -45
  66. lgt_jobs/lgt_data/repositories/__init__.py +0 -0
  67. lgt_jobs/lgt_data/repositories/post/__init__.py +0 -0
  68. lgt_jobs/lgt_data/repositories/post/posts.py +28 -0
  69. lgt_jobs/runner.py +2 -2
  70. lgt_jobs/simple_job.py +1 -1
  71. leadguru_jobs-0.638.0.dist-info/RECORD +0 -55
  72. lgt_jobs/lgt_common/lgt_logging.py +0 -15
  73. {leadguru_jobs-0.638.0.dist-info → leadguru_jobs-0.640.0.dist-info}/WHEEL +0 -0
  74. {leadguru_jobs-0.638.0.dist-info → leadguru_jobs-0.640.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,95 @@
1
+ import copy
2
+ from datetime import UTC, datetime
3
+ from typing import List, Optional
4
+
5
+ from bson import ObjectId
6
+
7
+ from lgt_jobs.lgt_data.enums import UserRole
8
+ from lgt_jobs.lgt_data.models.base import BaseModel
9
+ from lgt_jobs.lgt_data.models.external.discord.user import DiscordUser
10
+ from lgt_jobs.lgt_data.models.external.slack.user import SlackUser
11
+ from lgt_jobs.lgt_data.models.notifications.notification_settings import NotificationSettings
12
+ from lgt_jobs.lgt_data.models.people.profile import Profile
13
+ from lgt_jobs.lgt_data.models.user.general_settings import GeneralSettings
14
+
15
+
16
+ class UserModel(BaseModel):
17
+ def __init__(self):
18
+ super().__init__()
19
+ self.email: Optional[str] = None
20
+ self.password: Optional[str] = None
21
+ self.roles: List[str] = []
22
+ self.user_name: str = ''
23
+ self.company: str = ''
24
+ self.company_size: Optional[int] = None
25
+ self.company_industries: Optional[List[str]] = None
26
+ self.company_technologies: Optional[List[str]] = None
27
+ self.company_locations: Optional[List[str]] = None
28
+ self.company_web_site: str = ''
29
+ self.company_description: str = ''
30
+ self.position: str = ''
31
+ self.leads_limit: Optional[int] = None
32
+ self.leads_proceeded: Optional[int] = None
33
+ self.leads_filtered: Optional[int] = None
34
+ self.leads_limit_updated_at: Optional[int] = None
35
+ self.paid_lead_price: int = 1
36
+ self.state: int = 0
37
+ self.credits_exceeded_at: Optional[datetime] = None
38
+ self.unanswered_leads_period = None
39
+ self.inactive = None
40
+ self.slack_users: List[SlackUser] = []
41
+ self.discord_users: List[DiscordUser] = []
42
+ self.verified: bool = False
43
+ self.subscription_id: ObjectId | None = None
44
+ self.subscription_expired_at: datetime | None = None
45
+ self.balance: str | None = None
46
+ self.subscription_name: str | None = None
47
+ self.subscription_expiration_notified = False
48
+ self.subscription_expiration_warning_notified = False
49
+ self.notification_settings: NotificationSettings | None = None
50
+ self.general_settings: GeneralSettings | None = None
51
+
52
+ @classmethod
53
+ def from_dic(cls, dic: dict):
54
+ if not dic:
55
+ return None
56
+
57
+ model: UserModel = cls()
58
+ for k, v in dic.items():
59
+ setattr(model, k, v)
60
+
61
+ if '_id' in dic:
62
+ setattr(model, 'id', dic['_id'])
63
+
64
+ model.slack_profile = Profile.from_dic(dic.get('slack_profile'))
65
+ model.slack_users = [SlackUser.from_dic(user) for user in dic.get('slack_users', [])]
66
+ model.discord_users = [DiscordUser.from_dic(user) for user in dic.get('discord_users', [])]
67
+ model.notification_settings = NotificationSettings.from_dic(dic.get('notification_settings'))
68
+ model.general_settings = GeneralSettings.from_dic(dic.get('general_settings'))
69
+ return model
70
+
71
+ def to_dic(self):
72
+ result = copy.deepcopy(self.__dict__)
73
+
74
+ if result.get('slack_profile'):
75
+ result['slack_profile'] = result.get('slack_profile').__dict__
76
+ if result.get('notification_settings'):
77
+ result['notification_settings'] = NotificationSettings.to_dic(result.get('notification_settings'))
78
+ if result.get('general_settings'):
79
+ result['general_settings'] = GeneralSettings.to_dic(result.get('general_settings'))
80
+
81
+ return result
82
+
83
+ @property
84
+ def is_admin(self):
85
+ return UserRole.ADMIN in self.roles
86
+
87
+ @property
88
+ def subscription_expired(self):
89
+ return self.subscription_expired_at.replace(tzinfo=UTC) < datetime.now(UTC)
90
+
91
+ def get_slack_user(self, slack_email: str) -> SlackUser:
92
+ return next(filter(lambda x: slack_email == x.email, self.slack_users), None)
93
+
94
+ def get_discord_user(self, login: str) -> DiscordUser:
95
+ return next(filter(lambda x: login == x.login, self.discord_users), None)
@@ -0,0 +1,20 @@
1
+ from lgt_jobs.lgt_data.models.base import DictionaryModel
2
+ from lgt_jobs.lgt_data.models.leads.extended_lead import ExtendedLeadModel
3
+ from lgt_jobs.lgt_data.models.notifications.notification_settings import NotificationSettings
4
+
5
+
6
+ class UserFollowUps(DictionaryModel):
7
+ actual_follow_ups: list[ExtendedLeadModel] = []
8
+ overdue_follow_ups: list[ExtendedLeadModel] = []
9
+ email: str | None = None
10
+ notification_settings: NotificationSettings | None = None
11
+
12
+ @classmethod
13
+ def from_dic(cls, dic: dict):
14
+ if not dic:
15
+ return None
16
+ model: UserFollowUps | None = super().from_dic(dic)
17
+ model.actual_follow_ups = [ExtendedLeadModel.from_dic(lead) for lead in dic.get('actual_follow_ups', [])]
18
+ model.overdue_follow_ups = [ExtendedLeadModel.from_dic(lead) for lead in dic.get('overdue_follow_ups', [])]
19
+ model.notification_settings = NotificationSettings.from_dic(dic.get('notification_settings'))
20
+ return model
@@ -0,0 +1,19 @@
1
+ from typing import List
2
+
3
+ from lgt_jobs.lgt_data.models.user.user import UserModel
4
+
5
+
6
+ class UsersPage:
7
+ users: List[UserModel]
8
+ count: int = 0
9
+
10
+ def __init__(self, users: List[UserModel], count: int):
11
+ self.users = users
12
+ self.count = count
13
+
14
+ @staticmethod
15
+ def from_dic(dic: dict):
16
+ users = [UserModel.from_dic(doc) for doc in dic.get('page', [])]
17
+ count = dic.get('count', 0)
18
+ return UsersPage(users=users, count=count)
19
+
@@ -0,0 +1,12 @@
1
+ from datetime import datetime, UTC
2
+
3
+ from lgt_jobs.lgt_data.models.base import DictionaryModel
4
+
5
+
6
+ class UserVerificationModel(DictionaryModel):
7
+ pass
8
+
9
+ def __init__(self):
10
+ super().__init__()
11
+ self.email = None
12
+ self.created_at = datetime.now(UTC)
File without changes
@@ -0,0 +1,41 @@
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.leads.lead import LeadModel
5
+ from lgt_jobs.lgt_data.models.people.people import SlackMemberInformation
6
+ from lgt_jobs.lgt_data.models.user_leads.user_lead import UserLeadModel
7
+
8
+
9
+ class ExtendedUserLeadModel(UserLeadModel):
10
+ pass
11
+
12
+ def __init__(self):
13
+ super().__init__()
14
+ self.contact: SlackMemberInformation | None = None
15
+ self.previous_publications = []
16
+ self.bots: List[BotInfo] = []
17
+ self.user_email: str | None = None
18
+
19
+ @classmethod
20
+ def from_dic(cls, dic: dict):
21
+ if not dic:
22
+ return None
23
+
24
+ result: ExtendedUserLeadModel | None = super().from_dic(dic)
25
+ if not result:
26
+ return None
27
+
28
+ result.contact = SlackMemberInformation.from_dic(dic.get('contact'))
29
+ result.previous_publications = [LeadModel.from_dic(lead) for lead in dic.get('previous_publications', [])]
30
+ return result
31
+
32
+ def to_dic(self):
33
+ result = super().to_dic()
34
+ result["contact"] = self.contact.to_dic()
35
+ return result
36
+
37
+ def to_csv(self, board_name: str) -> List[str]:
38
+ return [self.message.source, self.contact.real_name, self.contact.title, self.contact.email,
39
+ self.notes, board_name, self.status,
40
+ self.followup_date.strftime("%d.%m.%Y %H:%M") if self.followup_date else "",
41
+ self.message.message.replace('\n', ' ').strip()]
@@ -0,0 +1,33 @@
1
+ from datetime import datetime, UTC
2
+
3
+ from bson import ObjectId
4
+
5
+ from lgt_jobs.lgt_data.models.leads.lead import LeadModel
6
+
7
+
8
+ class UserLeadModel(LeadModel):
9
+ pass
10
+
11
+ def __init__(self):
12
+ super().__init__()
13
+ self.order: int = 0
14
+ self.followup_date: datetime | None = None
15
+ self.user_id: ObjectId | None = None
16
+ self.chat_viewed_at: datetime | None = None
17
+ self.board_id: ObjectId | None = None
18
+
19
+ @classmethod
20
+ def from_dic(cls, dic: dict):
21
+ if not dic:
22
+ return None
23
+
24
+ result: UserLeadModel | None = super().from_dic(dic)
25
+ if not result:
26
+ return None
27
+
28
+ if result.followup_date:
29
+ result.followup_date.replace(tzinfo=UTC)
30
+
31
+ result.chat_viewed_at = dic.get('chat_viewed_at')
32
+ return result
33
+
@@ -6,14 +6,30 @@ from dateutil import tz
6
6
  from pymongo import MongoClient, UpdateOne
7
7
  from bson.objectid import ObjectId
8
8
  from lgt_jobs.lgt_data.enums import SourceType
9
- from lgt_jobs.lgt_data.model import (LeadModel, BaseModel, UserModel, UserResetPasswordModel, BoardModel, BoardedStatus,
10
- DedicatedBotModel, SlackMemberInformation, UserTemplateModel,
11
- ExtendedUserLeadModel, UserLeadModel, ExtendedLeadModel, UserContact, ChatMessage,
12
- GroupedMessagesModel, UserVerificationModel, UsersPage, Subscription,
13
- ScheduledMessage)
14
9
  from datetime import datetime, UTC, timedelta
15
10
  from collections import OrderedDict
16
- from lgt_jobs.lgt_data.models.message_request import MessageRequest
11
+
12
+ from lgt_jobs.lgt_data.models.base import BaseModel
13
+ from lgt_jobs.lgt_data.models.boards.board import BoardModel
14
+ from lgt_jobs.lgt_data.models.boards.status import BoardedStatus
15
+ from lgt_jobs.lgt_data.models.bots.dedicated_bot import DedicatedBotModel
16
+ from lgt_jobs.lgt_data.models.chat.grouped_messages import GroupedMessagesModel
17
+ from lgt_jobs.lgt_data.models.chat.message import ChatMessage
18
+ from lgt_jobs.lgt_data.models.chat.scheduled_message import ScheduledMessage
19
+ from lgt_jobs.lgt_data.models.chat.request import MessageRequest
20
+ from lgt_jobs.lgt_data.models.contacts.contact import UserContact
21
+ from lgt_jobs.lgt_data.models.leads.extended_lead import ExtendedLeadModel
22
+ from lgt_jobs.lgt_data.models.leads.lead import LeadModel
23
+ from lgt_jobs.lgt_data.models.people.people import SlackMemberInformation
24
+ from lgt_jobs.lgt_data.models.templates.template import UserTemplateModel
25
+ from lgt_jobs.lgt_data.models.user.reset_password import UserResetPasswordModel
26
+ from lgt_jobs.lgt_data.models.user.subscription import Subscription
27
+ from lgt_jobs.lgt_data.models.user.user import UserModel
28
+ from lgt_jobs.lgt_data.models.user.user_follow_ups import UserFollowUps
29
+ from lgt_jobs.lgt_data.models.user.user_page import UsersPage
30
+ from lgt_jobs.lgt_data.models.user.verification import UserVerificationModel
31
+ from lgt_jobs.lgt_data.models.user_leads.extended_user_lead import ExtendedUserLeadModel
32
+ from lgt_jobs.lgt_data.models.user_leads.user_lead import UserLeadModel
17
33
 
18
34
  client = MongoClient(os.environ.get('MONGO_CONNECTION_STRING', 'mongodb://127.0.0.1:27017/'))
19
35
 
@@ -73,6 +89,10 @@ class UserMongoRepository(BaseMongoRepository):
73
89
  connected_slack_email = kwargs.get('connected_slack_email')
74
90
  soon_subscription_expiration = kwargs.get('soon_subscription_expiration')
75
91
  has_new_message = kwargs.get('has_new_message')
92
+ has_new_message_request = kwargs.get('has_new_message_request')
93
+ has_deactivated_sources = kwargs.get('has_deactivated_sources')
94
+ has_new_replies = kwargs.get('has_new_replies')
95
+ has_new_reactions = kwargs.get('has_new_reactions')
76
96
  min_days = kwargs.get('min_days_soon_subscription_expiration', 3)
77
97
 
78
98
  if subscription_expired:
@@ -87,6 +107,22 @@ class UserMongoRepository(BaseMongoRepository):
87
107
  pipeline['notification_settings.incoming_messages.enabled'] = True
88
108
  pipeline['notification_settings.incoming_messages.need_to_notify'] = True
89
109
 
110
+ if has_new_message_request:
111
+ pipeline['notification_settings.inbox.enabled'] = True
112
+ pipeline['notification_settings.inbox.need_to_notify'] = True
113
+
114
+ if has_deactivated_sources:
115
+ pipeline['notification_settings.source_deactivation.enabled'] = True
116
+ pipeline['notification_settings.source_deactivation.need_to_notify'] = True
117
+
118
+ if has_new_replies:
119
+ pipeline['notification_settings.bulk_replies.enabled'] = True
120
+ pipeline['notification_settings.bulk_replies.need_to_notify'] = True
121
+
122
+ if has_new_reactions:
123
+ pipeline['notification_settings.bulk_reactions.enabled'] = True
124
+ pipeline['notification_settings.bulk_reactions.need_to_notify'] = True
125
+
90
126
  if connected_slack_email:
91
127
  pipeline['slack_users.email'] = connected_slack_email
92
128
 
@@ -106,7 +142,7 @@ class UserMongoRepository(BaseMongoRepository):
106
142
  search = kwargs.get('search')
107
143
  sort_field = kwargs.get('sort_field', 'email')
108
144
  sort_direction = kwargs.get('sort_direction', 'ASCENDING')
109
- sort_direction: int = pymongo.ASCENDING if sort_direction == 'ASCENDING' else pymongo.DESCENDING
145
+ sort_direction = pymongo.ASCENDING if sort_direction == 'ASCENDING' else pymongo.DESCENDING
110
146
  if not include_inactive:
111
147
  match_pipeline['inactive'] = False
112
148
  if email:
@@ -176,6 +212,81 @@ class UserLeadMongoRepository(BaseMongoRepository):
176
212
  collection_name = 'user_leads'
177
213
  model = ExtendedUserLeadModel
178
214
 
215
+ def get_leads_with_follow_ups(self):
216
+ now = datetime.now(UTC)
217
+ this_day = datetime(now.year, now.month, now.day, 21, 00, 00, tzinfo=UTC)
218
+ after_week = this_day + timedelta(7)
219
+ pipeline = [
220
+ {'$match': {'archived': False, 'followup_date': {'$ne': None}}},
221
+ {
222
+ '$lookup': {
223
+ 'from': 'users',
224
+ 'localField': 'user_id',
225
+ 'foreignField': '_id',
226
+ 'as': 'user'
227
+ }
228
+ },
229
+ {
230
+ '$lookup': {
231
+ 'from': 'slack_contact',
232
+ 'localField': 'message.sender_id',
233
+ 'foreignField': 'sender_id',
234
+ 'as': 'contact'
235
+ }
236
+ },
237
+ {'$addFields': {'contact': {'$first': '$contact'}, 'user': {'$first': '$user'}}},
238
+ {
239
+ '$project': {
240
+ 'user_id': 1,
241
+ 'user_email': '$user.email',
242
+ 'notification_settings': '$user.notification_settings',
243
+ 'followup_to': {
244
+ '$cond': {
245
+ 'if': {'$eq': ['$user.notification_settings.follow_ups.type', 'once_a_day']},
246
+ 'then': this_day,
247
+ 'else': after_week
248
+ }
249
+ },
250
+ 'followup_date': 1,
251
+ 'contact': 1,
252
+ 'message.sender_id': 1,
253
+ 'message.source': 1
254
+ }
255
+ },
256
+ {'$group': {'_id': '$user_id', 'leads': {'$push': '$$ROOT'}}},
257
+ {
258
+ '$addFields': {
259
+ 'actual_follow_ups': {
260
+ '$filter': {
261
+ 'input': '$leads',
262
+ 'as': 'lead',
263
+ 'cond': {
264
+ '$and': [
265
+ {'$gte': ['$$lead.followup_date', now]},
266
+ {'$lte': ['$$lead.followup_date', '$$lead.followup_to']}
267
+ ]
268
+ }
269
+ }
270
+ },
271
+ 'overdue_follow_ups': {
272
+ '$filter': {
273
+ 'input': '$leads',
274
+ 'as': 'lead',
275
+ 'cond': {'$lt': ['$$lead.followup_date', now]}
276
+ }
277
+ }
278
+ }
279
+ },
280
+ {
281
+ '$addFields': {
282
+ 'email': {'$first': '$leads.user_email'},
283
+ 'notification_settings': {'$first': '$leads.notification_settings'}}
284
+ },
285
+ {'$match': {'actual_follow_ups': {'$ne': []}}},
286
+ {'$unset': ['leads']}
287
+ ]
288
+ return [UserFollowUps.from_dic(doc) for doc in list(self.collection().aggregate(pipeline))]
289
+
179
290
  def update_source(self, source_id: str, source_data: dict):
180
291
  self.collection().update_many({'message.source.source_id': source_id},
181
292
  {'$set': {'message.source': source_data}})
@@ -222,22 +333,22 @@ class UserLeadMongoRepository(BaseMongoRepository):
222
333
  archived = kwargs.get('archived')
223
334
  from_date = kwargs.get('from_date')
224
335
  to_date = kwargs.get('to_date')
225
- has_followup = kwargs.get('has_followup', None)
226
- followup_to = kwargs.get('followup_to_date', None)
227
- followup_from = kwargs.get('followup_from_date', None)
228
- created_to = kwargs.get('created_to_date', None)
229
- created_from = kwargs.get('created_from_date', None)
230
- sender_ids = kwargs.get('sender_ids', None)
231
- text = kwargs.get('text', None)
232
- stop_words = kwargs.get('stop_words', None)
233
- tags = kwargs.get('tags', None)
234
- configs = kwargs.get('config', None)
235
- bots_names = kwargs.get('bots_names', None)
236
- locations = kwargs.get('locations', None)
237
- with_chat = kwargs.get('with_chat', None)
238
- leads_ids = kwargs.get('leads_ids', None)
239
- exclude_leads = kwargs.get('exclude_leads', None)
240
- exclude_senders = kwargs.get('exclude_senders', None)
336
+ has_followup = kwargs.get('has_followup')
337
+ followup_to = kwargs.get('followup_to_date')
338
+ followup_from = kwargs.get('followup_from_date')
339
+ created_to = kwargs.get('created_to_date')
340
+ created_from = kwargs.get('created_from_date')
341
+ sender_ids = kwargs.get('sender_ids')
342
+ text = kwargs.get('text')
343
+ stop_words = kwargs.get('stop_words')
344
+ tags = kwargs.get('tags')
345
+ configs = kwargs.get('config')
346
+ bots_names = kwargs.get('bots_names')
347
+ locations = kwargs.get('locations')
348
+ with_chat = kwargs.get('with_chat')
349
+ leads_ids = kwargs.get('leads_ids')
350
+ exclude_leads = kwargs.get('exclude_leads')
351
+ exclude_senders = kwargs.get('exclude_senders')
241
352
  deleted = kwargs.get('deleted', False)
242
353
 
243
354
  pipeline['message.profile.display_name'] = {
@@ -549,18 +660,17 @@ class LeadMongoRepository(BaseMongoRepository):
549
660
  from_date: datetime = kwargs.get('from_date')
550
661
  to_date: datetime = kwargs.get('to_date')
551
662
 
552
- country = kwargs.get('country', None)
553
- user_id = kwargs.get('user_id', None)
554
- tags = kwargs.get('tags', None)
555
- text = kwargs.get('text', None)
556
- stop_words = kwargs.get('stop_words', None)
557
- exclude_leads = kwargs.get('exclude_leads', None)
558
- exclude_senders = kwargs.get('exclude_senders', None)
559
- excluded_channels = kwargs.get('excluded_channels', None)
560
- sender_ids = kwargs.get('sender_ids', None)
561
- configs = kwargs.get('config', None)
562
- bots_names = kwargs.get('bots_names', None)
563
- locations = kwargs.get('locations', None)
663
+ country = kwargs.get('country')
664
+ user_id = kwargs.get('user_id')
665
+ tags = kwargs.get('tags')
666
+ text = kwargs.get('text')
667
+ stop_words = kwargs.get('stop_words')
668
+ exclude_leads = kwargs.get('exclude_leads')
669
+ exclude_senders = kwargs.get('exclude_senders')
670
+ sender_ids = kwargs.get('sender_ids')
671
+ configs = kwargs.get('config')
672
+ bots_names = kwargs.get('bots_names')
673
+ locations = kwargs.get('locations')
564
674
  publication_text = kwargs.get('publication_text')
565
675
 
566
676
  pipeline['message.profile.display_name'] = {
@@ -610,13 +720,6 @@ class LeadMongoRepository(BaseMongoRepository):
610
720
  if exclude_senders:
611
721
  pipeline['message.sender_id'] = {'$nin': exclude_senders}
612
722
 
613
- if excluded_channels:
614
- pipeline['$and'] = []
615
- for ws, channels in excluded_channels.items():
616
- if channels is not None:
617
- pipeline['$and'].append(
618
- {'$or': [{'message.name': {'$ne': ws}}, {'message.channel_id': {'$nin': channels}}]})
619
-
620
723
  if sender_ids:
621
724
  pipeline['message.sender_id'] = {'$in': sender_ids}
622
725
 
@@ -848,6 +951,9 @@ class DedicatedBotRepository(BaseMongoRepository):
848
951
  kwargs["only_valid"] = only_valid
849
952
  pipeline = self.__create_bots_filter(**kwargs)
850
953
  docs = self.collection().find(pipeline)
954
+ sort_by = kwargs.get('sort_by')
955
+ if sort_by:
956
+ docs = docs.sort({sort_by: 1})
851
957
  return [DedicatedBotModel.from_dic(doc) for doc in docs]
852
958
 
853
959
  def get_source_ids_for_user(self, **kwargs):
@@ -864,7 +970,7 @@ class DedicatedBotRepository(BaseMongoRepository):
864
970
 
865
971
  @staticmethod
866
972
  def __create_bots_filter(**kwargs):
867
- pipeline = {}
973
+ pipeline: dict = {}
868
974
  name = kwargs.get('name')
869
975
  source_type = kwargs.get('source_type', None)
870
976
  user_name = kwargs.get('user_name')
@@ -876,6 +982,8 @@ class DedicatedBotRepository(BaseMongoRepository):
876
982
  source_id = kwargs.get('source_id')
877
983
  server_id = kwargs.get('server_id')
878
984
  active_server_id = kwargs.get('active_server_id')
985
+ invalid_creds = kwargs.get('invalid_creds')
986
+ sort_by = kwargs.get('sort_by')
879
987
 
880
988
  if bot_id:
881
989
  pipeline["_id"] = to_object_id(bot_id)
@@ -904,12 +1012,18 @@ class DedicatedBotRepository(BaseMongoRepository):
904
1012
  if only_valid:
905
1013
  pipeline['invalid_creds'] = False
906
1014
 
1015
+ if invalid_creds is not None:
1016
+ pipeline['invalid_creds'] = invalid_creds
1017
+
907
1018
  if not include_deleted:
908
1019
  pipeline['deleted'] = False
909
1020
 
910
1021
  if not include_paused and source_type == SourceType.DISCORD:
911
1022
  pipeline['paused'] = False
912
1023
 
1024
+ if sort_by:
1025
+ pipeline['sort_by'] = {'$exists': True}
1026
+
913
1027
  return pipeline
914
1028
 
915
1029
 
@@ -1144,15 +1258,59 @@ class ChatRepository(BaseMongoRepository):
1144
1258
 
1145
1259
  if from_date:
1146
1260
  start = datetime(from_date.year, from_date.month, from_date.day, tzinfo=tz.tzutc())
1147
- pipeline['last_action_at']['$gte'] = start
1261
+ pipeline['created_at']['$gte'] = start
1148
1262
 
1149
1263
  if to_date:
1150
1264
  end = datetime(to_date.year, to_date.month, to_date.day, 23, 59, 59, tzinfo=tz.tzutc())
1151
- pipeline['last_action_at']['$lte'] = end
1265
+ pipeline['created_at']['$lte'] = end
1152
1266
 
1153
1267
  return [ChatMessage.from_dic(msg)
1154
1268
  for msg in self.collection().find(pipeline).sort("id", pymongo.ASCENDING)]
1155
1269
 
1270
+ def get_aggregated_list(self, **kwargs):
1271
+ match_pipeline = {}
1272
+ user_id = kwargs.get('user_id')
1273
+ from_date = kwargs.get('from_date')
1274
+ to_date = kwargs.get('to_date')
1275
+ sort_by = kwargs.get('sort_by', 'id')
1276
+ viewed = kwargs.get('viewed')
1277
+
1278
+ if user_id:
1279
+ match_pipeline['user_id'] = to_object_id(user_id)
1280
+
1281
+ if from_date or to_date:
1282
+ match_pipeline['created_at'] = {}
1283
+
1284
+ if viewed is not None:
1285
+ match_pipeline['viewed'] = viewed
1286
+
1287
+ if from_date:
1288
+ start = datetime(from_date.year, from_date.month, from_date.day, tzinfo=tz.tzutc())
1289
+ match_pipeline['created_at']['$gte'] = start
1290
+
1291
+ if to_date:
1292
+ end = datetime(to_date.year, to_date.month, to_date.day, 23, 59, 59, tzinfo=tz.tzutc())
1293
+ match_pipeline['created_at']['$lte'] = end
1294
+
1295
+ pipeline = [
1296
+ {'$match': match_pipeline},
1297
+ {
1298
+ '$lookup': {
1299
+ 'from': 'slack_contact',
1300
+ 'localField': 'sender_id',
1301
+ 'foreignField': 'sender_id',
1302
+ 'as': 'user'
1303
+ }
1304
+ },
1305
+ {
1306
+ '$addFields': {'user': {'$first': '$user'}}
1307
+ },
1308
+ {
1309
+ '$sort': {sort_by: 1}
1310
+ }]
1311
+
1312
+ return self.collection().aggregate(pipeline)
1313
+
1156
1314
  def get_grouped_messages(self, **kwargs):
1157
1315
  match_pipeline = {}
1158
1316
  pipeline = [
@@ -1202,6 +1360,28 @@ class MessageRequestsRepository(BaseMongoRepository):
1202
1360
  self.collection().update_one({'user_id': to_object_id(user_id), 'sender_id': sender_id},
1203
1361
  {'$set': message_request.to_dic()}, upsert=True)
1204
1362
 
1363
+ def get_many(self, user_id: str, viewed: bool = False):
1364
+ pipeline = [
1365
+ {
1366
+ '$match': {'user_id': to_object_id(user_id), 'viewed': viewed}
1367
+ },
1368
+ {
1369
+ '$lookup': {
1370
+ 'from': 'slack_contact',
1371
+ 'localField': 'sender_id',
1372
+ 'foreignField': 'sender_id',
1373
+ 'as': 'user'
1374
+ }
1375
+ },
1376
+ {
1377
+ '$addFields': {'user': {'$first': '$user'}}
1378
+ },
1379
+ {
1380
+ '$sort': {'created_at': 1}
1381
+ }
1382
+ ]
1383
+ return self.collection().aggregate(pipeline)
1384
+
1205
1385
  def find(self, user_id: str, sender_id: str):
1206
1386
  return self.collection().find_one({'user_id': to_object_id(user_id), 'sender_id': sender_id})
1207
1387
 
File without changes
File without changes
@@ -0,0 +1,28 @@
1
+ from bson import ObjectId
2
+
3
+ from lgt_jobs.lgt_data.models.post.post import Post
4
+ from lgt_jobs.lgt_data.mongo_repository import BaseMongoRepository
5
+
6
+
7
+ class PostsRepository(BaseMongoRepository):
8
+ collection_name = 'posts'
9
+
10
+ def get_users_posts(self, ids: list[str]) -> dict[ObjectId, list[Post]]:
11
+ pipeline = [
12
+ {
13
+ '$lookup': {
14
+ 'from': 'posted_messages',
15
+ 'as': 'messages',
16
+ 'let': {'id': '$_id'},
17
+ 'pipeline': [
18
+ {'$match': {'$expr': {'$and': [{'$in': ['$id', ids]}, {'$eq': ['$$id', '$post_id']}]}}}
19
+ ]}},
20
+ {
21
+ '$match': {'messages': {'$ne': []}}
22
+ },
23
+ {
24
+ '$group': {'_id': '$user_id', 'posts': {'$push': '$$ROOT'}}
25
+ }]
26
+ docs = self.collection().aggregate(pipeline)
27
+ posts_map = {doc['_id']: [Post.from_dic(post) for post in doc['posts']] for doc in docs}
28
+ return posts_map
lgt_jobs/runner.py CHANGED
@@ -2,8 +2,8 @@ import json
2
2
  from datetime import datetime, UTC
3
3
  from lgt_jobs.lgt_common.pubsub.messages import publish_message2_pubsub
4
4
  from lgt_jobs.lgt_data.engine import DelayedJob
5
- from .basejobs import InvalidJobTypeException, BaseBackgroundJobData, BaseBackgroundJob
6
- from .env import background_jobs_topic
5
+ from lgt_jobs.basejobs import InvalidJobTypeException, BaseBackgroundJobData, BaseBackgroundJob
6
+ from lgt_jobs.env import background_jobs_topic
7
7
 
8
8
 
9
9
  def datetime_converter(o):
lgt_jobs/simple_job.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from abc import ABC
2
2
  from typing import Optional
3
3
  from pydantic import BaseModel
4
- from .basejobs import BaseBackgroundJobData, BaseBackgroundJob
4
+ from lgt_jobs.basejobs import BaseBackgroundJobData, BaseBackgroundJob
5
5
 
6
6
 
7
7
  class SimpleTestJobData(BaseBackgroundJobData, BaseModel):