cmd/badges/example.py PYTHON 273 lines View on github.com → Search inside
1import json2import os3from pathlib import Path4import re5import string6import subprocess78# import boto3910from datetime import datetime, timedelta11import time12import math13from shutil import rmtree14from tempfile import gettempdir1516# s3 = boto3.client('s3')17bucket_name = 'sloccloccode'1819def lambda_handler(event, context):20    if 'path' not in event:21        return {22            "statusCode": 400,23            "statusDescription": "400",24            "isBase64Encoded": False,25            "headers": {26                "Content-Type": "text/html"27            },28            "body": '''You be invalid'''29        }3031    filename, url, path = process_path(event['path'])3233    if filename == None or url == None or path == None:34        return {35            "statusCode": 400,36            "statusDescription": "400",37            "isBase64Encoded": False,38            "headers": {39                "Content-Type": "text/html"40            },41            "body": '''You be invalid'''42        }4344    get_process_file(filename=filename, url=url, path=path)4546    with open(Path(gettempdir()) / filename, encoding='utf-8') as f:47        content = f.read()4849    j = json.loads(content)50    title = 'Total lines'51    s = format_count(sum([x['Lines'] for x in j]))5253    if 'category' in event['queryStringParameters']:54        t = event['queryStringParameters']['category']5556        if t == 'code':57            title = 'Code lines'58            s = format_count(sum([x['Code'] for x in j]))59        elif t == 'blanks':60            title = 'Blank lines'61            s = format_count(sum([x['Blank'] for x in j]))62        elif t == 'lines':63            pass # it's the default anyway64        elif t == 'comments':65            title = 'Comments'66            s = format_count(sum([x['Comment'] for x in j]))67        elif t == 'cocomo':68            title = 'COCOMO $'69            wage = '56286'70            if 'avg-wage' in event['queryStringParameters']:71                wage = event['queryStringParameters']['avg-wage']7273            if wage.isdigit():74                s = format_count(estimate_cost(sum([x['Code'] for x in j]), int(wage)))75            else:76                s = format_count(estimate_cost(sum([x['Code'] for x in j])))7778    text_length = '250'79    if len(s) <= 3:80        text_length = '200'8182    return {83        "statusCode": 200,84        "statusDescription": "200 OK",85        "isBase64Encoded": False,86        "headers": {87            "Content-Type": "image/svg+xml;charset=utf-8",88            "Cache-Control": "max-age=86400"89        },90        "body": '''<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="100" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h69v20H0z"/><path fill="#4c1" d="M69 0h31v20H69z"/><path fill="url(#b)" d="M0 0h100v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="355" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="590">''' + title + '''</text><text x="355" y="140" transform="scale(.1)" textLength="590">''' + title +'''</text><text x="835" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="''' + text_length + '''">''' + s + '''</text><text x="835" y="140" transform="scale(.1)" textLength="''' + text_length + '''">''' + s + '''</text></g> </svg>'''91    }929394def get_process_file(filename, url, path):95    s = boto3.resource('s3')96    o = s.Object(bucket_name, filename)9798    try:99        unixtime = s3time_to_unix(o.last_modified)100    except Exception:101        # force an update in this case102        unixtime = time.time() - 186400103104    diff = int(time.time() - unixtime)105    if diff < 86400:106        o.download_file(Path(gettempdir()) / filename)107    else:108        clone_and_process(filename=filename, url=url, path=path)109110111def clone_and_process(filename, url, path):112    import git113114    download_scc()115116    os.chdir(gettempdir())117118    rmtree(Path(gettempdir()) / 'scc-tmp-path')119    git.exec_command('clone', '--depth=1', url, 'scc-tmp-path', cwd=gettempdir())120121    subprocess.run(['./scc', '-f', 'json', '-o', str(Path(gettempdir()) / filename), 'scc-tmp-path'])122123    with open(Path(gettempdir()) / filename, 'rb') as f:124        s3.upload_fileobj(f, bucket_name, filename)125126    rmtree(Path(gettempdir()) / 'scc-tmp-path')127128129def download_scc():130    my_file = Path(gettempdir()) / 'scc'131    if my_file.exists() == False:132        with open(my_file, 'wb') as f:133            s3.download_fileobj(bucket_name, 'scc', f)134    my_file.chmod(0o755)135136137def s3time_to_unix(last_modified):138    datetime_object = datetime.strptime(str(last_modified).split(' ')[0], '%Y-%m-%d')139    unixtime = time.mktime(datetime_object.timetuple())140    return unixtime141142143def process_path(path):144    path = re.sub('', '', path, flags=re.MULTILINE )145146    s = [clean_string(x) for x in path.lower().split('/') if x != '']147148    if len(s) != 3:149        return None, None, None150151    # Cheap clean check152    for x in s:153        if x == '':154            return None, None, None155156    # URL for cloning157    url = 'https://'158159    if s[0] == 'github':160        url += 'github.com/'161    if s[0] == 'bitbucket':162        url += 'bitbucket.org/'163    if s[0] == 'gitlab':164        url += 'gitlab.com/'165166    url += s[1] + '/'167    url += s[2] + '.git'168169    # File for json170    filename = s[0]171    filename += '.' + s[1]172    filename += '.' + s[2] + '.json'173174    # Need path175    path = s[2]176177    return (filename, url, path)178179180def clean_string(s):181    valid = string.ascii_lowercase182    valid += string.digits183    valid += '-'184    valid += '.'185    valid += '_'186187    clean = ''188189    for c in s:190        if c in valid:191            clean += c192193    return clean194195196def format_count(count):197    ranges = [198        (1e18, 'E'),199        (1e15, 'P'),200        (1e12, 'T'),201        (1e9, 'G'),202        (1e6, 'M'),203        (1e3, 'k'),204    ]205206    for x, y in ranges:207        if count >= x:208            t = str(round(count / x, 1))209            if len(t) > 3:210                t = t[:t.find('.')]211            return t + y212213    return str(round(count, 1))214215216# EstimateEffort calculate the effort applied using generic COCOMO2 weighted values217def estimate_effort(slocCount):218    return float(3.2) * math.pow(float(slocCount)/1000, 1.05) * 1219220221# EstimateCost calculates the cost in dollars applied using generic COCOMO2 weighted values based222# on the average yearly wage223def estimate_cost(slocCount, averageWage=56286):224    return estimate_effort(slocCount) * float(averageWage/12) * float(1.8)225226227if __name__ == '__main__':228229    # last_modified = '2019-06-22 07:13:19+00:00'230    # unixtime = s3time_to_unix(last_modified)231232    # diff = int(time.time() - unixtime)233    # print(diff > 86400)234    # if diff < 86400:235    #     print('pull from s3 and return')236    # else:237    #     print('clone him and reprocess')238239    x, y, z = process_path('/github/boyter/really-cheap-chatbot/')240    print(x, y, z)241    '''242    https://gitlab.com/esr/loccount.git243    https://bitbucket.org/grumdrig/pq-web.git244    https://github.com/boyter/scc.git245    '''246247    print(format_count(100))248    print(format_count(1000))249    print(format_count(2500))250    print(format_count(436465))251    print(format_count(263804))252    print(format_count(86400))253254    print(format_count(81.99825581739397))255256    print('')257258    # with open('/home/bboyter/Projects/scc-lambda/tmp.json', encoding='utf-8') as f:259    #     content = f.read()260261    # j = json.loads(content)262    # print('lines: ' + format_count(sum([x['Lines'] for x in j])))263    # print('code: ' + format_count(sum([x['Code'] for x in j])))264    # print('comment: ' + format_count(sum([x['Comment'] for x in j])))265    # print('blank: ' + format_count(sum([x['Blank'] for x in j])))266    # print('complexity: ' + format_count(sum([x['Complexity'] for x in j])))267268269    print(format_count(estimate_cost(710)))270    # s3 = boto3.resource('s3')271    # o = s3.Object('sloccloccode','github.boyter.really-cheap-chatbot.json')272    # print(o.last_modified)273    # o.download_file('/tmp/github.boyter.really-cheap-chatbot.json')

