• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DataBiosphere / consent / #5929

19 May 2025 12:36PM UTC coverage: 78.551% (-0.2%) from 78.727%
#5929

push

web-flow
DT-1564, Progress Report DAC flow (#2523)

175 of 228 new or added lines in 17 files covered. (76.75%)

5 existing lines in 3 files now uncovered.

10027 of 12765 relevant lines covered (78.55%)

0.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.88
/src/main/java/org/broadinstitute/consent/http/service/dao/DarCollectionServiceDAO.java
1
package org.broadinstitute.consent.http.service.dao;
2

3
import com.google.inject.Inject;
4
import java.sql.SQLException;
5
import java.util.ArrayList;
6
import java.util.Date;
7
import java.util.List;
8
import java.util.Objects;
9
import org.broadinstitute.consent.http.db.DatasetDAO;
10
import org.broadinstitute.consent.http.db.ElectionDAO;
11
import org.broadinstitute.consent.http.db.UserDAO;
12
import org.broadinstitute.consent.http.enumeration.ElectionStatus;
13
import org.broadinstitute.consent.http.enumeration.ElectionType;
14
import org.broadinstitute.consent.http.enumeration.UserRoles;
15
import org.broadinstitute.consent.http.enumeration.VoteType;
16
import org.broadinstitute.consent.http.models.DataAccessRequest;
17
import org.broadinstitute.consent.http.models.Election;
18
import org.broadinstitute.consent.http.models.User;
19
import org.jdbi.v3.core.Handle;
20
import org.jdbi.v3.core.Jdbi;
21
import org.jdbi.v3.core.statement.Update;
22

23
public class DarCollectionServiceDAO {
24

25
  private final DatasetDAO datasetDAO;
26
  private final ElectionDAO electionDAO;
27
  private final Jdbi jdbi;
28
  private final UserDAO userDAO;
29

30
  @Inject
31
  public DarCollectionServiceDAO(DatasetDAO datasetDAO, ElectionDAO electionDAO, Jdbi jdbi,
32
      UserDAO userDAO) {
1✔
33
    this.datasetDAO = datasetDAO;
1✔
34
    this.electionDAO = electionDAO;
1✔
35
    this.jdbi = jdbi;
1✔
36
    this.userDAO = userDAO;
1✔
37
  }
1✔
38

39
  /**
40
   * Find all Dar + Dataset combinations that are available to the user. - Admins have
41
   * all available to them - Chairs can only create elections for datasets in their DACs
42
   * <p>
43
   * DataAccessRequests with no elections, or with previously canceled elections, are valid for
44
   * initiating a new set of elections. Any DAR elections in open state should be ignored.
45
   *
46
   * @param user   The User initiating new elections for a data access request
47
   * @param dar    The DataAccessRequest
48
   * @return List of reference ids for which a DAR election was created
49
   */
50
  public List<String> createElectionsForDarByUser(User user, DataAccessRequest dar)
51
      throws SQLException {
52
    final Date now = new Date();
1✔
53
    boolean isAdmin = user.hasUserRole(UserRoles.ADMIN);
1✔
54
    List<String> createdElectionReferenceIds = new ArrayList<>();
1✔
55
    // If the user is not an admin, we need to know what datasets they have access to.
56
    List<Integer> dacUserDatasetIds = isAdmin ?
1✔
57
        List.of() :
1✔
58
        datasetDAO.findDatasetIdsByDACUserId(user.getUserId());
1✔
59
    jdbi.useHandle(
1✔
60
        handle -> {
61
          // By default, new connections are set to auto-commit which breaks our rollback strategy.
62
          // Turn that off for this connection. This will not affect existing or new connections and
63
          // only applies to the current one in this handle.
64
          handle.getConnection().setAutoCommit(false);
1✔
65
          List<Update> inserts = new ArrayList<>();
1✔
66
          // For each Dataset in each DAR, :
67
          //    1. Archive existing, non-open, Elections
68
          //    2. Create an Access Election
69
          //    3. Create member votes for access election
70
          //        3a. Chair Vote for chair
71
          //        3b. Final Vote for chair
72
          //        3c. Agreement Vote for chair if not manual review
73
          //    4. Create an RP Election
74
          //    5. Create member votes for rp election
75
          //        5a. Chair Vote for chair
76

77
          // Only take actions on the most recent DAR/Progress report in the collection
78
          // This means a chair cannot reopen an election in any other DAR in the collection besides
79
          // the most recently submitted progress report.
80
          dar.getDatasetIds().forEach(datasetId -> {
1✔
81
              // If there is an existing open election for this DAR+Dataset, we can ignore it
82
              Election lastDataAccessElection = electionDAO.findLastElectionByReferenceIdDatasetIdAndType(
1✔
83
                  dar.getReferenceId(), datasetId, ElectionType.DATA_ACCESS.getValue());
1✔
84
              boolean ignore =
1✔
85
                  Objects.nonNull(lastDataAccessElection) && lastDataAccessElection.getStatus()
1✔
86
                      .equalsIgnoreCase(ElectionStatus.OPEN.getValue());
1✔
87

88
              // If the user is not an admin, then the dataset must be in the list of the user's DAC Datasets
89
              // Otherwise, we need to skip election creation for this DAR as well.
90
              if (!isAdmin && !dacUserDatasetIds.contains(datasetId)) {
1✔
91
                ignore = true;
1✔
92
              }
93
              if (!ignore) {
1✔
94
                // Archive all old elections for this DAR + Dataset
95
                List<Integer> oldElectionIds = electionDAO
1✔
96
                    .findElectionsByReferenceIdAndDatasetId(dar.getReferenceId(), datasetId)
1✔
97
                    .stream().map(Election::getElectionId)
1✔
98
                    .toList();
1✔
99
                if (!oldElectionIds.isEmpty()) {
1✔
100
                  electionDAO.archiveElectionByIds(oldElectionIds, now);
1✔
101
                }
102
                List<User> voteUsers = findVoteUsersForDataset(datasetId);
1✔
103
                inserts.add(createElectionInsert(handle, ElectionType.DATA_ACCESS.getValue(),
1✔
104
                    dar.getReferenceId(), now, datasetId));
1✔
105
                inserts.addAll(createVoteInsertsForUsers(handle, voteUsers,
1✔
106
                    ElectionType.DATA_ACCESS.getValue(), dar.getReferenceId(), datasetId, now,
1✔
107
                    dar.requiresManualReview()));
1✔
108
                inserts.add(
1✔
109
                    createElectionInsert(handle, ElectionType.RP.getValue(), dar.getReferenceId(),
1✔
110
                        now, datasetId));
111
                inserts.addAll(
1✔
112
                    createVoteInsertsForUsers(handle, voteUsers, ElectionType.RP.getValue(),
1✔
113
                        dar.getReferenceId(), datasetId, now,
1✔
114
                        dar.requiresManualReview()));
1✔
115
                createdElectionReferenceIds.add(dar.getReferenceId());
1✔
116
              }
117
            });
1✔
118
          inserts.forEach(Update::execute);
1✔
119
          handle.commit();
1✔
120
        });
1✔
121
    return createdElectionReferenceIds;
1✔
122
  }
123

124
  private List<Update> createVoteInsertsForUsers(Handle handle, List<User> voteUsers,
125
      String electionType, String referenceId, Integer datasetId, Date now,
126
      Boolean isManualReview) {
127
    List<Update> userVotes = new ArrayList<>();
1✔
128
    voteUsers.forEach(
1✔
129
        u -> {
130
          // All users get a minimum of one DAC vote type for both RP and DataAccess election types
131
          userVotes.add(createVoteInsert(handle, VoteType.DAC.getValue(), electionType, referenceId,
1✔
132
              datasetId, now, u.getUserId()));
1✔
133
          // Chairpersons get a Chairperson vote for both RP and DataAccess election types
134
          if (u.hasUserRole(UserRoles.CHAIRPERSON)) {
1✔
135
            userVotes.add(
1✔
136
                createVoteInsert(handle, VoteType.CHAIRPERSON.getValue(), electionType, referenceId,
1✔
137
                    datasetId, now, u.getUserId()));
1✔
138
            // Chairpersons get Final and Agreement votes for DataAccess elections
139
            if (ElectionType.DATA_ACCESS.getValue().equals(electionType)) {
1✔
140
              userVotes.add(createVoteInsert(handle, VoteType.FINAL.getValue(),
1✔
141
                  ElectionType.DATA_ACCESS.getValue(), referenceId, datasetId, now, u.getUserId()));
1✔
142
              if (!isManualReview) {
1✔
143
                userVotes.add(createVoteInsert(handle, VoteType.AGREEMENT.getValue(),
1✔
144
                    ElectionType.DATA_ACCESS.getValue(), referenceId, datasetId, now,
1✔
145
                    u.getUserId()));
1✔
146
              }
147
            }
148
          }
149
        });
1✔
150
    return userVotes;
1✔
151
  }
152

153
  private Update createVoteInsert(Handle handle, String voteType, String electionType,
154
      String referenceId, Integer datasetId, Date now, Integer userId) {
155
    final String sql =
1✔
156
        " INSERT INTO vote (createdate, user_id, electionid, type, remindersent) "
157
            + " (SELECT :createDate, :userId, election_id, :voteType, false "
158
            + "  FROM election "
159
            + "  WHERE election_type = :electionType "
160
            + "  AND reference_id = :referenceId "
161
            + "  AND dataset_id = :datasetId "
162
            + "  ORDER BY create_date desc "
163
            + "  LIMIT 1) ";
164
    Update insert = handle.createUpdate(sql);
1✔
165
    insert.bind("createDate", now);
1✔
166
    insert.bind("userId", userId);
1✔
167
    insert.bind("voteType", voteType);
1✔
168
    insert.bind("electionType", electionType);
1✔
169
    insert.bind("referenceId", referenceId);
1✔
170
    insert.bind("datasetId", datasetId);
1✔
171
    return insert;
1✔
172
  }
173

174
  private List<User> findVoteUsersForDataset(Integer datasetId) {
175
    List<User> dacUsers =
1✔
176
        new ArrayList<>(userDAO.findUsersForDatasetsByRole(
1✔
177
            List.of(datasetId),
1✔
178
            List.of(UserRoles.CHAIRPERSON.getRoleName(), UserRoles.MEMBER.getRoleName())));
1✔
179
    return dacUsers.isEmpty() ?
1✔
UNCOV
180
        new ArrayList<>(userDAO.findNonDacUsersEnabledToVote()) :
×
181
        dacUsers;
1✔
182
  }
183

184
  private Update createElectionInsert(
185
      Handle handle, String electionType, String referenceId, Date now, Integer datasetId) {
186
    final String sql =
1✔
187
        " INSERT INTO election "
188
            + "        (election_type, status, create_date, reference_id, dataset_id, version) "
189
            + " VALUES (:electionType, :status, :createDate, :referenceId, :datasetId, "
190
            + "         (SELECT coalesce (MAX(version), 0) + 1 "
191
            + "          FROM election AS election_version "
192
            + "          WHERE reference_id = :referenceId "
193
            + "          AND election_type = :electionType "
194
            + "          AND dataset_id = :datasetId) "
195
            + "        )";
196
    Update insert = handle.createUpdate(sql);
1✔
197
    insert.bind("electionType", electionType);
1✔
198
    insert.bind("referenceId", referenceId);
1✔
199
    insert.bind("createDate", now);
1✔
200
    insert.bind("datasetId", datasetId);
1✔
201
    insert.bind("status", ElectionStatus.OPEN.getValue());
1✔
202
    return insert;
1✔
203
  }
204
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc