mirror of
				https://github.com/open-webui/open-webui
				synced 2025-06-26 18:26:48 +00:00 
			
		
		
		
	enh: contributions stats script
This commit is contained in:
		
							parent
							
								
									176a7fc986
								
							
						
					
					
						commit
						b1fb298bee
					
				@ -1,64 +0,0 @@
 | 
			
		||||
# Run with
 | 
			
		||||
#    caddy run --envfile ./example.env --config ./Caddyfile.localhost
 | 
			
		||||
#
 | 
			
		||||
# This is configured for
 | 
			
		||||
#    - Automatic HTTPS (even for localhost)
 | 
			
		||||
#    - Reverse Proxying to Ollama API Base URL (http://localhost:11434/api)
 | 
			
		||||
#    - CORS
 | 
			
		||||
#    - HTTP Basic Auth API Tokens (uncomment basicauth section)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# CORS Preflight (OPTIONS) + Request (GET, POST, PATCH, PUT, DELETE)
 | 
			
		||||
(cors-api) {
 | 
			
		||||
	@match-cors-api-preflight method OPTIONS
 | 
			
		||||
	handle @match-cors-api-preflight {
 | 
			
		||||
		header {
 | 
			
		||||
			Access-Control-Allow-Origin "{http.request.header.origin}"
 | 
			
		||||
			Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
 | 
			
		||||
			Access-Control-Allow-Headers "Origin, Accept, Authorization, Content-Type, X-Requested-With"
 | 
			
		||||
			Access-Control-Allow-Credentials "true"
 | 
			
		||||
			Access-Control-Max-Age "3600"
 | 
			
		||||
			defer
 | 
			
		||||
		}
 | 
			
		||||
		respond "" 204
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@match-cors-api-request {
 | 
			
		||||
		not {
 | 
			
		||||
			header Origin "{http.request.scheme}://{http.request.host}"
 | 
			
		||||
		}
 | 
			
		||||
		header Origin "{http.request.header.origin}"
 | 
			
		||||
	}
 | 
			
		||||
	handle @match-cors-api-request {
 | 
			
		||||
		header {
 | 
			
		||||
			Access-Control-Allow-Origin "{http.request.header.origin}"
 | 
			
		||||
			Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
 | 
			
		||||
			Access-Control-Allow-Headers "Origin, Accept, Authorization, Content-Type, X-Requested-With"
 | 
			
		||||
			Access-Control-Allow-Credentials "true"
 | 
			
		||||
			Access-Control-Max-Age "3600"
 | 
			
		||||
			defer
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# replace localhost with example.com or whatever
 | 
			
		||||
localhost {
 | 
			
		||||
	## HTTP Basic Auth
 | 
			
		||||
	## (uncomment to enable)
 | 
			
		||||
	# basicauth {
 | 
			
		||||
	# 	# see .example.env for how to generate tokens
 | 
			
		||||
	# 	{env.OLLAMA_API_ID} {env.OLLAMA_API_TOKEN_DIGEST}
 | 
			
		||||
	# }
 | 
			
		||||
 | 
			
		||||
	handle /api/* {
 | 
			
		||||
		# Comment to disable CORS
 | 
			
		||||
		import cors-api
 | 
			
		||||
 | 
			
		||||
		reverse_proxy localhost:11434
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	# Same-Origin Static Web Server
 | 
			
		||||
	file_server {
 | 
			
		||||
		root ./build/
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										74
									
								
								contribution_stats.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								contribution_stats.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,74 @@
 | 
			
		||||
import os
 | 
			
		||||
import subprocess
 | 
			
		||||
from collections import Counter
 | 
			
		||||
 | 
			
		||||
CONFIG_FILE_EXTENSIONS = (".json", ".yml", ".yaml", ".ini", ".conf", ".toml")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_text_file(filepath):
 | 
			
		||||
    # Check for binary file by scanning for null bytes.
 | 
			
		||||
    try:
 | 
			
		||||
        with open(filepath, "rb") as f:
 | 
			
		||||
            chunk = f.read(4096)
 | 
			
		||||
        if b"\0" in chunk:
 | 
			
		||||
            return False
 | 
			
		||||
        return True
 | 
			
		||||
    except Exception:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def should_skip_file(path):
 | 
			
		||||
    base = os.path.basename(path)
 | 
			
		||||
    # Skip dotfiles and dotdirs
 | 
			
		||||
    if base.startswith("."):
 | 
			
		||||
        return True
 | 
			
		||||
    # Skip config files by extension
 | 
			
		||||
    if base.lower().endswith(CONFIG_FILE_EXTENSIONS):
 | 
			
		||||
        return True
 | 
			
		||||
    return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_tracked_files():
 | 
			
		||||
    try:
 | 
			
		||||
        output = subprocess.check_output(["git", "ls-files"], text=True)
 | 
			
		||||
        files = output.strip().split("\n")
 | 
			
		||||
        files = [f for f in files if f and os.path.isfile(f)]
 | 
			
		||||
        return files
 | 
			
		||||
    except subprocess.CalledProcessError:
 | 
			
		||||
        print("Error: Are you in a git repository?")
 | 
			
		||||
        return []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    files = get_tracked_files()
 | 
			
		||||
    email_counter = Counter()
 | 
			
		||||
    total_lines = 0
 | 
			
		||||
 | 
			
		||||
    for file in files:
 | 
			
		||||
        if should_skip_file(file):
 | 
			
		||||
            continue
 | 
			
		||||
        if not is_text_file(file):
 | 
			
		||||
            continue
 | 
			
		||||
        try:
 | 
			
		||||
            blame = subprocess.check_output(
 | 
			
		||||
                ["git", "blame", "-e", file], text=True, errors="replace"
 | 
			
		||||
            )
 | 
			
		||||
            for line in blame.splitlines():
 | 
			
		||||
                # The email always inside <>
 | 
			
		||||
                if "<" in line and ">" in line:
 | 
			
		||||
                    try:
 | 
			
		||||
                        email = line.split("<")[1].split(">")[0].strip()
 | 
			
		||||
                    except Exception:
 | 
			
		||||
                        continue
 | 
			
		||||
                    email_counter[email] += 1
 | 
			
		||||
                    total_lines += 1
 | 
			
		||||
        except subprocess.CalledProcessError:
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
    for email, lines in email_counter.most_common():
 | 
			
		||||
        percent = (lines / total_lines * 100) if total_lines else 0
 | 
			
		||||
        print(f"{email}: {lines} {percent:.2f}%")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    main()
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user