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

HicServices / RDMP / 19129669231

06 Nov 2025 08:36AM UTC coverage: 57.107% (-0.2%) from 57.309%
19129669231

push

github

JFriel
bump version

11418 of 21469 branches covered (53.18%)

Branch coverage included in aggregate %.

32412 of 55282 relevant lines covered (58.63%)

17597.55 hits per line

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

68.56
/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,086✔
57
        set => SetField(ref _cohortRefreshPipelineID, value);
1,588✔
58
    }
59

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

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

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

81
    /// <inheritdoc/>
82
    public int? Cohort_ID
83
    {
84
        get => _cohort_ID;
6,344✔
85
        set => SetField(ref _cohort_ID, value);
894✔
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,180✔
117
        set => SetField(ref _requestTicket, value);
1,582✔
118
    }
119

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

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

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

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

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

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

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

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

177
    #endregion
178

179
    #region Relationships
180

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

185
    /// <inheritdoc/>
186
    [NoMappingToDatabase]
187
    public ISqlParameter[] GlobalExtractionFilterParameters =>
188
        Repository.GetAllObjectsWithParent<GlobalExtractionFilterParameter>(this)
182✔
189
            .Cast<ISqlParameter>().ToArray();
182✔
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)
466✔
209
        .Cast<ISelectedDataSets>().ToArray();
466✔
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 => DefaultPipeline_ID == null?null: ((IDataExportRepository) Repository).CatalogueRepository.GetAllObjects<Pipeline>().FirstOrDefault(p => p.ID == DefaultPipeline_ID);
2!
224

225
    /// <inheritdoc cref="CohortIdentificationConfiguration_ID"/>
226
    [NoMappingToDatabase]
227
    public CohortIdentificationConfiguration CohortIdentificationConfiguration =>
228
        CohortIdentificationConfiguration_ID == null
10✔
229
            ? null
10✔
230
            : ((IDataExportRepository)Repository).CatalogueRepository.GetObjectByID<CohortIdentificationConfiguration>(
10✔
231
                CohortIdentificationConfiguration_ID.Value);
10✔
232

233
    /// <inheritdoc cref="CohortRefreshPipeline_ID"/>
234
    [NoMappingToDatabase]
235
    public IPipeline CohortRefreshPipeline =>
236
        CohortRefreshPipeline_ID == null
6✔
237
            ? null
6✔
238
            : (IPipeline)((IDataExportRepository)Repository).CatalogueRepository.GetObjectByID<Pipeline>(
6✔
239
                CohortRefreshPipeline_ID.Value);
6✔
240

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

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

249
    #endregion
250

251
    /// <summary>
252
    /// Returns <see cref="INamed.Name"/>
253
    /// </summary>
254
    [NoMappingToDatabase]
255
    [UsefulProperty]
256
    public string ProjectName => Project.Name;
2✔
257

258
    public ExtractionConfiguration()
×
259
    {
260
        // Default (also default in db)
261
        Separator = ",";
×
262
    }
×
263

264
    /// <summary>
265
    /// Creates a new extraction configuration in the <paramref name="repository"/> database for the provided <paramref name="project"/>.
266
    /// </summary>
267
    /// <param name="repository"></param>
268
    /// <param name="project"></param>
269
    /// <param name="name"></param>
270
    public ExtractionConfiguration(IDataExportRepository repository, IProject project, string name = null)
380✔
271
    {
272
        Repository = repository;
380✔
273

274
        Repository.InsertAndHydrate(this, new Dictionary<string, object>
380✔
275
        {
380✔
276
            { "dtCreated", DateTime.Now },
380✔
277
            { "Project_ID", project.ID },
380✔
278
            { "Username", Environment.UserName },
380✔
279
            { "Description", "Initial Configuration" },
380✔
280
            { "Name", string.IsNullOrWhiteSpace(name) ? $"New ExtractionConfiguration{Guid.NewGuid()}" : name },
380✔
281
            { "Separator", "," }
380✔
282
        });
380✔
283
    }
380✔
284

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

294
    /// <summary>
295
    /// Reads an existing <see cref="IExtractionConfiguration"/> out of the  <paramref name="repository"/> database.
296
    /// </summary>
297
    /// <param name="repository"></param>
298
    /// <param name="r"></param>
299
    internal ExtractionConfiguration(IDataExportRepository repository, DbDataReader r)
300
        : base(repository, r)