Code quality findings 31

Ensure functions have docstrings for documentation
missing-docstring
def lambda_handler(event, context):
Use 'is' for None comparisons (e.g., x is None)
none-comparison
if filename == None or url == None or path == None:
Ensure functions have docstrings for documentation
missing-docstring
def get_process_file(filename, url, path):
Catch specific exceptions instead of Exception to avoid masking bugs
broad-except
except Exception:
Ensure functions have docstrings for documentation
missing-docstring
def clone_and_process(filename, url, path):
Ensure functions have docstrings for documentation
missing-docstring
def download_scc():
Ensure functions have docstrings for documentation
missing-docstring
def s3time_to_unix(last_modified):
Ensure functions have docstrings for documentation
missing-docstring
def process_path(path):
Ensure functions have docstrings for documentation
missing-docstring
def clean_string(s):
Ensure functions have docstrings for documentation
missing-docstring
def format_count(count):
Ensure functions have docstrings for documentation
missing-docstring
def estimate_effort(slocCount):
Ensure functions have docstrings for documentation
missing-docstring
def estimate_cost(slocCount, averageWage=56286):
Use logging module for better control and configurability
print-statement
# print(diff > 86400)
Use logging module for better control and configurability
print-statement
# print('pull from s3 and return')
Use logging module for better control and configurability
print-statement
# print('clone him and reprocess')
Use logging module for better control and configurability
print-statement
print(x, y, z)
Use logging module for better control and configurability
print-statement
print(format_count(100))
Use logging module for better control and configurability
print-statement
print(format_count(1000))
Use logging module for better control and configurability
print-statement
print(format_count(2500))
Use logging module for better control and configurability
print-statement
print(format_count(436465))
Use logging module for better control and configurability
print-statement
print(format_count(263804))
Use logging module for better control and configurability
print-statement
print(format_count(86400))
Use logging module for better control and configurability
print-statement
print(format_count(81.99825581739397))
Use logging module for better control and configurability
print-statement
print('')
Use logging module for better control and configurability
print-statement
# print('lines: ' + format_count(sum([x['Lines'] for x in j])))
Use logging module for better control and configurability
print-statement
# print('code: ' + format_count(sum([x['Code'] for x in j])))
Use logging module for better control and configurability
print-statement
# print('comment: ' + format_count(sum([x['Comment'] for x in j])))
Use logging module for better control and configurability
print-statement
# print('blank: ' + format_count(sum([x['Blank'] for x in j])))
Use logging module for better control and configurability
print-statement
# print('complexity: ' + format_count(sum([x['Complexity'] for x in j])))
Use logging module for better control and configurability
print-statement
print(format_count(estimate_cost(710)))
Use logging module for better control and configurability
print-statement
# print(o.last_modified)

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.