History of stackoverflow
The changes to the source code of this entry in the repository, newest first, each with the differences it made to the entry's files. They are also listed on GitLab.
[entries] Add the date when each entry was added, and remove icon_hash beff7c9
The dates are those of the commits that first added the entries' files (2020-08-11 for the entries of the initial commit); renamed entries keep the date of their first name. Changed: none
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index a74fad0..bc4c216 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -42,7 +42,7 @@ The network has the following properties:
tags = ['Economic', 'Preferences', 'Unweighted', 'Timestamps', 'Metadata']
url = 'https://archive.org/details/stack-exchange-data-dump-2011-12-xml'
citation = [('Stack Exchange, Inc., "Stack Exchange Data Dump - Dec 2011."', 'https://archive.org/details/stack-exchange-data-dump-2011-12-xml')]
-icon_hash = '583222868cf714f319c427a2'
+date_added = '2020-08-11'
upstream_license = ("Creative Commons Attribution-ShareAlike 3.0 (CC BY-SA 3.0)",
"https://creativecommons.org/licenses/by-sa/3.0/")
upstream_prefix = 'https://archive.org/download/stack-exchange-data-dump-2011-12-xml/Stack%20Exchange%20Data%20Dump%20-%20Dec%202011/Content'
[entries] Prefix the properties in the descriptions with (Node), (Edge) or (Graph) bc3dbbd
The lists of properties at the end of the descriptions marked edge properties with "Edge" but left node properties unmarked, or grouped them in "Nodes: `a`, ...; `b`, ..." items, so the kind of a property was often unclear. Every item now starts with (Node), (Edge) or (Graph), followed by the property names, a colon and the explanation, one item per property or group of properties; qualifications (kinds of nodes, networks) follow the names in parentheses. Properties that were only named got a short explanation, and a few items that named the wrong kind of property were corrected.
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index aade040..a74fad0 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -29,12 +29,16 @@ Only the users with at least one favorite and the questions that are a favorite
The network has the following properties:
-- `is_user`: whether the node is a user (otherwise it is a question).
-- `so_id`: the Stack Overflow id of the user or question (users have pages at `https://stackoverflow.com/users/<so_id>`, and questions at `https://stackoverflow.com/questions/<so_id>`).
-- `created`: the creation time of the user account or the question (Unix time; -1 if unknown).
-- Users: `name`, the display name of the user; `reputation`, the reputation score of the user (-1 if unknown).
-- Questions: `title` and `tags` (as written on the site, e.g. `<c#><winforms>`), the title and the tags of the question; `score`, the score (up votes minus down votes) of the question; `views`, the number of times it was viewed; `missing`, whether the question is absent from the dump (then `title` and `tags` are empty, `created` and `views` are -1, and `score` is 0).
-- Edges: `time`, the day at which the user marked the question as favorite (Unix time of midnight UTC)."""
+- (Node) `is_user`: whether the node is a user (otherwise it is a question).
+- (Node) `so_id`: the Stack Overflow id of the user or question (users have pages at `https://stackoverflow.com/users/<so_id>`, and questions at `https://stackoverflow.com/questions/<so_id>`).
+- (Node) `created`: the creation time of the user account or the question (Unix time; -1 if unknown).
+- (Node) `name` (users): the display name of the user.
+- (Node) `reputation` (users): the reputation score of the user (-1 if unknown).
+- (Node) `title`, `tags` (questions): the title and the tags of the question (as written on the site, e.g. `<c#><winforms>`).
+- (Node) `score` (questions): the score (up votes minus down votes) of the question.
+- (Node) `views` (questions): the number of times it was viewed.
+- (Node) `missing` (questions): whether the question is absent from the dump (then `title` and `tags` are empty, `created` and `views` are -1, and `score` is 0).
+- (Edge) `time`: the day at which the user marked the question as favorite (Unix time of midnight UTC)."""
tags = ['Economic', 'Preferences', 'Unweighted', 'Timestamps', 'Metadata']
url = 'https://archive.org/details/stack-exchange-data-dump-2011-12-xml'
citation = [('Stack Exchange, Inc., "Stack Exchange Data Dump - Dec 2011."', 'https://archive.org/details/stack-exchange-data-dump-2011-12-xml')]
[entries] Use American spelling in the descriptions 802d564
British spellings in the titles and descriptions of 69 entries (e.g. labelled, organisation, millimetres, centres, anonymised, neighbours, analysed, modelled, colour, licence) are replaced by the American ones. Proper names keep their spelling (Otago Harbour, the Centre for Water Systems, the Centre jeunesse de Montréal, the Dutch Ministry of Defence), as do quoted upstream terms, citations, and property names.
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index 5b405ce..aade040 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -23,7 +23,7 @@ import calendar
from lxml import etree
title = "Stack Overflow favorites (2011)"
-description = """A bipartite network of users and the questions they marked as favorites on Stack Overflow, the question-and-answer website on computer programming of the Stack Exchange network. Users of Stack Overflow could mark a question as a "favorite" (a feature later renamed "bookmarks") to follow it and find it again later. The data are from the Stack Exchange data dump of December 2011, which contains the public content of the site from its start in July 2008 to early December 2011, published by Stack Exchange under the CC BY-SA 3.0 licence. Nodes are users and questions, and an undirected edge connects a user to a question that the user had marked as favorite at the time of the dump, timestamped with the day it was marked. Favorites that were later removed are not in the data.
+description = """A bipartite network of users and the questions they marked as favorites on Stack Overflow, the question-and-answer website on computer programming of the Stack Exchange network. Users of Stack Overflow could mark a question as a "favorite" (a feature later renamed "bookmarks") to follow it and find it again later. The data are from the Stack Exchange data dump of December 2011, which contains the public content of the site from its start in July 2008 to early December 2011, published by Stack Exchange under the CC BY-SA 3.0 license. Nodes are users and questions, and an undirected edge connects a user to a question that the user had marked as favorite at the time of the dump, timestamped with the day it was marked. Favorites that were later removed are not in the data.
Only the users with at least one favorite and the questions that are a favorite of at least one user are included. The favorites of 17,359 questions that are not in the dump (presumably deleted after being marked, while the votes were kept) are included, but these questions have no metadata.
[entries] Rebuild the Wikimedia and Stack Overflow entries from the original dumps b65b158
- edit_wikibooks, edit_wikinews, edit_wikiquote, edit_wiktionary, wikipedia_link, wiki_categories, wiki_link_dyn: from the Wikimedia dumps of 2026-10-01 (newer than KONECT's extractions), with page titles, user names (including unregistered editors, marked `anonymous`), timestamps and namespaces; the sister projects gain the language editions added since. - stackoverflow: from the Stack Exchange data dump of December 2011 (the one KONECT used), with KONECT's swapped sides corrected and user and question properties.
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index d7aa4f1..5b405ce 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -17,33 +17,142 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
from .. import *
+import re
+import subprocess
+import calendar
+from lxml import etree
title = "Stack Overflow favorites (2011)"
-description = """A bipartite network of users and the posts they have favorited, from the online Q&A site Stack Overflow. An edge (i,j) connects a user i to a post j if that user \"favorited\" that post (a kind of a rating). Edges are timestamped.[^icon]
-[^icon]: Description obtained from the [ICON](https://icon.colorado.edu) project."""
-tags = ['Economic', 'Preferences', 'Unweighted', 'Timestamps']
-url = 'http://konect.cc/networks/stackexchange-stackoverflow'
-citation = [('J. Kunegis, "Stack overflow network dataset" KONECT, the Koblenz Network Collection (2016).', 'https://doi.org/10.1145/2487788.2488173')]
+description = """A bipartite network of users and the questions they marked as favorites on Stack Overflow, the question-and-answer website on computer programming of the Stack Exchange network. Users of Stack Overflow could mark a question as a "favorite" (a feature later renamed "bookmarks") to follow it and find it again later. The data are from the Stack Exchange data dump of December 2011, which contains the public content of the site from its start in July 2008 to early December 2011, published by Stack Exchange under the CC BY-SA 3.0 licence. Nodes are users and questions, and an undirected edge connects a user to a question that the user had marked as favorite at the time of the dump, timestamped with the day it was marked. Favorites that were later removed are not in the data.
+
+Only the users with at least one favorite and the questions that are a favorite of at least one user are included. The favorites of 17,359 questions that are not in the dump (presumably deleted after being marked, while the votes were kept) are included, but these questions have no metadata.
+
+The network has the following properties:
+
+- `is_user`: whether the node is a user (otherwise it is a question).
+- `so_id`: the Stack Overflow id of the user or question (users have pages at `https://stackoverflow.com/users/<so_id>`, and questions at `https://stackoverflow.com/questions/<so_id>`).
+- `created`: the creation time of the user account or the question (Unix time; -1 if unknown).
+- Users: `name`, the display name of the user; `reputation`, the reputation score of the user (-1 if unknown).
+- Questions: `title` and `tags` (as written on the site, e.g. `<c#><winforms>`), the title and the tags of the question; `score`, the score (up votes minus down votes) of the question; `views`, the number of times it was viewed; `missing`, whether the question is absent from the dump (then `title` and `tags` are empty, `created` and `views` are -1, and `score` is 0).
+- Edges: `time`, the day at which the user marked the question as favorite (Unix time of midnight UTC)."""
+tags = ['Economic', 'Preferences', 'Unweighted', 'Timestamps', 'Metadata']
+url = 'https://archive.org/details/stack-exchange-data-dump-2011-12-xml'
+citation = [('Stack Exchange, Inc., "Stack Exchange Data Dump - Dec 2011."', 'https://archive.org/details/stack-exchange-data-dump-2011-12-xml')]
icon_hash = '583222868cf714f319c427a2'
-upstream_prefix = 'http://konect.cc/files'
-files = [('download.tsv.stackexchange-stackoverflow.tar.bz2', None, 'konect')]
+upstream_license = ("Creative Commons Attribution-ShareAlike 3.0 (CC BY-SA 3.0)",
+ "https://creativecommons.org/licenses/by-sa/3.0/")
+upstream_prefix = 'https://archive.org/download/stack-exchange-data-dump-2011-12-xml/Stack%20Exchange%20Data%20Dump%20-%20Dec%202011/Content'
+volumes = [f'stackoverflow.com.7z.{i:03d}' for i in range(1, 8)]
+files = [(volumes, None, None)]
+member = '122011 Stack Overflow/{}.xml'
def fetch_upstream(force=False):
- return fetch_upstream_files(__name__.split(".")[-1], upstream_prefix, files,
- force)
+ # A multi-volume 7z archive (4.5 GB), whose volumes are stored as they
+ # are (open_upstream_file() would recompress them)
+ name = __name__.split(".")[-1]
+ os.makedirs(f"cache/upstream/{name}", exist_ok=True)
+ for vol in volumes:
+ fname = f"cache/upstream/{name}/{vol}"
+ if os.path.exists(fname) and not force:
+ continue
+ def download():
+ with open_url(f"{upstream_prefix}/{vol}") as f, \
+ open(fname + ".part", "wb") as out:
+ shutil.copyfileobj(f, out, 1 << 24)
+ retry_download(download)
+ os.replace(fname + ".part", fname)
+
+@contextmanager
+def open_member(name, table):
+ """Stream one XML file of the archive, decompressed with 7z."""
+ with subprocess.Popen(["7z", "e", "-so",
+ f"cache/upstream/{name}/{volumes[0]}",
+ member.format(table)],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL) as p:
+ yield p.stdout
+ p.stdout.close()
+ if p.returncode not in (0, None, -13): # -13: closed early (SIGPIPE)
+ raise RuntimeError(f"7z failed with code {p.returncode}")
+
+_id = re.compile(rb'<row Id="(-?\d+)"')
+
+def read_rows(f, ids):
+ """Attributes of the rows of a dump XML file whose ids are in `ids`
+ (rows are on separate lines)."""
+ rows = {}
+ for line in f:
+ m = _id.search(line)
+ if m is None or int(m.group(1)) not in ids:
+ continue
+ rows[int(m.group(1))] = dict(etree.fromstring(line.strip()).attrib)
+ return rows
+
+def unix_time(s):
+ """Unix time of an ISO date of the dump (UTC), or -1."""
+ if not s:
+ return -1
+ s = s[:19]
+ fmt = "%Y-%m-%dT%H:%M:%S" if len(s) > 10 else "%Y-%m-%d"
+ return calendar.timegm(time.strptime(s, fmt))
@cache_network()
-@coerce_props()
+@coerce_props(vforce={"name": "string", "title": "string", "tags": "string"})
@annotate()
def parse(alts=None):
- global files
name = __name__.split(".")[-1]
- for fnames, alt, fmt in files:
- if alts is not None and alt not in alts:
- continue
- if isinstance(fnames, str):
- fnames = [fnames]
- with ExitStack() as stack:
- fs = [stack.enter_context(open_upstream_file(name, fn, "rb")) for fn in fnames]
- g = parse_graph(fs, fmt, directed=False)
- yield alt, g
+ fav = re.compile(rb'PostId="(\d+)" VoteTypeId="5" UserId="(-?\d+)" CreationDate="([^"]*)"')
+ favs = {}
+ with open_member(name, "votes") as f:
+ for line in f:
+ if b'VoteTypeId="5"' not in line:
+ continue
+ m = fav.search(line)
+ if m is None:
+ raise ValueError(f"unexpected favorite vote: {line}")
+ key = (int(m.group(2)), int(m.group(1)))
+ t = unix_time(m.group(3).decode())
+ # Repeated favorites (none expected): the earliest is kept
+ favs[key] = min(t, favs.get(key, t))
+ users = {}
+ posts = {}
+ for (u, p) in favs:
+ users.setdefault(u, len(users))
+ posts.setdefault(p, len(posts))
+ NU = len(users)
+ g = Graph(directed=False)
+ g.add_vertex(NU + len(posts))
+ g.ep.time = g.new_ep("int64_t")
+ g.add_edge_list([(users[u], NU + posts[p], t) for (u, p), t in favs.items()],
+ eprops=[g.ep.time])
+ del favs
+ g.vp.is_user = g.new_vp("bool")
+ g.vp.is_user.a[:NU] = True
+ g.vp.so_id = g.new_vp("int64_t")
+ for u, i in users.items():
+ g.vp.so_id[i] = u
+ for p, i in posts.items():
+ g.vp.so_id[NU + i] = p
+ for k in ["created", "reputation", "views"]:
+ g.vp[k] = g.new_vp("int64_t", val=-1)
+ g.vp.score = g.new_vp("int64_t")
+ g.vp.missing = g.new_vp("bool")
+ g.vp.missing.a[NU:] = True
+ for k in ["name", "title", "tags"]:
+ g.vp[k] = g.new_vp("string")
+ with open_member(name, "users") as f:
+ for u, r in read_rows(f, users).items():
+ v = users[u]
+ g.vp.name[v] = r.get("DisplayName", "")
+ g.vp.reputation[v] = int(r.get("Reputation", -1))
+ g.vp.created[v] = unix_time(r.get("CreationDate"))
+ with open_member(name, "posts") as f:
+ for p, r in read_rows(f, posts).items():
+ v = NU + posts[p]
+ g.vp.missing[v] = False
+ g.vp.title[v] = r.get("Title", "")
+ g.vp.tags[v] = r.get("Tags", "")
+ g.vp.score[v] = int(r.get("Score", 0))
+ g.vp.views[v] = int(r.get("ViewCount", -1))
+ g.vp.created[v] = unix_time(r.get("CreationDate"))
+ yield None, g
Update copyright information 73a4f0d
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index 6577035..d7aa4f1 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -1,7 +1,7 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2020-2024 Tiago de Paula Peixoto <tiago@skewed.de>
+# Copyright (C) 2020-2026 Tiago de Paula Peixoto <tiago@skewed.de>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
Update copyright information 3d288ea
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index ae79a76..6577035 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -1,7 +1,7 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2020-2023 Tiago de Paula Peixoto <tiago@skewed.de>
+# Copyright (C) 2020-2024 Tiago de Paula Peixoto <tiago@skewed.de>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
Update copyright information 8d005ae
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index 671a04b..ae79a76 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -1,7 +1,7 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2021 Tiago de Paula Peixoto <tiago@skewed.de>
+# Copyright (C) 2020-2023 Tiago de Paula Peixoto <tiago@skewed.de>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
Update copyright info db2efcc
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index f964e63..671a04b 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -1,7 +1,7 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2020 Tiago de Paula Peixoto <tiago@skewed.de>
+# Copyright (C) 2021 Tiago de Paula Peixoto <tiago@skewed.de>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
Un-orphan KONECT datasets 894eba5
KONECT has been brought back from the dead for the time being.
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index 21cb125..f964e63 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -26,7 +26,6 @@ url = 'http://konect.cc/networks/stackexchange-stackoverflow'
citation = [('J. Kunegis, "Stack overflow network dataset" KONECT, the Koblenz Network Collection (2016).', 'https://doi.org/10.1145/2487788.2488173')]
icon_hash = '583222868cf714f319c427a2'
upstream_prefix = 'http://konect.cc/files'
-orphan = True
files = [('download.tsv.stackexchange-stackoverflow.tar.bz2', None, 'konect')]
def fetch_upstream(force=False):
Update citations to use DOIs e878874
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index 4af7c2f..21cb125 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -23,7 +23,7 @@ description = """A bipartite network of users and the posts they have favorited,
[^icon]: Description obtained from the [ICON](https://icon.colorado.edu) project."""
tags = ['Economic', 'Preferences', 'Unweighted', 'Timestamps']
url = 'http://konect.cc/networks/stackexchange-stackoverflow'
-citation = [('J. Kunegis, "Stack overflow network dataset" KONECT, the Koblenz Network Collection (2016).', 'http://konect.cc/networks/stackexchange-stackoverflow')]
+citation = [('J. Kunegis, "Stack overflow network dataset" KONECT, the Koblenz Network Collection (2016).', 'https://doi.org/10.1145/2487788.2488173')]
icon_hash = '583222868cf714f319c427a2'
upstream_prefix = 'http://konect.cc/files'
orphan = True
Add support for 'orphan' datasets and tag konect entries e330b80
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index c0f5745..4af7c2f 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -26,6 +26,7 @@ url = 'http://konect.cc/networks/stackexchange-stackoverflow'
citation = [('J. Kunegis, "Stack overflow network dataset" KONECT, the Koblenz Network Collection (2016).', 'http://konect.cc/networks/stackexchange-stackoverflow')]
icon_hash = '583222868cf714f319c427a2'
upstream_prefix = 'http://konect.cc/files'
+orphan = True
files = [('download.tsv.stackexchange-stackoverflow.tar.bz2', None, 'konect')]
def fetch_upstream(force=False):
Change license from GPLv3 to AGPLv3 d754be5
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index 198ea58..c0f5745 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -3,18 +3,18 @@
#
# Copyright (C) 2020 Tiago de Paula Peixoto <tiago@skewed.de>
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+# details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU Affero General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
from .. import *
Add explicit attribution to descriptions obtained from ICON 1645314
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
index d2ab907..198ea58 100644
--- a/entries/stackoverflow/__init__.py
+++ b/entries/stackoverflow/__init__.py
@@ -19,7 +19,8 @@
from .. import *
title = "Stack Overflow favorites (2011)"
-description = "A bipartite network of users and the posts they have favorited, from the online Q&A site Stack Overflow. An edge (i,j) connects a user i to a post j if that user \"favorited\" that post (a kind of a rating). Edges are timestamped."
+description = """A bipartite network of users and the posts they have favorited, from the online Q&A site Stack Overflow. An edge (i,j) connects a user i to a post j if that user \"favorited\" that post (a kind of a rating). Edges are timestamped.[^icon]
+[^icon]: Description obtained from the [ICON](https://icon.colorado.edu) project."""
tags = ['Economic', 'Preferences', 'Unweighted', 'Timestamps']
url = 'http://konect.cc/networks/stackexchange-stackoverflow'
citation = [('J. Kunegis, "Stack overflow network dataset" KONECT, the Koblenz Network Collection (2016).', 'http://konect.cc/networks/stackexchange-stackoverflow')]
Initial commit cc85872
diff --git a/entries/stackoverflow/__init__.py b/entries/stackoverflow/__init__.py
new file mode 100644
index 0000000..d2ab907
--- /dev/null
+++ b/entries/stackoverflow/__init__.py
@@ -0,0 +1,48 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2020 Tiago de Paula Peixoto <tiago@skewed.de>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from .. import *
+
+title = "Stack Overflow favorites (2011)"
+description = "A bipartite network of users and the posts they have favorited, from the online Q&A site Stack Overflow. An edge (i,j) connects a user i to a post j if that user \"favorited\" that post (a kind of a rating). Edges are timestamped."
+tags = ['Economic', 'Preferences', 'Unweighted', 'Timestamps']
+url = 'http://konect.cc/networks/stackexchange-stackoverflow'
+citation = [('J. Kunegis, "Stack overflow network dataset" KONECT, the Koblenz Network Collection (2016).', 'http://konect.cc/networks/stackexchange-stackoverflow')]
+icon_hash = '583222868cf714f319c427a2'
+upstream_prefix = 'http://konect.cc/files'
+files = [('download.tsv.stackexchange-stackoverflow.tar.bz2', None, 'konect')]
+
+def fetch_upstream(force=False):
+ return fetch_upstream_files(__name__.split(".")[-1], upstream_prefix, files,
+ force)
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+ global files
+ name = __name__.split(".")[-1]
+ for fnames, alt, fmt in files:
+ if alts is not None and alt not in alts:
+ continue
+ if isinstance(fnames, str):
+ fnames = [fnames]
+ with ExitStack() as stack:
+ fs = [stack.enter_context(open_upstream_file(name, fn, "rb")) for fn in fnames]
+ g = parse_graph(fs, fmt, directed=False)
+ yield alt, g