Code Monkey home page Code Monkey logo

snowballing's Introduction

John Snow / Snowballing

This project provides tools for perfoming a liberature review through snowballing. It includes a Chrome plugin that assists the forward step of literature snowballing, Jupyter Notebook widgets that assist the backward and forward steps of literature snowballing, notebooks that assist in inserting citations in the database, and notebooks for analyzing the snowballing and producing citation graphs, publication place histograms, and a summarization of the snowballing steps.

This package was tested on Python 3.6 using Windows and Python 3.7 using Linux, but it should support Python > 3.5 in any operating system. Please, open an issue if it is not the case.

Please, find the project documentation at https://joaofelipe.github.io/snowballing

Getting started

First of all, clone this repo:

$ git clone https://github.com/JoaoFelipe/snowballing.git

To install the tool, you need run:

$ pip install -e snowballing

or

$ python setup.py install

For starting a new literature review project, please run:

$ snowballing start literature --profile bibtex

This command will create a directory called literature (you are free to use other name in the command) with the notebooks for performing the snowballing and analyzing it, and an example database. Note that the command specifies the profile bibtex, which is more user friendly. If you don't specify a profile, it will use the default profile (see below).

Inside the directory, start Jupyter:

$ cd literature
$ jupyter notebook

And open the file Index.ipynb. This file contains all the instructions for understanding the database and performing the snowballing.

Profiles

This tool is highly configurable through variables in the config module. These variables can be set using the database/__init__.py file. We consider each set of configurations a profile.

Currently, this tool supports starting the snowballing project using two profiles: default and bibtex.

Default Profile

This tools started as part of a literature snowballing. The functions were developped out of necessity in a adhoc way. Thus, there are some bad design decisions.

One of these bad design decisions is the name of Work attributes. Some of them do not match well stablished BibTex fields. For instance:

  • Work name refer to BibTex title

  • Work pp refer to BibTex pages

  • Work authors refer to BibTex author

Others have a bad names and direct translation to BibTex fields

  • Work place1 does not indicate the actual publication place (i.e, city, country). Instead, it indicates the venue. It represents both BibTex journal and BibTex booktitle attributes.

  • However, place1 is expected to be used only as a fallback for a lack of place field. The place field has the same semantics of place1, but it uses Place objects defined in database/places.py. Defining Place objects is harder than just adding Work with string venues.

Finally, it is hard to distinguish tool-attributes from bibtex-fields, when looking at the work. For instance:

  • The user-defined due attribute that indicates why a tool is unrelated to the snowballing subject should not be exported to BibTex

  • Similarly, the tool-defined category attribute that indicates the state of the Work in the snowballing should no be exported as well

While this profile has these drawbacks, it also has some positive aspects:

  • The Place objects keep the database consistent and allow to group Work by venue

  • This profiles has been heavily tested in actual literature reviews

BibTex Profile

This profiles seeks to be more user-friendly by using BibTex fields as Work attributes and using _ as a prefix to distinguish user-attributes from BibTex fields. Hence, in comparison to the default profile:

  • name became title

  • pp became pages

  • authors became author

  • place1 and place were removed. This profile uses journal and booktitle as strings instead

  • due became _due

  • category became _category

  • link became _url

In addition to these changes, we also removed the display and may_be_related_to attributes, but it is possible to add them back in the configuration.