1,302✔
301
    {
302
        Project_ID = int.Parse(r["Project_ID"].ToString());
1,302✔
303

304
        if (!string.IsNullOrWhiteSpace(r["Cohort_ID"].ToString()))
1,302✔
305
            Cohort_ID = int.Parse(r["Cohort_ID"].ToString());
412✔
306

307
        RequestTicket = r["RequestTicket"].ToString();
1,302✔
308
        ReleaseTicket = r["ReleaseTicket"].ToString();
1,302✔
309

310
        var dt = r["dtCreated"];
1,302✔
311

312
        if (dt == null || dt == DBNull.Value)
1,302!
313
            dtCreated = null;
×
314
        else
315
            dtCreated = (DateTime)dt;
1,302✔
316

317
        Username = r["Username"] as string;
1,302✔
318
        Description = r["Description"] as string;
1,302✔
319
        Separator = r["Separator"] as string;
1,302✔
320
        IsReleased = (bool)r["IsReleased"];
1,302✔
321
        Name = r["Name"] as string;
1,302✔
322

323
        if (r["ClonedFrom_ID"] == DBNull.Value)
1,302✔
324
            ClonedFrom_ID = null;
1,300✔
325
        else
326
            ClonedFrom_ID = Convert.ToInt32(r["ClonedFrom_ID"]);
2✔
327

328
        DefaultPipeline_ID = ObjectToNullableInt(r["DefaultPipeline_ID"]);
1,302✔
329
        CohortIdentificationConfiguration_ID = ObjectToNullableInt(r["CohortIdentificationConfiguration_ID"]);
1,302✔
330
        CohortRefreshPipeline_ID = ObjectToNullableInt(r["CohortRefreshPipeline_ID"]);
1,302✔
331
    }
1,302✔
332

333
    /// <inheritdoc/>
334
    public string GetSearchString() => $"{ToString()}_{RequestTicket}_{ReleaseTicket}";
×
335

336
    /// <inheritdoc/>
337
    public ISqlParameter[] GetAllParameters() => GlobalExtractionFilterParameters;
×
338

339
    /// <summary>
340
    /// Returns the configuration Name
341
    /// </summary>
342
    /// <returns></returns>
343
    public override string ToString() => Name;
50✔
344

345
    public bool ShouldBeReadOnly(out string reason)
346
    {
347
        if (IsReleased)
36✔
348
        {
349
            reason = $"{ToString()} has already been released";
4✔
350
            return true;
4✔
351
        }
352

353
        reason = null;
32✔
354
        return false;
32✔
355
    }
356

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

372
                // Clone GlobalExtractionFilterParameters
373
                foreach (var param in GlobalExtractionFilterParameters.OfType<GlobalExtractionFilterParameter>())
16!
374
                {
375
                    // Create the new parameter with the same SQL declaration
376
                    var clonedParam = new GlobalExtractionFilterParameter(repo, clone, param.ParameterSQL);
×
377

378
                    // Copy value and comment if present
379
                    clonedParam.Value = param.Value;
×
380
                    clonedParam.Comment = param.Comment;
×
381

382
                    clonedParam.SaveToDatabase();
×
383
                }
384

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

391
                    // 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
392
                    foreach (var cloneExtractableColumn in GetAllExtractableColumnsFor(selected.ExtractableDataSet).Select(static extractableColumn => extractableColumn.ShallowClone()))
70✔
393
                    {
394
                        cloneExtractableColumn.ExtractionConfiguration_ID = clone.ID;
18✔
395
                        cloneExtractableColumn.SaveToDatabase();
18✔
396
                    }
397

398
                    //clone should copy across the forced joins (if any)
399
                    foreach (var oldForcedJoin in Repository.GetAllObjectsWithParent<SelectedDataSetsForcedJoin>(
16!
400
                                 selected))
8✔
401
                        new SelectedDataSetsForcedJoin((IDataExportRepository)Repository, newSelectedDataSet,
×
402
                            oldForcedJoin.TableInfo);
×
403

404
                    // clone should copy any ExtractionProgresses
405
                    if (selected.ExtractionProgressIfAny != null)
8✔
406
                    {
407
                        var old = selected.ExtractionProgressIfAny;
4✔
408
                        var clonedProgress = new ExtractionProgress(repo, newSelectedDataSet, old.StartDate,
4✔
409
                            old.EndDate, old.NumberOfDaysPerBatch, old.Name, old.ExtractionInformation_ID);
4✔
410

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

416
                    try
417
                    {
418
                        //clone the root filter container
419
                        var rootContainer = (FilterContainer)GetFilterContainerFor(selected.ExtractableDataSet);
8✔
420

421
                        //turns out there wasn't one to clone at all
422
                        if (rootContainer == null)
8✔
423
                            continue;
4✔
424

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

439
                clone.dtCreated = DateTime.Now;
8✔
440
                clone.IsReleased = false;
8✔
441
                clone.Username = Environment.UserName;
8✔
442
                clone.Description = "TO" + "DO:Populate change log here";
8✔
443
                clone.ReleaseTicket = null;
8✔
444

445
                //wire up some changes
446
                clone.ClonedFrom_ID = ID;
8✔
447
                clone.SaveToDatabase();
8✔
448

449
                repo.EndTransaction(true);
8✔
450

451
                return clone;
8✔
452
            }
453
            catch (Exception)
×
454
            {
455
                repo.EndTransaction(false);
×
456
                throw;
×
457
            }
458
        }
