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

HicServices / RDMP / 15020927240

14 May 2025 12:40PM UTC coverage: 57.504% (-0.03%) from 57.535%
15020927240

push

github

JFriel
update tests

11401 of 21378 branches covered (53.33%)

Branch coverage included in aggregate %.

32343 of 54693 relevant lines covered (59.14%)

17532.33 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

177
    #endregion
178

179
    #region Relationships
180

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

185
    /// <inheritdoc/>
186
    [NoMappingToDatabase]
187
    public ISqlParameter[] GlobalExtractionFilterParameters =>
188
        Repository.GetAllObjectsWithParent<GlobalExtractionFilterParameter>(this)
172✔
189
            .Cast<ISqlParameter>().ToArray();
172✔
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)
458✔
209
        .Cast<ISelectedDataSets>().ToArray();
458✔
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)
378✔
276
    {
277
        Repository = repository;
378✔
278

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

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

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

315
        var dt = r["dtCreated"];
1,010✔
316

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

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

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

333
        DefaultPipeline_ID = ObjectToNullableInt(r["DefaultPipeline_ID"]);
1,010✔
334
        CohortIdentificationConfiguration_ID = ObjectToNullableInt(r["CohortIdentificationConfiguration_ID"]);
1,010✔
335
        CohortRefreshPipeline_ID = ObjectToNullableInt(r["CohortRefreshPipeline_ID"]);
1,010✔
336
    }
1,010✔
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;
54✔
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
                //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 (var cloneExtractableColumn in GetAllExtractableColumnsFor(selected.ExtractableDataSet).Select(static extractableColumn => extractableColumn.ShallowClone()))
70✔
385
                    {
386
                        cloneExtractableColumn.ExtractionConfiguration_ID = clone.ID;
18✔
387
                        cloneExtractableColumn.SaveToDatabase();
18✔
388
                    }
389

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

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

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

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

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

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

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

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

441
                repo.EndTransaction(true);
8✔
442

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

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

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

463
    /// <inheritdoc/>
464
    public IProject GetProject() => Repository.GetObjectByID<Project>(Project_ID);
12✔
465

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

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

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

486
        var repo = (IDataExportRepository)Repository;
66✔
487

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

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

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

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

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

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

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

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

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

550
        var dataExportRepo = (IDataExportRepository)Repository;
74✔
551

552
        selectedDataSet = new SelectedDataSets(dataExportRepo, this, extractableDataSet, null);
74✔
553

554
        var mandatoryExtractionFiltersToApplyToDataset = extractableDataSet.Catalogue.GetAllMandatoryFilters();
74✔
555

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

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

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

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

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

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

585
        //add Core or ProjectSpecific columns
586
        foreach (var all in extractableDataSet.Catalogue.GetAllExtractionInformation(ExtractionCategory.Any))
1,732✔
587
            if (all.ExtractionCategory == ExtractionCategory.Core ||
792✔
588
                all.ExtractionCategory == ExtractionCategory.ProjectSpecific)
792✔
589
                if (legacyColumns.All(l => l.CatalogueExtractionInformation_ID != all.ID))
764✔
590
                    AddColumnToExtraction(extractableDataSet, all);
764✔
591
    }
74✔
592

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

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

614
        var query = column.SelectSQL;
776✔
615

616
        ExtractableColumn addMe;
617

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

624
        addMe.UpdateValuesToMatch(column);
776✔
625

626
        return addMe;
776✔
627
    }
628

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

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

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

658
        var server = DataAccessPortal.ExpectServer(loggingServer, DataAccessContext.Logging);
66✔
659

660
        LogManager lm;
661

662
        try
663
        {
664
            lm = new LogManager(server);
66✔
665

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

676
        return lm;
66✔
677
    }
678

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

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

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

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

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

700
        var cata = sds.ExtractableDataSet.Catalogue;
34✔
701

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

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

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

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

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

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

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

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

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