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

HicServices / RDMP / 6237307473

19 Sep 2023 04:02PM UTC coverage: 57.015% (-0.4%) from 57.44%
6237307473

push

github

web-flow
Feature/rc4 (#1570)

* Syntax tidying
* Dependency updates
* Event handling singletons (ThrowImmediately and co)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: James A Sutherland <>
Co-authored-by: James Friel <jfriel001@dundee.ac.uk>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

10734 of 20259 branches covered (0.0%)

Branch coverage included in aggregate %.

5922 of 5922 new or added lines in 565 files covered. (100.0%)

30687 of 52390 relevant lines covered (58.57%)

7361.8 hits per line

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

69.34
/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;
786✔
57
        set => SetField(ref _cohortRefreshPipelineID, value);
858✔
58
    }
59

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

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

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

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

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

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

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

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

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

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

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

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

177
    #endregion
178

179
    #region Relationships
180

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

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

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

196
    /// <inheritdoc/>
197
    [NoMappingToDatabase]
198
    public IEnumerable<ISupplementalExtractionResults> SupplementalExtractionResults =>
199
        Repository.GetAllObjectsWithParent<SupplementalExtractionResults>(this);
2✔
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)
292✔
209
        .Cast<ISelectedDataSets>().ToArray();
292✔
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();
14✔
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()}";
26✔
251

252
    private string GetExtractionLoggingName() => $"(ExtractionConfiguration ID={ID})";
26✔
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)
312✔
276
    {
277
        Repository = repository;
312✔
278

279
        Repository.InsertAndHydrate(this, new Dictionary<string, object>
312!
280
        {
312✔
281
            { "dtCreated", DateTime.Now },
312✔
282
            { "Project_ID", project.ID },
312✔
283
            { "Username", Environment.UserName },
312✔
284
            { "Description", "Initial Configuration" },
312✔
285
            { "Name", string.IsNullOrWhiteSpace(name) ? $"New ExtractionConfiguration{Guid.NewGuid()}" : name },
312✔
286
            { "Separator", "," }
312✔
287
        });
312✔
288
    }
312✔
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)
640✔
306
    {
307
        Project_ID = int.Parse(r["Project_ID"].ToString());
640✔
308

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

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

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

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

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

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

333
        DefaultPipeline_ID = ObjectToNullableInt(r["DefaultPipeline_ID"]);
640✔
334
        CohortIdentificationConfiguration_ID = ObjectToNullableInt(r["CohortIdentificationConfiguration_ID"]);
640✔
335
        CohortRefreshPipeline_ID = ObjectToNullableInt(r["CohortRefreshPipeline_ID"]);
640✔
336
    }
640✔
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 succesful 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
                //find each of the selected datasets for ourselves and clone those too
378
                foreach (SelectedDataSets selected in SelectedDataSets)
32✔
379
                {
380
                    //clone the link meaning that the dataset is now selected for the clone configuration too
381
                    var newSelectedDataSet = new SelectedDataSets(repo, clone, selected.ExtractableDataSet, null);
8✔
382

383
                    // 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
384
                    foreach (IColumn extractableColumn in GetAllExtractableColumnsFor(selected.ExtractableDataSet))
52✔
385
                    {
386
                        var cloneExtractableColumn = ((ExtractableColumn)extractableColumn).ShallowClone();
18✔
387
                        cloneExtractableColumn.ExtractionConfiguration_ID = clone.ID;
18✔
388
                        cloneExtractableColumn.SaveToDatabase();
18✔
389
                    }
390

391
                    //clone should copy accross the forced joins (if any)
392
                    foreach (var oldForcedJoin in Repository.GetAllObjectsWithParent<SelectedDataSetsForcedJoin>(
16!
393
                                 selected))
8✔
394
                        new SelectedDataSetsForcedJoin((IDataExportRepository)Repository, newSelectedDataSet,
×
395
                            oldForcedJoin.TableInfo);
×
396

397
                    // clone should copy any ExtractionProgresses
398
                    if (selected.ExtractionProgressIfAny != null)
8✔
399
                    {
400
                        var old = selected.ExtractionProgressIfAny;
4✔
401
                        var clonedProgress = new ExtractionProgress(repo, newSelectedDataSet, old.StartDate,
4✔
402
                            old.EndDate, old.NumberOfDaysPerBatch, old.Name, old.ExtractionInformation_ID);
4✔
403

404
                        // Notice that we do not set the ProgressDate because the cloned copy should be extracting from the begining
405
                        // when it is run.  We don't want the user to have to manually reset it
406
                        clonedProgress.SaveToDatabase();
4✔
407
                    }
408

409
                    try
410
                    {
411
                        //clone the root filter container
412
                        var rootContainer = (FilterContainer)GetFilterContainerFor(selected.ExtractableDataSet);
8✔
413

414
                        //turns out there wasn't one to clone at all
415
                        if (rootContainer == null)
8✔
416
                            continue;
4✔
417

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

432
                clone.dtCreated = DateTime.Now;
8✔
433
                clone.IsReleased = false;
8✔
434
                clone.Username = Environment.UserName;
8✔
435
                clone.Description = "TO" + "DO:Populate change log here";
8✔
436
                clone.ReleaseTicket = null;
8✔
437

438
                //wire up some changes
439
                clone.ClonedFrom_ID = ID;
8✔
440
                clone.SaveToDatabase();
8✔
441

442
                repo.EndTransaction(true);
8✔
443

444
                return clone;
8✔
445
            }
446
            catch (Exception)
×
447
            {
448
                repo.EndTransaction(false);
×
449
                throw;
×
450
            }
451
        }
