• 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

92.66
/Rdmp.Core/CommandLine/DatabaseCreation/ExampleDatasetsCreation.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.Data;
9
using System.IO;
10
using System.Linq;
11
using System.Text.RegularExpressions;
12
using BadMedicine;
13
using BadMedicine.Datasets;
14
using FAnsi;
15
using FAnsi.Discovery;
16
using FAnsi.Discovery.ConnectionStringDefaults;
17
using Microsoft.Data.SqlClient;
18
using Rdmp.Core.CohortCommitting.Pipeline;
19
using Rdmp.Core.CohortCommitting.Pipeline.Sources;
20
using Rdmp.Core.CommandExecution;
21
using Rdmp.Core.CommandExecution.AtomicCommands;
22
using Rdmp.Core.CommandLine.Options;
23
using Rdmp.Core.CommandLine.Runners;
24
using Rdmp.Core.Curation;
25
using Rdmp.Core.Curation.Data;
26
using Rdmp.Core.Curation.Data.Aggregation;
27
using Rdmp.Core.Curation.Data.Cohort;
28
using Rdmp.Core.Curation.Data.Pipelines;
29
using Rdmp.Core.Curation.FilterImporting;
30
using Rdmp.Core.Curation.FilterImporting.Construction;
31
using Rdmp.Core.DataExport.Data;
32
using Rdmp.Core.DataExport.DataExtraction.Pipeline.Destinations;
33
using Rdmp.Core.DataExport.DataRelease.Pipeline;
34
using Rdmp.Core.DataFlowPipeline;
35
using Rdmp.Core.Repositories;
36
using Rdmp.Core.ReusableLibraryCode.Checks;
37
using Rdmp.Core.ReusableLibraryCode.Progress;
38
using TypeGuesser;
39

40
namespace Rdmp.Core.CommandLine.DatabaseCreation;
41