459
    }
8✔
460

461
    private ExtractionConfiguration ShallowClone()
462
    {
463
        var clone = new ExtractionConfiguration(DataExportRepository, Project);
8✔
464
        CopyShallowValuesTo(clone);
8✔
465

466
        clone.Name = $"Clone of {Name}";
8✔
467
        clone.SaveToDatabase();
8✔
468
        return clone;
8✔
469
    }
470

471
    /// <inheritdoc/>
472
    public IProject GetProject() => Repository.GetObjectByID<Project>(Project_ID);
12✔
473

474
    /// <inheritdoc/>
475
    public ExtractableColumn[] GetAllExtractableColumnsFor(IExtractableDataSet dataset)
476
    {
477
        return
306✔
478
            Repository.GetAllObjectsWhere<ExtractableColumn>("ExtractionConfiguration_ID", ID)
306✔
479
                .Where(e => e.ExtractableDataSet_ID == dataset.ID).ToArray();
5,298✔
480
    }
481

482
    /// <inheritdoc/>
483
    public IContainer GetFilterContainerFor(IExtractableDataSet dataset)
484
    {
485
        return Repository.GetAllObjectsWhere<SelectedDataSets>("ExtractionConfiguration_ID", ID)
356✔
486
            .Single(sds => sds.ExtractableDataSet_ID == dataset.ID)
602✔
487
            .RootFilterContainer;
356✔
488
    }
489

490
    private ExternalDatabaseServer GetDistinctLoggingServer(bool testLoggingServer)
491
    {
492
        var uniqueLoggingServerID = -1;
66✔
493

494
        var repo = (IDataExportRepository)Repository;
66✔
495

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

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

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

517
        return repo.CatalogueRepository.GetObjectByID<ExternalDatabaseServer>(uniqueLoggingServerID);
66✔
518
    }
519

520
    /// <inheritdoc/>
521
    public IExtractableCohort GetExtractableCohort() => Cohort_ID == null
82✔
522
        ? null
82✔
523
        : (IExtractableCohort)Repository.GetObjectByID<ExtractableCohort>(Cohort_ID.Value);
82✔
524

525
    /// <inheritdoc/>
526
    public IExtractableDataSet[] GetAllExtractableDataSets()
527
    {
528
        return
108✔
529
            Repository.GetAllObjectsWithParent<SelectedDataSets>(this)
108✔
530
                .Select(sds => sds.ExtractableDataSet)
182✔
531
                .ToArray();
108✔
532
    }
533

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

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

554
        //it is already part of the configuration
555
        if (SelectedDataSets.Any(s => s.ExtractableDataSet_ID == extractableDataSet.ID))
106!
556
            return;
×
557

558
        var dataExportRepo = (IDataExportRepository)Repository;
74✔
559

560
        selectedDataSet = new SelectedDataSets(dataExportRepo, this, extractableDataSet, null);
74✔
561

562
        var mandatoryExtractionFiltersToApplyToDataset = extractableDataSet.Catalogue.GetAllMandatoryFilters();
74✔
563

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

575
            selectedDataSet.RootFilterContainer_ID = rootFilterContainer.ID;
×
576
            selectedDataSet.SaveToDatabase();
×
577

578
            var globals = GlobalExtractionFilterParameters;
×
579
            var importer = new FilterImporter(new DeployedExtractionFilterFactory(dataExportRepo), globals);
×
580

581
            var mandatoryFilters =
×
582
                importer.ImportAllFilters(rootFilterContainer, mandatoryExtractionFiltersToApplyToDataset, null);
×
583

584
            foreach (var filter in mandatoryFilters.Cast<DeployedExtractionFilter>())
×
585
            {
586
                filter.FilterContainer_ID = rootFilterContainer.ID;
×
587
                filter.SaveToDatabase();
×
588
            }
589
        }
590

591
        var legacyColumns = GetAllExtractableColumnsFor(extractableDataSet).Cast<ExtractableColumn>().ToArray();
74✔
592

593
        //add Core or ProjectSpecific columns
594
        foreach (var all in extractableDataSet.Catalogue.GetAllExtractionInformation(ExtractionCategory.Any))
1,732✔
595
            if (all.ExtractionCategory == ExtractionCategory.Core ||
792✔
596
                all.ExtractionCategory == ExtractionCategory.ProjectSpecific)
792✔
597
                if (legacyColumns.All(l => l.CatalogueExtractionInformation_ID != all.ID))
764✔
598
                    AddColumnToExtraction(extractableDataSet, all);
