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

HicServices / RDMP / 16338436905

17 Jul 2025 07:01AM UTC coverage: 57.31% (-0.004%) from 57.314%
16338436905

push

github

web-flow
RDMP-319: Clone and delete GlobalExtractionFilterParameters Adds logi… (#2209)

* RDMP-319: Clone and delete GlobalExtractionFilterParameters Adds logic to DeepCloneWithNewIDs to clone GlobalExtractionFilterParameters with their values and comments. Updates DeleteInDatabase to remove only the parameters associated with the current configuration.

* Update CHANGELOG.md

11364 of 21372 branches covered (53.17%)

Branch coverage included in aggregate %.

2 of 7 new or added lines in 1 file covered. (28.57%)

32295 of 54809 relevant lines covered (58.92%)

17441.67 hits per line

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

68.55
/Rdmp.Core/DataExport/Data/ExtractionConfiguration.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 FAnsi.Discovery;
12
using Rdmp.Core.Curation.Data;
13
using Rdmp.Core.Curation.Data.Cohort;
14
using Rdmp.Core.Curation.Data.Defaults;
15
using Rdmp.Core.Curation.Data.Pipelines;
16
using Rdmp.Core.Curation.FilterImporting;
17
using Rdmp.Core.DataExport.DataExtraction.Pipeline.Sources;
18
using Rdmp.Core.DataExport.DataRelease.Audit;
19
using Rdmp.Core.Logging;
20
using Rdmp.Core.Logging.PastEvents;
21
using Rdmp.Core.MapsDirectlyToDatabaseTable;
22
using Rdmp.Core.MapsDirectlyToDatabaseTable.Attributes;
23
using Rdmp.Core.QueryBuilding;
24
using Rdmp.Core.Repositories;
25
using Rdmp.Core.ReusableLibraryCode;
26
using Rdmp.Core.ReusableLibraryCode.Annotations;
27
using Rdmp.Core.ReusableLibraryCode.DataAccess;
28

29
namespace Rdmp.Core.DataExport.Data;
30

31
/// <inheritdoc cref="IExtractionConfiguration"/>
32
public class ExtractionConfiguration : DatabaseEntity, IExtractionConfiguration, ICollectSqlParameters, INamed,
33
    ICustomSearchString
34
{
35
    #region Database Properties
36

37
    private DateTime? _dtCreated;
38
    private int? _cohort_ID;
39
    private string _requestTicket;
40
    private string _releaseTicket;
41
    private int _project_ID;
42
    private string _username;
43
    private string _separator;
44
    private string _description;
45
    private bool _isReleased;
46
    private string _name;
47
    private int? _clonedFrom_ID;
48

49
    private int? _defaultPipeline_ID;
50
    private int? _cohortIdentificationConfigurationID;
51
    private int? _cohortRefreshPipelineID;
52

53
    /// <inheritdoc/>
54
    public int? CohortRefreshPipeline_ID
55
    {
56
        get => _cohortRefreshPipelineID;
1,018✔
57
        set => SetField(ref _cohortRefreshPipelineID, value);
1,178✔
58
    }
59

60
    /// <inheritdoc/>
61
    public int? CohortIdentificationConfiguration_ID
62
    {
63
        get => _cohortIdentificationConfigurationID;
1,032✔
64
        set => SetField(ref _cohortIdentificationConfigurationID, value);
1,178✔
65
    }
66

67
    /// <inheritdoc/>
68
    public int? DefaultPipeline_ID
69
    {
70
        get => _defaultPipeline_ID;
1,014✔
71
        set => SetField(ref _defaultPipeline_ID, value);
1,172✔
72
    }
73

74
    /// <inheritdoc/>
75
    public DateTime? dtCreated
76
    {
77
        get => _dtCreated;
1,012✔
78
        set => SetField(ref _dtCreated, value);
1,304✔
79
    }
80

81
    /// <inheritdoc/>
82
    public int? Cohort_ID
83
    {
84
        get => _cohort_ID;
5,484✔
85
        set => SetField(ref _cohort_ID, value);
868✔
86
    }
87

88
    /// <inheritdoc/>
89
    public bool IsExtractable(out string reason)
90
    {
91
        if (IsReleased)
×
92
        {
93
            reason = "ExtractionConfiguration is released so cannot be executed";
×
94
            return false;
×
95
        }
96

97
        if (Cohort_ID == null)
×
98
        {
99
            reason = "No cohort has been configured for ExtractionConfiguration";
×
100
            return false;
×
101
        }
102

103
        if (!GetAllExtractableDataSets().Any())
×
104
        {
105
            reason = "ExtractionConfiguration does not have an selected datasets";
×
106
            return false;
×
107
        }
108

109
        reason = null;
×
110
        return true;
×
111
    }
112

113
    /// <inheritdoc/>
114
    public string RequestTicket
115
    {
116
        get => _requestTicket;
1,112✔
117
        set => SetField(ref _requestTicket, value);
1,172✔
118
    }
119

120
    /// <inheritdoc/>
121
    public string ReleaseTicket
122
    {
123
        get => _releaseTicket;
1,120✔
124
        set => SetField(ref _releaseTicket, value);
1,180✔
125
    }
126

127
    /// <inheritdoc/>
128
    public int Project_ID
129
    {
130
        get => _project_ID;
2,934✔
131
        set => SetField(ref _project_ID, value);
1,296✔
132
    }
133

134
    /// <inheritdoc/>
135
    public string Username
136
    {
137
        get => _username;
1,014✔
138
        set => SetField(ref _username, value);
1,304✔
139
    }
140

141
    /// <inheritdoc/>
142
    public string Separator
143
    {
144
        get => _separator;
1,196✔
145
        set => SetField(ref _separator, value);
1,296✔
146
    }
147

148
    /// <inheritdoc/>
149
    public string Description
150
    {
151
        get => _description;
1,022✔
152
        set => SetField(ref _description, value);
1,304✔
153
    }
154

155
    /// <inheritdoc/>
156
    public bool IsReleased
157
    {
158
        get => _isReleased;
2,484✔
159
        set => SetField(ref _isReleased, value);
1,220✔
160
    }
161

162
    /// <inheritdoc/>
163
    [NotNull]
164
    public string Name
165
    {
166
        get => _name;
1,158✔
167
        set => SetField(ref _name, value);
1,330✔
168
    }
169

170
    /// <inheritdoc/>
171
    public int? ClonedFrom_ID
172
    {
173
        get => _clonedFrom_ID;
1,008✔
174
        set => SetField(ref _clonedFrom_ID, value);
1,180✔
175
    }
176

177
    #endregion
178

179
    #region Relationships
180

181
    /// <inheritdoc/>
182
    [NoMappingToDatabase]
183
    public IProject Project => Repository.GetObjectByID<Project>(Project_ID);
1,056✔
184

185
    /// <inheritdoc/>
186
    [NoMappingToDatabase]
187
    public ISqlParameter[] GlobalExtractionFilterParameters =>
188
        Repository.GetAllObjectsWithParent<GlobalExtractionFilterParameter>(this)
180✔
189
            .Cast<ISqlParameter>().ToArray();
180✔
190

191
    /// <inheritdoc/>
192
    [NoMappingToDatabase]
193
    public IEnumerable<ICumulativeExtractionResults> CumulativeExtractionResults =>
194
        Repository.GetAllObjectsWithParent<CumulativeExtractionResults>(this);
186✔
195

196
    /// <inheritdoc/>
197
    [NoMappingToDatabase]
198
    public IEnumerable<ISupplementalExtractionResults> SupplementalExtractionResults =>
199
        Repository.GetAllObjectsWithParent<SupplementalExtractionResults>(this);
26✔
200

201
    /// <inheritdoc/>
202
    [NoMappingToDatabase]
203
    public IExtractableCohort Cohort =>
204
        Cohort_ID == null ? null : Repository.GetObjectByID<ExtractableCohort>(Cohort_ID.Value);
22✔
205

206
    /// <inheritdoc/>
207
    [NoMappingToDatabase]
208
    public ISelectedDataSets[] SelectedDataSets => Repository.GetAllObjectsWithParent<SelectedDataSets>(this)
438✔
209
        .Cast<ISelectedDataSets>().ToArray();
438✔
210

211
    /// <inheritdoc/>
212
    [NoMappingToDatabase]
213
    public IReleaseLog[] ReleaseLog
214
    {
215
        get
216
        {
217
            return CumulativeExtractionResults.Select(c => c.GetReleaseLogEntryIfAny()).Where(l => l != null).ToArray();
54✔
218
        }
219
    }
220

221
    /// <inheritdoc cref="DefaultPipeline_ID"/>
222
    [NoMappingToDatabase]
223
    public IPipeline DefaultPipeline =>
224
        DefaultPipeline_ID == null
2!
225
            ? null
2✔
226
            : (IPipeline)((IDataExportRepository)Repository).CatalogueRepository.GetObjectByID<Pipeline>(
2✔
227
                DefaultPipeline_ID.Value);
2✔
228

229

230
    /// <inheritdoc cref="CohortIdentificationConfiguration_ID"/>
231
    [NoMappingToDatabase]
232
    public CohortIdentificationConfiguration CohortIdentificationConfiguration =>
233
        CohortIdentificationConfiguration_ID == null
10✔
234
            ? null
10✔
235
            : ((IDataExportRepository)Repository).CatalogueRepository.GetObjectByID<CohortIdentificationConfiguration>(
10✔
236
                CohortIdentificationConfiguration_ID.Value);
10✔
237

238
    /// <inheritdoc cref="CohortRefreshPipeline_ID"/>
239
    [NoMappingToDatabase]
240
    public IPipeline CohortRefreshPipeline =>
241
        CohortRefreshPipeline_ID == null
6✔
242
            ? null
6✔
243
            : (IPipeline)((IDataExportRepository)Repository).CatalogueRepository.GetObjectByID<Pipeline>(
6✔
244
                CohortRefreshPipeline_ID.Value);
6✔
245

246
    /// <summary>
247
    /// Returns a name suitable for describing the extraction of a dataset(s) from this configuration (in a <see cref="DataLoadInfo"/>)
248
    /// </summary>
249
    /// <returns></returns>
250
    public string GetLoggingRunName() => $"{Project.Name} {GetExtractionLoggingName()}";
66✔
251

252
    private string GetExtractionLoggingName() => $"(ExtractionConfiguration ID={ID})";
66✔
253

254
    #endregion
255

256
    /// <summary>
257
    /// Returns <see cref="INamed.Name"/>
258
    /// </summary>
259
    [NoMappingToDatabase]
260
    [UsefulProperty]
261
    public string ProjectName => Project.Name;
2✔
262

263
    public ExtractionConfiguration()
×
264
    {
265
        // Default (also default in db)
266
        Separator = ",";
×
267
    }
×
268

269
    /// <summary>
270
    /// Creates a new extraction configuration in the <paramref name="repository"/> database for the provided <paramref name="project"/>.
271
    /// </summary>
272
    /// <param name="repository"></param>
273
    /// <param name="project"></param>
274
    /// <param name="name"></param>
275
    public ExtractionConfiguration(IDataExportRepository repository, IProject project, string name = null)
358✔
276
    {
277
        Repository = repository;
358✔
278

279
        Repository.InsertAndHydrate(this, new Dictionary<string, object>
358!
280
        {
358✔
281
            { "dtCreated", DateTime.Now },
358✔
282
            { "Project_ID", project.ID },
358✔
283
            { "Username", Environment.UserName },
358✔
284
            { "Description", "Initial Configuration" },
358✔
285
            { "Name", string.IsNullOrWhiteSpace(name) ? $"New ExtractionConfiguration{Guid.NewGuid()}" : name },
358✔
286
            { "Separator", "," }
358✔
287
        });
358✔
288
    }
358✔
289

290
    /// <summary>
291
    /// Provides a short human readable representation of the <see cref="Project"/> to which this
292
    /// <see cref="ExtractionConfiguration"/> is associated with
293
    /// </summary>
294
    /// <param name="shortString">True for a short representation.  False for a longer representation.</param>
295
    /// <returns></returns>
296
    public string GetProjectHint(bool shortString) =>
297
        shortString ? $"({Project.ProjectNumber})" : $"'{Project.Name}' (PNo. {Project.ProjectNumber})";
4!
298

299
    /// <summary>
300
    /// Reads an existing <see cref="IExtractionConfiguration"/> out of the  <paramref name="repository"/> database.
301
    /// </summary>
302
    /// <param name="repository"></param>
303
    /// <param name="r"></param>
304
    internal ExtractionConfiguration(IDataExportRepository repository, DbDataReader r)
305
        : base(repository, r)
914✔
306
    {
307
        Project_ID = int.Parse(r["Project_ID"].ToString());
914✔
308

309
        if (!string.IsNullOrWhiteSpace(r["Cohort_ID"].ToString()))
914✔
310
            Cohort_ID = int.Parse(r["Cohort_ID"].ToString());
410✔
311

312
        RequestTicket = r["RequestTicket"].ToString();
914✔
313
        ReleaseTicket = r["ReleaseTicket"].ToString();
914✔
314

315
        var dt = r["dtCreated"];
914✔
316

317
        if (dt == null || dt == DBNull.Value)
914!
318
            dtCreated = null;
×
319
        else
320
            dtCreated = (DateTime)dt;
914✔
321

322
        Username = r["Username"] as string;
914✔
323
        Description = r["Description"] as string;
914✔
324
        Separator = r["Separator"] as string;
914✔
325
        IsReleased = (bool)r["IsReleased"];
914✔
326
        Name = r["Name"] as string;
914✔
327

328
        if (r["ClonedFrom_ID"] == DBNull.Value)
914✔
329
            ClonedFrom_ID = null;
912✔
330
        else
331
            ClonedFrom_ID = Convert.ToInt32(r["ClonedFrom_ID"]);
2✔
332

333
        DefaultPipeline_ID = ObjectToNullableInt(r["DefaultPipeline_ID"]);
914✔
334
        CohortIdentificationConfiguration_ID = ObjectToNullableInt(r["CohortIdentificationConfiguration_ID"]);
914✔
335
        CohortRefreshPipeline_ID = ObjectToNullableInt(r["CohortRefreshPipeline_ID"]);
914✔
336
    }
914✔
337

338
    /// <inheritdoc/>
339
    public string GetSearchString() => $"{ToString()}_{RequestTicket}_{ReleaseTicket}";
×
340

341
    /// <inheritdoc/>
342
    public ISqlParameter[] GetAllParameters() => GlobalExtractionFilterParameters;
×
343

344
    /// <summary>
345
    /// Returns the configuration Name
346
    /// </summary>
347
    /// <returns></returns>
348
    public override string ToString() => Name;
50✔
349

350
    public bool ShouldBeReadOnly(out string reason)
351
    {
352
        if (IsReleased)
36✔
353
        {
354
            reason = $"{ToString()} has already been released";
4✔
355
            return true;
4✔
356
        }
357

358
        reason = null;
32✔
359
        return false;
32✔
360
    }
361

362
    /// <summary>
363
    /// Creates a complete copy of the <see cref="IExtractionConfiguration"/>, all selected datasets, filters etc.  The copy is created directly into
364
    /// the <see cref="DatabaseEntity.Repository"/> database using a transaction (to prevent a half successful clone being generated).
365
    /// </summary>
366
    /// <returns></returns>
367
    public ExtractionConfiguration DeepCloneWithNewIDs()
368
    {
369
        var repo = (IDataExportRepository)Repository;
8✔
370
        using (repo.BeginNewTransaction())
8✔
371
        {
372
            try
373
            {
374
                //clone the root object (the configuration) - this includes cloning the link to the correct project and cohort       
375
                var clone = ShallowClone();
8✔
376

377
                // Clone GlobalExtractionFilterParameters
378
                foreach (var param in GlobalExtractionFilterParameters.OfType<GlobalExtractionFilterParameter>())
16!
379
                {
380
                    // Create the new parameter with the same SQL declaration
NEW
381
                    var clonedParam = new GlobalExtractionFilterParameter(repo, clone, param.ParameterSQL);
×
382

383
                    // Copy value and comment if present
NEW
384
                    clonedParam.Value = param.Value;
×
NEW
385
                    clonedParam.Comment = param.Comment;
×
386

NEW
387
                    clonedParam.SaveToDatabase();
×
388
                }
389

390
                //find each of the selected datasets for ourselves and clone those too
391
                foreach (SelectedDataSets selected in SelectedDataSets)
32✔
392
                {
393
                    //clone the link meaning that the dataset is now selected for the clone configuration too
394
                    var newSelectedDataSet = new SelectedDataSets(repo, clone, selected.ExtractableDataSet, null);
8✔
395

396
                    // now clone each of the columns for each of the datasets that we just created links to (make them the same as the old configuration
397
                    foreach (var cloneExtractableColumn in GetAllExtractableColumnsFor(selected.ExtractableDataSet).Select(static extractableColumn => extractableColumn.ShallowClone()))
70✔
398
                    {
399
                        cloneExtractableColumn.ExtractionConfiguration_ID = clone.ID;
18✔
400
                        cloneExtractableColumn.SaveToDatabase();
18✔
401
                    }
402

403
                    //clone should copy across the forced joins (if any)
404
                    foreach (var oldForcedJoin in Repository.GetAllObjectsWithParent<SelectedDataSetsForcedJoin>(
16!
405
                                 selected))
8✔
406
                        new SelectedDataSetsForcedJoin((IDataExportRepository)Repository, newSelectedDataSet,
×
407
                            oldForcedJoin.TableInfo);
×
408

409
                    // clone should copy any ExtractionProgresses
410
                    if (selected.ExtractionProgressIfAny != null)
8✔
411
                    {
412
                        var old = selected.ExtractionProgressIfAny;
4✔
413
                        var clonedProgress = new ExtractionProgress(repo, newSelectedDataSet, old.StartDate,
4✔
414
                            old.EndDate, old.NumberOfDaysPerBatch, old.Name, old.ExtractionInformation_ID);
4✔
415

416
                        // Notice that we do not set the ProgressDate because the cloned copy should be extracting from the beginning
417
                        // when it is run.  We don't want the user to have to manually reset it
418
                        clonedProgress.SaveToDatabase();
4✔
419
                    }
420

421
                    try
422
                    {
423
                        //clone the root filter container
424
                        var rootContainer = (FilterContainer)GetFilterContainerFor(selected.ExtractableDataSet);
8✔
425

426
                        //turns out there wasn't one to clone at all
427
                        if (rootContainer == null)
8✔
428
                            continue;
4✔
429

430
                        //there was one to clone so clone it recursively (all subcontainers) including filters then set the root filter to the new clone
431
                        var cloneRootContainer = rootContainer.DeepCloneEntireTreeRecursivelyIncludingFilters();
4✔
432
                        newSelectedDataSet.RootFilterContainer_ID = cloneRootContainer.ID;
4✔
433
                        newSelectedDataSet.SaveToDatabase();
4✔
434
                    }
4✔
435
                    catch (Exception e)
×
436
                    {
437
                        clone.DeleteInDatabase();
×
438
                        throw new Exception(
×
439
                            $"Problem occurred during cloning filters, problem was {e.Message} deleted the clone configuration successfully",
×
440
                            e);
×
441
                    }
442
                }
443

444
                clone.dtCreated = DateTime.Now;
8✔
445
                clone.IsReleased = false;
8✔
446
                clone.Username = Environment.UserName;
8✔
447
                clone.Description = "TO" + "DO:Populate change log here";
8✔
448
                clone.ReleaseTicket = null;
8✔
449

450
                //wire up some changes
451
                clone.ClonedFrom_ID = ID;
8✔
452
                clone.SaveToDatabase();
8✔
453

454
                repo.EndTransaction(true);
8✔
455

456
                return clone;
8✔
457
            }
458
            catch (Exception)
×
459
            {
460
                repo.EndTransaction(false);
×
461
                throw;
×
462
            }
463
        }
464
    }
8✔
465

466
    private ExtractionConfiguration ShallowClone()
467
    {
468
        var clone = new ExtractionConfiguration(DataExportRepository, Project);
8✔
469
        CopyShallowValuesTo(clone);
8✔
470

471
        clone.Name = $"Clone of {Name}";
8✔
472
        clone.SaveToDatabase();
8✔
473
        return clone;
8✔
474
    }
475

476
    /// <inheritdoc/>
477
    public IProject GetProject() => Repository.GetObjectByID<Project>(Project_ID);
12✔
478

479
    /// <inheritdoc/>
480
    public ExtractableColumn[] GetAllExtractableColumnsFor(IExtractableDataSet dataset)
481
    {
482
        return
286✔
483
            Repository.GetAllObjectsWhere<ExtractableColumn>("ExtractionConfiguration_ID", ID)
286✔
484
                .Where(e => e.ExtractableDataSet_ID == dataset.ID).ToArray();
5,278✔
485
    }
486

487
    /// <inheritdoc/>
488
    public IContainer GetFilterContainerFor(IExtractableDataSet dataset)
489
    {
490
        return Repository.GetAllObjectsWhere<SelectedDataSets>("ExtractionConfiguration_ID", ID)
350✔
491
            .Single(sds => sds.ExtractableDataSet_ID == dataset.ID)
596✔
492
            .RootFilterContainer;
350✔
493
    }
494

495
    private ExternalDatabaseServer GetDistinctLoggingServer(bool testLoggingServer)
496
    {
497
        var uniqueLoggingServerID = -1;
66✔
498

499
        var repo = (IDataExportRepository)Repository;
66✔
500

501
        foreach (int? catalogueID in GetAllExtractableDataSets().Select(ds => ds.Catalogue_ID))
510✔
502
        {
503
            if (catalogueID == null)
126!
504
                throw new Exception(
×
505
                    "Cannot get logging server because some ExtractableDatasets in the configuration do not have associated Catalogues (possibly the Catalogue was deleted)");
×
506

507
            var catalogue = repo.CatalogueRepository.GetObjectByID<Catalogue>((int)catalogueID);
126✔
508

509
            var loggingServer = catalogue.LiveLoggingServer_ID ?? throw new Exception(
126!
510
                $"Catalogue {catalogue.Name} does not have a {(testLoggingServer ? "test" : "")} logging server configured");
126✔
511
            if (uniqueLoggingServerID == -1)
126✔
512
            {
513
                uniqueLoggingServerID = (int)catalogue.LiveLoggingServer_ID;
66✔
514
            }
515
            else
516
            {
517
                if (uniqueLoggingServerID != catalogue.LiveLoggingServer_ID)
60!
518
                    throw new Exception("Catalogues in configuration have different logging servers");
×
519
            }
520
        }
521

522
        return repo.CatalogueRepository.GetObjectByID<ExternalDatabaseServer>(uniqueLoggingServerID);
66✔
523
    }
524

525
    /// <inheritdoc/>
526
    public IExtractableCohort GetExtractableCohort() => Cohort_ID == null
82✔
527
        ? null
82✔
528
        : (IExtractableCohort)Repository.GetObjectByID<ExtractableCohort>(Cohort_ID.Value);
82✔
529

530
    /// <inheritdoc/>
531
    public IExtractableDataSet[] GetAllExtractableDataSets()
532
    {
533
        return
108✔
534
            Repository.GetAllObjectsWithParent<SelectedDataSets>(this)
108✔
535
                .Select(sds => sds.ExtractableDataSet)
182✔
536
                .ToArray();
108✔
537
    }
538

539
    /// <summary>
540
    /// Makes the provided <paramref name="extractableDataSet"/> extractable in the current <see cref="IExtractionConfiguration"/>.  This
541
    /// includes selecting it (<see cref="ISelectedDataSets"/>) and replicating any mandatory filters.
542
    /// </summary>
543
    /// <param name="extractableDataSet"></param>
544
    public void AddDatasetToConfiguration(IExtractableDataSet extractableDataSet)
545
    {
546
        AddDatasetToConfiguration(extractableDataSet, out _);
54✔
547
    }
54✔
548

549
    /// <summary>
550
    /// Makes the provided <paramref name="extractableDataSet"/> extractable in the current <see cref="IExtractionConfiguration"/>.  This
551
    /// includes selecting it (<see cref="ISelectedDataSets"/>) and replicating any mandatory filters.
552
    /// </summary>
553
    /// <param name="extractableDataSet"></param>
554
    /// <param name="selectedDataSet">The RDMP object that indicates that the dataset is extracted in this configuration</param>
555
    public void AddDatasetToConfiguration(IExtractableDataSet extractableDataSet, out ISelectedDataSets selectedDataSet)
556
    {
557
        selectedDataSet = null;
54✔
558

559
        //it is already part of the configuration
560
        if (SelectedDataSets.Any(s => s.ExtractableDataSet_ID == extractableDataSet.ID))
86!
561
            return;
×
562

563
        var dataExportRepo = (IDataExportRepository)Repository;
54✔
564

565
        selectedDataSet = new SelectedDataSets(dataExportRepo, this, extractableDataSet, null);
54✔
566

567
        var mandatoryExtractionFiltersToApplyToDataset = extractableDataSet.Catalogue.GetAllMandatoryFilters();
54✔
568

569
        //add mandatory filters
570
        if (mandatoryExtractionFiltersToApplyToDataset.Any())
54!
571
        {
572
            //first we need a root container e.g. an AND container
573
            //add the AND container and set it as the root container for the dataset configuration
574
            var rootFilterContainer = new FilterContainer(dataExportRepo)
×
575
            {
×
576
                Operation = FilterContainerOperation.AND
×
577
            };
×
578
            rootFilterContainer.SaveToDatabase();
×
579

580
            selectedDataSet.RootFilterContainer_ID = rootFilterContainer.ID;
×
581
            selectedDataSet.SaveToDatabase();
×
582

583
            var globals = GlobalExtractionFilterParameters;
×
584
            var importer = new FilterImporter(new DeployedExtractionFilterFactory(dataExportRepo), globals);
×
585

586
            var mandatoryFilters =
×
587
                importer.ImportAllFilters(rootFilterContainer, mandatoryExtractionFiltersToApplyToDataset, null);
×
588

589
            foreach (var filter in mandatoryFilters.Cast<DeployedExtractionFilter>())
×
590
            {
591
                filter.FilterContainer_ID = rootFilterContainer.ID;
×
592
                filter.SaveToDatabase();
×
593
            }
594
        }
595

596
        var legacyColumns = GetAllExtractableColumnsFor(extractableDataSet).Cast<ExtractableColumn>().ToArray();
54✔
597

598
        //add Core or ProjectSpecific columns
599
        foreach (var all in extractableDataSet.Catalogue.GetAllExtractionInformation(ExtractionCategory.Any))
1,652✔
600
            if (all.ExtractionCategory == ExtractionCategory.Core ||
772✔
601
                all.ExtractionCategory == ExtractionCategory.ProjectSpecific)
772✔
602
                if (legacyColumns.All(l => l.CatalogueExtractionInformation_ID != all.ID))
744✔
603
                    AddColumnToExtraction(extractableDataSet, all);
744✔
604
    }
54✔
605

606
    /// <inheritdoc/>
607
    public void RemoveDatasetFromConfiguration(IExtractableDataSet extractableDataSet)
608
    {
609
        var match = SelectedDataSets.SingleOrDefault(s => s.ExtractableDataSet_ID == extractableDataSet.ID);
6✔
610
        match?.DeleteInDatabase();
2✔
611
    }
2✔
612

613
    /// <summary>
614
    /// Makes the given <paramref name="column"/> SELECT Sql part of the query for linking and extracting the provided <paramref name="forDataSet"/>
615
    /// for this <see cref="IExtractionConfiguration"/>.
616
    /// </summary>
617
    /// <param name="forDataSet"></param>
618
    /// <param name="column"></param>
619
    /// <returns></returns>
620
    public ExtractableColumn AddColumnToExtraction(IExtractableDataSet forDataSet, IColumn column)
621
    {
622
        if (string.IsNullOrWhiteSpace(column.SelectSQL))
756!
623
            throw new ArgumentException(
×
624
                $"IColumn ({column.GetType().Name}) {column} has a blank value for SelectSQL, fix this in the CatalogueManager",
×
625
                nameof(column));
×
626

627
        var query = column.SelectSQL;
756✔
628

629
        ExtractableColumn addMe;
630

631
        if (column is ExtractionInformation extractionInformation)
756!
632
            addMe = new ExtractableColumn((IDataExportRepository)Repository, forDataSet, this, extractionInformation, -1, query);
756✔
633
        else
634
            addMe = new ExtractableColumn((IDataExportRepository)Repository, forDataSet, this, null, -1,
×
635
                query); // its custom column of some kind, not tied to a catalogue entry
×
636

637
        addMe.UpdateValuesToMatch(column);
756✔
638

639
        return addMe;
756✔
640
    }
641

642
    /// <summary>
643
    /// Returns the logging server that should be used to audit extraction executions of this <see cref="IExtractionConfiguration"/>.
644
    /// </summary>
645
    /// <returns></returns>
646
    public LogManager GetExplicitLoggingDatabaseServerOrDefault()
647
    {
648
        ExternalDatabaseServer loggingServer;
649
        try
650
        {
651
            loggingServer = GetDistinctLoggingServer(false);
66✔
652
        }
66✔
653
        catch (Exception e)
×
654
        {
655
            //failed to get a logging server correctly
656

657
            //see if there is a default
658
            var defaultGetter = Project.DataExportRepository.CatalogueRepository;
×
659
            var defaultLoggingServer = defaultGetter.GetDefaultFor(PermissableDefaults.LiveLoggingServer_ID);
×
660

661
            //there is a default?
662
            if (defaultLoggingServer != null)
×
663
                loggingServer = (ExternalDatabaseServer)defaultLoggingServer;
×
664
            else
665
                //no, there is no default or user does not want to use it.
666
                throw new Exception(
×
667
                    "There is no default logging server configured and there was a problem asking Catalogues for a logging server instead.  Configure a default logging server via ManageExternalServersUI",
×
668
                    e);
×
669
        }
×
670

671
        var server = DataAccessPortal.ExpectServer(loggingServer, DataAccessContext.Logging);
66✔
672

673
        LogManager lm;
674

675
        try
676
        {
677
            lm = new LogManager(server);
66✔
678

679
            if (!lm.ListDataTasks().Contains(ExecuteDatasetExtractionSource.AuditTaskName))
66!
680
                throw new Exception(
×
681
                    $"The logging database {server} does not contain a DataLoadTask called '{ExecuteDatasetExtractionSource.AuditTaskName}' (all data exports are logged under this task regardless of dataset/Catalogue)");
×
682
        }
66✔
683
        catch (Exception e)
×
684
        {
685
            throw new Exception($"Problem figuring out what logging server to use:{Environment.NewLine}\t{e.Message}",
×
686
                e);
×
687
        }
688

689
        return lm;
66✔
690
    }
691

692
    /// <inheritdoc/>
693
    public void Unfreeze()
694
    {
695
        foreach (var l in ReleaseLog)
×
696
            l.DeleteInDatabase();
×
697

698
        foreach (var r in CumulativeExtractionResults)
×
699
            r.DeleteInDatabase();
×
700

701
        IsReleased = false;
×
702
        SaveToDatabase();
×
703
    }
×
704

705
    /// <inheritdoc/>
706
    public IMapsDirectlyToDatabaseTable[] GetGlobals()
707
    {
708
        var sds = SelectedDataSets.FirstOrDefault(s => s.ExtractableDataSet.Catalogue != null);
70✔
709

710
        if (sds == null)
36✔
711
            return Array.Empty<IMapsDirectlyToDatabaseTable>();
2✔
712

713
        var cata = sds.ExtractableDataSet.Catalogue;
34✔
714

715
        return
34✔
716
            cata.GetAllSupportingSQLTablesForCatalogue(FetchOptions.ExtractableGlobals)
34✔
717
                .Cast<IMapsDirectlyToDatabaseTable>()
34✔
718
                .Union(
34✔
719
                    cata.GetAllSupportingDocuments(FetchOptions.ExtractableGlobals))
34✔
720
                .ToArray();
34✔
721
    }
722

723
    /// <inheritdoc/>
724
    public override void DeleteInDatabase()
725
    {
726
        // Delete only GlobalExtractionFilterParameters for this configuration
727
        foreach (var param in Repository.GetAllObjectsWithParent<GlobalExtractionFilterParameter>(this))
64!
NEW
728
            param.DeleteInDatabase();
×
729

730
        foreach (var result in Repository.GetAllObjectsWithParent<SupplementalExtractionResults>(this))
64!
731
            result.DeleteInDatabase();
×
732

733
        base.DeleteInDatabase();
32✔
734
    }
32✔
735

736
    /// <inheritdoc/>
737
    public IHasDependencies[] GetObjectsThisDependsOn()
738
    {
739
        return new[] { Project };
×
740
    }
741

742
    /// <inheritdoc/>
743
    public IHasDependencies[] GetObjectsDependingOnThis() => Array.Empty<IHasDependencies>();
×
744

745
    public DiscoveredServer GetDistinctLoggingDatabase() =>
746
        GetDistinctLoggingServer(false).Discover(DataAccessContext.Logging).Server;
×
747

748
    public DiscoveredServer GetDistinctLoggingDatabase(out IExternalDatabaseServer serverChosen)
749
    {
750
        serverChosen = GetDistinctLoggingServer(false);
×
751
        return serverChosen.Discover(DataAccessContext.Logging).Server;
×
752
    }
753

754
    public string GetDistinctLoggingTask() => ExecuteDatasetExtractionSource.AuditTaskName;
×
755

756
    /// <summary>
757
    /// Returns runs from the data extraction task where the run was for this ExtractionConfiguration
758
    /// </summary>
759
    /// <param name="runs"></param>
760
    /// <returns></returns>
761
    public IEnumerable<ArchivalDataLoadInfo> FilterRuns(IEnumerable<ArchivalDataLoadInfo> runs)
762
    {
763
        // allow for the project name changing but not our ID
764
        return runs.Where(r => r.Description.Contains(GetExtractionLoggingName()));
×
765
    }
766
}
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