1 /*
2  *  Make.org Core API
3  *  Copyright (C) 2018 Make.org
4  *
5  * This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU Affero General Public License as
7  *  published by the Free Software Foundation, either version 3 of the
8  *  License, or (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Affero General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Affero General Public License
16  *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  *
18  */
19 
20 package org.make.api.userhistory
21 
22 import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
23 import akka.actor.typed.{ActorRef, Behavior}
24 import akka.persistence.typed.PersistenceId
25 import akka.persistence.typed.scaladsl.{Effect, EventSourcedBehavior, ReplyEffect, RetentionCriteria}
26 import grizzled.slf4j.Logging
27 import org.make.api.sessionhistory.Ack
28 import org.make.api.technical.EffectBuilderHelper._
29 import org.make.api.userhistory.UserHistoryResponse.SessionEventsInjected
30 import org.make.core.technical.Pagination
31 import org.make.core.history.HistoryActions._
32 import org.make.core.proposal.{ProposalId, QualificationKey, VoteKey}
33 
34 import java.time.ZonedDateTime
35 
36 object UserHistoryActor extends Logging {
37 
38   val JournalPluginId: String = "make-api.event-sourcing.users.journal"
39   val SnapshotPluginId: String = "make-api.event-sourcing.users.snapshot"
40   val QueryJournalPluginId: String = "make-api.event-sourcing.users.query"
41 
42   def apply(): Behavior[UserHistoryCommand] = {
43     Behaviors.setup { implicit context =>
44       EventSourcedBehavior
45         .withEnforcedReplies[UserHistoryCommand, UserHistoryEvent[_], UserVotesAndQualifications](
46           persistenceId = PersistenceId.ofUniqueId(context.self.path.name),
47           emptyState = UserVotesAndQualifications(Map.empty),
48           commandHandler(),
49           eventHandler
50         )
51         .withJournalPluginId(JournalPluginId)
52         .withSnapshotPluginId(SnapshotPluginId)
53         .withRetention(RetentionCriteria.snapshotEvery(numberOfEvents = 10, keepNSnapshots = 50))
54     }
55   }
56 
57   def getVotesValues(
58     userHistory: UserVotesAndQualifications,
59     proposalIds: Seq[ProposalId]
60   ): UserHistoryResponse[Map[ProposalId, VoteAndQualifications]] = {
61     UserHistoryResponse(userHistory.votesAndQualifications.filter {
62       case (proposalId, _) => proposalIds.contains(proposalId)
63     })
64   }
65 
66   def getVotedProposals(
67     userHistory: UserVotesAndQualifications,
68     filterVotes: Option[Seq[VoteKey]],
69     filterQualifications: Option[Seq[QualificationKey]],
70     proposalIds: Option[Seq[ProposalId]],
71     limit: Pagination.Limit,
72     offset: Pagination.Offset
73   ): UserHistoryResponse[Seq[ProposalId]] = {
74     val response: Seq[ProposalId] = userHistory.votesAndQualifications.filter {
75       case (proposalId, votesAndQualifications) =>
76         proposalIds.forall(_.contains(proposalId)) &&
77           filterVotes.forall(_.contains(votesAndQualifications.voteKey)) &&
78           filterQualifications.forall { qualifications =>
79             votesAndQualifications.qualificationKeys.exists { case (value, _) => qualifications.contains(value) }
80           } && votesAndQualifications.trust.isTrusted
81     }.toSeq.sortBy { case (_, votesAndQualifications) => votesAndQualifications.date }.map {
82       case (proposalId, _) => proposalId
83     }.slice(offset.extractInt, offset.extractInt + limit.extractInt)
84     UserHistoryResponse(response)
85   }
86 
87   def commandHandler()(implicit context: ActorContext[_]): (
88     UserVotesAndQualifications,
89     UserHistoryCommand
90   ) => ReplyEffect[UserHistoryEvent[_], UserVotesAndQualifications] = {
91     case (_, command: InjectSessionEvents) =>
92       Effect
93         .persist(command.events)
94         .thenReply(command.replyTo)(_ => UserHistoryResponse(SessionEventsInjected))
95     case (state, RequestVoteValues(_, values, replyTo)) => onRetrieveVoteValues(state, values, replyTo)
96     case (
97         state,
98         RequestUserVotedProposalsPaginate(_, filterVotes, filterQualifications, proposalsIds, limit, offset, replyTo)
99         ) =>
100       onRetrieveUserVotedProposals(state, filterVotes, filterQualifications, proposalsIds, limit, offset, replyTo)
101     case (_, UserHistoryTransactionalEnvelope(_, event: TransactionalUserHistoryEvent[_], replyTo)) =>
102       Effect
103         .persist[UserHistoryEvent[_], UserVotesAndQualifications](event)
104         .thenPublish(event)
105         .thenReply(replyTo)(_ => UserHistoryResponse(Ack))
106     case (_, UserHistoryEnvelope(_, event: UserHistoryEvent[_])) =>
107       Effect
108         .persist[UserHistoryEvent[_], UserVotesAndQualifications](event)
109         .thenPublish(event)
110         .thenNoReply()
111     case (_, Stop(_, replyTo)) => Effect.stop().thenReply(replyTo)(_ => UserHistoryResponse(Ack))
112   }
113 
114   def eventHandler: (UserVotesAndQualifications, UserHistoryEvent[_]) => UserVotesAndQualifications = {
115     case (state, LogUserVoteEvent(_, _, UserAction(date, _, UserVote(proposalId, voteKey, voteTrust)))) =>
116       applyVoteEvent(state, proposalId, voteKey, date, voteTrust)
117     case (state, LogUserUnvoteEvent(_, _, UserAction(_, _, UserUnvote(proposalId, voteKey, _)))) =>
118       applyUnvoteEvent(state, proposalId, voteKey)
119     case (
120         state,
121         LogUserQualificationEvent(_, _, UserAction(_, _, UserQualification(proposalId, qualificationKey, voteTrust)))
122         ) =>
123       applyQualificationEvent(state, proposalId, qualificationKey, voteTrust)
124     case (
125         state,
126         LogUserUnqualificationEvent(_, _, UserAction(_, _, UserUnqualification(proposalId, qualificationKey, _)))
127         ) =>
128       applyUnqualificationEvent(state, proposalId, qualificationKey)
129     case (state, _) => state
130   }
131 
132   def onRetrieveVoteValues(
133     state: UserVotesAndQualifications,
134     proposalIds: Seq[ProposalId],
135     replyTo: ActorRef[UserHistoryResponse[Map[ProposalId, VoteAndQualifications]]]
136   ): ReplyEffect[UserHistoryEvent[_], UserVotesAndQualifications] = {
137     Effect.reply(replyTo)(getVotesValues(state, proposalIds))
138   }
139 
140   def onRetrieveUserVotedProposals(
141     state: UserVotesAndQualifications,
142     filterVotes: Option[Seq[VoteKey]],
143     filterQualifications: Option[Seq[QualificationKey]],
144     proposalIds: Option[Seq[ProposalId]],
145     limit: Pagination.Limit,
146     offset: Pagination.Offset,
147     replyTo: ActorRef[UserHistoryResponse[Seq[ProposalId]]]
148   ): ReplyEffect[UserHistoryEvent[_], UserVotesAndQualifications] = {
149     Effect.reply(replyTo)(getVotedProposals(state, filterVotes, filterQualifications, proposalIds, limit, offset))
150   }
151 
152   def applyVoteEvent(
153     state: UserVotesAndQualifications,
154     proposalId: ProposalId,
155     voteKey: VoteKey,
156     voteDate: ZonedDateTime,
157     voteTrust: VoteTrust
158   ): UserVotesAndQualifications = {
159     state.votesAndQualifications.get(proposalId) match {
160       case Some(voteAndQualifications) if voteAndQualifications.voteKey == voteKey => state
161       case _ =>
162         state.copy(votesAndQualifications = state.votesAndQualifications + (proposalId -> VoteAndQualifications(
163           voteKey,
164           Map.empty,
165           voteDate,
166           voteTrust
167         ))
168         )
169     }
170   }
171 
172   def applyQualificationEvent(
173     state: UserVotesAndQualifications,
174     proposalId: ProposalId,
175     qualificationKey: QualificationKey,
176     voteTrust: VoteTrust
177   ): UserVotesAndQualifications = {
178     state.votesAndQualifications.get(proposalId) match {
179       case None => state
180       case Some(voteAndQualifications) =>
181         val newVoteAndQualifications: VoteAndQualifications = voteAndQualifications
182           .copy(qualificationKeys = voteAndQualifications.qualificationKeys + (qualificationKey -> voteTrust))
183         state
184           .copy(votesAndQualifications = state.votesAndQualifications + (proposalId -> newVoteAndQualifications))
185     }
186   }
187 
188   def applyUnqualificationEvent(
189     state: UserVotesAndQualifications,
190     proposalId: ProposalId,
191     qualificationKey: QualificationKey
192   ): UserVotesAndQualifications = {
193     state.votesAndQualifications.get(proposalId) match {
194       case None => state
195       case Some(voteAndQualifications) =>
196         val newVoteAndQualifications =
197           voteAndQualifications
198             .copy(qualificationKeys = voteAndQualifications.qualificationKeys.filter {
199               case (key, _) => key != qualificationKey
200             })
201         state
202           .copy(votesAndQualifications = state.votesAndQualifications + (proposalId -> newVoteAndQualifications))
203     }
204   }
205 
206   def applyUnvoteEvent(
207     state: UserVotesAndQualifications,
208     proposalId: ProposalId,
209     voteKey: VoteKey
210   ): UserVotesAndQualifications = {
211     if (state.votesAndQualifications.get(proposalId).exists(_.voteKey == voteKey)) {
212       state.copy(votesAndQualifications = state.votesAndQualifications - proposalId)
213     } else {
214       state
215     }
216   }
217 }
Line Stmt Id Pos Tree Symbol Tests Code
38 15741 1490 - 1529 Literal <nosymbol> org.make.api.sessionhistory.sessionhistorycoordinatortest "make-api.event-sourcing.users.journal"
39 12048 1563 - 1603 Literal <nosymbol> org.make.api.sessionhistory.sessionhistorycoordinatortest "make-api.event-sourcing.users.snapshot"
40 10271 1641 - 1678 Literal <nosymbol> org.make.api.sessionhistory.sessionhistorycoordinatortest "make-api.event-sourcing.users.query"
43 12661 1732 - 2292 Apply akka.actor.typed.scaladsl.Behaviors.setup org.make.api.sessionhistory.sessionhistorycoordinatortest akka.actor.typed.scaladsl.Behaviors.setup[org.make.api.userhistory.UserHistoryCommand](((implicit context: akka.actor.typed.scaladsl.ActorContext[org.make.api.userhistory.UserHistoryCommand]) => akka.persistence.typed.scaladsl.EventSourcedBehavior.withEnforcedReplies[org.make.api.userhistory.UserHistoryCommand, org.make.api.userhistory.UserHistoryEvent[_], org.make.api.userhistory.UserVotesAndQualifications](akka.persistence.typed.PersistenceId.ofUniqueId(context.self.path.name), UserVotesAndQualifications.apply(scala.Predef.Map.empty[org.make.core.proposal.ProposalId, Nothing]), UserHistoryActor.this.commandHandler()(context), UserHistoryActor.this.eventHandler).withJournalPluginId(UserHistoryActor.this.JournalPluginId).withSnapshotPluginId(UserHistoryActor.this.SnapshotPluginId).withRetention(akka.persistence.typed.scaladsl.RetentionCriteria.snapshotEvery(10, 50))))
46 12760 1922 - 1970 Apply akka.persistence.typed.PersistenceId.ofUniqueId akka.persistence.typed.PersistenceId.ofUniqueId(context.self.path.name)
46 16255 1947 - 1969 Select akka.actor.ActorPath.name context.self.path.name
47 15062 1995 - 2032 Apply org.make.api.userhistory.UserVotesAndQualifications.apply UserVotesAndQualifications.apply(scala.Predef.Map.empty[org.make.core.proposal.ProposalId, Nothing])
47 8921 2022 - 2031 TypeApply scala.collection.immutable.Map.empty scala.Predef.Map.empty[org.make.core.proposal.ProposalId, Nothing]
48 13607 2044 - 2060 ApplyToImplicitArgs org.make.api.userhistory.UserHistoryActor.commandHandler UserHistoryActor.this.commandHandler()(context)
49 9675 2072 - 2084 Select org.make.api.userhistory.UserHistoryActor.eventHandler UserHistoryActor.this.eventHandler
51 15754 2124 - 2139 Select org.make.api.userhistory.UserHistoryActor.JournalPluginId UserHistoryActor.this.JournalPluginId
52 12064 2171 - 2187 Select org.make.api.userhistory.UserHistoryActor.SnapshotPluginId UserHistoryActor.this.SnapshotPluginId
53 10140 2212 - 2285 Apply akka.persistence.typed.scaladsl.RetentionCriteria.snapshotEvery akka.persistence.typed.scaladsl.RetentionCriteria.snapshotEvery(10, 50)
53 16567 1776 - 2286 Apply akka.persistence.typed.scaladsl.EventSourcedBehavior.withRetention akka.persistence.typed.scaladsl.EventSourcedBehavior.withEnforcedReplies[org.make.api.userhistory.UserHistoryCommand, org.make.api.userhistory.UserHistoryEvent[_], org.make.api.userhistory.UserVotesAndQualifications](akka.persistence.typed.PersistenceId.ofUniqueId(context.self.path.name), UserVotesAndQualifications.apply(scala.Predef.Map.empty[org.make.core.proposal.ProposalId, Nothing]), UserHistoryActor.this.commandHandler()(context), UserHistoryActor.this.eventHandler).withJournalPluginId(UserHistoryActor.this.JournalPluginId).withSnapshotPluginId(UserHistoryActor.this.SnapshotPluginId).withRetention(akka.persistence.typed.scaladsl.RetentionCriteria.snapshotEvery(10, 50))
61 13499 2471 - 2604 Apply org.make.api.userhistory.UserHistoryResponse.apply UserHistoryResponse.apply[scala.collection.immutable.Map[org.make.core.proposal.ProposalId,org.make.core.history.HistoryActions.VoteAndQualifications]](userHistory.votesAndQualifications.filter(((x0$1: (org.make.core.proposal.ProposalId, org.make.core.history.HistoryActions.VoteAndQualifications)) => x0$1 match { case (_1: org.make.core.proposal.ProposalId, _2: org.make.core.history.HistoryActions.VoteAndQualifications): (org.make.core.proposal.ProposalId, org.make.core.history.HistoryActions.VoteAndQualifications)((proposalId @ _), _) => proposalIds.contains[org.make.core.proposal.ProposalId](proposalId) })))
61 15076 2491 - 2603 Apply scala.collection.IterableOps.filter userHistory.votesAndQualifications.filter(((x0$1: (org.make.core.proposal.ProposalId, org.make.core.history.HistoryActions.VoteAndQualifications)) => x0$1 match { case (_1: org.make.core.proposal.ProposalId, _2: org.make.core.history.HistoryActions.VoteAndQualifications): (org.make.core.proposal.ProposalId, org.make.core.history.HistoryActions.VoteAndQualifications)((proposalId @ _), _) => proposalIds.contains[org.make.core.proposal.ProposalId](proposalId) }))
62 8934 2565 - 2597 Apply scala.collection.SeqOps.contains proposalIds.contains[org.make.core.proposal.ProposalId](proposalId)
76 10002 3081 - 3103 Apply scala.collection.SeqOps.contains x$1.contains[org.make.core.proposal.ProposalId](proposalId)
77 11946 3137 - 3179 Apply scala.collection.SeqOps.contains x$2.contains[org.make.core.proposal.VoteKey](votesAndQualifications.voteKey)
77 15534 3148 - 3178 Select org.make.core.history.HistoryActions.VoteAndQualifications.voteKey votesAndQualifications.voteKey
77 18372 3118 - 3180 Apply scala.Option.forall filterVotes.forall(((x$2: Seq[org.make.core.proposal.VoteKey]) => x$2.contains[org.make.core.proposal.VoteKey](votesAndQualifications.voteKey)))
78 8947 3194 - 3367 Apply scala.Option.forall filterQualifications.forall(((qualifications: Seq[org.make.core.proposal.QualificationKey]) => votesAndQualifications.qualificationKeys.exists(((x0$2: (org.make.core.proposal.QualificationKey, org.make.core.history.HistoryActions.VoteTrust)) => x0$2 match { case (_1: org.make.core.proposal.QualificationKey, _2: org.make.core.history.HistoryActions.VoteTrust): (org.make.core.proposal.QualificationKey, org.make.core.history.HistoryActions.VoteTrust)((value @ _), _) => qualifications.contains[org.make.core.proposal.QualificationKey](value) }))))
79 16450 3323 - 3353 Apply scala.collection.SeqOps.contains qualifications.contains[org.make.core.proposal.QualificationKey](value)
79 13007 3254 - 3355 Apply scala.collection.IterableOnceOps.exists votesAndQualifications.qualificationKeys.exists(((x0$2: (org.make.core.proposal.QualificationKey, org.make.core.history.HistoryActions.VoteTrust)) => x0$2 match { case (_1: org.make.core.proposal.QualificationKey, _2: org.make.core.history.HistoryActions.VoteTrust): (org.make.core.proposal.QualificationKey, org.make.core.history.HistoryActions.VoteTrust)((value @ _), _) => qualifications.contains[org.make.core.proposal.QualificationKey](value) }))
80 13512 3062 - 3409 Apply scala.Boolean.&& proposalIds.forall(((x$1: Seq[org.make.core.proposal.ProposalId]) => x$1.contains[org.make.core.proposal.ProposalId](proposalId))).&&(filterVotes.forall(((x$2: Seq[org.make.core.proposal.VoteKey]) => x$2.contains[org.make.core.proposal.VoteKey](votesAndQualifications.voteKey)))).&&(filterQualifications.forall(((qualifications: Seq[org.make.core.proposal.QualificationKey]) => votesAndQualifications.qualificationKeys.exists(((x0$2: (org.make.core.proposal.QualificationKey, org.make.core.history.HistoryActions.VoteTrust)) => x0$2 match { case (_1: org.make.core.proposal.QualificationKey, _2: org.make.core.history.HistoryActions.VoteTrust): (org.make.core.proposal.QualificationKey, org.make.core.history.HistoryActions.VoteTrust)((value @ _), _) => qualifications.contains[org.make.core.proposal.QualificationKey](value) }))))).&&(votesAndQualifications.trust.isTrusted)
80 14947 3371 - 3409 Select org.make.core.history.HistoryActions.VoteTrust.isTrusted votesAndQualifications.trust.isTrusted
81 11962 3429 - 3429 ApplyToImplicitArgs scala.math.LowPriorityOrderingImplicits.ordered math.this.Ordering.ordered[java.time.ZonedDateTime](scala.Predef.$conforms[java.time.ZonedDateTime])
81 9873 3467 - 3494 Select org.make.core.history.HistoryActions.VoteAndQualifications.date votesAndQualifications.date
81 15734 3429 - 3429 TypeApply scala.Predef.$conforms scala.Predef.$conforms[java.time.ZonedDateTime]
83 16466 3595 - 3611 Select org.make.core.technical.Pagination.extractInt limit.extractInt
83 18272 3556 - 3573 Select org.make.core.technical.Pagination.extractInt offset.extractInt
83 9168 2959 - 3612 Apply scala.collection.IterableOps.slice userHistory.votesAndQualifications.filter(((x0$1: (org.make.core.proposal.ProposalId, org.make.core.history.HistoryActions.VoteAndQualifications)) => x0$1 match { case (_1: org.make.core.proposal.ProposalId, _2: org.make.core.history.HistoryActions.VoteAndQualifications): (org.make.core.proposal.ProposalId, org.make.core.history.HistoryActions.VoteAndQualifications)((proposalId @ _), (votesAndQualifications @ _)) => proposalIds.forall(((x$1: Seq[org.make.core.proposal.ProposalId]) => x$1.contains[org.make.core.proposal.ProposalId](proposalId))).&&(filterVotes.forall(((x$2: Seq[org.make.core.proposal.VoteKey]) => x$2.contains[org.make.core.proposal.VoteKey](votesAndQualifications.voteKey)))).&&(filterQualifications.forall(((qualifications: Seq[org.make.core.proposal.QualificationKey]) => votesAndQualifications.qualificationKeys.exists(((x0$2: (org.make.core.proposal.QualificationKey, org.make.core.history.HistoryActions.VoteTrust)) => x0$2 match { case (_1: org.make.core.proposal.QualificationKey, _2: org.make.core.history.HistoryActions.VoteTrust): (org.make.core.proposal.QualificationKey, org.make.core.history.HistoryActions.VoteTrust)((value @ _), _) => qualifications.contains[org.make.core.proposal.QualificationKey](value) }))))).&&(votesAndQualifications.trust.isTrusted) })).toSeq.sortBy[java.time.ZonedDateTime](((x0$3: (org.make.core.proposal.ProposalId, org.make.core.history.HistoryActions.VoteAndQualifications)) => x0$3 match { case (_1: org.make.core.proposal.ProposalId, _2: org.make.core.history.HistoryActions.VoteAndQualifications): (org.make.core.proposal.ProposalId, org.make.core.history.HistoryActions.VoteAndQualifications)(_, (votesAndQualifications @ _)) => votesAndQualifications.date }))(math.this.Ordering.ordered[java.time.ZonedDateTime](scala.Predef.$conforms[java.time.ZonedDateTime])).map[org.make.core.proposal.ProposalId](((x0$4: (org.make.core.proposal.ProposalId, org.make.core.history.HistoryActions.VoteAndQualifications)) => x0$4 match { case (_1: org.make.core.proposal.ProposalId, _2: org.make.core.history.HistoryActions.VoteAndQualifications): (org.make.core.proposal.ProposalId, org.make.core.history.HistoryActions.VoteAndQualifications)((proposalId @ _), _) => proposalId })).slice(offset.extractInt, offset.extractInt.+(limit.extractInt))
83 12756 3575 - 3611 Apply scala.Int.+ offset.extractInt.+(limit.extractInt)
84 14968 3617 - 3646 Apply org.make.api.userhistory.UserHistoryResponse.apply UserHistoryResponse.apply[Seq[org.make.core.proposal.ProposalId]](response)
93 13417 3916 - 3930 Select org.make.api.userhistory.InjectSessionEvents.events command.events
94 15748 3993 - 4014 Select org.make.api.userhistory.UserHistoryResponse.SessionEventsInjected org.make.api.userhistory.UserHistoryResponse.SessionEventsInjected
94 18290 3892 - 4016 Apply akka.persistence.typed.scaladsl.EffectBuilder.thenReply akka.persistence.typed.scaladsl.Effect.persist[org.make.api.userhistory.UserHistoryEvent[_], org.make.api.userhistory.UserVotesAndQualifications](command.events).thenReply[org.make.api.userhistory.UserHistoryResponse[org.make.api.userhistory.UserHistoryResponse.SessionEventsInjected.type]](command.replyTo)(((x$3: org.make.api.userhistory.UserVotesAndQualifications) => UserHistoryResponse.apply[org.make.api.userhistory.UserHistoryResponse.SessionEventsInjected.type](org.make.api.userhistory.UserHistoryResponse.SessionEventsInjected)))
94 9895 3951 - 3966 Select org.make.api.userhistory.InjectSessionEvents.replyTo command.replyTo
94 12190 3973 - 4015 Apply org.make.api.userhistory.UserHistoryResponse.apply UserHistoryResponse.apply[org.make.api.userhistory.UserHistoryResponse.SessionEventsInjected.type](org.make.api.userhistory.UserHistoryResponse.SessionEventsInjected)
95 16363 4076 - 4120 Apply org.make.api.userhistory.UserHistoryActor.onRetrieveVoteValues UserHistoryActor.this.onRetrieveVoteValues(state, values, replyTo)
100 12654 4284 - 4392 Apply org.make.api.userhistory.UserHistoryActor.onRetrieveUserVotedProposals UserHistoryActor.this.onRetrieveUserVotedProposals(state, filterVotes, filterQualifications, proposalsIds, limit, offset, replyTo)
103 9185 4502 - 4581 Apply akka.persistence.typed.scaladsl.Effect.persist akka.persistence.typed.scaladsl.Effect.persist[org.make.api.userhistory.UserHistoryEvent[_], org.make.api.userhistory.UserVotesAndQualifications](event)
105 9685 4502 - 4668 Apply akka.persistence.typed.scaladsl.EffectBuilder.thenReply org.make.api.technical.EffectBuilderHelper.PublishOps[org.make.api.userhistory.UserHistoryEvent[_], org.make.api.userhistory.UserVotesAndQualifications](akka.persistence.typed.scaladsl.Effect.persist[org.make.api.userhistory.UserHistoryEvent[_], org.make.api.userhistory.UserVotesAndQualifications](event)).thenPublish(event)(context).thenReply[org.make.api.userhistory.UserHistoryResponse[org.make.api.sessionhistory.Ack.type]](replyTo)(((x$4: org.make.api.userhistory.UserVotesAndQualifications) => UserHistoryResponse.apply[org.make.api.sessionhistory.Ack.type](org.make.api.sessionhistory.Ack)))
105 14924 4663 - 4666 Select org.make.api.sessionhistory.Ack org.make.api.sessionhistory.Ack
105 11726 4643 - 4667 Apply org.make.api.userhistory.UserHistoryResponse.apply UserHistoryResponse.apply[org.make.api.sessionhistory.Ack.type](org.make.api.sessionhistory.Ack)
110 15637 4743 - 4873 Apply akka.persistence.typed.scaladsl.EffectBuilder.thenNoReply org.make.api.technical.EffectBuilderHelper.PublishOps[org.make.api.userhistory.UserHistoryEvent[_], org.make.api.userhistory.UserVotesAndQualifications](akka.persistence.typed.scaladsl.Effect.persist[org.make.api.userhistory.UserHistoryEvent[_], org.make.api.userhistory.UserVotesAndQualifications](event)).thenPublish(event)(context).thenNoReply()
111 17895 4966 - 4966 Literal <nosymbol> ()
111 12209 4966 - 4969 Select org.make.api.sessionhistory.Ack org.make.api.sessionhistory.Ack
111 12666 4908 - 4971 Apply akka.persistence.typed.scaladsl.EffectBuilder.thenReply akka.persistence.typed.scaladsl.Effect.stop[Nothing, org.make.api.userhistory.UserVotesAndQualifications]().thenReply[org.make.api.userhistory.UserHistoryResponse[Unit]](replyTo)(((x$5: org.make.api.userhistory.UserVotesAndQualifications) => UserHistoryResponse.apply[Unit]({ org.make.api.sessionhistory.Ack; () })))
111 16377 4946 - 4970 Apply org.make.api.userhistory.UserHistoryResponse.apply UserHistoryResponse.apply[Unit]({ org.make.api.sessionhistory.Ack; () })
116 9062 5194 - 5253 Apply org.make.api.userhistory.UserHistoryActor.applyVoteEvent UserHistoryActor.this.applyVoteEvent(state, proposalId, voteKey, date, voteTrust)
118 14941 5360 - 5404 Apply org.make.api.userhistory.UserHistoryActor.applyUnvoteEvent UserHistoryActor.this.applyUnvoteEvent(state, proposalId, voteKey)
123 11484 5568 - 5639 Apply org.make.api.userhistory.UserHistoryActor.applyQualificationEvent UserHistoryActor.this.applyQualificationEvent(state, proposalId, qualificationKey, voteTrust)
128 9582 5799 - 5861 Apply org.make.api.userhistory.UserHistoryActor.applyUnqualificationEvent UserHistoryActor.this.applyUnqualificationEvent(state, proposalId, qualificationKey)
137 12443 6154 - 6211 Apply akka.persistence.typed.scaladsl.Effect.reply akka.persistence.typed.scaladsl.Effect.reply[org.make.api.userhistory.UserHistoryResponse[Map[org.make.core.proposal.ProposalId,org.make.core.history.HistoryActions.VoteAndQualifications]], Nothing, org.make.api.userhistory.UserVotesAndQualifications](replyTo)(UserHistoryActor.this.getVotesValues(state, proposalIds))
137 15649 6176 - 6210 Apply org.make.api.userhistory.UserHistoryActor.getVotesValues UserHistoryActor.this.getVotesValues(state, proposalIds)
149 17916 6646 - 6733 Apply org.make.api.userhistory.UserHistoryActor.getVotedProposals UserHistoryActor.this.getVotedProposals(state, filterVotes, filterQualifications, proposalIds, limit, offset)
149 16463 6624 - 6734 Apply akka.persistence.typed.scaladsl.Effect.reply akka.persistence.typed.scaladsl.Effect.reply[org.make.api.userhistory.UserHistoryResponse[Seq[org.make.core.proposal.ProposalId]], Nothing, org.make.api.userhistory.UserVotesAndQualifications](replyTo)(UserHistoryActor.this.getVotedProposals(state, filterVotes, filterQualifications, proposalIds, limit, offset))
159 12571 6945 - 6989 Apply scala.collection.MapOps.get state.votesAndQualifications.get(proposalId)
160 9079 7040 - 7080 Apply java.lang.Object.== voteAndQualifications.voteKey.==(voteKey)
162 11369 7196 - 7308 Apply org.make.core.history.HistoryActions.VoteAndQualifications.apply org.make.core.history.HistoryActions.VoteAndQualifications.apply(voteKey, scala.Predef.Map.empty[org.make.core.proposal.QualificationKey, Nothing], voteDate, voteTrust)
162 12453 7114 - 7319 Apply org.make.api.userhistory.UserVotesAndQualifications.copy state.copy(state.votesAndQualifications.+[org.make.core.history.HistoryActions.VoteAndQualifications](scala.Predef.ArrowAssoc[org.make.core.proposal.ProposalId](proposalId).->[org.make.core.history.HistoryActions.VoteAndQualifications](org.make.core.history.HistoryActions.VoteAndQualifications.apply(voteKey, scala.Predef.Map.empty[org.make.core.proposal.QualificationKey, Nothing], voteDate, voteTrust))))
162 15551 7150 - 7309 Apply scala.collection.immutable.MapOps.+ state.votesAndQualifications.+[org.make.core.history.HistoryActions.VoteAndQualifications](scala.Predef.ArrowAssoc[org.make.core.proposal.ProposalId](proposalId).->[org.make.core.history.HistoryActions.VoteAndQualifications](org.make.core.history.HistoryActions.VoteAndQualifications.apply(voteKey, scala.Predef.Map.empty[org.make.core.proposal.QualificationKey, Nothing], voteDate, voteTrust)))
162 9890 7182 - 7308 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[org.make.core.proposal.ProposalId](proposalId).->[org.make.core.history.HistoryActions.VoteAndQualifications](org.make.core.history.HistoryActions.VoteAndQualifications.apply(voteKey, scala.Predef.Map.empty[org.make.core.proposal.QualificationKey, Nothing], voteDate, voteTrust))
164 15427 7248 - 7257 TypeApply scala.collection.immutable.Map.empty scala.Predef.Map.empty[org.make.core.proposal.QualificationKey, Nothing]
178 17933 7534 - 7578 Apply scala.collection.MapOps.get state.votesAndQualifications.get(proposalId)
182 12898 7774 - 7847 Apply scala.collection.immutable.MapOps.+ voteAndQualifications.qualificationKeys.+[org.make.core.history.HistoryActions.VoteTrust](scala.Predef.ArrowAssoc[org.make.core.proposal.QualificationKey](qualificationKey).->[org.make.core.history.HistoryActions.VoteTrust](voteTrust))
182 14683 7817 - 7846 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[org.make.core.proposal.QualificationKey](qualificationKey).->[org.make.core.history.HistoryActions.VoteTrust](voteTrust)
182 15439 7749 - 7749 Select org.make.core.history.HistoryActions.VoteAndQualifications.copy$default$3 voteAndQualifications.copy$default$3
182 9353 7749 - 7749 Select org.make.core.history.HistoryActions.VoteAndQualifications.copy$default$1 voteAndQualifications.copy$default$1
182 9780 7716 - 7848 Apply org.make.core.history.HistoryActions.VoteAndQualifications.copy voteAndQualifications.copy(x$2, x$1, x$3, x$4)
182 11718 7749 - 7749 Select org.make.core.history.HistoryActions.VoteAndQualifications.copy$default$4 voteAndQualifications.copy$default$4
184 18408 7857 - 7976 Apply org.make.api.userhistory.UserVotesAndQualifications.copy state.copy(state.votesAndQualifications.+[org.make.core.history.HistoryActions.VoteAndQualifications](scala.Predef.ArrowAssoc[org.make.core.proposal.ProposalId](proposalId).->[org.make.core.history.HistoryActions.VoteAndQualifications](newVoteAndQualifications)))
184 11867 7904 - 7975 Apply scala.collection.immutable.MapOps.+ state.votesAndQualifications.+[org.make.core.history.HistoryActions.VoteAndQualifications](scala.Predef.ArrowAssoc[org.make.core.proposal.ProposalId](proposalId).->[org.make.core.history.HistoryActions.VoteAndQualifications](newVoteAndQualifications))
184 15898 7936 - 7974 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[org.make.core.proposal.ProposalId](proposalId).->[org.make.core.history.HistoryActions.VoteAndQualifications](newVoteAndQualifications)
193 14700 8167 - 8211 Apply scala.collection.MapOps.get state.votesAndQualifications.get(proposalId)
198 9055 8396 - 8513 Apply scala.collection.IterableOps.filter voteAndQualifications.qualificationKeys.filter(((x0$1: (org.make.core.proposal.QualificationKey, org.make.core.history.HistoryActions.VoteTrust)) => x0$1 match { case (_1: org.make.core.proposal.QualificationKey, _2: org.make.core.history.HistoryActions.VoteTrust): (org.make.core.proposal.QualificationKey, org.make.core.history.HistoryActions.VoteTrust)((key @ _), _) => key.!=(qualificationKey) }))
198 15533 8336 - 8514 Apply org.make.core.history.HistoryActions.VoteAndQualifications.copy voteAndQualifications.copy(x$2, x$1, x$3, x$4)
198 17662 8371 - 8371 Select org.make.core.history.HistoryActions.VoteAndQualifications.copy$default$4 voteAndQualifications.copy$default$4
198 11624 8371 - 8371 Select org.make.core.history.HistoryActions.VoteAndQualifications.copy$default$3 voteAndQualifications.copy$default$3
198 14862 8371 - 8371 Select org.make.core.history.HistoryActions.VoteAndQualifications.copy$default$1 voteAndQualifications.copy$default$1
199 12774 8476 - 8499 Apply java.lang.Object.!= key.!=(qualificationKey)
202 14592 8523 - 8642 Apply org.make.api.userhistory.UserVotesAndQualifications.copy state.copy(state.votesAndQualifications.+[org.make.core.history.HistoryActions.VoteAndQualifications](scala.Predef.ArrowAssoc[org.make.core.proposal.ProposalId](proposalId).->[org.make.core.history.HistoryActions.VoteAndQualifications](newVoteAndQualifications)))
202 12079 8602 - 8640 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[org.make.core.proposal.ProposalId](proposalId).->[org.make.core.history.HistoryActions.VoteAndQualifications](newVoteAndQualifications)
202 18201 8570 - 8641 Apply scala.collection.immutable.MapOps.+ state.votesAndQualifications.+[org.make.core.history.HistoryActions.VoteAndQualifications](scala.Predef.ArrowAssoc[org.make.core.proposal.ProposalId](proposalId).->[org.make.core.history.HistoryActions.VoteAndQualifications](newVoteAndQualifications))
211 9076 8810 - 8883 Apply scala.Option.exists state.votesAndQualifications.get(proposalId).exists(((x$6: org.make.core.history.HistoryActions.VoteAndQualifications) => x$6.voteKey.==(voteKey)))
211 12792 8862 - 8882 Apply java.lang.Object.== x$6.voteKey.==(voteKey)
212 15090 8929 - 8970 Apply scala.collection.immutable.MapOps.- state.votesAndQualifications.-(proposalId)
212 17535 8893 - 8971 Block org.make.api.userhistory.UserVotesAndQualifications.copy state.copy(state.votesAndQualifications.-(proposalId))
212 11637 8893 - 8971 Apply org.make.api.userhistory.UserVotesAndQualifications.copy state.copy(state.votesAndQualifications.-(proposalId))
214 15544 8991 - 8996 Ident org.make.api.userhistory.UserHistoryActor.state state