42
/// <summary>
43
/// Handles the creation of example RDMP datasets and metadata object (catalogues, cohorts , projects etc).
44
/// </summary>
45
public partial class ExampleDatasetsCreation
46
{
47
    private IRDMPPlatformRepositoryServiceLocator _repos;
48
    private IBasicActivateItems _activator;
49
    public const int NumberOfPeople = 5000;
50
    public const int NumberOfRowsPerDataset = 10000;
51

52
    public ExampleDatasetsCreation(IBasicActivateItems activator, IRDMPPlatformRepositoryServiceLocator repos)
2✔
53
    {
54
        _repos = repos;
2✔
55
        _activator = activator;
2✔
56
    }
2✔
57

58
    internal void Create(DiscoveredDatabase db, ICheckNotifier notifier, PlatformDatabaseCreationOptions options)
59
    {
60
        if (db.Exists())
2✔
61
            if (options.DropDatabases)
2!
62
                db.Drop();
2✔
63
            else
64
                throw new Exception(
×
65
                    $"Database {db.GetRuntimeName()} already exists and allowDrop option was not specified");
×
66

67
        if (db.Server.Builder is SqlConnectionStringBuilder b)
2✔
68
        {
69
            var keywords = _repos.CatalogueRepository
2✔
70
                .GetAllObjects<ConnectionStringKeyword>()
2✔
71
                .Where(k => k.DatabaseType == DatabaseType.MicrosoftSQLServer)
8✔
72
                .ToArray();
2✔
73

74
            AddKeywordIfSpecified(b.TrustServerCertificate, nameof(b.TrustServerCertificate), keywords);
2✔
75
        }
76

77
        notifier.OnCheckPerformed(new CheckEventArgs($"About to create {db.GetRuntimeName()}", CheckResult.Success));
2✔
78
        //create a new database for the datasets
79
        db.Create();
2✔
80

81
        notifier.OnCheckPerformed(
2✔
82
            new CheckEventArgs($"Successfully created {db.GetRuntimeName()}", CheckResult.Success));
2✔
83

84
        //fixed seed so everyone gets the same datasets
85
        var r = new Random(options.Seed);
2✔
86

87
        notifier.OnCheckPerformed(new CheckEventArgs("Generating people", CheckResult.Success));
2✔
88
        //people
89
        var people = new PersonCollection();
2✔
90
        people.GeneratePeople(options.NumberOfPeople, r);
2✔
91

92
        //datasets
93
        var biochem = ImportCatalogue(Create<Biochemistry>(db, people, r, notifier, options.NumberOfRowsPerDataset,
2✔
94
            "chi", "Healthboard", "SampleDate", "TestCode"));
2✔
95
        var demography = ImportCatalogue(Create<Demography>(db, people, r, notifier, options.NumberOfRowsPerDataset,
2✔
96
            "chi", "dtCreated", "hb_extract"));
2✔
97
        var prescribing = ImportCatalogue(Create<Prescribing>(db, people, r, notifier, options.NumberOfRowsPerDataset,
2✔
98
            "chi", "PrescribedDate", "Name")); //<- this is slooo!
2✔
99
        var admissions = ImportCatalogue(Create<HospitalAdmissions>(db, people, r, notifier,
2✔
100
            options.NumberOfRowsPerDataset, "chi", "AdmissionDate"));
2✔
101

102
        //Create but do not import the CarotidArteryScan dataset so that users can test out referencing a brand new table
103
        Create<CarotidArteryScan>(db, people, r, notifier, options.NumberOfRowsPerDataset, "RECORD_NUMBER");
2✔
104

105
        //the following should not be extractable
106
        ForExtractionInformations(demography,
2✔
107
            e => e.DeleteInDatabase(),
42✔
108
            "chi_num_of_curr_record",
2✔
109
            "surname",
2✔
110
            "forename",
2✔
111
            "current_address_L1",
2✔
112
            "current_address_L2",
2✔
113
            "current_address_L3",
2✔
114
            "current_address_L4",
2✔
115
            "birth_surname",
2✔
116
            "previous_surname",
2✔
117
            "midname",
2✔
118
            "alt_forename",
2✔
119
            "other_initials",
2✔
120
            "previous_address_L1",
2✔
121
            "previous_address_L2",
2✔
122
            "previous_address_L3",
2✔
123
            "previous_address_L4",
2✔
124
            "previous_postcode",
2✔
125
            "date_address_changed",
2✔
126
            "adr",
2✔
127
            "previous_gp_accept_date",
2✔
128
            "hic_dataLoadRunID");
2✔
129

130
        //the following should be special approval only
131
        ForExtractionInformations(demography,
2✔
132
            e =>
2✔
133
            {
2✔
134
                e.ExtractionCategory = ExtractionCategory.SpecialApprovalRequired;
8✔
135
                e.SaveToDatabase();
8✔
136
            },
8✔
137
            "current_postcode",
2✔
138
            "current_gp",
2✔
139
            "previous_gp",
2✔
140
            "date_of_birth");
2✔
141

142

143
        CreateAdmissionsViews(db);
2✔
144
        var vConditions = ImportCatalogue(db.ExpectTable("vConditions", null, TableType.View));
2✔
145
        var vOperations = ImportCatalogue(db.ExpectTable("vOperations", null, TableType.View));
2✔
146

147
        CreateGraph(biochem, "Test Codes", "TestCode", false, null);
2✔
148
        CreateGraph(biochem, "Test Codes By Date", "SampleDate", true, "TestCode");
2✔
149

150
        CreateFilter(biochem, "Creatinine", "TestCode", "TestCode like '%CRE%'",
2✔
151
            @"Serum creatinine is a blood measurement.  It is an indicator of renal health.");
2✔
152
        CreateFilter(biochem, "Test Code", "TestCode", "TestCode like @code", "Filters any test code set");
2✔
153

154
        CreateExtractionInformation(demography, "Age", "date_of_birth",
2✔
155
            "FLOOR(DATEDIFF(DAY, date_of_birth, GETDATE()) / 365.25) As Age");
2✔
156
        var fAge = CreateFilter(demography, "Older at least x years", "Age",
2✔
157
            "FLOOR(DATEDIFF(DAY, date_of_birth, GETDATE()) / 365.25) >= @age",
2✔
158
            "Patients age is greater than or equal to the provided @age");
2✔
159
        SetParameter(fAge, "@age", "int", "16");
2✔
160

161
        CreateGraph(demography, "Patient Ages", "Age", false, null);
2✔
162

163
        CreateGraph(prescribing, "Approved Name", "ApprovedName", false, null);
2✔
164
        CreateGraph(prescribing, "Approved Name Over Time", "PrescribedDate", true, "ApprovedName");
2✔
165

166
        CreateGraph(prescribing, "Bnf", "FormattedBnfCode", false, null);
2✔
167
        CreateGraph(prescribing, "Bnf Over Time", "PrescribedDate", true, "FormattedBnfCode");
2✔
168

169
        CreateFilter(
2✔
170
            CreateGraph(vConditions, "Conditions frequency", "Field", false, "Condition"),
2✔
171
            "Common Conditions Only",
2✔
172
            @"(Condition in 
2✔
173
(select top 40 Condition from vConditions c
2✔
174
 WHERE Condition <> 'NULL' AND Condition <> 'Nul' 
2✔
175
 group by Condition order by count(*) desc))");
2✔
176

177
        CreateFilter(
2✔
178
            CreateGraph(vOperations, "Operation frequency", "Field", false, "Operation"),
2✔
179
            "Common Operation Only",
2✔
180
            @"(Operation in 
2✔
181
(select top 40 Operation from vOperations c
2✔
182
 WHERE Operation <> 'NULL' AND Operation <> 'Nul' 
2✔
183
 group by Operation order by count(*) desc))");
2✔
184

185
        //group these all into the same folder
186
        admissions.Folder = @"\admissions";
2✔
187
        admissions.SaveToDatabase();
2✔
188
        vConditions.Folder = @"\admissions";
2✔
189
        vConditions.SaveToDatabase();
2✔
190
        vOperations.Folder = @"\admissions";
2✔
191
        vOperations.SaveToDatabase();
2✔
192

193

194
        var cmdCreateCohortTable = new ExecuteCommandCreateNewCohortStore(_activator, db, false, "chi", "varchar(10)");
2✔
195
        cmdCreateCohortTable.Execute();
2✔
196
        var externalCohortTable = cmdCreateCohortTable.Created;
2✔
197

198
        //Find the pipeline for committing cohorts
199
        var cohortCreationPipeline =
2!
200
            _repos.CatalogueRepository.GetAllObjects<Pipeline>().FirstOrDefault(p =>
2✔
201
                p?.Source?.Class == typeof(CohortIdentificationConfigurationSource).FullName) ??
10!
202
            throw new Exception("Could not find a cohort committing pipeline");
2✔
203

204
        //A cohort creation query
205
        var f = CreateFilter(vConditions, "Lung Cancer Condition", "Condition", "Condition like 'C349'",
2✔
206
            "ICD-10-CM Diagnosis Code C34.9 Malignant neoplasm of unspecified part of bronchus or lung");
2✔
207

208
        var cic = CreateCohortIdentificationConfiguration((ExtractionFilter)f);
2✔
209

210
        var cohort = CommitCohortToNewProject(cic, externalCohortTable, cohortCreationPipeline, "Lung Cancer Project",
2✔
211
            "P1 Lung Cancer Patients", 123, out var project);
2✔
212

213
        var cohortTable = cohort.ExternalCohortTable.DiscoverCohortTable();
2✔
214
        using (var con = cohortTable.Database.Server.GetConnection())
2✔
215
        {
216
            con.Open();
2✔
217
            //delete half the records (so we can simulate cohort refresh)
218
            using var cmd = cohortTable.Database.Server.GetCommand(
2✔
219
                $"DELETE TOP (10) PERCENT from {cohortTable.GetFullyQualifiedName()}", con);
2✔
220
            cmd.ExecuteNonQuery();
2✔
221
        }
222

223
        var ec1 = CreateExtractionConfiguration(project, cohort, "First Extraction (2016 - project 123)", true,
2✔
224
            notifier, biochem, prescribing, demography);
2✔
225
        var ec2 = CreateExtractionConfiguration(project, cohort, "Project 123 - 2017 Refresh", true, notifier, biochem,
2✔
226
            prescribing, demography, admissions);
2✔
227
        var ec3 = CreateExtractionConfiguration(project, cohort, "Project 123 - 2018 Refresh", true, notifier, biochem,
2✔
228
            prescribing, demography, admissions);
2✔
229

230
        ReleaseAllConfigurations(notifier, ec1, ec2, ec3);
2✔
231

232

233
        if (options.Nightmare)
2!
234
        {
235
            var nightmare = new NightmareDatasets(_repos, db)
×
236
            {
×
237
                Factor = options.NightmareFactor
×
238
            };
×
239
            nightmare.Create(externalCohortTable);
×
240
        }
241
    }
2✔
242

243
    private void AddKeywordIfSpecified(bool isEnabled, string name, ConnectionStringKeyword[] alreadyDeclared)
244
    {
245
        if (isEnabled && !alreadyDeclared.Any(k => k.Name.Equals(name)))
2✔
246
        {
247
            var keyword = new ConnectionStringKeyword(_activator.RepositoryLocator.CatalogueRepository,
2✔
248
                DatabaseType.MicrosoftSQLServer, name, "true");
2✔
249

250
            //pass it into the system wide static keyword collection for use with all databases of this type all the time
251
            DiscoveredServerHelper.AddConnectionStringKeyword(keyword.DatabaseType, keyword.Name, keyword.Value,
2✔
252
                ConnectionStringKeywordPriority.SystemDefaultMedium);
2✔
253
        }
254
    }
2✔
255

256
    private void ReleaseAllConfigurations(ICheckNotifier notifier,
257
        params ExtractionConfiguration[] extractionConfigurations)
258
    {
259
        var releasePipeline = _repos.CatalogueRepository.GetAllObjects<Pipeline>()
2✔
260
            .FirstOrDefault(p => p?.Destination?.Class == typeof(BasicDataReleaseDestination).FullName);
20!
261

262
        try
263
        {
264
            //cleanup any old releases
265
            var project = extractionConfigurations.Select(ec => ec.Project).Distinct().Single();
8✔
266

267
            var folderProvider = new ReleaseFolderProvider();
2✔
268
            var dir = folderProvider.GetFromProjectFolder(project);
2✔
269
            if (dir.Exists)
2!
270
                dir.Delete(true);
×
271
        }
2✔
272
        catch (Exception ex)
×
273
        {
274
            notifier.OnCheckPerformed(new CheckEventArgs("Could not detect/delete release folder for extractions",
×
275
                CheckResult.Warning, ex));
×
276
            return;
×
277
        }
278

279

280
        if (releasePipeline != null)
2✔
281
            try
282
            {
283
                var optsRelease = new ReleaseOptions
2✔
284
                {
2✔
285
                    Configurations = string.Join(",",
2✔
286
                        extractionConfigurations.Select(ec => ec.ID.ToString()).Distinct().ToArray()),
6✔
287
                    Pipeline = releasePipeline.ID.ToString()
2✔
288
                };
2✔
289

290
                var runnerRelease = new ReleaseRunner(optsRelease);
2✔
291
                runnerRelease.Run(_repos, ThrowImmediatelyDataLoadEventListener.Quiet, notifier,
2✔
292
                    new GracefulCancellationToken());
2✔
293
            }
2✔
294
            catch (Exception ex)
×
295
            {
296
                notifier.OnCheckPerformed(new CheckEventArgs("Could not Release ExtractionConfiguration (never mind)",
×
297
                    CheckResult.Warning, ex));
×
298
            }
×
299
    }
2✔
300

301
    private static void ForExtractionInformations(ICatalogue catalogue, Action<ExtractionInformation> action,
302
        params string[] extractionInformations)
303
    {
304
        foreach (var e in extractionInformations.Select(s => GetExtractionInformation(catalogue, s)))
158✔
305
            action(e);
50✔
306
    }
4✔
307

308
    private static void SetParameter(IFilter filter, string paramterToSet, string dataType, string value)
309
    {
310
        var p = filter.GetAllParameters().Single(fp => fp.ParameterName == paramterToSet);
4✔
311
        p.ParameterSQL = $"DECLARE {paramterToSet} AS {dataType}";
2✔
312
        p.Value = value;
2✔
313
        p.SaveToDatabase();
2✔
314
    }
2✔
315

316
    private ExtractionInformation CreateExtractionInformation(ICatalogue catalogue, string name, string columnInfoName,
317
        string selectSQL)
318
    {
319
        var col =
2!
320
            catalogue.GetTableInfoList(false).SelectMany(t => t.ColumnInfos)
2✔
321
                .SingleOrDefault(c => c.GetRuntimeName() == columnInfoName) ??
78✔
322
            throw new Exception($"Could not find ColumnInfo called '{columnInfoName}' in Catalogue {catalogue}");
2✔
323
        var ci = new CatalogueItem(_repos.CatalogueRepository, catalogue, name)
2✔
324
        {
2✔
325
            ColumnInfo_ID = col.ID
2✔
326
        };
2✔
327
        ci.SaveToDatabase();
2✔
328

329
        return new ExtractionInformation(_repos.CatalogueRepository, ci, col, selectSQL);
2✔
330
    }
331

332
    private ExtractionConfiguration CreateExtractionConfiguration(Project project, ExtractableCohort cohort,
333
        string name, bool isReleased, ICheckNotifier notifier, params ICatalogue[] catalogues)
334
    {
335
        var extractionConfiguration = new ExtractionConfiguration(_repos.DataExportRepository, project)
6✔
336
        {
6✔
337
            Name = name,
6✔
338
            Cohort_ID = cohort.ID
6✔
339
        };
6✔
340
        extractionConfiguration.SaveToDatabase();
6✔
341

342
        foreach (var c in catalogues)
56✔
343
        {
344
            //Get its extractableness
345
            var eds = _repos.DataExportRepository.GetAllObjectsWithParent<ExtractableDataSet>(c).SingleOrDefault()
22!
346
                      ?? new ExtractableDataSet(_repos.DataExportRepository, c); //or make it extractable
22✔
347

348
            extractionConfiguration.AddDatasetToConfiguration(eds);
22✔
349
        }
350

351
        var extractionPipeline = _repos.CatalogueRepository.GetAllObjects<Pipeline>().FirstOrDefault(p =>
6✔
352
            p?.Destination?.Class == typeof(ExecuteDatasetExtractionFlatFileDestination).FullName);
54!
353

354
        if (isReleased && extractionConfiguration != null)
6✔
355
        {
356
            var optsExtract = new ExtractionOptions
6✔
357
            {
6✔
358
                Pipeline = extractionPipeline.ID.ToString(),
6✔
359
                ExtractionConfiguration = extractionConfiguration.ID.ToString()
6✔
360
            };
6✔
361
            var runnerExtract = new ExtractionRunner(_activator, optsExtract);
6✔
362
            try
363
            {
364
                runnerExtract.Run(_repos, ThrowImmediatelyDataLoadEventListener.Quiet, notifier,
6✔
365
                    new GracefulCancellationToken());
6✔
366
            }
6✔
367
            catch (Exception ex)
×
368
            {
369
                notifier.OnCheckPerformed(new CheckEventArgs("Could not run ExtractionConfiguration (never mind)",
×
370
                    CheckResult.Warning, ex));
×
371
            }
×
372

373
            extractionConfiguration.IsReleased = true;
6✔
374
            extractionConfiguration.SaveToDatabase();
6✔
375
        }
376

377
        return extractionConfiguration;
6✔
378
    }
379

380
    private ExtractableCohort CommitCohortToNewProject(CohortIdentificationConfiguration cic,
381
        ExternalCohortTable externalCohortTable, IPipeline cohortCreationPipeline, string projectName,
382
        string cohortName, int projectNumber, out Project project)
383
    {
384
        //create a new data extraction Project
385
        project = new Project(_repos.DataExportRepository, projectName)
2✔
386
        {
2✔
387
            ProjectNumber = projectNumber,
2✔
388
            ExtractionDirectory = Path.GetTempPath()
2✔
389
        };
2✔
390
        project.SaveToDatabase();
2✔
391

392
        //create a cohort
393
        var auditLogBuilder = new ExtractableCohortAuditLogBuilder();
2✔
394

395
        var request = new CohortCreationRequest(project,
2✔
396
            new CohortDefinition(null, cohortName, 1, projectNumber, externalCohortTable), _repos.DataExportRepository,
2✔
397
            ExtractableCohortAuditLogBuilder.GetDescription(cic))
2✔
398
        {
2✔
399
            CohortIdentificationConfiguration = cic
2✔
400
        };
2✔
401

402
        var engine = request.GetEngine(cohortCreationPipeline, ThrowImmediatelyDataLoadEventListener.Quiet);
2✔
403

404
        engine.ExecutePipeline(new GracefulCancellationToken());
2✔
405

406
        return request.CohortCreatedIfAny;
2✔
407
    }
408

409
    private CohortIdentificationConfiguration CreateCohortIdentificationConfiguration(ExtractionFilter inclusionFilter1)
410
    {
411
        //Create the top level configuration object
412
        var cic = new CohortIdentificationConfiguration(_repos.CatalogueRepository, "Tayside Lung Cancer Cohort");
2✔
413

414
        //create a UNION container for Inclusion Criteria
415
        var container = new CohortAggregateContainer(_repos.CatalogueRepository, SetOperation.UNION)
2✔
416
        {
2✔
417
            Name = "Inclusion Criteria"
2✔
418
        };
2✔
419
        container.SaveToDatabase();
2✔
420

421
        cic.RootCohortAggregateContainer_ID = container.ID;
2✔
422
        cic.SaveToDatabase();
2✔
423

424
        //Create a new cohort set to the 'Inclusion Criteria' based on the filters Catalogue
425
        var cata = inclusionFilter1.ExtractionInformation.CatalogueItem.Catalogue;
2✔
426
        var ac = cic.CreateNewEmptyConfigurationForCatalogue(cata,
2✔
427
            (a, b) => throw new Exception("Problem encountered with chi column(s)"), false);
2✔
428
        container.AddChild(ac, 0);
2✔
429

430
        //Add the filter to the WHERE logic of the cohort set
431
        var whereContainer = new AggregateFilterContainer(_repos.CatalogueRepository, FilterContainerOperation.OR);
2✔
432

433
        ac.Name = $"People with {inclusionFilter1.Name}";
2✔
434
        ac.RootFilterContainer_ID = whereContainer.ID;
2✔
435
        cic.EnsureNamingConvention(ac); //this will put cicx at the front and cause implicit SaveToDatabase
2✔
436

437
        var filterImporter = new FilterImporter(new AggregateFilterFactory(_repos.CatalogueRepository), null);
2✔
438
        var cloneFilter = filterImporter.ImportFilter(whereContainer, inclusionFilter1, null);
2✔
439

440
        whereContainer.AddChild(cloneFilter);
2✔
441

442
        return cic;
2✔
443
    }
444

445
    private IFilter CreateFilter(AggregateConfiguration graph, string name, string whereSql)
446
    {
447
        AggregateFilterContainer container;
448
        if (graph.RootFilterContainer_ID == null)
4!
449
        {
450
            container = new AggregateFilterContainer(_repos.CatalogueRepository, FilterContainerOperation.AND);
4✔
451
            graph.RootFilterContainer_ID = container.ID;
4✔
452
            graph.SaveToDatabase();
4✔
453
        }
454
        else
455
        {
456
            container = (AggregateFilterContainer)graph.RootFilterContainer;
×
457
        }
458

459
        var filter = new AggregateFilter(_repos.CatalogueRepository, name, container)
4✔
460
        {
4✔
461
            WhereSQL = whereSql
4✔
462
        };
4✔
463
        filter.SaveToDatabase();
4✔
464

465
        return filter;
4✔
466
    }
467

468
    private static void CreateAdmissionsViews(DiscoveredDatabase db)
469
    {
470
        using var con = db.Server.GetConnection();
2✔
471
        con.Open();
2✔
472
        using (var cmd = db.Server.GetCommand(
2✔
473
                   """
2✔
474
                   create view vConditions as
2✔
475

2✔
476
                   SELECT chi,DateOfBirth,AdmissionDate,DischargeDate,Condition,Field
2✔
477
                   FROM
2✔
478
                   (
2✔
479
                     SELECT chi,DateOfBirth,AdmissionDate,DischargeDate,MainCondition,OtherCondition1,OtherCondition2,OtherCondition3
2✔
480
                     FROM HospitalAdmissions
2✔
481
                   ) AS cp
2✔
482
                   UNPIVOT
2✔
483
                   (
2✔
484
                     Condition FOR Field IN (MainCondition,OtherCondition1,OtherCondition2,OtherCondition3)
2✔
485
                   ) AS up;
2✔
486
                   """, con))
2✔
487
        {
488
            cmd.ExecuteNonQuery();
2✔
489
        }
2✔
490

491

492
        using (var cmd = db.Server.GetCommand(
2✔
493
                   """
2✔
494
                   create view vOperations as
2✔
495

2✔
496
                   SELECT chi,DateOfBirth,AdmissionDate,DischargeDate,Operation,Field
2✔
497
                   FROM
2✔
498
                   (
2✔
499
                     SELECT chi,DateOfBirth,AdmissionDate,DischargeDate,MainOperation,OtherOperation1,OtherOperation2,OtherOperation3
2✔
500
                     FROM HospitalAdmissions
2✔
501
                   ) AS cp
2✔
502
                   UNPIVOT
2✔
503
                   (
2✔
504
                     Operation FOR Field IN (MainOperation,OtherOperation1,OtherOperation2,OtherOperation3)
2✔
505
                   ) AS up;
2✔
506
                   """, con))
2✔
507
        {
508
            cmd.ExecuteNonQuery();
2✔
509
        }
2✔
510
    }
2✔
511

512
    private IFilter CreateFilter(ICatalogue cata, string name, string parentExtractionInformation, string whereSql,
513
        string desc)
514
    {
515
        var filter = new ExtractionFilter(_repos.CatalogueRepository, name,
8✔
516
            GetExtractionInformation(cata, parentExtractionInformation))
8✔
517
        {
8✔
518
            WhereSQL = whereSql,
8✔
519
            Description = desc
8✔
520
        };
8✔
521
        filter.SaveToDatabase();
8✔
522

523
        var parameterCreator = new ParameterCreator(filter.GetFilterFactory(), null, null);
8✔
524
        parameterCreator.CreateAll(filter, null);
8✔
525

526
        return filter;
8✔
527
    }
528

529
    /// <summary>
530
    /// Creates a new AggregateGraph for the given dataset (<paramref name="cata"/>)
531
    /// </summary>
532
    /// <param name="cata"></param>
533
    /// <param name="name">The name to give the graph</param>
534
    /// <param name="dimension1">The first dimension e.g. pass only one dimension to create a bar chart</param>
535
    /// <param name="isAxis">True if <paramref name="dimension1"/> should be created as a axis (creates a line chart)</param>
536
    /// <param name="dimension2">Optional second dimension to create (this will be the pivot column)</param>
537
    private AggregateConfiguration CreateGraph(ICatalogue cata, string name, string dimension1, bool isAxis,
538
        string dimension2)
539
    {
540
        var ac = new AggregateConfiguration(_repos.CatalogueRepository, cata, name)
18✔
541
        {
18✔
542
            CountSQL = "count(*) as NumberOfRecords"
18✔
543
        };
18✔
544
        ac.SaveToDatabase();
18✔
545
        ac.IsExtractable = true;
18✔
546

547
        var mainDimension = ac.AddDimension(GetExtractionInformation(cata, dimension1));
18✔
548
        var otherDimension = string.IsNullOrWhiteSpace(dimension2)
18✔
549
            ? null
18✔
550
            : ac.AddDimension(GetExtractionInformation(cata, dimension2));
18✔
551

552
        if (isAxis)
18✔
553
        {
554
            var axis = new AggregateContinuousDateAxis(_repos.CatalogueRepository, mainDimension)
6✔
555
            {
6✔
556
                StartDate = "'1970-01-01'",
6✔
557
                AxisIncrement = FAnsi.Discovery.QuerySyntax.Aggregation.AxisIncrement.Year
6✔
558
            };
6✔
559
            axis.SaveToDatabase();
6✔
560
        }
561

562
        if (otherDimension != null)
18✔
563
        {
564
            ac.PivotOnDimensionID = otherDimension.ID;
10✔
565
            ac.SaveToDatabase();
10✔
566
        }
567

568
        return ac;
18✔
569
    }
570

571
    private static ExtractionInformation GetExtractionInformation(ICatalogue cata, string name)
572
    {
573
        try
574
        {
575
            return cata.GetAllExtractionInformation(ExtractionCategory.Any).Single(ei =>
86✔
576
                ei.GetRuntimeName().Equals(name, StringComparison.CurrentCultureIgnoreCase));
2,446✔
577
        }
578
        catch
×
579
        {
580
            throw new Exception($"Could not find an ExtractionInformation called '{name}' in dataset {cata.Name}");
×
581
        }
582
    }
86✔
583

584
    private static DiscoveredTable Create<T>(DiscoveredDatabase db, PersonCollection people, Random r,
585
        ICheckNotifier notifier, int numberOfRecords, params string[] primaryKey) where T : IDataGenerator
586
    {
587
        var dataset = typeof(T).Name;
10✔
588
        notifier.OnCheckPerformed(new CheckEventArgs($"Generating {numberOfRecords} records for {dataset}",
10✔
589
            CheckResult.Success));
10✔
590

591
        var factory = new DataGeneratorFactory();
10✔
592

593
        //half a million biochemistry results
594
        var biochem = factory.Create(typeof(T), r);
10✔
595
        var dt = biochem.GetDataTable(people, numberOfRecords);
10✔
596

597
        //prune "nulls"
598
        foreach (DataRow dr in dt.Rows)
200,020✔
599
            for (var i = 0; i < dt.Columns.Count; i++)
6,160,000✔
600
                if (string.Equals(dr[i] as string, "NULL", StringComparison.CurrentCultureIgnoreCase))
2,980,000✔
601
                    dr[i] = DBNull.Value;
80,496✔
602

603

604
        notifier.OnCheckPerformed(new CheckEventArgs($"Uploading {dataset}", CheckResult.Success));
10✔
605
        var tbl = db.CreateTable(dataset, dt, GetExplicitColumnDefinitions<T>());
10✔
606

607
        if (primaryKey.Length != 0)
10✔
608
        {
609
            notifier.OnCheckPerformed(new CheckEventArgs($"Creating Primary Key {dataset}", CheckResult.Success));
10✔
610
            var cols = primaryKey.Select(s => tbl.DiscoverColumn(s)).ToArray();
36✔
611
            tbl.CreatePrimaryKey(5000, cols);
10✔
612
        }
613

614
        return tbl;
10✔
615
    }
616

617
    private static DatabaseColumnRequest[] GetExplicitColumnDefinitions<T>() where T : IDataGenerator
618
    {
619
        return typeof(T) == typeof(HospitalAdmissions)
10✔
620
            ? new[]
10✔
621
            {
10✔
622
                new DatabaseColumnRequest("MainOperation", new DatabaseTypeRequest(typeof(string), 4)),
10✔
623
                new DatabaseColumnRequest("MainOperationB", new DatabaseTypeRequest(typeof(string), 4)),
10✔
624
                new DatabaseColumnRequest("OtherOperation1", new DatabaseTypeRequest(typeof(string), 4)),
10✔
625
                new DatabaseColumnRequest("OtherOperation1B", new DatabaseTypeRequest(typeof(string), 4)),
10✔
626
                new DatabaseColumnRequest("OtherOperation2", new DatabaseTypeRequest(typeof(string), 4)),
10✔
627
                new DatabaseColumnRequest("OtherOperation2B", new DatabaseTypeRequest(typeof(string), 4)),
10✔
628
                new DatabaseColumnRequest("OtherOperation3", new DatabaseTypeRequest(typeof(string), 4)),
10✔
629
                new DatabaseColumnRequest("OtherOperation3B", new DatabaseTypeRequest(typeof(string), 4))
10✔
630
            }
10✔
631
            : null;
10✔
632
    }
633

634
    private ITableInfo ImportTableInfo(DiscoveredTable tbl)
635
    {
636
        var importer = new TableInfoImporter(_repos.CatalogueRepository, tbl);
12✔
637
        importer.DoImport(out var ti, out _);
12✔
638

639
        return ti;
12✔
640
    }
641

642
    private ICatalogue ImportCatalogue(DiscoveredTable tbl) => ImportCatalogue(ImportTableInfo(tbl));
12✔
643

644
    private ICatalogue ImportCatalogue(ITableInfo ti)
645
    {
646
        var forwardEngineer = new ForwardEngineerCatalogue(ti, ti.ColumnInfos);
12✔
647
        forwardEngineer.ExecuteForwardEngineering(out var cata, out _, out var eis);
12✔
648

649
        //get descriptions of the columns from BadMedicine
650
        var desc = new Descriptions();
12✔
651
        cata.Description = Trim(desc.Get(cata.Name));
12✔
652
        if (cata.Description != null)
12✔
653
        {
654
            cata.SaveToDatabase();
8✔
655

656
            foreach (var ci in cata.CatalogueItems)
340✔
657
            {
658
                var ciDescription = Trim(desc.Get(cata.Name, ci.Name));
162✔
659
                if (ciDescription != null)
162✔
660
                {
661
                    ci.Description = ciDescription;
148✔
662
                    ci.SaveToDatabase();
148✔
663
                }
664
            }
665
        }
666

667
        var chi = eis.SingleOrDefault(e => e.GetRuntimeName().Equals("chi", StringComparison.CurrentCultureIgnoreCase));
198✔
668
        if (chi != null)
12✔
669
        {
670
            chi.IsExtractionIdentifier = true;
12✔
671
            chi.SaveToDatabase();
12✔
672

673
            new ExtractableDataSet(_repos.DataExportRepository, cata);
12✔
674
        }
675

676
        return cata;
12✔
677
    }
678

679
    private static string Trim(string s)
680
    {
681
        if (string.IsNullOrWhiteSpace(s))
174✔
682
            return null;
18✔
683

684
        //replace 2+ tabs and spaces with single spaces
685
        return SpaceTabs().Replace(s, " ").Trim();
156✔
686
    }
687

688
    [GeneratedRegex("[ \\t]{2,}")]
689
    private static partial Regex SpaceTabs();
690
}
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