452
    }
8✔
453

454
    private ExtractionConfiguration ShallowClone()
455
    {
456
        var clone = new ExtractionConfiguration(DataExportRepository, Project);
8✔
457
        CopyShallowValuesTo(clone);
8✔
458

459
        clone.Name = $"Clone of {Name}";
8✔
460
        clone.SaveToDatabase();
8✔
461
        return clone;
8✔
462
    }
463

464
    /// <inheritdoc/>
465
    public IProject GetProject() => Repository.GetObjectByID<Project>(Project_ID);
×
466

467
    /// <inheritdoc/>
468
    public ExtractableColumn[] GetAllExtractableColumnsFor(IExtractableDataSet dataset)
469
    {
470
        return
224✔
471
            Repository.GetAllObjectsWhere<ExtractableColumn>("ExtractionConfiguration_ID", ID)
224✔
472
                .Where(e => e.ExtractableDataSet_ID == dataset.ID).ToArray();
4,340✔
473
    }
474

475
    /// <inheritdoc/>
476
    public IContainer GetFilterContainerFor(IExtractableDataSet dataset)
477
    {
478
        return Repository.GetAllObjectsWhere<SelectedDataSets>("ExtractionConfiguration_ID", ID)
226✔
479
            .Single(sds => sds.ExtractableDataSet_ID == dataset.ID)
472✔
480
            .RootFilterContainer;
226✔
481
    }
482

483
    private ExternalDatabaseServer GetDistinctLoggingServer(bool testLoggingServer)
484
    {
485
        var uniqueLoggingServerID = -1;
26✔
486

487
        var repo = (IDataExportRepository)Repository;
26✔
488

489
        foreach (int? catalogueID in GetAllExtractableDataSets().Select(ds => ds.Catalogue_ID))
310✔
490
        {
491
            if (catalogueID == null)
86!
492
                throw new Exception(
×
493
                    "Cannot get logging server because some ExtractableDatasets in the configuration do not have associated Catalogues (possibly the Catalogue was deleted)");
×
494

495
            var catalogue = repo.CatalogueRepository.GetObjectByID<Catalogue>((int)catalogueID);
86✔
496

497
            var loggingServer = catalogue.LiveLoggingServer_ID ?? throw new Exception(
86!
498
                $"Catalogue {catalogue.Name} does not have a {(testLoggingServer ? "test" : "")} logging server configured");
86✔
499
            if (uniqueLoggingServerID == -1)
86✔
500
            {
501
                uniqueLoggingServerID = (int)catalogue.LiveLoggingServer_ID;
26✔
502
            }
503
            else
504
            {
505
                if (uniqueLoggingServerID != catalogue.LiveLoggingServer_ID)
60!
506
                    throw new Exception("Catalogues in configuration have different logging servers");
×
507
            }
508
        }
509

510
        return repo.CatalogueRepository.GetObjectByID<ExternalDatabaseServer>(uniqueLoggingServerID);
26✔
511
    }
512

513
    /// <inheritdoc/>
514
    public IExtractableCohort GetExtractableCohort() => Cohort_ID == null
62✔
515
        ? null
62✔
516
        : (IExtractableCohort)Repository.GetObjectByID<ExtractableCohort>(Cohort_ID.Value);
62✔
517

518
    /// <inheritdoc/>
519
    public IExtractableDataSet[] GetAllExtractableDataSets()
520
    {
521
        return
40✔
522
            Repository.GetAllObjectsWithParent<SelectedDataSets>(this)
40✔
523
                .Select(sds => sds.ExtractableDataSet)
114✔
524
                .ToArray();
40✔
525
    }
526

527
    /// <summary>
528
    /// Makes the provided <paramref name="extractableDataSet"/> extractable in the current <see cref="IExtractionConfiguration"/>.  This
529
    /// includes selecting it (<see cref="ISelectedDataSets"/>) and replicating any mandatory filters.
530
    /// </summary>
531
    /// <param name="extractableDataSet"></param>
532
    public void AddDatasetToConfiguration(IExtractableDataSet extractableDataSet)
533
    {
534
        AddDatasetToConfiguration(extractableDataSet, out _);
44✔
535
    }
44✔
536

537
    /// <summary>
538
    /// Makes the provided <paramref name="extractableDataSet"/> extractable in the current <see cref="IExtractionConfiguration"/>.  This
539
    /// includes selecting it (<see cref="ISelectedDataSets"/>) and replicating any mandatory filters.
540
    /// </summary>
541
    /// <param name="extractableDataSet"></param>
542
    /// <param name="selectedDataSet">The RDMP object that indicates that the dataset is extracted in this configuration</param>
543
    public void AddDatasetToConfiguration(IExtractableDataSet extractableDataSet, out ISelectedDataSets selectedDataSet)
544
    {
545
        selectedDataSet = null;
44✔
546

547
        //it is already part of the configuration
548
        if (SelectedDataSets.Any(s => s.ExtractableDataSet_ID == extractableDataSet.ID))
76!
549
            return;
×
550

551
        var dataExportRepo = (IDataExportRepository)Repository;
44✔
552

553
        selectedDataSet = new SelectedDataSets(dataExportRepo, this, extractableDataSet, null);
44✔
554

555
        var mandatoryExtractionFiltersToApplyToDataset = extractableDataSet.Catalogue.GetAllMandatoryFilters();
44✔
556

557
        //add mandatory filters
558
        if (mandatoryExtractionFiltersToApplyToDataset.Any())
44!
559
        {
560
            //first we need a root container e.g. an AND container
561
            //add the AND container and set it as the root container for the dataset configuration
562
            var rootFilterContainer = new FilterContainer(dataExportRepo)
×
563
            {
×
564
                Operation = FilterContainerOperation.AND
×
565
            };
×
566
            rootFilterContainer.SaveToDatabase();
×
567

568
            selectedDataSet.RootFilterContainer_ID = rootFilterContainer.ID;
×
569
            selectedDataSet.SaveToDatabase();
×
570

571
            var globals = GlobalExtractionFilterParameters;
×
572
            var importer = new FilterImporter(new DeployedExtractionFilterFactory(dataExportRepo), globals);
×
573

574
            var mandatoryFilters =
×
575
                importer.ImportAllFilters(rootFilterContainer, mandatoryExtractionFiltersToApplyToDataset, null);
×
576

577
            foreach (var filter in mandatoryFilters.Cast<DeployedExtractionFilter>())
×
578
            {
579
                filter.FilterContainer_ID = rootFilterContainer.ID;
×
580
                filter.SaveToDatabase();
×
581
            }
582
        }
583

584
        var legacyColumns = GetAllExtractableColumnsFor(extractableDataSet).Cast<ExtractableColumn>().ToArray();
44✔
585

586
        //add Core or ProjectSpecific columns
587
        foreach (var all in extractableDataSet.Catalogue.GetAllExtractionInformation(ExtractionCategory.Any))
852✔
588
            if (all.ExtractionCategory == ExtractionCategory.Core ||
382✔
589
                all.ExtractionCategory == ExtractionCategory.ProjectSpecific)
382✔
590
                if (legacyColumns.All(l => l.CatalogueExtractionInformation_ID != all.ID))
354✔
591
                    AddColumnToExtraction(extractableDataSet, all);
354✔
592
    }
44✔
593

594
    /// <inheritdoc/>
595
    public void RemoveDatasetFromConfiguration(IExtractableDataSet extractableDataSet)
596
    {
597
        var match = SelectedDataSets.SingleOrDefault(s => s.ExtractableDataSet_ID == extractableDataSet.ID);
6✔
598
        match?.DeleteInDatabase();
2✔
599
    }
2✔
600

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

615
        var query = column.SelectSQL;
366✔
616

617
        ExtractableColumn addMe;
618

619
        if (column is ExtractionInformation extractionInformation)
366!
620
            addMe = new ExtractableColumn((IDataExportRepository)Repository, forDataSet, this, extractionInformation, -1, query);
366✔
621
        else
622
            addMe = new ExtractableColumn((IDataExportRepository)Repository, forDataSet, this, null, -1,
×
623
                query); // its custom column of some kind, not tied to a catalogue entry
×
624

625
        addMe.UpdateValuesToMatch(column);
366✔
626

627
        return addMe;
366✔
628
    }
629

630
    /// <summary>
631
    /// Returns the logging server that should be used to audit extraction executions of this <see cref="IExtractionConfiguration"/>.
632
    /// </summary>
633
    /// <returns></returns>
634
    public LogManager GetExplicitLoggingDatabaseServerOrDefault()
