├── LICENSE.txt ├── settings.py ├── 1_users_repos.py ├── .gitattributes ├── README.md ├── aux_check_pickles.py ├── 2_who_else_starred.py ├── 5_similiarity.py ├── 3_what_else_they_starred.py ├── 4_condense_data.py ├── 6_recommendations.py ├── .gitignore ├── github_utils.py └── LICENSE /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This software is free for personal and academic use. In commercial settings you can use this software for two weeks for evaluation. Contact the author for a commercial license. 2 | -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | 'settings' 2 | 3 | # recommendations for... 4 | user = 'username_here' 5 | 6 | # github username and password 7 | auth = ( 'github_username', 'github_password' ) 8 | 9 | # where to store data 10 | # no slashes at the end 11 | users_dir = 'users' 12 | repos_dir = 'repos' 13 | -------------------------------------------------------------------------------- /1_users_repos.py: -------------------------------------------------------------------------------- 1 | 'get starred repos for a given user' 2 | 3 | import sys 4 | #import codecs 5 | import cPickle as pickle 6 | from github_utils import * 7 | from settings import * 8 | 9 | try: 10 | output_file = sys.argv[1] 11 | except IndexError: 12 | output_file = '{}.pkl'.format( user ) 13 | 14 | starred = get_all_starred( user ) 15 | 16 | f = open( output_file, 'wb' ) 17 | pickle.dump( starred, f ) 18 | 19 | """ 20 | f = codecs.open( output_file, "wb", "utf-8" ) 21 | f.write( starred ) 22 | """ 23 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | StarDose 2 | ======== 3 | 4 | A recommender system for GitHub repositories. Uses [requests](http://docs.python-requests.org/en/latest/) as HTTP client. 5 | 6 | * input: a username (to get the repos the user starred) 7 | * output: other repos the user may be interested in 8 | 9 | Edit `settings.py` and you're ready to run the scripts in order. 10 | 11 | You might want to tweak `6_recommendations.py`, see comments in the source. 12 | 13 | 14 | 15 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/zygmuntz/stardose/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 16 | 17 | -------------------------------------------------------------------------------- /aux_check_pickles.py: -------------------------------------------------------------------------------- 1 | 'try to load each pickle file in the users dir, finally output names of those with errors' 2 | 3 | import sys 4 | import cPickle as pickle 5 | from glob import glob 6 | from github_utils import * 7 | from settings import * 8 | 9 | input_files = glob( users_dir + '*.pkl' ) 10 | input_files.sort() 11 | 12 | errors = [] 13 | 14 | for f in input_files: 15 | print f 16 | 17 | try: 18 | data = pickle.load( open( f, 'rb' )) 19 | except: 20 | print "*** error ***\n" 21 | errors.append( f ) 22 | 23 | print "\nFiles with errors:" 24 | for f in errors: 25 | print f 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /2_who_else_starred.py: -------------------------------------------------------------------------------- 1 | 'given starred repos for the target user, get other gazers for these repos' 2 | 'save to repos/.pkl' 3 | 4 | import sys 5 | #import codecs 6 | import cPickle as pickle 7 | from github_utils import * 8 | from settings import * 9 | 10 | try: 11 | input_file = sys.argv[1] 12 | except IndexError: 13 | input_file = '{}.pkl'.format( user ) 14 | 15 | starred = pickle.load( open( input_file, 'rb' )) 16 | 17 | for r in starred: 18 | full_name = r['full_name'] 19 | print full_name 20 | 21 | if repo_data_available( full_name ): 22 | continue 23 | 24 | gazers = get_all_gazers( full_name ) 25 | save_repo( full_name, gazers ) 26 | print 27 | 28 | 29 | -------------------------------------------------------------------------------- /5_similiarity.py: -------------------------------------------------------------------------------- 1 | "compute similiarity data between target user and the other users" 2 | 3 | import cPickle as pickle 4 | from github_utils import * 5 | from settings import * 6 | 7 | # TODO: move to settings 8 | target_file = '{}.pkl'.format( user ) 9 | input_file = 'condensed_users_and_repos.pkl' 10 | output_file = 'similiarity.pkl' 11 | 12 | print "loading data..." 13 | 14 | data = pickle.load( open( input_file )) 15 | target_starred = get_target_starred( target_file ) 16 | 17 | print "{} target starred.".format( len( target_starred )) 18 | 19 | user_star_counts = {} 20 | user_stars_in_common = {} 21 | 22 | for u in data['users']: 23 | user_star_counts[u] = len( data['users'][u] ) 24 | 25 | repos_in_common = [ x for x in data['users'][u] if x in target_starred and not x.startswith( 'zygmuntz/' ) ] 26 | user_stars_in_common[u] = len( repos_in_common ) 27 | 28 | #print user_star_counts 29 | #print user_stars_in_common 30 | 31 | pickle.dump( { 'user_star_counts': user_star_counts, 'user_stars_in_common': user_stars_in_common }, open( output_file, 'wb' )) 32 | -------------------------------------------------------------------------------- /3_what_else_they_starred.py: -------------------------------------------------------------------------------- 1 | "get gazers' data for repos in a given dir" 2 | "specifically, what other repos each user has starred" 3 | 'save to users/.pkl' 4 | 5 | "this script takes a while to download data from GitHub. It can be stopped and re-run, it will resume." 6 | 7 | import sys 8 | import codecs 9 | import cPickle as pickle 10 | from glob import glob 11 | from github_utils import * 12 | from settings import * 13 | 14 | repo_files = glob( repos_dir + '/*.pkl' ) 15 | repo_counter = 0 16 | repos_count = len( repo_files ) 17 | 18 | for f in repo_files: 19 | print "{} ({}/{})".format( f, repo_counter, repos_count ) 20 | print 21 | 22 | repo_counter += 1 23 | 24 | gazers = pickle.load( open( f, 'rb' )) 25 | gazers_count = len( gazers ) 26 | counter = 0 27 | 28 | for g in gazers: 29 | counter += 1 30 | gazer = g['login'] 31 | 32 | # skip already downloaded 33 | if data_available_for( gazer ): 34 | continue 35 | 36 | print "{} ({}/{})".format( gazer, counter, gazers_count ) 37 | starred = get_all_starred( gazer ) 38 | save_gazer( gazer, starred ) 39 | 40 | print 41 | 42 | -------------------------------------------------------------------------------- /4_condense_data.py: -------------------------------------------------------------------------------- 1 | 'condense data from users/ into a one file' 2 | 3 | import sys 4 | import os 5 | import codecs 6 | import cPickle as pickle 7 | from glob import glob 8 | from pprint import pprint 9 | from github_utils import * 10 | from settings import * 11 | 12 | try: 13 | output_file = sys.argv[1] 14 | except IndexError: 15 | output_file = 'condensed_users_and_repos.pkl' 16 | 17 | input_files = glob( users_dir + '/*.pkl' ) 18 | input_files.sort() 19 | 20 | errors = [] 21 | users = {} # user_name: repo_names 22 | repos = {} # repo_name: data ( repos for all users ) 23 | counter = 0 24 | 25 | for f in input_files: 26 | counter += 1 27 | 28 | try: 29 | data = pickle.load( open( f, 'rb' )) 30 | except: 31 | print "*** couldn't load ***\n" 32 | errors.append( f ) 33 | continue 34 | 35 | file_name = os.path.basename( f ) 36 | user_name = file_name.split( '.' )[0] 37 | print user_name 38 | 39 | user_repos = {} 40 | for r in data: 41 | 42 | repos[r] = data[r] 43 | user_repos[r] = True # dictionary for fast searching 44 | 45 | users[user_name] = user_repos 46 | 47 | # tmp saves 48 | if counter % 5000 == 0: 49 | print "saving to {}...".format( output_file ) 50 | pickle.dump( { 'users': users, 'repos': repos }, open( output_file, 'wb' )) 51 | 52 | if errors: 53 | print "\nFiles with errors:" 54 | for f in errors: 55 | print f 56 | 57 | print "saving to {}...".format( output_file ) 58 | 59 | pickle.dump( { 'users': users, 'repos': repos }, open( output_file, 'wb' )) 60 | 61 | 62 | -------------------------------------------------------------------------------- /6_recommendations.py: -------------------------------------------------------------------------------- 1 | "get actual recommendations" 2 | "tweak below" 3 | 4 | import sys 5 | import codecs 6 | import cPickle as pickle 7 | from collections import defaultdict 8 | from math import log1p, sqrt 9 | from github_utils import * 10 | from settings import * 11 | 12 | # TODO: move to settings 13 | input_file = 'condensed_users_and_repos.pkl' 14 | similiarity_file = 'similiarity.pkl' 15 | target_file = '{}.pkl'.format( user ) 16 | output_file = 'recommendations.txt' 17 | 18 | print "loading data..." 19 | 20 | data = pickle.load( open( input_file )) 21 | similiarity = pickle.load( open( similiarity_file )) 22 | target_starred = get_target_starred( target_file ) 23 | 24 | ### 25 | 26 | # TODO: move to settings 27 | threshold = 1 28 | user_weights = get_weights( similiarity, threshold ) 29 | 30 | 31 | popularity = defaultdict( float ) 32 | 33 | for u in data['users']: 34 | weight = user_weights[u] 35 | for r in data['users'][u]: 36 | popularity[r] += weight 37 | 38 | # remove already starred repos 39 | popularity = { x: popularity[x] for x in popularity if x not in target_starred } 40 | 41 | 42 | ########################## 43 | ### LOOK AND EDIT HERE ### 44 | ########################## 45 | 46 | # if you want to select only less popular repos... 47 | # popularity = { x: popularity[x] for x in popularity if data['repos'][x]['stargazers_count'] < 200 } 48 | 49 | # downplay popular repos 50 | # sqrt seems to work well 51 | # need to add + k, +1 is too little (repos with 1 star showing high) 52 | popularity = { x: popularity[x] / ( sqrt( data['repos'][x]['stargazers_count'] + 10 )) for x in popularity if x not in target_starred } 53 | 54 | most_popular = sorted( popularity, key = popularity.get, reverse = True ) 55 | 56 | o_f = codecs.open( output_file, "wb", "utf-8" ) 57 | 58 | # when 'ascii' codec can't encode character (...) in description, 59 | # omit description when writing 60 | for r in most_popular[:3000]: 61 | try: 62 | line = "github.com/{}\nscore: {}\t stars:{}\n{}\n\n".format( r, popularity[r], data['repos'][r]['stargazers_count'], data['repos'][r]['description'] ) 63 | o_f.write( line ) 64 | except Exception, e: 65 | #print e 66 | print "omitted description of {}".format( r ) 67 | line = "github.com/{}\nscore: {}\t stars:{}\n\n".format( r, popularity[r], data['repos'][r]['stargazers_count'] ) 68 | o_f.write( line ) 69 | 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /github_utils.py: -------------------------------------------------------------------------------- 1 | 'basic building blocks for the system' 2 | 3 | import requests as reqs 4 | import re 5 | import cPickle as pickle 6 | import os 7 | from settings import * 8 | 9 | def get_all_starred( user ): 10 | starred, links, remaining_queries = get_starred( user ) 11 | while True: 12 | if not links: 13 | break 14 | 15 | try: 16 | next_url = links['next'] 17 | except KeyError: 18 | break 19 | 20 | page, links, remaining_queries = get_starred( user, next_url ) 21 | starred.extend( page ) 22 | 23 | print "Total: {}, remaining queries: {}".format( len( starred ), remaining_queries ) 24 | return starred 25 | 26 | 27 | 'return a page of starred repos' 28 | def get_starred( user, url = None, format = 'json' ): 29 | 30 | global auth 31 | 32 | if not url: 33 | url = "https://api.github.com/users/{}/starred?per_page=100".format( user ) 34 | 35 | print url 36 | r = reqs.get( url, auth = auth ) 37 | 38 | ret_links = {} 39 | try: 40 | ret_links = get_links( r.headers['link'] ) 41 | except KeyError: 42 | pass 43 | 44 | remaining_queries = int( r.headers['x-ratelimit-remaining'] ) 45 | 46 | if format == 'text': 47 | return ( r.text, ret_links, remaining_queries ) 48 | else: 49 | return ( r.json(), ret_links, remaining_queries ) 50 | 51 | 52 | ### 53 | 54 | 'call get_gazers as many times as needed' 55 | def get_all_gazers( repo ): 56 | gazers, links, remaining_queries = get_gazers( repo ) 57 | while True: 58 | if not links: 59 | break 60 | 61 | try: 62 | next_url = links['next'] 63 | except KeyError: 64 | break 65 | 66 | page, links, remaining_queries = get_gazers( repo, next_url ) 67 | gazers.extend( page ) 68 | 69 | print "Total: {}, remaining queries: {}".format( len( gazers ), remaining_queries ) 70 | return gazers 71 | 72 | 73 | 'return a page of users who starred a given repo in text format' 74 | def get_gazers( repo, url = None ): 75 | 76 | global auth 77 | 78 | if not url: 79 | url = "https://api.github.com/repos/{}/stargazers?per_page=100".format( repo ) 80 | 81 | print url 82 | r = reqs.get( url, auth = auth ) 83 | 84 | ret_links = {} 85 | try: 86 | ret_links = get_links( r.headers['link'] ) 87 | except KeyError: 88 | pass 89 | 90 | remaining_queries = int( r.headers['x-ratelimit-remaining'] ) 91 | 92 | return ( r.json(), ret_links, remaining_queries ) 93 | 94 | 95 | 'parse pagination links returned by github' 96 | def get_links( link_header ): 97 | 98 | ret_links = {} 99 | 100 | links = link_header.split( ', ' ) 101 | for link in links: 102 | match_href = re.match( '<(.+?)>', link ) 103 | href = match_href.group( 1 ) 104 | 105 | match_rel = re.search( 'rel="(.+?)"', link ); 106 | rel = match_rel.group( 1 ) 107 | 108 | ret_links[rel] = href 109 | 110 | return ret_links 111 | 112 | # save a repo's gazers 113 | def save_repo( repo, gazers ): 114 | global repos_dir 115 | repo = get_saveable_repo_name( repo ) 116 | output_file = '{}/{}.pkl'.format( repos_dir, repo ) 117 | f = open( output_file, 'wb' ) 118 | pickle.dump( gazers, f ) 119 | 120 | def get_saveable_repo_name( repo ): 121 | a, r = repo.split( '/' ) 122 | return a + '_' + r 123 | 124 | # save a user's starred repos 125 | def save_gazer( gazer, starred ): 126 | global users_dir 127 | 128 | # save only some selected data for each repo 129 | repos = {} 130 | for r in starred: 131 | 132 | # ignore user's own repos 133 | try: 134 | if r['owner']['login'] == gazer: 135 | continue 136 | except Exception, e: 137 | print e 138 | pprint( r ) 139 | continue 140 | 141 | repo_name = r['full_name'] 142 | repo_data = { x: r[x] for x in ( 'id', 'description', 'stargazers_count', 'watchers' ) } 143 | repos[repo_name] = repo_data 144 | 145 | 146 | 147 | output_file = '{}/{}.pkl'.format( users_dir, gazer ) 148 | f = open( output_file, 'wb' ) 149 | pickle.dump( repos, f ) 150 | 151 | # check if there's starred data for a given user 152 | def data_available_for( gazer ): 153 | global users_dir 154 | 155 | target_file = '{}/{}.pkl'.format( users_dir, gazer ) 156 | if os.path.isfile( target_file ): 157 | return True 158 | 159 | """ 160 | try: 161 | open( target_file, 'rb' ) 162 | return True 163 | except: 164 | return False 165 | """ 166 | 167 | # check if there's data for a given repo 168 | def repo_data_available( repo ): 169 | global repos_dir 170 | 171 | repo = get_saveable_repo_name( repo ) 172 | target_file = '{}/{}.pkl'.format( repos_dir, repo ) 173 | if os.path.isfile( target_file ): 174 | return True 175 | 176 | def get_target_starred( input_file ): 177 | data = pickle.load( open( input_file, 'rb' )) 178 | target_starred = {} 179 | for r in data: 180 | full_name = r['full_name'] 181 | target_starred[full_name] = True 182 | return target_starred 183 | 184 | def get_weights( similiarity, threshold = 2 ): 185 | weights = {} 186 | user_star_counts = similiarity['user_star_counts'] 187 | user_stars_in_common = similiarity['user_stars_in_common'] 188 | 189 | for u in user_star_counts: 190 | 191 | stars_in_common = user_stars_in_common[u] 192 | if stars_in_common < threshold: 193 | weights[u] = 0.0 194 | continue 195 | 196 | try: 197 | weights[u] = 1.0 * stars_in_common / user_star_counts[u] 198 | except ZeroDivisionError: 199 | weights[u] = 0.0 200 | 201 | return weights -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2.0 2 | 3 | Copyright (c) 2013 Zygmunt Zając 4 | 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | This license establishes the terms under which a given free software 11 | Package may be copied, modified, distributed, and/or redistributed. 12 | The intent is that the Copyright Holder maintains some artistic 13 | control over the development of that Package while still keeping the 14 | Package available as open source and free software. 15 | 16 | You are always permitted to make arrangements wholly outside of this 17 | license directly with the Copyright Holder of a given Package. If the 18 | terms of this license do not permit the full use that you propose to 19 | make of the Package, you should contact the Copyright Holder and seek 20 | a different licensing arrangement. 21 | 22 | Definitions 23 | 24 | "Copyright Holder" means the individual(s) or organization(s) 25 | named in the copyright notice for the entire Package. 26 | 27 | "Contributor" means any party that has contributed code or other 28 | material to the Package, in accordance with the Copyright Holder's 29 | procedures. 30 | 31 | "You" and "your" means any person who would like to copy, 32 | distribute, or modify the Package. 33 | 34 | "Package" means the collection of files distributed by the 35 | Copyright Holder, and derivatives of that collection and/or of 36 | those files. A given Package may consist of either the Standard 37 | Version, or a Modified Version. 38 | 39 | "Distribute" means providing a copy of the Package or making it 40 | accessible to anyone else, or in the case of a company or 41 | organization, to others outside of your company or organization. 42 | 43 | "Distributor Fee" means any fee that you charge for Distributing 44 | this Package or providing support for this Package to another 45 | party. It does not mean licensing fees. 46 | 47 | "Standard Version" refers to the Package if it has not been 48 | modified, or has been modified only in ways explicitly requested 49 | by the Copyright Holder. 50 | 51 | "Modified Version" means the Package, if it has been changed, and 52 | such changes were not explicitly requested by the Copyright 53 | Holder. 54 | 55 | "Original License" means this Artistic License as Distributed with 56 | the Standard Version of the Package, in its current version or as 57 | it may be modified by The Perl Foundation in the future. 58 | 59 | "Source" form means the source code, documentation source, and 60 | configuration files for the Package. 61 | 62 | "Compiled" form means the compiled bytecode, object code, binary, 63 | or any other form resulting from mechanical transformation or 64 | translation of the Source form. 65 | 66 | 67 | Permission for Use and Modification Without Distribution 68 | 69 | (1) You are permitted to use the Standard Version and create and use 70 | Modified Versions for any purpose without restriction, provided that 71 | you do not Distribute the Modified Version. 72 | 73 | 74 | Permissions for Redistribution of the Standard Version 75 | 76 | (2) You may Distribute verbatim copies of the Source form of the 77 | Standard Version of this Package in any medium without restriction, 78 | either gratis or for a Distributor Fee, provided that you duplicate 79 | all of the original copyright notices and associated disclaimers. At 80 | your discretion, such verbatim copies may or may not include a 81 | Compiled form of the Package. 82 | 83 | (3) You may apply any bug fixes, portability changes, and other 84 | modifications made available from the Copyright Holder. The resulting 85 | Package will still be considered the Standard Version, and as such 86 | will be subject to the Original License. 87 | 88 | 89 | Distribution of Modified Versions of the Package as Source 90 | 91 | (4) You may Distribute your Modified Version as Source (either gratis 92 | or for a Distributor Fee, and with or without a Compiled form of the 93 | Modified Version) provided that you clearly document how it differs 94 | from the Standard Version, including, but not limited to, documenting 95 | any non-standard features, executables, or modules, and provided that 96 | you do at least ONE of the following: 97 | 98 | (a) make the Modified Version available to the Copyright Holder 99 | of the Standard Version, under the Original License, so that the 100 | Copyright Holder may include your modifications in the Standard 101 | Version. 102 | 103 | (b) ensure that installation of your Modified Version does not 104 | prevent the user installing or running the Standard Version. In 105 | addition, the Modified Version must bear a name that is different 106 | from the name of the Standard Version. 107 | 108 | (c) allow anyone who receives a copy of the Modified Version to 109 | make the Source form of the Modified Version available to others 110 | under 111 | 112 | (i) the Original License or 113 | 114 | (ii) a license that permits the licensee to freely copy, 115 | modify and redistribute the Modified Version using the same 116 | licensing terms that apply to the copy that the licensee 117 | received, and requires that the Source form of the Modified 118 | Version, and of any works derived from it, be made freely 119 | available in that license fees are prohibited but Distributor 120 | Fees are allowed. 121 | 122 | 123 | Distribution of Compiled Forms of the Standard Version 124 | or Modified Versions without the Source 125 | 126 | (5) You may Distribute Compiled forms of the Standard Version without 127 | the Source, provided that you include complete instructions on how to 128 | get the Source of the Standard Version. Such instructions must be 129 | valid at the time of your distribution. If these instructions, at any 130 | time while you are carrying out such distribution, become invalid, you 131 | must provide new instructions on demand or cease further distribution. 132 | If you provide valid instructions or cease distribution within thirty 133 | days after you become aware that the instructions are invalid, then 134 | you do not forfeit any of your rights under this license. 135 | 136 | (6) You may Distribute a Modified Version in Compiled form without 137 | the Source, provided that you comply with Section 4 with respect to 138 | the Source of the Modified Version. 139 | 140 | 141 | Aggregating or Linking the Package 142 | 143 | (7) You may aggregate the Package (either the Standard Version or 144 | Modified Version) with other packages and Distribute the resulting 145 | aggregation provided that you do not charge a licensing fee for the 146 | Package. Distributor Fees are permitted, and licensing fees for other 147 | components in the aggregation are permitted. The terms of this license 148 | apply to the use and Distribution of the Standard or Modified Versions 149 | as included in the aggregation. 150 | 151 | (8) You are permitted to link Modified and Standard Versions with 152 | other works, to embed the Package in a larger work of your own, or to 153 | build stand-alone binary or bytecode versions of applications that 154 | include the Package, and Distribute the result without restriction, 155 | provided the result does not expose a direct interface to the Package. 156 | 157 | 158 | Items That are Not Considered Part of a Modified Version 159 | 160 | (9) Works (including, but not limited to, modules and scripts) that 161 | merely extend or make use of the Package, do not, by themselves, cause 162 | the Package to be a Modified Version. In addition, such works are not 163 | considered parts of the Package itself, and are not subject to the 164 | terms of this license. 165 | 166 | 167 | General Provisions 168 | 169 | (10) Any use, modification, and distribution of the Standard or 170 | Modified Versions is governed by this Artistic License. By using, 171 | modifying or distributing the Package, you accept this license. Do not 172 | use, modify, or distribute the Package, if you do not accept this 173 | license. 174 | 175 | (11) If your Modified Version has been derived from a Modified 176 | Version made by someone other than you, you are nevertheless required 177 | to ensure that your Modified Version complies with the requirements of 178 | this license. 179 | 180 | (12) This license does not grant you the right to use any trademark, 181 | service mark, tradename, or logo of the Copyright Holder. 182 | 183 | (13) This license includes the non-exclusive, worldwide, 184 | free-of-charge patent license to make, have made, use, offer to sell, 185 | sell, import and otherwise transfer the Package with respect to any 186 | patent claims licensable by the Copyright Holder that are necessarily 187 | infringed by the Package. If you institute patent litigation 188 | (including a cross-claim or counterclaim) against any party alleging 189 | that the Package constitutes direct or contributory patent 190 | infringement, then this Artistic License to you shall terminate on the 191 | date that such litigation is filed. 192 | 193 | (14) Disclaimer of Warranty: 194 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 195 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 196 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 197 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 198 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 199 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 200 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 201 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 202 | --------------------------------------------------------------------------------