764✔
599
    }
74✔
600

601
    /// <inheritdoc/>
602
    public void RemoveDatasetFromConfiguration(IExtractableDataSet extractableDataSet)
603
    {
604
        var match = SelectedDataSets.SingleOrDefault(s => s.ExtractableDataSet_ID == extractableDataSet.ID);
6✔
605
        match?.DeleteInDatabase();
2✔
606
    }
2✔
607

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

622
        var query = column.SelectSQL;
776✔
623

624
        ExtractableColumn addMe;
625

626
        if (column is ExtractionInformation extractionInformation)
776!
627
            addMe = new ExtractableColumn((IDataExportRepository)Repository, forDataSet, this, extractionInformation, -1, query);
776✔
628
        else
629
            addMe = new ExtractableColumn((IDataExportRepository)Repository, forDataSet, this, null, -1,
×
630
                query); // its custom column of some kind, not tied to a catalogue entry
×
631

632
        addMe.UpdateValuesToMatch(column);
776✔
633

634
        return addMe;
776✔
635
    }
636

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

652
            //see if there is a default
653
            var defaultGetter = Project.DataExportRepository.CatalogueRepository;
×
654
            var defaultLoggingServer = defaultGetter.GetDefaultFor(PermissableDefaults.LiveLoggingServer_ID);
×
655

656
            //there is a default?
657
            if (defaultLoggingServer != null)
×
658
                loggingServer = (ExternalDatabaseServer)defaultLoggingServer;
×
659
            else
660
                //no, there is no default or user does not want to use it.
661
                throw new Exception(
×
662
                    "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",
×
663
                    e);
×
664
        }
×
665

666
        var server = DataAccessPortal.ExpectServer(loggingServer, DataAccessContext.Logging);
66✔
667

668
        LogManager lm;
669

670
        try
671
        {
672
            lm = new LogManager(server);
66✔
673

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

684
        return lm;
66✔
685
    }
686

687
    /// <inheritdoc/>
688
    public void Unfreeze()
689
    {
690
        foreach (var l in ReleaseLog)
×
691
            l.DeleteInDatabase();
×
692

693
        foreach (var r in CumulativeExtractionResults)
×
694
            r.DeleteInDatabase();
×
695

696
        IsReleased = false;
×
697
        SaveToDatabase();
×
698
    }
×
699

700
    /// <inheritdoc/>
701
    public IMapsDirectlyToDatabaseTable[] GetGlobals()
702
    {
703
        var sds = SelectedDataSets.FirstOrDefault(s => s.ExtractableDataSet.Catalogue != null);
70✔
704

705
        if (sds == null)
36✔
706
            return Array.Empty<IMapsDirectlyToDatabaseTable>();
2✔
707

708
        var cata = sds.ExtractableDataSet.Catalogue;
34✔
709

710
        return
34✔
711
            cata.GetAllSupportingSQLTablesForCatalogue(FetchOptions.ExtractableGlobals)
34✔
712
                .Cast<IMapsDirectlyToDatabaseTable>()
34✔
713
                .Union(
34✔
714
                    cata.GetAllSupportingDocuments(FetchOptions.ExtractableGlobals))
34✔
715
                .ToArray();
34✔
716
    }
717

718
    /// <inheritdoc/>
719
    public override void DeleteInDatabase()
720
    {
721
        // Delete only GlobalExtractionFilterParameters for this configuration
722
        foreach (var param in Repository.GetAllObjectsWithParent<GlobalExtractionFilterParameter>(this))
64!
723
            param.DeleteInDatabase();
×
724

725
        foreach (var result in Repository.GetAllObjectsWithParent<SupplementalExtractionResults>(this))
64!
726
            result.DeleteInDatabase();
×
727

728
        base.DeleteInDatabase();
32✔
729
    }
32✔
730

731
    /// <inheritdoc/>
732
    public IHasDependencies[] GetObjectsThisDependsOn()
733
    {
734
        return new[] { Project };
×
735
    }
736

737
    /// <inheritdoc/>
738
    public IHasDependencies[] GetObjectsDependingOnThis() => Array.Empty<IHasDependencies>();
×
739

740
    public DiscoveredServer GetDistinctLoggingDatabase() =>
741
        GetDistinctLoggingServer(false).Discover(DataAccessContext.Logging).Server;
×
742

743
    public DiscoveredServer GetDistinctLoggingDatabase(out IExternalDatabaseServer serverChosen)
744
    {
745
        serverChosen = GetDistinctLoggingServer(false);
×
746
        return serverChosen.Discover(DataAccessContext.Logging).Server;
×
747
    }
748

749
    public string GetDistinctLoggingTask() => ExecuteDatasetExtractionSource.AuditTaskName;
×
750

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

© 2025 Coveralls, Inc