With this profile, it is easier to work without breaking the database (as you don't need to specify existing Place objects), and it is easier to identify which attributes should be exported to BibTex when you use the search function.

Supporting tools

This tool uses other tools that require external installations to assist in the literature review. You may or may not need them all. In this section, we present these tools and describe how to install them.

John Snow Chrome Plugin

This plugin modifies the Google Scholar search page to include "BibTex", "Work", and "Add" buttons to assist in adding papers to the tool. It is specially helpful in the forward step of the snowballing, as it allows uses to configure a "citation_var", click on the "cited by" and add work with their citations.

To install it:

  • Use the following command to generate a folder with the Chrome plugin:
    • $ snowballing plugin
  • Activate the developer mode in Chrome and click in "Load unpacked" to load the plugin from the generated folder

To run it:

  • Go to the snowballing project folder
  • Start the plugin server:
    • $ snowballing web
  • Load a Google Scholar page
  • Note that if you click on the plugin icon, it now shows configuration options

Selenium Webdriver

Three notebooks use Selenium to search google scholar:

  • SearchScholar.ipynb : Use a search string to search work from scholar and add it using the tool
  • Forward.ipynb : Perform forward snowballing using the Scholar "cited by" link
  • Validate.ipynb : Validate dabatase items by searching each item at Google Scholar and comparing their BibTex

While I recommend to use the Chrome Plugin for the former two (Google is less lenient to the Selenium method, since it performs many requests at the same time), it is hard to escape from Selenium in the third method. Hence, I suggest to install and configure a WebDriver.

I have tested the tool with both the geckodriver (for Firefox) and the ChromeDriver (for Chrome).

To install it:

  • Install the desired WebDriver and Browser
  • Add it to the PATH environment variable
  • Configure variable config.WEB_DRIVER in your database/__init__.py to indicate the proper WebDriver

Text Editor

Some text editors support command line arguments to open files on specific lines. We use these commands both on the Chrome Plugin and on the Validate.ipynb notebook to jump to Work definitions in year files stored at database/work. Please, consider installing a text editor that support this feature and configuring it accordingly.

For Visual Studio Code, configure the variable config.LINE_PARAMS in your database/__init__.py to "--goto {year_path}:{line}"

For Sublime Text, configure the variable config.LINE_PARAMS in your database/__init__.py to "{year_path}:{line}"

GraphViz

We use GraphViz to generate the snowballing history (provenance) in the notebook SnowballingProvenance.ipynb. Please, consider installing it.

ProvToolBox

We also use provtoolbox to visualize the snowballing provenance in the notebook SnowballingProvenance.ipynb. It is more verbose than the other visualization format, but if you prefer to visualize it in a provenance format, consider installing it as well.

PDFReferencesExtractor

PDFReferencesExtractor is a helpful tool for extracting citations from PDFs to assist in backward snowballing. It is integrated into the Converter widget of the notebook Backward.ipynb.

To install it:

  • Clone the repository https://github.com/helenocampos/PDFReferencesExtractor
  • Install Java and Maven
  • Run $ mvn install
  • Configure the variable config.PDF_EXTRACTOR in your database/__init__.py to something link "java -jar refExtractor.jar full {path}". Note that java must be in your path, and refExtractor.jar refer to the compiled PDFReferencesExtractor file that was generated into the target directory

To use it:

  • Run the Converter Widget in Backward.ipynb
  • Select the "PDF" mode
  • Write the PDF path (or drag a PDF file into the input textarea)
  • It will generate a BibTex in the output area
  • Change the mode to BibTex to generate a list of Info structures
  • Click on "Set article_list" to save the list into a variable
  • Run the remainder of the notebook to use it

Contributing

Feel free to contribute to the project!

For installing in development mode, clone the repository and use pip install -e:

$ git clone [email protected]:JoaoFelipe/snowballing.git
$ cd snowballing
$ pip install -e snowballing

For testing it:

$ cd snowballing
$ python test.py

Known Usages

This tools has been used to support two papers:

  • Pimentel, J. F., Freire, J., Murta, L., & Braganholo, V. (2019). A survey on collecting, managing, and analyzing provenance from scripts. ACM Computing Surveys (CSUR), 52(3), 1-38.

    Material: https://dew-uff.github.io/scripts-provenance

  • Mourão. E., Pimentel, J. F., Murta, L., Kalinowski, M., Mendes, E., Wohlin, C. (2020 in press). On the Performance of Hybrid Search Strategies for Systematic Literature Reviews in Software Engineering. Information and Software Technology (IST).

    Material: https://gems-uff.github.io/hybrid-strategies

It also has been used to calculate the h-index of a conference: https://github.com/JoaoFelipe/ipaw-index

Contact

Do not hesitate to contact me:

License Terms

The MIT License (MIT)

Copyright (c) 2017 Joao Felipe Pimentel [email protected]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

snowballing's People

Contributors

danielpcampagna avatar ericamourao avatar guifabrin avatar joaofelipe avatar joaofelipe021 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

snowballing's Issues

Fix widgets for ipywidgets 7

The new ipywidgets uses the default output to display itself. Thus, all the places that use clear_output also removes the widget, instead of removing just the widget output

IPython dependency throws error

Problem

Installing the latest snowballing version via PyPI command and running the file Insert.ipynb. I've got the following error:

Captura de tela de 2020-08-24 15-01-50

IPython actually removed these methods (str_to_bytes and bytes_to_str) in this commit a089b.

Also, you removed the import for these methods, in this commit de084, on 15 Apr 2018.

And the last release in PyPI is from 2 Apr. 2019, thus, I suppose it is considering these changes.
But not...

Workaround:

Install snowballing mannually have worked fine for me:

git clone [email protected]:JoaoFelipe/snowballing.git
cd snowballing
python setup.py sdist
cd dist
pip install snowballing-1.0.0.tar.gz

Solution:

Deploy to PyPi, the release v1.0.0 should be enough.

Support Jupyter Lab

Jupyter Lab will not support display(Javascript(...)). It means that it is necessary to create alternatives for all the widgets that use javascript:

  • ArticleNavigator uses Javascript to remove and reinsert cells. A possible solution is to create a Code widget instead of using actual Jupyter cells
  • Graph uses Javascript to present zoomable graphs. A zoomable svg widget or a Jupyter lab mime extension could solve this issue

Backward an article twice

Problem description

During the Backward step, using Snowballing v1.0.0, in cell BackwardSnowballing("ujcich2018a", articles=article_list), some articles did not show the insert command.
Running the insert commands which were drawn for me, I've got this database folder; thus, I did repeat the Backward by only considering in the article_list the articles which were not drawn in the first run.

These are the bibtex of the articles that I use in the second run:

@misc{european_parliament_regulation_2016,
	title = {Regulation ({EU}) 2016/679 of the {European} {Parliament} and of the {Council} of 27 {April} 2016 ({General} {Data} {Protection} {Regulation})},
	volume = {59},
	url = {https://eur-lex.europa.eu/legal-content/EN/ALL/?uri=CELEX%3A32016R0679},
	urldate = {2020-08-26},
	author = {European Parliament and Council of the European Union},
	month = apr,
	year = {2016},
	pages = {1--88}
}
@misc{ashford_much_nodate,
	title = {Much {GDPR} prep is a waste of time, warns {PwC}},
	url = {https://www.computerweekly.com/news/450427632/Much-GDPR-prep-is-a-waste-of-time-warns-PwC},
	abstract = {Many organisations are not paying enough attention to technology in their preparations for the General Data Protection Regulation and risk compliance failure},
	language = {en},
	urldate = {2020-09-01},
	journal = {ComputerWeekly.com},
	author = {Ashford, Warwick},
	file = {/home/daniel/Zotero/storage/SJJMPSJL/Much-GDPR-prep-is-a-waste-of-time-warns-PwC.html}
}

@article{tankard_what_2016,
	title = {What the {GDPR} means for businesses},
	volume = {2016},
	doi = {10.1016/S1353-4858(16)30056-3},
	number = {6},
	journal = {Network Security},
	author = {Tankard, Colin},
	year = {2016},
	note = {Publisher: Elsevier},
	pages = {5--8},
	file = {/home/daniel/Zotero/storage/NRBYB4GQ/S1353485816300563.html}
}

@inproceedings{gjermundrod2016privacytracker,
  title={privacyTracker: a privacy-by-design GDPR-compliant framework with verifiable data traceability controls},
  author={Gjermundr{\o}d, Harald and Dionysiou, Ioanna and Costa, Kyriakos},
  booktitle={International Conference on Web Engineering},
  pages={3--15},
  year={2016},
  organization={Springer}
}

However, I got the following error message:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-8f60ae862c3c> in <module>
----> 1 BackwardSnowballing("ujcich2018a", articles=article_list)

~/.local/lib/python3.6/site-packages/snowballing/snowballing.py in __init__(self, citation_var, citation_file, articles, force_citation_file)
    672             articles=articles,
    673             backward=True,
--> 674             force_citation_file=force_citation_file
    675         )
    676 

~/.local/lib/python3.6/site-packages/snowballing/snowballing.py in __init__(self, citation_var, citation_file, articles, backward, force_citation_file)
    537         self.view = VBox(hboxes)
    538 
--> 539         self.set_articles(articles)
    540         self.erase_article_form()
    541 

~/.local/lib/python3.6/site-packages/snowballing/snowballing.py in set_articles(self, articles)
    548     def set_articles(self, articles):
    549         """Set list of articles and restart slider"""
--> 550         self.articles = list(self.valid_articles(articles))
    551         self.disable_show = True
    552         self.selector_widget.value = 0

~/.local/lib/python3.6/site-packages/snowballing/snowballing.py in valid_articles(self, articles, show)
    594                 backward=self.backward,
    595                 citation_file=self.citation_file if self.force_citation_file else None,
--> 596                 warning=lambda x: self.to_display.append(x)
    597             )
    598             if should["add"]:

~/.local/lib/python3.6/site-packages/snowballing/operations.py in should_add_info(info, citation, article, backward, citation_file, warning, set_scholar, article_rules, bibtex_rules, add_citation)
    935     """Check if there is anything to add for this info"""
    936     convert = ConvertDict(article_rules or config.ARTICLE_TO_INFO)
--> 937     info = convert.run(info, article=article)
    938     nwork = consume(info, "_nwork")
    939     should_add = {

~/.local/lib/python3.6/site-packages/snowballing/rules.py in run(self, original, article, new, skip_result)
     75             self.apply(new, article, cp, self.rules["<article>"])
     76         if "<after>" in self.rules:
---> 77             self.apply(new, original, cp, self.rules["<after>"])
     78         if "<result>" in self.rules and not skip_result:
     79             return self.process_element(self.rules["<result>"], new, original, cp)

~/.local/lib/python3.6/site-packages/snowballing/rules.py in apply(self, new, original, current, commands, key)
     48                 # Previous values do not matter
     49                 new_key = self.process_element(command[0], new, original, current)
---> 50                 new_value = self.process_element(command[1], new, original, current)
     51                 if new_key is not None and new_value is not None:
     52                     new[new_key] = new_value

~/.local/lib/python3.6/site-packages/snowballing/rules.py in process_element(self, command, new, original, current)
     40             if command.__code__.co_argcount == 2:
     41                 return command(original, new)
---> 42             return command(original, new, current)
     43 
     44     def apply(self, new, original, current, commands, key=None):

~/.local/lib/python3.6/site-packages/snowballing/config.py in <lambda>(old, new, current)
    268                 setitem(new, "display", last_name_first_author(new["authors"])) if "display" not in new else None,
    269                 setitem(new, "pyref", info_to_pyref(new)) if "pyref" not in new else None,
--> 270                 setitem(new, "_nwork", find_work_by_info(new, set())),
    271             ]
    272 

~/.local/lib/python3.6/site-packages/snowballing/config_helpers.py in find_work_by_info(paper, pyrefs)
    118 def find_work_by_info(paper, pyrefs=None):
    119     from .operations import find_work_by_info
--> 120     return find_work_by_info(paper, pyrefs=None)
    121 
    122 

~/.local/lib/python3.6/site-packages/snowballing/operations.py in find_work_by_info(paper, pyrefs, rules)
    593 
    594     for key, work in worklist:
--> 595         work, letter = compare_paper_to_work(letter, key, work, paper)
    596         if work:
    597             update_old(old_paper, paper, rules)

~/.local/lib/python3.6/site-packages/snowballing/operations.py in compare_paper_to_work(letter, key, work, paper)
    520         letter = max(ord(lastletter) + 1, letter)
    521 
--> 522     if config.info_work_match(paper, work):
    523         dset(paper, "pyref", key)
    524         return work, letter

~/.local/lib/python3.6/site-packages/snowballing/config.py in info_work_match(info, work)
    758         required -= 0.1
--> 759     if compare_str(getattr(work, "name"), info.get("name")) > required:
    760         return True
    761 

~/.local/lib/python3.6/site-packages/snowballing/utils.py in compare_str(first, second)
    368         0.75
    369     """
--> 370     return difflib.SequenceMatcher(None, first, second).ratio()
    371 
    372 

/usr/lib/python3.6/difflib.py in ratio(self)
    642         """
    643 
--> 644         matches = sum(triple[-1] for triple in self.get_matching_blocks())
    645         return _calculate_ratio(matches, len(self.a) + len(self.b))
    646 

/usr/lib/python3.6/difflib.py in get_matching_blocks(self)
    465         if self.matching_blocks is not None:
    466             return self.matching_blocks
--> 467         la, lb = len(self.a), len(self.b)
    468 
    469         # This is most naturally expressed as a recursive algorithm, but

TypeError: object of type 'int' has no len()

Inspecting the ~/.local/lib/python3.6/site-packages/snowballing/config.py:579, I have noted that getattr(work, "name") has returning the year of a

Solution

It needed that compare_str (in ~/.local/lib/python3.6/site-packages/snowballing/config.py:579) only receives strings as a parameter.

    if compare_str(str(getattr(work, "name")), info.get("name")) > required:

Erro no start com profile bibtex

Primeiramente parabéns pelo projeto.
Estou a procura de uma ferramenta com a qual eu possa realizar o snowballing para fazer uma revisão sistemática e estou verificando se me adapto a sua.
Tive um problema já no inicio:
Este comando abaixo não identifica o argumento --profile. Tentei com -p e deu o mesmo problema.
$ snowballing start literature --profile bibtex

usage: snowballing [-h] {start,search,ref} ...
snowballing: error: unrecognized arguments: -p

Meu ambiente:
MacOS: 10.13.6
python: 3.8.2

Sem o parametro criou a pasta literature, mas, vendo a documentação, me pareceu que o uso do bibtex facilitaria a vida rsrsrs

Um abraço

Failed to start a review project

Hi, it returned to me the error information shown below when I run snowballing start review1. And I found there's no example directory under the snowballing package after pip installation.

  • OS: MacOS 10.13
  • Python version: 3.6
  • Env: Python venv
Traceback (most recent call last):
  File "/Users/pc/.virtualenvs/reviews1/bin/snowballing", line 11, in <module>
    sys.exit(main())
  File "/Users/pc/.virtualenvs/reviews1/lib/python3.6/site-packages/snowballing/__init__.py", line 94, in main
    args.func(args)
  File "/Users/pc/.virtualenvs/reviews1/lib/python3.6/site-packages/snowballing/__init__.py", line 36, in start
    recursive_copy('../example', args.path)
  File "/Users/pc/.virtualenvs/reviews1/lib/python3.6/site-packages/snowballing/__init__.py", line 30, in recursive_copy
    fil.write(resource(origin))
  File "/Users/pc/.virtualenvs/reviews1/lib/python3.6/site-packages/snowballing/__init__.py", line 13, in resource
    content = resource_string(__name__, filename)
  File "/Users/pc/.virtualenvs/reviews1/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1214, in resource_string
    self, resource_name
  File "/Users/pc/.virtualenvs/reviews1/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1456, in get_resource_string
    return self._get(self._fn(self.module_path, resource_name))
  File "/Users/pc/.virtualenvs/reviews1/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1576, in _get
    with open(path, 'rb') as stream:
FileNotFoundError: [Errno 2] No such file or directory: '/Users/pc/.virtualenvs/reviews1/lib/python3.6/site-packages/snowballing/../example'

Backwards Snowballing: AttributeError: 'WorkOk' object has no attribute 'place'

Windows10
Python 3.7.1

Hey there I am trying to use this for a project, however it seems I can not get it to run :(.

After setting up and deleting the example files I tried inserting two sample files to try it out. I could insert them just fine. However when I got to the "Backwards Snowballing" part I get error messages after calling the "BackwardSnowballing" Function Error below.

this does not always happen, it depends on the titles I am inserting. However all they are all proper bibtex style and properly recognized by the Converter Widget.

This is basically a vanilla clone from this Repository and all I did was to do the first step of inserting two initial works.

BackwardSnowballing("wiegand2014a", articles=article_list)


AttributeError Traceback (most recent call last)
in
----> 1 BackwardSnowballing("wiegand2014a", articles=article_list)

C:\ProgramData\Anaconda3\lib\site-packages\snowballing\snowballing.py in init(self, citation_var, citation_file, articles, force_citation_file)
573 articles=articles,
574 backward=True,
--> 575 force_citation_file=force_citation_file
576 )
577

C:\ProgramData\Anaconda3\lib\site-packages\snowballing\snowballing.py in init(self, citation_var, citation_file, articles, backward, force_citation_file)
303 self.view = VBox(hboxes)
304
--> 305 self.set_articles(articles)
306 self.erase_article_form()
307

C:\ProgramData\Anaconda3\lib\site-packages\snowballing\snowballing.py in set_articles(self, articles)
345 def set_articles(self, articles):
346 """Set list of articles and restart slider"""
--> 347 self.articles = list(self.valid_articles(articles))
348 self.disable_show = True
349 self.selector_widget.value = 0

C:\ProgramData\Anaconda3\lib\site-packages\snowballing\snowballing.py in valid_articles(self, articles, show)
417 set_pyref(info, check_existence=True)
418 set_place(info, check_existence=True)
--> 419 nwork = find_work_by_info(info, set())
420
421 if not self.work:

C:\ProgramData\Anaconda3\lib\site-packages\snowballing\operations.py in find_work_by_info(paper, pyrefs)
764
765 for key, work in worklist:
--> 766 work, letter = compare_paper_to_work(letter, key, work, paper)
767 if work:
768 return work

C:\ProgramData\Anaconda3\lib\site-packages\snowballing\operations.py in compare_paper_to_work(letter, key, work, paper)
685 if "place" in paper:
686 place = getattr(config.MODULES['places'], paper["place"], None)
--> 687 if place is not None and place.name == work.place.name:
688 required -= 0.1
689

AttributeError: 'WorkOk' object has no attribute 'place'`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.