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

moosetechnology / GitProjectHealth / 15141210937

20 May 2025 03:08PM UTC coverage: 72.446% (-0.8%) from 73.222%
15141210937

Pull #196

github

web-flow
Merge 4b8d57d52 into 118855527
Pull Request #196: Pipeline 2

796 of 1798 new or added lines in 15 files covered. (44.27%)

38 existing lines in 2 files now uncovered.

15999 of 22084 relevant lines covered (72.45%)

0.72 hits per line

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

18.24
/src/GitLabHealth-Model-Analysis/GitMetricExporter.class.st
1
Class {
2
        #name : #GitMetricExporter,
3
        #superclass : #Object,
4
        #instVars : [
5
                'glhImporter',
6
                'jiraImporter',
7
                'label',
8
                'entities',
9
                'projectCache',
10
                'sinceTimeLimit',
11
                'runningPeriods',
12
                'maxCommitWindow',
13
                'over',
14
                'emailDomain',
15
                'metrics',
16
                'analyses'
17
        ],
18
        #category : #'GitLabHealth-Model-Analysis'
19
}
20

21
{ #category : #'as yet unclassified' }
22
GitMetricExporter class >> demoPeriod [
×
23

×
24
        ^ self new
×
25
                  addAPeriodFrom: '01 march 2023' to: '31 may 2023';
×
26
                  addAPeriodFrom: '01 december 2023' to: '29 february 2024';
×
27
                  addAPeriodFrom: '01 march 2024' to: '31 may 2024'
×
28
]
×
29

30
{ #category : #adding }
31
GitMetricExporter >> addAPeriodFrom: since to: until [
×
32

×
33
        runningPeriods add: {
×
34
                        (#since -> since asDate).
×
35
                        (#until -> until asDate) } asDictionary.
×
36
        
×
37
        ^runningPeriods 
×
38
]
×
39

40
{ #category : #adding }
41
GitMetricExporter >> addEntitiesFromUserNames: userNames [
×
42
        "import all the project since a certain time"
×
43
        
×
44
        self
×
45
                deprecated: 'Use #setupAnalysesForUsersWithNames instead'
×
46
                on: '03 Sept 2024'
×
47
                in:
×
48
                'Pharo-11.0.0+build.726.sha.aece1b5473acf3830a0e082c1bc3a15d4ff3522b (64 Bit)'.
×
49

×
50
        'Import all projects from the Git repository' recordInfo.
×
51
        projectCache := projectCache ifNil: [
×
52
                                glhImporter importProjectsSince: sinceTimeLimit ].
×
53

×
54
        "then collect the project in which the user has be seen commited"
×
55
        entities addAll: (userNames collect: [ :username |
×
56
                         | projects  i size |
×
57
                         projects := self
×
58
                                             findParticipationOfCommitAuthorNamed: username
×
59
                                             amongProjects: projectCache.
×
60

×
61
                        "metrics := GitMetric4User new.
×
62
                         metrics
×
63
                                 glhImporter: glhImporter;
×
64
                                 jiraImporter: jiraImporter;
×
65
                                 findUserNamed: username."
×
66

×
67

×
68

×
69
                         i := 0.
×
70
                         size := projects size.
×
71
                         metrics itsProjects: (projects collect: [ :p |
×
72
                                          (' ' join: {
×
73
                                                           'complete import of project:'.
×
74
                                                           p name printString.
×
75
                                                           '['.
×
76
                                                           (i := i + 1) printString.
×
77
                                                           '/'.
×
78
                                                           size.
×
79
                                                           ']' }) recordInfo.
×
80

×
NEW
81
                                          p id -> (glhImporter completeImportedProject: p) ]) asDictionary.
×
82

×
83
                         metrics ]).
×
84

×
85
        ^ self
×
86
]
×
87

88
{ #category : #adding }
89
GitMetricExporter >> addEntitiesFromUserNamesAndProjects: usersWithProjects [
×
90

×
91
        self
×
92
                deprecated: 'Use #setupAnalysesForUsersWithNames instead'
×
93
                on: '03 Sept 2024'
×
94
                in:
×
95
                'Pharo-11.0.0+build.726.sha.aece1b5473acf3830a0e082c1bc3a15d4ff3522b (64 Bit)'.
×
96

×
97
        entities addAll: (usersWithProjects associations collect: [ :assoc |
×
98
                         | username projects  |
×
99
                         username := assoc key.
×
100
                         projects := assoc value.
×
101

×
102
                        " metrics := GitMetric4User new.
×
103
                         metrics
×
104
                                 glhImporter: glhImporter;
×
105
                                 jiraImporter: jiraImporter;
×
106
                                 findUserNamed: username.
×
107
                         metrics loadProjectsFromIds: projects."
×
108

×
109
                         username ]).
×
110

×
111
        ^ self 
×
112
]
×
113

114
{ #category : #accessing }
115
GitMetricExporter >> analyses [
×
116

×
117
        ^ analyses
×
118
]
×
119

120
{ #category : #accessing }
121
GitMetricExporter >> analyses: anObject [
1✔
122

1✔
123
        ^ analyses := anObject
1✔
124
]
1✔
125

126
{ #category : #utilities }
127
GitMetricExporter >> constructFilePathFor: runningOver andEntities: entitiesName [
×
128

×
129
        | file |
×
130
        file := (FileLocator home
×
131
                 /
×
132
                         ('GitlabHealth-' , label printString , '-'
×
133
                          , runningOver printString , '-' , entitiesName printString
×
134
                          , '-'
×
135
                          , (DateAndTime now printString replaceAll: $: with: $-)
×
136
                          , '.csv')) asFileReference.
×
137
        ^ file
×
138
]
×
139

140
{ #category : #exporting }
141
GitMetricExporter >> csvMetricsFor: metricCollection at: date [
1✔
142

1✔
143
        | csvMetrics |
1✔
144
        csvMetrics := metricCollection collect: [ :metric |
1✔
145
                              metric new description , ' - ' , date asString
1✔
146
                              -> [ :groupAnalyses |
1✔
147
                                      | analysis |
1✔
148
                                      analysis := self
1✔
149
                                                          getAnalysisWithDate: date
1✔
150
                                                          from: groupAnalyses.
1✔
151
                                      analysis
1✔
152
                                              ifNil: [ nil ]
1✔
153
                                              ifNotNil: [ analysis metrics at: metric new name ] ] ].
1✔
154

1✔
155
        ^ csvMetrics asOrderedDictionary
1✔
156
]
1✔
157

158
{ #category : #accessing }
159
GitMetricExporter >> entities [
×
160
        "You should use `setupAnalysisForProjectsWithIds:` and `setupAnalysisForUsersWithNames:` to fill entities and not directly the instance"
×
161

×
162
        ^ entities
×
163
]
×
164

165
{ #category : #accessing }
166
GitMetricExporter >> entities: aCollection [ 
×
167
        entities := aCollection
×
168
]
×
169

170
{ #category : #exporting }
171
GitMetricExporter >> exportFor: usersWithProjects [
×
172

×
173
        self
×
174
                deprecated: 'Use #exportInCSV instead'
×
175
                on: '03 Sept 2024'
×
176
                in:
×
177
                'Pharo-11.0.0+build.726.sha.aece1b5473acf3830a0e082c1bc3a15d4ff3522b (64 Bit)'.
×
178

×
179
        self exportFor: usersWithProjects over: {
×
180
                        Date.
×
181
                        Week.
×
182
                        Month.
×
183
                        Year }
×
184
]
×
185

186
{ #category : #exporting }
187
GitMetricExporter >> exportFor: usersWithProjects over: aCollectionOfDateWeekMonthOrYear [
×
188

×
189
        self
×
190
                deprecated: 'Use #exportInCSV instead'
×
191
                on: '03 Sept 2024'
×
192
                in:
×
193
                'Pharo-11.0.0+build.726.sha.aece1b5473acf3830a0e082c1bc3a15d4ff3522b (64 Bit)'.
×
194

×
195
        entities ifNil: [
×
196
                self addEntitiesFromUserNamesAndProjects: usersWithProjects ].
×
197

×
198
        self exportFor: aCollectionOfDateWeekMonthOrYear
×
199
]
×
200

201
{ #category : #exporting }
202
GitMetricExporter >> exportInCSV [
×
203

×
204
        self exportUserAnalysesInCSV.
×
205
        self exportProjectAnalysesInCSV.
×
206
]
×
207

208
{ #category : #exporting }
209
GitMetricExporter >> exportInDB: repository [
×
210

×
211
        repository enableSingleton.
×
212
        analyses do: [ :analysis | analysis save ]
×
213
]
×
214

215
{ #category : #exporting }
216
GitMetricExporter >> exportInS3: bucketName accessKey: accessKey secretKey: secretKey region: region [
×
217

×
218
        | s3 bucket ston date fileName |
×
219
        AWSS3Config default
×
220
                accessKeyId: accessKey;
×
221
                secretKey: secretKey;
×
222
                regionName: region.
×
223

×
224
        s3 := AWSS3 new.
×
225
        bucket := s3 bucketNamed: bucketName.
×
226

×
227
        ston := STON toString: analyses.
×
228

×
229
        date := Date today asString copyReplaceAll: ' ' asString with: '-'.
×
230
        fileName := ('-' join: {
×
231
                                     'analyse'.
×
232
                                     label.
×
233
                                     over asString.
×
234
                                     date }) , '.ston'.
×
235

×
236
        bucket atKey: fileName putObject: ston
×
237
]
×
238

239
{ #category : #exporting }
240
GitMetricExporter >> exportProjectAnalysesInCSV [
1✔
241

1✔
242
        | exportBrowserModel groupedByOver group groupOver groupByName periods csvMetrics file |
1✔
243
        exportBrowserModel := MiExportModel new.
1✔
244

1✔
245
        groupedByOver := self projectAnalyses groupedBy: #over.
1✔
246

1✔
247
        groupedByOver associations do: [ :groupAssociation |
1✔
248
                group := groupAssociation value.
1✔
249
                groupOver := groupAssociation key.
1✔
250

1✔
251

1✔
252
                groupByName := group groupedBy: #projectID.
1✔
253
                exportBrowserModel entitiesList: groupByName.
1✔
254

1✔
255
                exportBrowserModel removeColumnForQueryNamed: #Type.
1✔
256
                exportBrowserModel removeColumnForQueryNamed: #Name.
1✔
257

1✔
258
                exportBrowserModel
1✔
259
                        addColumnForQuery: [ :groupAnalyses |
1✔
260
                        (groupAnalyses at: 1) projectName ]
1✔
261
                        withName: #'Project name'.
1✔
262
                exportBrowserModel
1✔
263
                        addColumnForQuery: [ :groupAnalyses |
1✔
264
                        (groupAnalyses at: 1) projectID ]
1✔
265
                        withName: #'Project ID'.
1✔
266

1✔
267
                periods := (group groupedBy: #period) keys.
1✔
268

1✔
269
                periods do: [ :period |
1✔
270
                        csvMetrics := self
1✔
271
                                              csvMetricsFor: self projectMetrics
1✔
272
                                              at: (period at: #since).
1✔
273

1✔
274

1✔
275
                        csvMetrics associations do: [ :association |
1✔
276
                                exportBrowserModel
1✔
277
                                        addColumnForQuery: association value
1✔
278
                                        withName: association key ] ].
1✔
279
                file := self constructFilePathFor: groupOver andEntities: 'projects'.
1✔
280
                file writeStreamDo: [ :aStream |
1✔
281
                        exportBrowserModel writeCSVOn: aStream ] ]
1✔
282
]
1✔
283

284
{ #category : #exporting }
285
GitMetricExporter >> exportUserAnalysesInCSV [
×
286

×
287
        | exportBrowserModel groupedByOver group groupOver groupByName periods csvMetrics file |
×
288
        exportBrowserModel := MiExportModel new.
×
289
        groupedByOver := self userAnalyses groupedBy: #over.
×
290

×
291
        groupedByOver associations do: [ :groupAssociation |
×
292
                group := groupAssociation value.
×
293
                groupOver := groupAssociation key.
×
294

×
295
                groupByName := group groupedBy: #username.
×
296
                exportBrowserModel entitiesList: groupByName.
×
297
                exportBrowserModel removeColumnForQueryNamed: #Type.
×
298
                exportBrowserModel removeColumnForQueryNamed: #Name.
×
299

×
300
                exportBrowserModel
×
301
                        addColumnForQuery: [ :groupAnalyses |
×
302
                        (groupAnalyses at: 1) username ]
×
303
                        withName: #'User name'.
×
304

×
305
                exportBrowserModel
×
306
                        addColumnForQuery: [ :groupAnalyses | (groupAnalyses at: 1) email ]
×
307
                        withName: #'User email'.
×
308

×
309
                periods := (group groupedBy: #period) keys.
×
310
                periods do: [ :period |
×
311
                        csvMetrics := self
×
312
                                              csvMetricsFor: self userMetrics
×
313
                                              at: (period at: #since).
×
314
                        csvMetrics associations do: [ :association |
×
315
                                exportBrowserModel
×
316
                                        addColumnForQuery: association value
×
317
                                        withName: association key ] ].
×
318

×
319

×
320
                file := self
×
321
                                constructFilePathFor: groupOver
×
322
                                andEntities: 'users'.
×
323

×
324
                file writeStreamDo: [ :aStream |
×
325
                        exportBrowserModel writeCSVOn: aStream ] ]
×
326
]
×
327

328
{ #category : #projects }
329
GitMetricExporter >> findParticipationOfCommitAuthorNamed: username amongProjects: aCollectionOfProjects [
×
330

×
331
        | response itsProjects i size params |
×
332
        i := 1.
×
333
        size := aCollectionOfProjects size.
×
334
        itsProjects := aCollectionOfProjects select: [ :project |
×
335
                               (' ' join: {
×
336
                                                'search'.
×
337
                                                username printString.
×
338
                                                'in Project:'.
×
339
                                                project name printString.
×
340
                                                '['.
×
341
                                                (i := i + 1) printString.
×
342
                                                '/'.
×
343
                                                size.
×
344
                                                ']' }) recordInfo.
×
345

×
346
                                                        params := { 
×
347
                                                                #author -> username.
×
348
                                                                #since -> '01 january 2023' asDate asDateAndTime asString.
×
349
                                                                #until -> Date today asDateAndTime asString
×
350
                                                        } asDictionary.
×
351
                               response := glhImporter repoApi commits getAllInProject: project id withParams: params.
×
352
                               response := (response collect: [ :commitsJson | glhImporter parseCommitsResult: commitsJson]) flattened.
×
353
                               response isNotEmpty
×
354
                 ].
×
355

×
356
        ^ itsProjects
×
357
]
×
358

359
{ #category : #projects }
360
GitMetricExporter >> findProjectsOfUser: aCollection [ 
×
361
        self shouldBeImplemented.
×
362
]
×
363

364
{ #category : #analysis }
365
GitMetricExporter >> generateAnalysesOver: aDateWeekMonthOrYear [
×
366

×
367
        analyses := OrderedCollection new.
×
368
        runningPeriods do: [ :period |
×
369
                analyses addAll: (self
×
370
                                 generateUsersAnalysesDuringPeriod: period
×
371
                                 over: aDateWeekMonthOrYear).
×
372

×
373
                analyses addAll: (self
×
374
                                 generateProjectsAnalysesDuringPeriod: period
×
375
                                 over: aDateWeekMonthOrYear) ].
×
376

×
377
        ^ analyses
×
378
]
×
379

380
{ #category : #analysis }
381
GitMetricExporter >> generateProjectsAnalysesDuringPeriod: period over: aDateWeekMonthOrYear [
×
382

×
383
        ^self projectEntities collect: [ :project |
×
384
                | projectAnalysisReport |
×
385
                projectAnalysisReport := ProjectAnalysisReport new
×
386
                        projectName: project name;
×
387
                        projectID: project id;
×
388
                        period: period;
×
389
                        over: aDateWeekMonthOrYear.
×
390
                        
×
391
                
×
392
                  self projectMetrics do: [ :metric |
×
393
                                |newMetric result|
×
394
                           newMetric := metric new
×
395
                                               glhImporter: glhImporter;
×
396
                                               jiraImporter: jiraImporter;
×
397
                                               setPeriodSince: (period at: #since)
×
398
                                               until: (period at: #until);
×
399
                                               over: aDateWeekMonthOrYear;
×
400
                                               project: project.
×
401

×
402

×
403
                          result := newMetric calculate.
×
404
                          projectAnalysisReport metrics at: newMetric name put: result ]. 
×
405
                
×
406
                        projectAnalysisReport]
×
407
]
×
408

409
{ #category : #analysis }
410
GitMetricExporter >> generateUsersAnalysesDuringPeriod: period over: aDateWeekMonthOrYear [
×
411

×
412
        | userAnalysisReport newMetric result |
×
413
        ^ self userEntities collect: [ :user |
×
414
                  userAnalysisReport := UserAnalysisReport new
×
415
                                                username: user name;
×
416
                                                                                        email: (user email ifNil: [ user public_email]);
×
417
                                                period: period;
×
418
                                                over: aDateWeekMonthOrYear.
×
419

×
420
                  self userMetrics do: [ :metric |
×
421
                          newMetric := metric new
×
422
                                               glhImporter: glhImporter;
×
423
                                               jiraImporter: jiraImporter;
×
424
                                               setPeriodSince: (period at: #since)
×
425
                                               until: (period at: #until);
×
426
                                               over: aDateWeekMonthOrYear;
×
427
                                               user: user;
×
428
                                               emailDomain: emailDomain.
×
429

×
430

×
431
                          result := newMetric calculate.
×
432
                          userAnalysisReport metrics at: newMetric name put: result ].
×
433

×
434
                  userAnalysisReport ]
×
435
]
×
436

437
{ #category : #utilities }
438
GitMetricExporter >> getAnalysisWithDate: date from: analysisCollection [
1✔
439

1✔
440
        ^analysisCollection
1✔
441
                detect: [ :groupAnalysis | (groupAnalysis period at: #since) = date ]
1✔
442
                ifNone: [ nil ]
1✔
443
]
1✔
444

445
{ #category : #accessing }
446
GitMetricExporter >> glhImporter: anImporter [
×
447

×
448
        glhImporter := anImporter withInitialCommits: false; yourself. 
×
449
]
×
450

451
{ #category : #adding }
452
GitMetricExporter >> importAnalysesFromS3: bucketName accessKey: accessKey secretKey: secretKey region: region [
×
453

×
454
        | s3 bucket xmlObjects contentElements filesContent |
×
455
        AWSS3Config default
×
456
                accessKeyId: accessKey;
×
457
                secretKey: secretKey;
×
458
                regionName: region.
×
459

×
460
        s3 := AWSS3 new.
×
461
        bucket := s3 bucketNamed: bucketName.
×
462
        xmlObjects := bucket listObjects.
×
463
        contentElements := xmlObjects root elementsSelect: [ :element |
×
464
                                   element isNamed: 'Contents' ].
×
465

×
466
        filesContent := contentElements collect: [ :contentElement |
×
467
                                | keyValue |
×
468
                                keyValue := contentElement contentStringAt: 'Key'.
×
469
                                bucket getObject: keyValue ].
×
470

×
471
        ^ analyses := (filesContent collect: [ :file | STON fromString: file ])
×
472
                              flattened
×
473
]
×
474

475
{ #category : #initialization }
476
GitMetricExporter >> initialize [
1✔
477

1✔
478
        entities := OrderedCollection new.
1✔
479
        "set up the minimal date from which we are looking for the commits a particular user in projects"
1✔
480
        sinceTimeLimit := '2024-01-01' asDate asDateAndTime.
1✔
481
        runningPeriods := OrderedCollection new.
1✔
482
        maxCommitWindow := 3.
1✔
483
        over := Date.
1✔
484

1✔
485
        analyses := OrderedCollection new.
1✔
486

1✔
487
        metrics := Metric allSubclasses reject: #isAbstract.
1✔
488

1✔
489
]
1✔
490

491
{ #category : #accessing }
492
GitMetricExporter >> jiraImporter: anObject [
×
493

×
494
        jiraImporter := anObject
×
495
]
×
496

497
{ #category : #accessing }
498
GitMetricExporter >> label: aString [ 
×
499
        label := aString
×
500
]
×
501

502
{ #category : #accessing }
503
GitMetricExporter >> maxCommitWindow: anInteger [
×
504
        maxCommitWindow := anInteger abs
×
505
]
×
506

507
{ #category : #accessing }
508
GitMetricExporter >> metrics [
×
509

×
510
        ^ metrics
×
511
]
×
512

513
{ #category : #accessing }
514
GitMetricExporter >> metrics: anObject [
×
515

×
516
        metrics := anObject
×
517
]
×
518

519
{ #category : #adding }
520
GitMetricExporter >> onlyImportProjectsOfGroup: groupId [
×
521

×
522
        | group |
×
523
        group := glhImporter importGroup: groupId.
×
524
        projectCache := group toScope: GLHProject
×
525
]
×
526

527
{ #category : #accessing }
528
GitMetricExporter >> projectAnalyses [
1✔
529

1✔
530
        ^ analyses select: [ :analysis | analysis class = ProjectAnalysisReport ]
1✔
531
]
1✔
532

533
{ #category : #accessing }
534
GitMetricExporter >> projectEntities [
×
535

×
536
        ^ entities select: [ :entity | entity class = GLHProject ]
×
537
]
×
538

539
{ #category : #accessing }
540
GitMetricExporter >> projectMetrics [
1✔
541

1✔
542
        ^ metrics select: [ :metric | metric inheritsFrom: ProjectMetric ]
1✔
543
]
1✔
544

545
{ #category : #setup }
546
GitMetricExporter >> setupAnalysisForProjectsWithIds: projectIds [
×
547

×
548
        | projects |
×
549
        projects := glhImporter importProjects: projectIds.
×
550

×
551
        entities addAll: projects
×
552
]
×
553

554
{ #category : #setup }
555
GitMetricExporter >> setupAnalysisForUsersWithNames: userNames [
×
556
        "import all the project since a certain time"
×
557

×
558
        | users |
×
559
        users := userNames collect: [ :username |
×
NEW
560
                         glhImporter importUserByUsername: username ].
×
561

×
562
        glhImporter userCatalogue ifNotNil: [
×
563
                glhImporter userCatalogue scrapeAuthorNamesForUsers: users ].
×
564

×
565
        users do: [ :user |
×
566
                | projects |
×
567
                projects := glhImporter importContributedProjectsOfUser: user.
×
568

×
569
                projects do: [ :project |
×
NEW
570
                        glhImporter completeImportedProject: project ] ].
×
571

×
572
        entities addAll: users.
×
573

×
574
        ^ self
×
575
]
×
576

577
{ #category : #accessing }
578
GitMetricExporter >> userAnalyses [
×
579

×
580
        ^ analyses select: [ :analysis | analysis class = UserAnalysisReport ]
×
581
]
×
582

583
{ #category : #accessing }
584
GitMetricExporter >> userEntities [
×
585

×
586
        ^ entities select: [ :entity | entity class = GLHUser ]
×
587
]
×
588

589
{ #category : #accessing }
590
GitMetricExporter >> userMetrics [
×
591

×
592
        ^ metrics select: [ :metric | metric inheritsFrom: UserMetric ]
×
593
]
×
594

595
{ #category : #setup }
596
GitMetricExporter >> withEmailDomain: anEmailDomain [
×
597

×
598
        "define the email domain of your Git user. Usefull to link them between service (i.e. Jira)"
×
599
        emailDomain := anEmailDomain
×
600
]
×
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