635
    {
636
        ExternalDatabaseServer loggingServer;
637
        try
638
        {
639
            loggingServer = GetDistinctLoggingServer(false);
26✔
640
        }
26✔
641
        catch (Exception e)
×
642
        {
643
            //failed to get a logging server correctly
644

645
            //see if there is a default
646
            var defaultGetter = Project.DataExportRepository.CatalogueRepository;
×
647
            var defaultLoggingServer = defaultGetter.GetDefaultFor(PermissableDefaults.LiveLoggingServer_ID);
×
648

649
            //there is a default?
650
            if (defaultLoggingServer != null)
×
651
                loggingServer = (ExternalDatabaseServer)defaultLoggingServer;
×
652
            else
653
                //no, there is no default or user does not want to use it.
654
                throw new Exception(
×
655
                    "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",
×
656
                    e);
×
657
        }
×
658

659
        var server = DataAccessPortal.ExpectServer(loggingServer, DataAccessContext.Logging);
26✔
660

661
        LogManager lm;
662

663
        try
664
        {
665
            lm = new LogManager(server);
26✔
666

667
            if (!lm.ListDataTasks().Contains(ExecuteDatasetExtractionSource.AuditTaskName))
26!
668
                throw new Exception(
×
669
                    $"The logging database {server} does not contain a DataLoadTask called '{ExecuteDatasetExtractionSource.AuditTaskName}' (all data exports are logged under this task regardless of dataset/Catalogue)");
×
670
        }
26✔
671
        catch (Exception e)
×
672
        {
673
            throw new Exception($"Problem figuring out what logging server to use:{Environment.NewLine}\t{e.Message}",
×
674
                e);
×
675
        }
676

677
        return lm;
26✔
678
    }
679

680
    /// <inheritdoc/>
681
    public void Unfreeze()
682
    {
683
        foreach (var l in ReleaseLog)
×
684
            l.DeleteInDatabase();
×
685

686
        foreach (var r in CumulativeExtractionResults)
×
687
            r.DeleteInDatabase();
×
688

689
        IsReleased = false;
×
690
        SaveToDatabase();
×
691
    }
×
692

693
    /// <inheritdoc/>
694
    public IMapsDirectlyToDatabaseTable[] GetGlobals()
695
    {
696
        var sds = SelectedDataSets.FirstOrDefault(s => s.ExtractableDataSet.Catalogue != null);
6✔
697

698
        if (sds == null)
4✔
699
            return Array.Empty<IMapsDirectlyToDatabaseTable>();
2✔
700

701
        var cata = sds.ExtractableDataSet.Catalogue;
2✔
702

703
        return
2✔
704
            cata.GetAllSupportingSQLTablesForCatalogue(FetchOptions.ExtractableGlobals)
2✔
705
                .Cast<IMapsDirectlyToDatabaseTable>()
2✔
706
                .Union(
2✔
707
                    cata.GetAllSupportingDocuments(FetchOptions.ExtractableGlobals))
2✔
708
                .ToArray();
2✔
709
    }
710

711
    /// <inheritdoc/>
712
    public override void DeleteInDatabase()
713
    {
714
        foreach (var result in Repository.GetAllObjectsWithParent<SupplementalExtractionResults>(this))
64!
715
            result.DeleteInDatabase();
×
716

717
        base.DeleteInDatabase();
32✔
718
    }
32✔
719

720
    /// <inheritdoc/>
721
    public IHasDependencies[] GetObjectsThisDependsOn()
722
    {
723
        return new[] { Project };
×
724
    }
725

726
    /// <inheritdoc/>
727
    public IHasDependencies[] GetObjectsDependingOnThis() => Array.Empty<IHasDependencies>();
×
728

729
    public DiscoveredServer GetDistinctLoggingDatabase() =>
730
        GetDistinctLoggingServer(false).Discover(DataAccessContext.Logging).Server;
×
731

732
    public DiscoveredServer GetDistinctLoggingDatabase(out IExternalDatabaseServer serverChosen)
733
    {
734
        serverChosen = GetDistinctLoggingServer(false);
×
735
        return serverChosen.Discover(DataAccessContext.Logging).Server;
×
736
    }
737

738
    public string GetDistinctLoggingTask() => ExecuteDatasetExtractionSource.AuditTaskName;
×
739

740
    /// <summary>
741
    /// Returns runs from the data extraction task where the run was for this ExtractionConfiguration
742
    /// </summary>
743
    /// <param name="runs"></param>
744
    /// <returns></returns>
745
    public IEnumerable<ArchivalDataLoadInfo> FilterRuns(IEnumerable<ArchivalDataLoadInfo> runs)
746
    {
747
        // allow for the project name changing but not our ID
748
        return runs.Where(r => r.Description.Contains(GetExtractionLoggingName()));
×
749
    }
750
}
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