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

HicServices / RDMP / 20714560420

05 Jan 2026 11:53AM UTC coverage: 57.198% (-0.2%) from 57.378%
20714560420

push

github

JFriel
update deps

11495 of 21585 branches covered (53.25%)

Branch coverage included in aggregate %.

32571 of 55456 relevant lines covered (58.73%)

17789.06 hits per line

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

87.88
/Rdmp.Core/Repositories/DataExportRepository.cs
1
// Copyright (c) The University of Dundee 2018-2019
2
// This file is part of the Research Data Management Platform (RDMP).
3
// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
4
// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
5
// You should have received a copy of the GNU General Public License along with RDMP. If not, see <https://www.gnu.org/licenses/>.
6

7
using System;
8
using System.Collections.Generic;
9
using System.Data.Common;
10
using System.Linq;
11
using Rdmp.Core.Curation.Data;
12
using Rdmp.Core.DataExport.Data;
13
using Rdmp.Core.DataExport.DataRelease.Audit;
14
using Rdmp.Core.MapsDirectlyToDatabaseTable;
15
using Rdmp.Core.Repositories.Construction;
16
using Rdmp.Core.Repositories.Managers;
17
using Rdmp.Core.ReusableLibraryCode;
18

19
namespace Rdmp.Core.Repositories;
20

