gitflow-analytics 3.6.1__py3-none-any.whl → 3.6.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
1
  """Version information for gitflow-analytics."""
2
2
 
3
- __version__ = "3.6.1"
3
+ __version__ = "3.6.2"
4
4
  __version_info__ = tuple(int(x) for x in __version__.split("."))
gitflow_analytics/cli.py CHANGED
@@ -1448,6 +1448,11 @@ def analyze(
1448
1448
  else:
1449
1449
  click.echo("🔄 Force fetch enabled - analyzing all repositories")
1450
1450
 
1451
+ # Initialize counters before fetching (used in validation even if skipped)
1452
+ total_commits = 0
1453
+ total_tickets = 0
1454
+ total_developers = set()
1455
+
1451
1456
  # Step 1: Fetch data only for repos that need analysis
1452
1457
  if repos_needing_analysis:
1453
1458
  if display and display._live:
@@ -1552,11 +1557,6 @@ def analyze(
1552
1557
  progress.initialize_repositories(all_repo_list)
1553
1558
  progress.set_phase("Step 1: Data Fetching")
1554
1559
 
1555
- # Fetch data for repositories that need analysis
1556
- total_commits = 0
1557
- total_tickets = 0
1558
- total_developers = set() # Track unique developers
1559
-
1560
1560
  # Create top-level progress for all repositories
1561
1561
  with progress.progress(
1562
1562
  total=len(repos_needing_analysis),
@@ -5,7 +5,7 @@ import json
5
5
  import logging
6
6
  import os
7
7
  from contextlib import contextmanager
8
- from datetime import datetime, timedelta
8
+ from datetime import datetime, timedelta, timezone
9
9
  from pathlib import Path
10
10
  from typing import Any, Optional, Union
11
11
 
@@ -990,7 +990,7 @@ class GitAnalysisCache:
990
990
  "complexity_delta": commit_data.get("complexity_delta", 0.0),
991
991
  "story_points": commit_data.get("story_points"),
992
992
  "ticket_references": commit_data.get("ticket_references", []),
993
- "cached_at": datetime.utcnow(),
993
+ "cached_at": datetime.now(timezone.utc),
994
994
  }
995
995
  mappings.append(mapping)
996
996
 
@@ -1101,7 +1101,7 @@ class GitAnalysisCache:
1101
1101
  "complexity_delta": commit_data.get("complexity_delta"),
1102
1102
  "story_points": commit_data.get("story_points"),
1103
1103
  "ticket_references": commit_data.get("ticket_references"),
1104
- "cached_at": datetime.utcnow(),
1104
+ "cached_at": datetime.now(timezone.utc),
1105
1105
  }
1106
1106
 
1107
1107
  # Only include non-None values in update
@@ -3,7 +3,7 @@
3
3
  import logging
4
4
  import os
5
5
  import tempfile
6
- from datetime import datetime
6
+ from datetime import datetime, timezone
7
7
  from pathlib import Path
8
8
  from typing import Any
9
9
 
@@ -28,6 +28,19 @@ logger = logging.getLogger(__name__)
28
28
  Base: Any = declarative_base()
29
29
 
30
30
 
31
+ def utcnow_tz_aware() -> datetime:
32
+ """Return current UTC time as timezone-aware datetime.
33
+
34
+ WHY: SQLAlchemy DateTime(timezone=True) requires timezone-aware datetimes.
35
+ Using timezone-naive datetime.utcnow() causes query mismatches when filtering
36
+ by timezone-aware date ranges.
37
+
38
+ Returns:
39
+ Timezone-aware datetime in UTC
40
+ """
41
+ return datetime.now(timezone.utc)
42
+
43
+
31
44
  class CachedCommit(Base):
32
45
  """Cached commit analysis results."""
33
46
 
@@ -44,7 +57,7 @@ class CachedCommit(Base):
44
57
  author_name = Column(String)
45
58
  author_email = Column(String)
46
59
  message = Column(String)
47
- timestamp = Column(DateTime)
60
+ timestamp = Column(DateTime(timezone=True)) # CRITICAL: Preserve timezone for date filtering
48
61
  branch = Column(String)
49
62
  is_merge = Column(Boolean, default=False)
50
63
 
@@ -62,7 +75,7 @@ class CachedCommit(Base):
62
75
  ticket_references = Column(JSON) # List of ticket IDs
63
76
 
64
77
  # Cache metadata
65
- cached_at = Column(DateTime, default=datetime.utcnow)
78
+ cached_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
66
79
  cache_version = Column(String, default="1.0")
67
80
 
68
81
  # Indexes for performance
@@ -87,12 +100,12 @@ class DeveloperIdentity(Base):
87
100
  # Statistics
88
101
  total_commits = Column(Integer, default=0)
89
102
  total_story_points = Column(Integer, default=0)
90
- first_seen = Column(DateTime, default=datetime.utcnow)
91
- last_seen = Column(DateTime, default=datetime.utcnow)
103
+ first_seen = Column(DateTime(timezone=True), default=utcnow_tz_aware)
104
+ last_seen = Column(DateTime(timezone=True), default=utcnow_tz_aware)
92
105
 
93
106
  # Metadata
94
- created_at = Column(DateTime, default=datetime.utcnow)
95
- updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
107
+ created_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
108
+ updated_at = Column(DateTime(timezone=True), default=utcnow_tz_aware, onupdate=utcnow_tz_aware)
96
109
 
97
110
  __table_args__ = (
98
111
  Index("idx_primary_email", "primary_email"),
@@ -130,8 +143,8 @@ class PullRequestCache(Base):
130
143
  title = Column(String)
131
144
  description = Column(String)
132
145
  author = Column(String)
133
- created_at = Column(DateTime)
134
- merged_at = Column(DateTime, nullable=True)
146
+ created_at = Column(DateTime(timezone=True))
147
+ merged_at = Column(DateTime(timezone=True), nullable=True)
135
148
 
136
149
  # Extracted data
137
150
  story_points = Column(Integer, nullable=True)
@@ -141,7 +154,7 @@ class PullRequestCache(Base):
141
154
  commit_hashes = Column(JSON) # List of commit hashes
142
155
 
143
156
  # Cache metadata
144
- cached_at = Column(DateTime, default=datetime.utcnow)
157
+ cached_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
145
158
 
146
159
  __table_args__ = (Index("idx_repo_pr", "repo_path", "pr_number", unique=True),)
147
160
 
@@ -163,9 +176,9 @@ class IssueCache(Base):
163
176
  description = Column(String)
164
177
  status = Column(String)
165
178
  assignee = Column(String, nullable=True)
166
- created_at = Column(DateTime)
167
- updated_at = Column(DateTime)
168
- resolved_at = Column(DateTime, nullable=True)
179
+ created_at = Column(DateTime(timezone=True))
180
+ updated_at = Column(DateTime(timezone=True))
181
+ resolved_at = Column(DateTime(timezone=True), nullable=True)
169
182
 
170
183
  # Extracted data
171
184
  story_points = Column(Integer, nullable=True)
@@ -175,7 +188,7 @@ class IssueCache(Base):
175
188
  platform_data = Column(JSON) # Additional platform-specific fields
176
189
 
177
190
  # Cache metadata
178
- cached_at = Column(DateTime, default=datetime.utcnow)
191
+ cached_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
179
192
 
180
193
  __table_args__ = (
181
194
  Index("idx_platform_issue", "platform", "issue_id", unique=True),
@@ -215,7 +228,7 @@ class QualitativeCommitData(Base):
215
228
  confidence_score = Column(Float, nullable=False)
216
229
 
217
230
  # Timestamps
218
- analyzed_at = Column(DateTime, default=datetime.utcnow)
231
+ analyzed_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
219
232
  analysis_version = Column(String, default="1.0")
220
233
 
221
234
  # Indexes for efficient querying
@@ -250,8 +263,8 @@ class PatternCache(Base):
250
263
 
251
264
  # Usage tracking for cache management
252
265
  hit_count = Column(Integer, default=1)
253
- last_used = Column(DateTime, default=datetime.utcnow)
254
- created_at = Column(DateTime, default=datetime.utcnow)
266
+ last_used = Column(DateTime(timezone=True), default=utcnow_tz_aware)
267
+ created_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
255
268
 
256
269
  # Source tracking
257
270
  source_method = Column(String, nullable=False) # 'nlp' or 'llm'
@@ -284,7 +297,7 @@ class LLMUsageStats(Base):
284
297
  # API call metadata
285
298
  model_name = Column(String, nullable=False)
286
299
  api_provider = Column(String, default="openrouter")
287
- timestamp = Column(DateTime, default=datetime.utcnow)
300
+ timestamp = Column(DateTime(timezone=True), default=utcnow_tz_aware)
288
301
 
289
302
  # Usage metrics
290
303
  input_tokens = Column(Integer, nullable=False)
@@ -342,8 +355,8 @@ class TrainingData(Base):
342
355
 
343
356
  # Training metadata
344
357
  training_session_id = Column(String, nullable=False) # Groups related training data
345
- created_at = Column(DateTime, default=datetime.utcnow)
346
- updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
358
+ created_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
359
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=utcnow_tz_aware)
347
360
 
348
361
  # Quality assurance
349
362
  validated = Column(Boolean, default=False) # Human validation flag
@@ -399,7 +412,7 @@ class RepositoryAnalysisStatus(Base):
399
412
  unique_developers = Column(Integer, default=0)
400
413
 
401
414
  # Analysis metadata
402
- last_updated = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
415
+ last_updated = Column(DateTime, default=datetime.utcnow, onupdate=utcnow_tz_aware)
403
416
  analysis_version = Column(String, default="2.0") # For tracking schema changes
404
417
 
405
418
  # Configuration hash to detect config changes
@@ -438,8 +451,8 @@ class TrainingSession(Base):
438
451
  session_id = Column(String, unique=True, nullable=False)
439
452
 
440
453
  # Session metadata
441
- started_at = Column(DateTime, default=datetime.utcnow)
442
- completed_at = Column(DateTime)
454
+ started_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
455
+ completed_at = Column(DateTime(timezone=True))
443
456
  status = Column(String, default="running") # running, completed, failed
444
457
 
445
458
  # Configuration
@@ -500,7 +513,7 @@ class ClassificationModel(Base):
500
513
  name = Column(String, nullable=False)
501
514
  version = Column(String, nullable=False)
502
515
  model_type = Column(String, nullable=False) # 'sklearn', 'spacy', 'custom'
503
- created_at = Column(DateTime, default=datetime.utcnow)
516
+ created_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
504
517
 
505
518
  # Training information
506
519
  training_session_id = Column(String, ForeignKey("training_sessions.session_id"))
@@ -521,7 +534,7 @@ class ClassificationModel(Base):
521
534
  # Usage tracking
522
535
  active = Column(Boolean, default=True) # Whether model is active
523
536
  usage_count = Column(Integer, default=0) # Number of times used
524
- last_used = Column(DateTime)
537
+ last_used = Column(DateTime(timezone=True))
525
538
 
526
539
  # Model validation
527
540
  cross_validation_scores = Column(JSON) # Cross-validation results
@@ -564,11 +577,11 @@ class DailyCommitBatch(Base):
564
577
  unique_tickets = Column(JSON) # List of ticket IDs referenced on this day
565
578
 
566
579
  # Processing status
567
- fetched_at = Column(DateTime, default=datetime.utcnow)
580
+ fetched_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
568
581
  classification_status = Column(
569
582
  String, default="pending"
570
583
  ) # pending, processing, completed, failed
571
- classified_at = Column(DateTime, nullable=True)
584
+ classified_at = Column(DateTime(timezone=True), nullable=True)
572
585
 
573
586
  # Batch context for LLM classification
574
587
  context_summary = Column(String, nullable=True) # Brief summary of day's activity
@@ -613,9 +626,9 @@ class DetailedTicketData(Base):
613
626
  # People and dates
614
627
  assignee = Column(String, nullable=True)
615
628
  reporter = Column(String, nullable=True)
616
- created_at = Column(DateTime)
617
- updated_at = Column(DateTime)
618
- resolved_at = Column(DateTime, nullable=True)
629
+ created_at = Column(DateTime(timezone=True))
630
+ updated_at = Column(DateTime(timezone=True))
631
+ resolved_at = Column(DateTime(timezone=True), nullable=True)
619
632
 
620
633
  # Metrics for classification context
621
634
  story_points = Column(Integer, nullable=True)
@@ -636,7 +649,7 @@ class DetailedTicketData(Base):
636
649
  platform_data = Column(JSON) # Additional platform-specific fields
637
650
 
638
651
  # Fetch metadata
639
- fetched_at = Column(DateTime, default=datetime.utcnow)
652
+ fetched_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
640
653
  fetch_version = Column(String, default="2.0") # Version for schema evolution
641
654
 
642
655
  # Indexes for efficient lookup and context building
@@ -683,8 +696,8 @@ class CommitClassificationBatch(Base):
683
696
 
684
697
  # Processing results
685
698
  processing_status = Column(String, default="pending") # pending, processing, completed, failed
686
- started_at = Column(DateTime, default=datetime.utcnow)
687
- completed_at = Column(DateTime, nullable=True)
699
+ started_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
700
+ completed_at = Column(DateTime(timezone=True), nullable=True)
688
701
  processing_time_ms = Column(Float, nullable=True)
689
702
 
690
703
  # Quality metrics
@@ -741,7 +754,7 @@ class CommitTicketCorrelation(Base):
741
754
  matching_pattern = Column(String, nullable=True) # Regex pattern that matched
742
755
 
743
756
  # Timestamps
744
- created_at = Column(DateTime, default=datetime.utcnow)
757
+ created_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
745
758
  validated = Column(Boolean, default=False) # Manual validation flag
746
759
 
747
760
  # Indexes for efficient correlation lookup
@@ -801,8 +814,8 @@ class DailyMetrics(Base):
801
814
  complex_commits = Column(Integer, default=0) # Commits with >5 files changed
802
815
 
803
816
  # Metadata
804
- created_at = Column(DateTime, default=datetime.utcnow)
805
- updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
817
+ created_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
818
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=utcnow_tz_aware)
806
819
 
807
820
  # Indexes for efficient querying
808
821
  __table_args__ = (
@@ -846,7 +859,7 @@ class WeeklyTrends(Base):
846
859
  avg_commits_per_day = Column(Float, default=0.0)
847
860
 
848
861
  # Metadata
849
- calculated_at = Column(DateTime, default=datetime.utcnow)
862
+ calculated_at = Column(DateTime(timezone=True), default=utcnow_tz_aware)
850
863
 
851
864
  # Indexes for trend queries
852
865
  __table_args__ = (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitflow-analytics
3
- Version: 3.6.1
3
+ Version: 3.6.2
4
4
  Summary: Analyze Git repositories for developer productivity insights
5
5
  Author-email: Bob Matyas <bobmatnyc@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  gitflow_analytics/__init__.py,sha256=yN1dyAUu4l9qX-YNAGRItEf4RFFe-5GQiOntXPIfdxo,683
2
- gitflow_analytics/_version.py,sha256=gkE2IQ8OA_DdXSN7E1TswrWgDyJKqg5sTQJ2bF_27as,137
3
- gitflow_analytics/cli.py,sha256=Ky_jCZUHOxikbkqWzeKPtjPpPcPV9KFmhaDP5gBc0s0,262769
2
+ gitflow_analytics/_version.py,sha256=Yo0R3JqQQalk8q_bjgTlzcf-eHgi2oUhbEiu0U4YCzQ,137
3
+ gitflow_analytics/cli.py,sha256=fFgVKLAMlk0Dx-ZzB4b-0qR-veJxEQ6H1T6fv6-egjI,262752
4
4
  gitflow_analytics/config.py,sha256=XRuxvzLWyn_ML7mDCcuZ9-YFNAEsnt33vIuWxQQ_jxg,1033
5
5
  gitflow_analytics/constants.py,sha256=GXEncUJS9ijOI5KWtQCTANwdqxPfXpw-4lNjhaWTKC4,2488
6
6
  gitflow_analytics/verify_activity.py,sha256=aRQnmypf5NDasXudf2iz_WdJnCWtwlbAiJ5go0DJLSU,27050
@@ -24,7 +24,7 @@ gitflow_analytics/config/validator.py,sha256=l7AHjXYJ8wEmyA1rn2WiItZXtAiRb9YBLjF
24
24
  gitflow_analytics/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  gitflow_analytics/core/analyzer.py,sha256=59kGObzjziOb8geFyZMKCUvWmo3hcXE0eTgrjYEc1XA,58736
26
26
  gitflow_analytics/core/branch_mapper.py,sha256=1L1ctrhTEqMZ61eS1nZRkcyaarLipeQgotw4HdXcSmM,7407
27
- gitflow_analytics/core/cache.py,sha256=JLfr-7ayGGkdUX6e3_uURpWA0yiK7nrk5mvcHDfBYic,69124
27
+ gitflow_analytics/core/cache.py,sha256=2SBzry3FoLCJyhu-I-AgNTSzN_MkA-DunzOAxq_lyTw,69152
28
28
  gitflow_analytics/core/data_fetcher.py,sha256=YSanxAVKo44yR_T38YEpHnTZSmioP--0aQSuN29Y6LM,103294
29
29
  gitflow_analytics/core/git_auth.py,sha256=QP7U5_Mi9J-hEtoEhdjoMBl61nCukOGlL8PYXYSyN3g,6369
30
30
  gitflow_analytics/core/git_timeout_wrapper.py,sha256=14K8PHKSOonW4hJpLigB5XQNSWxmFbMFbrpu8cT1h-M,12534
@@ -51,7 +51,7 @@ gitflow_analytics/metrics/activity_scoring.py,sha256=h1uj_6dTKpCwNJfsimfaY0TB3Qa
51
51
  gitflow_analytics/metrics/branch_health.py,sha256=MkfyiUc1nHEakKBJ_uTlvxmofX1QX_s4hm4XBTYKVLM,17522
52
52
  gitflow_analytics/metrics/dora.py,sha256=U4Xk0tr7kPcpR7r-PevYBUDtZPkDIG-w_yS2DJOlTrk,27549
53
53
  gitflow_analytics/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
- gitflow_analytics/models/database.py,sha256=uUe6sgV1VYqgG7fXiMeLmPGmlulsKi0-mcq8gh3EW_g,43425
54
+ gitflow_analytics/models/database.py,sha256=DuVfDoyWUlzzQmf1JFQW7gKbPf2ylNO5gHTzUUl0hTU,44372
55
55
  gitflow_analytics/pm_framework/__init__.py,sha256=2wEfO1uF6TlsymnKeMJimdLYay3K9rigGThhpGVBA3A,3698
56
56
  gitflow_analytics/pm_framework/base.py,sha256=3fXjekfscGy_WslIbyVXdAhWDIIElei9okSyleUJgqU,15247
57
57
  gitflow_analytics/pm_framework/models.py,sha256=uikCapq6KGe_zbWymzvNFvJaN38Nld9i9gBJFKTVtNo,7115
@@ -133,9 +133,9 @@ gitflow_analytics/tui/widgets/export_modal.py,sha256=L-XKPOc6u-fow2TudPgDnC0kXZM
133
133
  gitflow_analytics/tui/widgets/progress_widget.py,sha256=Qny6Q1nU0Pr3aj4aHfXLaRjya9MH3rldR2HWYiaQyGE,6167
134
134
  gitflow_analytics/ui/__init__.py,sha256=UBhYhZMvwlSrCuGWjkIdoP2zNbiQxOHOli-I8mqIZUE,441
135
135
  gitflow_analytics/ui/progress_display.py,sha256=3xJnCOSs1DRVAfS-rTu37EsLfWDFW5-mbv-bPS9NMm4,59182
136
- gitflow_analytics-3.6.1.dist-info/licenses/LICENSE,sha256=xwvSwY1GYXpRpmbnFvvnbmMwpobnrdN9T821sGvjOY0,1066
137
- gitflow_analytics-3.6.1.dist-info/METADATA,sha256=dxz3wCV2RIKYtgxDXSjmNQk0PCWG8JrpOasAlsOmw-s,34122
138
- gitflow_analytics-3.6.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
139
- gitflow_analytics-3.6.1.dist-info/entry_points.txt,sha256=a3y8HnfLOvK1QVOgAkDY6VQXXm3o9ZSQRZrpiaS3hEM,65
140
- gitflow_analytics-3.6.1.dist-info/top_level.txt,sha256=CQyxZXjKvpSB1kgqqtuE0PCRqfRsXZJL8JrYpJKtkrk,18
141
- gitflow_analytics-3.6.1.dist-info/RECORD,,
136
+ gitflow_analytics-3.6.2.dist-info/licenses/LICENSE,sha256=xwvSwY1GYXpRpmbnFvvnbmMwpobnrdN9T821sGvjOY0,1066
137
+ gitflow_analytics-3.6.2.dist-info/METADATA,sha256=1yfOh855xYsJjdZPCzTNM3JODbzJZ3WRB65uW9wdp6g,34122
138
+ gitflow_analytics-3.6.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
139
+ gitflow_analytics-3.6.2.dist-info/entry_points.txt,sha256=a3y8HnfLOvK1QVOgAkDY6VQXXm3o9ZSQRZrpiaS3hEM,65
140
+ gitflow_analytics-3.6.2.dist-info/top_level.txt,sha256=CQyxZXjKvpSB1kgqqtuE0PCRqfRsXZJL8JrYpJKtkrk,18
141
+ gitflow_analytics-3.6.2.dist-info/RECORD,,