diff --git a/.gitignore b/.gitignore index cb01194..c937fcd 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ experiment_code/lans/.Rhistory experiment_code/rq1/correlations/* .RData .Rhistory -.config.json \ No newline at end of file +.config.json +*pyc \ No newline at end of file diff --git a/experiment_code/1-popularity/overvie.py b/experiment_code/1-popularity/overvie.py new file mode 100644 index 0000000..41650b4 --- /dev/null +++ b/experiment_code/1-popularity/overvie.py @@ -0,0 +1,181 @@ + +# coding: utf-8 +import MySQLdb +import json +import time +import random +import urllib2 +import pandas as pd + +with open("../.config.json") as fp: + config = json.load(fp) +local_db_config = config["local_db"] +conn = MySQLdb.connect(host=local_db_config["db_host"],user=local_db_config["db_user"], + passwd=local_db_config["db_passwd"],db=local_db_config["db_name"],port=3306,charset='utf8mb4') +cursor = conn.cursor() + +def profile_type(doc): + doc_id,repo_id,doc_location,doc_name,doc_type = doc + if doc_location.lower() in ["root",".github","docs"] and doc_type=="blob": + doc_name = doc_name.lower() + for fi in ["readme"]: + if doc_name.lower().find(fi) != -1: + return "readme" + for fi in ["license"]: + if doc_name.lower().find(fi) != -1: + return "license" + for fi in ["issue_template","pull_request_template"]: + if doc_name.lower().find(fi) != -1: + return "template" + for fi in ["code_of_conduct"]: + if doc_name.lower().find(fi) != -1: + return "COC" + for fi in ["contributing"]: + if doc_name.lower().find(fi) != -1: + return "contributing" + return None + elif doc_location.lower() in ["issue_template", "pull_request_template"] and doc_type=="blob": + return "template" + else: + return None + return None + + +def overview(): + cursor.execute("select id from random_repos") + repos = set([item[0] for item in cursor.fetchall()]) + prf_files = [] + cursor.execute("select * from files") + files = cursor.fetchall() + for f in files: + if f[1] in repos: + prf_files.append([f[0],f[1],profile_type(f)]) + # print f, profile_type(f) + + repo_counts = len(repos) + dist = {"readme":set(),"license":set(),"template":set(), + "COC":set(),"contributing":set()} + + for _f in prf_files: + doc_id,repo_id,doc_type = _f + if doc_type is not None: + dist[doc_type].add(repo_id) + for key, value in dist.items(): + print key, "%.2f%%"%(len(value)*100.0/repo_counts) + + cursor.execute("select count(id) from random_repos where description is not null") + print "desc", "%.2f%%"%(cursor.fetchone()[0]*100.0/repo_counts) + +def repo_profile(repo_id): + file_types = set() + cursor.execute("select * from files where repo_id=%s",(repo_id,)) + files = cursor.fetchall() + for f in files: + f_t = profile_type(f) + if f_t is not None: + file_types.add(f_t) + return file_types + +def by_language(): + cursor.execute("select id, language from random_repos") + repos = cursor.fetchall() + language_dist = {} + + file_dist = {} + + for item in repos: + repo_id, repo_lan = item + if repo_lan not in language_dist: + language_dist[repo_lan] = 0 + language_dist[repo_lan] += 1 + if repo_lan not in file_dist: + file_dist[repo_lan] = {} + repo_files = repo_profile(repo_id) + for rf in repo_files: + if rf not in file_dist[repo_lan]: + file_dist[repo_lan][rf] = 0 + file_dist[repo_lan][rf] += 1 + lans = sorted(language_dist.items(), key=lambda x: x[1], reverse=True) + print lans + for lan, lan_count in lans: + tmp_lan_dist = file_dist[lan] + percentages = [] + for _k in [ "readme","license","contributing","COC","template"]: + if _k not in tmp_lan_dist: + percentages.append(0) + else: + percentages.append(tmp_lan_dist[_k]) + p_str = "|".join(["%.2f%%"%(item*100.0/language_dist[lan]) for item in percentages]) + print "|%s|%d|%s|"%(lan,language_dist[lan],p_str) + +def by_role(): + cursor.execute("select id, owner_type from random_repos") + repos = cursor.fetchall() + role_type = {} + + file_dist = {} + + for item in repos: + repo_id, repo_lan = item + if repo_lan not in role_type: + role_type[repo_lan] = 0 + role_type[repo_lan] += 1 + if repo_lan not in file_dist: + file_dist[repo_lan] = {} + repo_files = repo_profile(repo_id) + for rf in repo_files: + if rf not in file_dist[repo_lan]: + file_dist[repo_lan][rf] = 0 + file_dist[repo_lan][rf] += 1 + lans = sorted(role_type.items(), key=lambda x: x[1], reverse=True) + print lans + for lan, lan_count in lans: + tmp_lan_dist = file_dist[lan] + percentages = [] + for _k in [ "readme","license","contributing","COC","template"]: + if _k not in tmp_lan_dist: + percentages.append(0) + else: + percentages.append(tmp_lan_dist[_k]) + p_str = "|".join(["%.2f%%"%(item*100.0/role_type[lan]) for item in percentages]) + print "|%s|%d|%s|"%(lan,role_type[lan],p_str) + + +import time +def _strtime2int(strtime): + return time.mktime(time.strptime(strtime, '%Y-%m-%dT%H:%M:%SZ')) + + +def by_age(): + cursor.execute("select id, created_at from random_repos") + repos = cursor.fetchall() + # 第一个是包含的,第二个是不包含的 + file_dist = { "readme":[[],[]],"license":[[],[]],"contributing":[[],[]],"COC":[[],[]],"template":[[],[]]} + + for item in repos: + repo_id, repo_create = item + repo_files = repo_profile(repo_id) + repo_age = (_strtime2int("2020-07-18T00:00:00Z") - _strtime2int(repo_create))/(60*60*24) + for key in file_dist.keys(): + if key in repo_files: + file_dist[key][0].append(repo_age) + else: + file_dist[key][1].append(repo_age) + for key, value in file_dist.items(): + stats1 = pd.Series(value[0]) + stats2 = pd.Series(value[1]) + print key, "Yes", stats1.min(), stats1.mean(),stats1.median(),stats1.std(),stats1.max() + print key, "No", stats2.min(), stats2.mean(),stats2.median(),stats2.std(),stats2.max() + print "----"*10 + + + +if __name__ == "__main__": + + # overview() + # by_language() + # by_role() + by_age() + + + diff --git a/experiment_code/dataset/get_repo_files.py b/experiment_code/dataset/get_repo_files.py index 70afebb..d44494b 100644 --- a/experiment_code/dataset/get_repo_files.py +++ b/experiment_code/dataset/get_repo_files.py @@ -103,7 +103,7 @@ def get_files(repo): break def get_dir_files(repo_id): - cursor.execute("select full_name from random_repos where id=%s",(repo_id,)) + cursor.execute("select full_name from random_repos_before_fitler where id=%s",(repo_id,)) repo_slug = cursor.fetchone()[0] file_url = "https://api.github.com/repos/%s/git/trees/%s" @@ -147,8 +147,13 @@ def get_dir_files(repo_id): print files print "***"*10 for f in files: - cursor.execute("insert into files(repo_id, location, name, type) values(%s,%s,%s,%s)", + cursor.execute("select id from files where repo_id=%s and location=%s and name=%s",(repo_id, f[0], f[1])) + tmp_r = cursor.fetchone() + if tmp_r is None: + cursor.execute("insert into files(repo_id, location, name, type) values(%s,%s,%s,%s)", (repo_id, f[0], f[1], f[2])) + else: + print "already" conn.commit() break diff --git a/idea.md b/idea.md index 5bbfca3..8275978 100644 --- a/idea.md +++ b/idea.md @@ -30,6 +30,40 @@ min、25%、mean、median、75%、max - age ### 2.2 Identification of documentation +GitHub official suggestions: + +#### code-fo-coduct +- https://docs.github.com/en/github/building-a-strong-community/adding-a-code-of-conduct-to-your-project + - root directory, docs directory,.github directory + - In the file name field, type CODE_OF_CONDUCT.md. + +#### contributing +- https://docs.github.com/en/github/building-a-strong-community/setting-guidelines-for-repository-contributors + - root, docs, .github + - Contributing guidelines filenames are not case sensitive, and can have an extension such as .md or .txt. + +#### template +- https://docs.github.com/en/github/building-a-strong-community/about-issue-and-pull-request-templates + - You must create templates on the repository's default branch. Templates created in other branches are not available for collaborators to use. + - root directory, the docs folder, or the hidden .github directory +- https://docs.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository +- https://docs.github.com/en/github/building-a-strong-community/creating-a-pull-request-template-for-your-repository + - - root, docs, .github + - 可以创建template 模板,然后再在配置文件里设置使用哪个template + +#### readme +- https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-readmes#relative-links-and-image-paths-in-readme-files + - root, docs, .github + +#### license +- https://docs.github.com/en/github/building-a-strong-community/adding-a-license-to-a-repository + - LICENSE or LICENSE.md (with all caps) + +- https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/licensing-a-repository + - Most people place their license text in a file named LICENSE.txt (or LICENSE.md) in the root of the repository. Some projects include information about their license in their README. For example, a project's README may include a note saying "This project is licensed under the terms of the MIT license." +- 现实情况是,即使使用copying、copyright等关键词GitHub也能关联起来 + +#### *Summary* - location: According to GitHub's conventions, community profile documentations can be placed in three directories. i.e., - root @@ -45,9 +79,37 @@ According to GitHub's conventions, community profile documentations can be place #### RQ1. What is the frequency/popularity of community profile documentation? - Overview + +| Type | desc | readme | license| contributing | COC|template | +|:-|:-|:-|:-|:-|:-|:-| +|Percentage|100.00%|99.25%|82.05%|28.70%|11.15%|24.05% + + - by programing language + +| Language | Count | readme | license| contributing | COC|template | +|:-|:-|:-|:-|:-|:-|:-|:-| +JavaScript|386|99.74%|86.53%|30.31%|13.73%|22.28%| +|Python|285|100.00%|84.56%|26.67%|8.07%|26.32%| +|Java|179|99.44%|79.89%|25.14%|14.53%|27.93%| +|C++|116|100.00%|76.72%|23.28%|6.90%|25.86%| +|Go|116|100.00%|95.69%|43.10%|12.07%|32.76%| +|PHP|111|97.30%|82.88%|36.94%|6.31%|28.83%| +|TypeScript|88|100.00%|89.77%|**56.82%**|**18.18%**|**45.45%**| +|C|83|100.00%|62.65%|24.10%|9.64%|20.48%| +|Ruby|79|100.00%|83.54%|26.58%|8.86%|17.72%| +|C#|71|100.00%|**98.59%**|28.17%|12.68%|25.35%| + - by user role + +| OwerType | Count | readme | license| contributing | COC|template | +|:-|:-|:-|:-|:-|:-|:-|:-| +|User|1033|98.94%|76.86%|13.36%|5.61%|13.75%| +|Organization|967|99.59%|87.59%|45.09%|17.06%|36.40%| + + - by age + - by forks (comparison of boxplot) #### RQ2. How is community profile documentation maintained?