21
/// <summary>
22
/// Pointer to the Data Export Repository database in which all export related DatabaseEntities are stored (e.g. ExtractionConfiguration).  Every DatabaseEntity class must exist in a
23
/// Microsoft Sql Server Database (See DatabaseEntity) and each object is compatible only with a specific type of TableRepository (i.e. the database that contains the
24
/// table matching their name).
25
/// 
26
/// <para>This class allows you to fetch objects and should be passed into constructors of classes you want to construct in the Data Export database.  This includes extraction
27
/// Projects, ExtractionConfigurations, ExtractableCohorts etc.</para>
28
/// 
29
/// <para>Data Export databases are only valid when you have a CatalogueRepository database too and are always paired to a specific CatalogueRepository database (i.e. there are
30
/// IDs in the data export database that specifically map to objects in the Catalogue database).  You can use the CatalogueRepository property to fetch/create objects
31
/// in the paired Catalogue database.</para>
32
/// </summary>
33
public class DataExportRepository : TableRepository, IDataExportRepository
34
{
35
    /// <summary>
36
    /// The paired Catalogue database which contains non extract metadata (i.e. datasets, aggregates, data loads etc).  Some objects in this database
37
    /// contain references to objects in the CatalogueRepository.
38
    /// </summary>
39
    public ICatalogueRepository CatalogueRepository { get; private set; }
6,070✔
40

41
    public IFilterManager FilterManager { get; private set; }
452✔
42

43
    public IDataExportPropertyManager DataExportPropertyManager { get; private set; }
600✔
44

45
    private Lazy<Dictionary<int, List<int>>> _packageContentsDictionary;
46

47
    public DataExportRepository(DbConnectionStringBuilder connectionString, ICatalogueRepository catalogueRepository) :
48
        base(null, connectionString)
392✔
49
    {
50
        CatalogueRepository = catalogueRepository;
392✔
51

52
        FilterManager = new DataExportFilterManager(this);
392✔
53

54
        _packageContentsDictionary = new Lazy<Dictionary<int, List<int>>>(GetPackageContentsDictionary);
392✔
55

56
        DataExportPropertyManager = new DataExportPropertyManager(false, this);
392✔
57

58
        Constructors.Add(typeof(SupplementalExtractionResults),
392✔
59
            (rep, r) => new SupplementalExtractionResults((IDataExportRepository)rep, r));
426✔
60
        Constructors.Add(typeof(CumulativeExtractionResults),
392✔
61
            (rep, r) => new CumulativeExtractionResults((IDataExportRepository)rep, r));
1,100✔
62
        Constructors.Add(typeof(DeployedExtractionFilter),
392✔
63
            (rep, r) => new DeployedExtractionFilter((IDataExportRepository)rep, r));
478✔
64
        Constructors.Add(typeof(DeployedExtractionFilterParameter),
392✔
65
            (rep, r) => new DeployedExtractionFilterParameter((IDataExportRepository)rep, r));
436✔
66
        Constructors.Add(typeof(ExternalCohortTable),
392✔
67
            (rep, r) => new ExternalCohortTable((IDataExportRepository)rep, r));
1,028✔
68
        Constructors.Add(typeof(ExtractableCohort), (rep, r) => new ExtractableCohort((IDataExportRepository)rep, r));
914✔
69
        Constructors.Add(typeof(ExtractableColumn), (rep, r) => new ExtractableColumn((IDataExportRepository)rep, r));
8,680✔
70
        Constructors.Add(typeof(ExtractableDataSetProject), (rep, r) => new ExtractableDataSetProject((IDataExportRepository)rep, r));
4,258✔
71
        Constructors.Add(typeof(ExtractableDataSet), (rep, r) => new ExtractableDataSet((IDataExportRepository)rep, r));
3,446✔
72
        Constructors.Add(typeof(ExtractionConfiguration),
392✔
73
            (rep, r) => new ExtractionConfiguration((IDataExportRepository)rep, r));
1,740✔
74
        Constructors.Add(typeof(FilterContainer), (rep, r) => new FilterContainer((IDataExportRepository)rep, r));
442✔
75
        Constructors.Add(typeof(GlobalExtractionFilterParameter),
392✔
76
            (rep, r) => new GlobalExtractionFilterParameter((IDataExportRepository)rep, r));
398✔
77
        Constructors.Add(typeof(Project), (rep, r) => new Project((IDataExportRepository)rep, r));
3,318✔
78
        Constructors.Add(typeof(SelectedDataSets), (rep, r) => new SelectedDataSets((IDataExportRepository)rep, r));
2,622✔
79
        Constructors.Add(typeof(ExtractableDataSetPackage),
392✔
80
            (rep, r) => new ExtractableDataSetPackage((IDataExportRepository)rep, r));
396✔
81
        Constructors.Add(typeof(ProjectCohortIdentificationConfigurationAssociation),
392✔
82
            (rep, r) => new ProjectCohortIdentificationConfigurationAssociation((IDataExportRepository)rep, r));
1,184✔
83
        Constructors.Add(typeof(SelectedDataSetsForcedJoin),
392✔
84
            (rep, r) => new SelectedDataSetsForcedJoin((IDataExportRepository)rep, r));
396✔
85
    }
392✔
86

87
    public IEnumerable<ICumulativeExtractionResults> GetAllCumulativeExtractionResultsFor(
88
        IExtractionConfiguration configuration, IExtractableDataSet dataset) =>
89
        GetAllObjects<CumulativeExtractionResults>(
112✔
90
            $"WHERE ExtractionConfiguration_ID={configuration.ID}AND ExtractableDataSet_ID={dataset.ID}");
112✔
91

92
    private readonly ObjectConstructor _constructor = new();
392✔
93

94
    protected override IMapsDirectlyToDatabaseTable ConstructEntity(Type t, DbDataReader reader) =>
95
        Constructors.TryGetValue(t, out var constructor)
24,730✔
96
            ? constructor(this, reader)
24,730✔
97
            : ObjectConstructor.ConstructIMapsDirectlyToDatabaseObject<IDataExportRepository>(t, this, reader);
24,730✔
98

99
    public CatalogueExtractabilityStatus GetExtractabilityStatus(ICatalogue c)
100
    {
101
        var eds = GetAllObjectsWithParent<ExtractableDataSet>(c).ToList();
40✔
102
        var isExtractable = !c.IsInternalDataset;
40✔
103
        var extractabilityStatus = new CatalogueExtractabilityStatus(isExtractable, eds.Count != 0 && eds.First().Projects.Any());
40!
104
        return extractabilityStatus;
40✔
105
    }
106

107
    public ISelectedDataSets[] GetSelectedDatasetsWithNoExtractionIdentifiers() =>
108
        SelectAll<SelectedDataSets>(@"
742✔
109
SELECT ID  FROM SelectedDataSets sds
742✔
110
where not exists (
742✔
111
select 1 FROM ExtractableColumn ec where 
742✔
112
ec.ExtractableDataSet_ID = sds.ExtractableDataSet_ID
742✔
113
AND
742✔
114
ec.IsExtractionIdentifier = 1
742✔
115
AND
742✔
116
ec.ExtractionConfiguration_ID = sds.ExtractionConfiguration_ID
742✔
117
)", "ID").ToArray();
742✔
118

119
    private readonly Dictionary<Type, IRowVerCache> _caches = new();
392✔
120

121
    public override T[] GetAllObjects<T>()
122
    {
123
        if (!_caches.ContainsKey(typeof(T)))
10,462✔
124
            _caches.Add(typeof(T), new RowVerCache<T>(this));
1,106✔
125

126
        return _caches[typeof(T)].GetAllObjects<T>();
10,462✔
127
    }
128

129
    public override T[] GetAllObjectsNoCache<T>() => base.GetAllObjects<T>();
8,322✔
130

131

132
    /// <inheritdoc/>
133
    public IExtractableDataSet[] GetAllDataSets(IExtractableDataSetPackage package, IExtractableDataSet[] allDataSets)
134
    {
135
        //we know of no children
136
        return !_packageContentsDictionary.Value.TryGetValue(package.ID, out var contents)
4✔
137
            ? Array.Empty<IExtractableDataSet>()
4✔
138
            : contents.Select(i => allDataSets.Single(ds => ds.ID == i)).ToArray();
8✔
139
    }
140

141

142
    public Dictionary<int, List<int>> GetPackageContentsDictionary()
143
    {
144
        var toReturn = new Dictionary<int, List<int>>();
2✔
145

146
        using var con = GetConnection();
2✔
147
        using var r = DiscoveredServer
2✔
148
            .GetCommand(
2✔
149
                "SELECT * FROM ExtractableDataSetPackage_ExtractableDataSet ORDER BY ExtractableDataSetPackage_ID", con)
2✔
150
            .ExecuteReader();
2✔
151

152
        var lastPackageId = -1;
2✔
153
        while (r.Read())
2!
154
        {
155
            var packageID = Convert.ToInt32(r["ExtractableDataSetPackage_ID"]);
×
156
            var dataSetID = Convert.ToInt32(r["ExtractableDataSet_ID"]);
×
157

158
            if (lastPackageId != packageID)
×
159
            {
160
                toReturn.Add(packageID, new List<int>());
×
161
                lastPackageId = packageID;
×
162
            }
163

164
            toReturn[packageID].Add(dataSetID);
×
165
        }
166

167
        return toReturn;
2✔
168
    }
2✔
169

170
    /// <summary>
171
    /// Adds the given <paramref name="dataSet"/> to the <paramref name="package"/> and updates the cached package contents
172
    /// in memory.
173
    /// 
174
    /// <para>This change is immediately written to the database</para>
175
    ///
176
    ///  <para>Throws ArgumentException if the <paramref name="dataSet"/> is already part of the package</para>
177
    /// </summary>
178
    /// <param name="package"></param>
179
    /// <param name="dataSet"></param>
180
    public void AddDataSetToPackage(IExtractableDataSetPackage package, IExtractableDataSet dataSet)
181
    {
182
        if (_packageContentsDictionary.Value.TryGetValue(package.ID, out var contents))
2!
183
        {
184
            if (contents.Contains(dataSet.ID))
×
185
                throw new ArgumentException($"dataSet {dataSet} is already part of package '{package}'",
×
186
                    nameof(dataSet));
×
187
        }
188
        else
189
        {
190
            _packageContentsDictionary.Value.Add(package.ID, new List<int>());
2✔
191
        }
192

193
        using (var con = GetConnection())
2✔
194
        {
195
            DiscoveredServer.GetCommand(
2✔
196
                $"INSERT INTO ExtractableDataSetPackage_ExtractableDataSet(ExtractableDataSetPackage_ID,ExtractableDataSet_ID) VALUES ({package.ID},{dataSet.ID})",
2✔
197
                con).ExecuteNonQuery();
2✔
198
        }
2✔
199

200
        _packageContentsDictionary.Value[package.ID].Add(dataSet.ID);
2✔
201
    }
2✔
202

203

204
    /// <summary>
205
    /// Removes the given <paramref name="dataSet"/> from the <paramref name="package"/> and updates the cached package contents
206
    /// in memory.
207
    /// 
208
    /// <para>This change is immediately written to the database</para>
209
    ///
210
    ///  <para>Throws ArgumentException if the <paramref name="dataSet"/> is not part of the package</para>
211
    /// </summary>
212
    /// <param name="package"></param>
213
    /// <param name="dataSet"></param>
214
    public void RemoveDataSetFromPackage(IExtractableDataSetPackage package, IExtractableDataSet dataSet)
215
    {
216
        if (!_packageContentsDictionary.Value[package.ID].Contains(dataSet.ID))
4✔
217
            throw new ArgumentException($"dataSet {dataSet} is not part of package {package} so cannot be removed",
2✔
218
                nameof(dataSet));
2✔
219

220
        using (var con = GetConnection())
2✔
221
        {
222
            DiscoveredServer.GetCommand(
2✔
223
                $"DELETE FROM ExtractableDataSetPackage_ExtractableDataSet WHERE ExtractableDataSetPackage_ID = {package.ID} AND ExtractableDataSet_ID ={dataSet.ID}",
2✔
224
                con).ExecuteNonQuery();
2✔
225
        }
2✔
226

227
        _packageContentsDictionary.Value[package.ID].Remove(dataSet.ID);
2✔
228
    }
2✔
229

230
    public IReleaseLog GetReleaseLogEntryIfAny(CumulativeExtractionResults cumulativeExtractionResults)
231
    {
232
        using var con = GetConnection();
58✔
233
        using var cmdselect = DatabaseCommandHelper
58✔
234
            .GetCommand($@"SELECT *
58✔
235
                                    FROM ReleaseLog
58✔
236
                                    where
58✔
237
                                    CumulativeExtractionResults_ID = {cumulativeExtractionResults.ID}", con.Connection,
58✔
238
                con.Transaction);
58✔
239
        using var r = cmdselect.ExecuteReader();
58✔
240
        return r.Read() ? new ReleaseLog(this, r) : null;
58!
241
    }
58✔
242
}
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