github-daily-digest 0.1.0

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.
@@ -0,0 +1,163 @@
1
+ module GithubDailyDigest
2
+ class LanguageAnalyzer
3
+ # Map of file extensions to languages
4
+ EXTENSION_TO_LANGUAGE = {
5
+ # Ruby
6
+ '.rb' => 'Ruby',
7
+ '.rake' => 'Ruby',
8
+ '.gemspec' => 'Ruby',
9
+
10
+ # JavaScript
11
+ '.js' => 'JavaScript',
12
+ '.jsx' => 'JavaScript',
13
+ '.mjs' => 'JavaScript',
14
+ '.cjs' => 'JavaScript',
15
+ '.ts' => 'TypeScript',
16
+ '.tsx' => 'TypeScript',
17
+
18
+ # HTML/CSS
19
+ '.html' => 'HTML',
20
+ '.htm' => 'HTML',
21
+ '.xhtml' => 'HTML',
22
+ '.erb' => 'HTML/ERB',
23
+ '.haml' => 'HTML/Haml',
24
+ '.slim' => 'HTML/Slim',
25
+ '.css' => 'CSS',
26
+ '.scss' => 'CSS/SCSS',
27
+ '.sass' => 'CSS/SASS',
28
+ '.less' => 'CSS/LESS',
29
+
30
+ # PHP
31
+ '.php' => 'PHP',
32
+ '.phtml' => 'PHP',
33
+
34
+ # Python
35
+ '.py' => 'Python',
36
+ '.pyd' => 'Python',
37
+ '.pyo' => 'Python',
38
+ '.pyw' => 'Python',
39
+
40
+ # Java
41
+ '.java' => 'Java',
42
+ '.class' => 'Java',
43
+ '.jar' => 'Java',
44
+
45
+ # C/C++
46
+ '.c' => 'C',
47
+ '.h' => 'C',
48
+ '.cpp' => 'C++',
49
+ '.cc' => 'C++',
50
+ '.cxx' => 'C++',
51
+ '.hpp' => 'C++',
52
+
53
+ # C#
54
+ '.cs' => 'C#',
55
+
56
+ # Go
57
+ '.go' => 'Go',
58
+
59
+ # Swift
60
+ '.swift' => 'Swift',
61
+
62
+ # Kotlin
63
+ '.kt' => 'Kotlin',
64
+ '.kts' => 'Kotlin',
65
+
66
+ # Rust
67
+ '.rs' => 'Rust',
68
+
69
+ # Shell
70
+ '.sh' => 'Shell',
71
+ '.bash' => 'Shell',
72
+ '.zsh' => 'Shell',
73
+ '.fish' => 'Shell',
74
+
75
+ # Data
76
+ '.json' => 'JSON',
77
+ '.xml' => 'XML',
78
+ '.yaml' => 'YAML',
79
+ '.yml' => 'YAML',
80
+ '.csv' => 'CSV',
81
+ '.toml' => 'TOML',
82
+
83
+ # Config
84
+ '.ini' => 'Config',
85
+ '.conf' => 'Config',
86
+ '.cfg' => 'Config',
87
+
88
+ # Markdown
89
+ '.md' => 'Markdown',
90
+ '.markdown' => 'Markdown',
91
+
92
+ # SQL
93
+ '.sql' => 'SQL',
94
+
95
+ # Other
96
+ '.txt' => 'Text',
97
+ '.gitignore' => 'Git',
98
+ '.dockerignore' => 'Docker',
99
+ 'Dockerfile' => 'Docker',
100
+ '.env' => 'Config'
101
+ }.freeze
102
+
103
+ # Identify language from filename
104
+ def self.identify_language(filepath)
105
+ # Get the file extension
106
+ ext = File.extname(filepath).downcase
107
+
108
+ # For files without extensions, check the full filename
109
+ basename = File.basename(filepath)
110
+
111
+ # Try to match by extension first
112
+ return EXTENSION_TO_LANGUAGE[ext] if EXTENSION_TO_LANGUAGE.key?(ext)
113
+
114
+ # Try to match by filename for special cases
115
+ return EXTENSION_TO_LANGUAGE[basename] if EXTENSION_TO_LANGUAGE.key?(basename)
116
+
117
+ # Default for unknown types
118
+ 'Other'
119
+ end
120
+
121
+ # Calculate language distribution from a list of files
122
+ # files should be an array of hashes with at least :path, :additions, :deletions
123
+ def self.calculate_distribution(files)
124
+ return {} if files.nil? || files.empty?
125
+
126
+ # Initialize counters
127
+ language_stats = Hash.new(0)
128
+ total_changes = 0
129
+
130
+ files.each do |file|
131
+ # Skip files with no path
132
+ next unless file[:path]
133
+
134
+ # Calculate the total lines changed for this file
135
+ lines_changed = (file[:additions] || 0) + (file[:deletions] || 0)
136
+
137
+ # Identify the language
138
+ language = identify_language(file[:path])
139
+
140
+ # Add to the counters
141
+ language_stats[language] += lines_changed
142
+ total_changes += lines_changed
143
+ end
144
+
145
+ # Convert to percentages
146
+ result = {}
147
+
148
+ language_stats.each do |language, count|
149
+ # Skip languages with 0 changes (shouldn't happen, but just in case)
150
+ next if count == 0
151
+
152
+ # Calculate percentage (rounded to 1 decimal place)
153
+ percentage = total_changes > 0 ? (count.to_f / total_changes * 100).round(1) : 0
154
+
155
+ # Only include if it's more than 0.1%
156
+ result[language] = percentage if percentage > 0.1
157
+ end
158
+
159
+ # Sort by percentage (descending)
160
+ result.sort_by { |_, percentage| -percentage }.to_h
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # A temporary solution file to generate proper markdown output with contribution weights
4
+ # Run this after generating JSON output from github-daily-digest
5
+
6
+ require 'json'
7
+ require 'optparse'
8
+
9
+ options = {
10
+ input_file: nil,
11
+ output_file: nil
12
+ }
13
+
14
+ OptionParser.new do |opts|
15
+ opts.banner = "Usage: ruby markdown_formatter.rb [options]"
16
+
17
+ opts.on("-i", "--input FILE", "Input JSON file") do |file|
18
+ options[:input_file] = file
19
+ end
20
+
21
+ opts.on("-o", "--output FILE", "Output Markdown file") do |file|
22
+ options[:output_file] = file
23
+ end
24
+ end.parse!
25
+
26
+ unless options[:input_file]
27
+ puts "Error: Input file is required"
28
+ exit 1
29
+ end
30
+
31
+ # Read the JSON file
32
+ json_data = JSON.parse(File.read(options[:input_file]))
33
+
34
+ # Generate markdown
35
+ markdown = "# GitHub Activity Digest\n\n"
36
+ markdown << "Generated on: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}\n\n"
37
+
38
+ # Add overview section
39
+ markdown << "## Overview\n\n"
40
+ markdown << "| Category | Value |\n"
41
+ markdown << "| --- | --- |\n"
42
+ markdown << "| **Time Period** | Last 7 days |\n"
43
+
44
+ # Get organizations
45
+ organizations = json_data.keys.reject { |k| k == "_meta" }
46
+ markdown << "| **Organizations** | #{organizations.join(', ')} |\n"
47
+ markdown << "| **Data Source** | GitHub API |\n\n"
48
+
49
+ # Process users
50
+ all_users = {}
51
+ organizations.each do |org|
52
+ json_data[org].each do |username, user_data|
53
+ next if username == "_meta"
54
+
55
+ # Skip users with no activity
56
+ next if user_data["changes"].to_i == 0 && user_data["pr_count"].to_i == 0
57
+
58
+ all_users[username] ||= {
59
+ username: username,
60
+ commits: 0,
61
+ prs: 0,
62
+ lines_changed: 0,
63
+ projects: [],
64
+ total_score: 0,
65
+ weights: {
66
+ "lines_of_code" => 0,
67
+ "complexity" => 0,
68
+ "technical_depth" => 0,
69
+ "scope" => 0,
70
+ "pr_reviews" => 0
71
+ },
72
+ summary: user_data["summary"] || ""
73
+ }
74
+
75
+ # Accumulate data
76
+ all_users[username][:commits] += user_data["changes"].to_i
77
+ all_users[username][:prs] += user_data["pr_count"].to_i
78
+ all_users[username][:lines_changed] += user_data["lines_changed"].to_i
79
+
80
+ # Get projects
81
+ if user_data["projects"].is_a?(Array)
82
+ all_users[username][:projects] += user_data["projects"]
83
+ end
84
+
85
+ # Get contribution weights
86
+ if user_data["contribution_weights"].is_a?(Hash)
87
+ weights = user_data["contribution_weights"]
88
+
89
+ all_users[username][:weights]["lines_of_code"] = [all_users[username][:weights]["lines_of_code"], weights["lines_of_code"].to_i].max
90
+ all_users[username][:weights]["complexity"] = [all_users[username][:weights]["complexity"], weights["complexity"].to_i].max
91
+ all_users[username][:weights]["technical_depth"] = [all_users[username][:weights]["technical_depth"], weights["technical_depth"].to_i].max
92
+ all_users[username][:weights]["scope"] = [all_users[username][:weights]["scope"], weights["scope"].to_i].max
93
+ all_users[username][:weights]["pr_reviews"] = [all_users[username][:weights]["pr_reviews"], weights["pr_reviews"].to_i].max
94
+ end
95
+
96
+ # Calculate total score
97
+ all_users[username][:total_score] = 0
98
+ all_users[username][:weights].each do |key, value|
99
+ all_users[username][:total_score] += value.to_i
100
+ end
101
+ end
102
+ end
103
+
104
+ # Sort users by total score
105
+ sorted_users = all_users.values.sort_by { |user| -user[:total_score] }
106
+
107
+ # Add active users section
108
+ markdown << "## Active Users\n\n"
109
+ markdown << "Users are sorted by their total contribution score, which is calculated as the sum of individual contribution weights.\n"
110
+ markdown << "Each contribution weight is on a scale of 0-10 and considers different aspects of contribution value.\n\n"
111
+
112
+ # Create users table
113
+ markdown << "| User | Commits | PRs | Lines Changed | Total Score | Contribution Weights | Summary | Projects |\n"
114
+ markdown << "|------|---------|-----|---------------|-------------|----------------------|---------|----------|\n"
115
+
116
+ sorted_users.each do |user|
117
+ # Format weights
118
+ weights_display = "LOC: #{user[:weights]['lines_of_code']} | " +
119
+ "Complexity: #{user[:weights]['complexity']} | " +
120
+ "Depth: #{user[:weights]['technical_depth']} | " +
121
+ "Scope: #{user[:weights]['scope']} | " +
122
+ "PR: #{user[:weights]['pr_reviews']}"
123
+
124
+ # Format projects
125
+ projects_display = user[:projects].uniq.join(", ")
126
+
127
+ # Create row
128
+ markdown << "| #{user[:username]} | #{user[:commits]} | #{user[:prs]} | #{user[:lines_changed]} | #{user[:total_score]} | #{weights_display} | #{user[:summary]} | #{projects_display} |\n"
129
+ end
130
+
131
+ # Output markdown
132
+ if options[:output_file]
133
+ File.write(options[:output_file], markdown)
134
+ puts "Markdown written to #{options[:output_file]}"
135
+ else
136
+ puts markdown
137
+ end