~cedric/newspipe

952c06d16141f068287eaca9386d7bc6c43f0fbd — Cédric Bonhomme 5 months ago 304c416
Updated bootstrap to version 5.0.0-alpha1 and removed dependency to jQuery.
M newspipe/static/js/articles.js => newspipe/static/js/articles.js +126 -149
@@ 20,154 20,135 @@

API_ROOT = '/api/v2.0/'

if (typeof jQuery === 'undefined') { throw new Error('Requires jQuery') }

function change_unread_counter(feed_id, increment) {
    var new_value = parseInt($("#unread-"+feed_id).text()) + increment;
    $("#unread-"+feed_id).text(new_value);
    $("#total-unread").text(parseInt($("#total-unread").text()) + increment);
   console.log(document.getElementById("unread-"+feed_id));
   
    el = document.getElementById("unread-"+feed_id)
    if (el != null) {
      var new_value = parseInt(el.textContent) + increment;
      document.getElementById("unread-"+feed_id).textContent = new_value;
    }
   
    document.getElementById("total-unread").textContent = parseInt(document.getElementById("total-unread").textContent) + increment;

    if (new_value == 0) {
        $("#unread-"+feed_id).hide();
    } else {
        $("#unread-"+feed_id).show();
        document.getElementById("unread-"+feed_id).display = "none";
    }
}

+function ($) {

    // Mark an article as read when it is opened in a new table
    $('.open-article').on('click', function(e) {
      var feed_id = $(this).parent().parent().attr("data-feed");
      var filter = $('#filters').attr("data-filter");
      if (filter == "unread") {
        $(this).parent().parent().remove();
        change_unread_counter(feed_id, -1);
// Mark an article as read when it is opened in a new table
document.getElementsByClassName('open-article').onclick = function fun() {
  var feed_id = $(this).parentNode.parentNode.attr("data-feed");
  var filter = $('#filters').attr("data-filter");
  if (filter == "unread") {
    $(this).parentNode.parentNode.remove();
    change_unread_counter(feed_id, -1);
  }
};


// Mark an article as read or unread.
var nodes = document.getElementsByClassName('readed');
Array.prototype.map.call(nodes, function(node) {
    node.onclick = function() {
      var article_id = node.parentNode.parentNode.parentNode.getAttribute("data-article");
      var feed_id = node.parentNode.parentNode.parentNode.getAttribute("data-feed");
      var filter = document.getElementById('filters').getAttribute("data-filter");

      var data;
      if (node.classList.contains('fa-square-o')) {
          data = JSON.stringify({
              readed: false
          })
          if (filter == "read") {
              node.parentNode.parentNode.parentNode.remove();
          }
          else {
              // here, filter == "all"
              // node.parentNode.parentNode.parentNode.children("td:nth-child(2)").css( "font-weight", "bold" );
              node.classList.remove('fa-square-o');
              node.classList.add('fa-check-square-o');
          }
          change_unread_counter(feed_id, 1);
      }
      else {
          data = JSON.stringify({readed: true})
          if (filter == "unread") {
              node.parentNode.parentNode.parentNode.remove();
          }
          else {
              // here, filter == "all"
              // node.parentNode.parentNode.parentNode.children("td:nth-child(2)").css( "font-weight", "normal" );
              node.classList.remove('fa-check-square-o');
              node.classList.add('fa-square-o');
          }
          change_unread_counter(feed_id, -1);
      }
    });



    // Mark an article as read or unread.
    $('.readed').on('click', function() {
        var article_id = $(this).parent().parent().parent().attr("data-article");
        var feed_id = $(this).parent().parent().parent().attr("data-feed");
        var filter = $('#filters').attr("data-filter");

        var data;
        if ($(this).hasClass('fa-square-o')) {
            data = JSON.stringify({
                readed: false
                })
            if (filter == "read") {
                $(this).parent().parent().parent().remove();
            }
            else {
                // here, filter == "all"
                $(this).parent().parent().parent().children("td:nth-child(2)").css( "font-weight", "bold" );
                $(this).removeClass('fa-square-o').addClass('fa-check-square-o');
            }
            change_unread_counter(feed_id, 1);
        }
        else {
            data = JSON.stringify({readed: true})
            if (filter == "unread") {
                $(this).parent().parent().parent().remove();
            }
            else {
                // here, filter == "all"
                $(this).parent().parent().parent().children("td:nth-child(2)").css( "font-weight", "normal" );
                $(this).removeClass('fa-check-square-o').addClass('fa-square-o');
            }
            change_unread_counter(feed_id, -1);
        }

        // sends the updates to the server
        $.ajax({
            type: 'PUT',
            // Provide correct Content-Type, so that Flask will know how to process it.
            contentType: 'application/json',
            // Encode your data as JSON.
            data: data,
            // This is the type of data you're expecting back from the server.
            url: API_ROOT + "article/" + article_id,
            success: function (result) {
                //console.log(result);
            },
            error: function(XMLHttpRequest, textStatus, errorThrown){
                console.log(XMLHttpRequest.responseText);
            }
        });
    });



    // Like or unlike an article
    $('.like').on('click', function() {
        var article_id = $(this).parent().parent().parent().attr("data-article");
        var data;
        if ($(this).hasClass("fa-heart")) {
            data = JSON.stringify({like: false})
            $(this).removeClass('fa-heart').addClass('fa-heart-o');
            if(window.location.pathname.indexOf('/favorites') != -1) {
                $(this).parent().parent().parent().remove();
            }
        }
        else {
            data = JSON.stringify({like: true})
            $(this).removeClass('fa-heart-o').addClass('fa-heart');
        }
      // sends the updates to the server
      fetch(API_ROOT + "article/" + article_id, {
        method: "PUT", 
        headers: {
          'Content-Type': 'application/json',
        },
        body: data
      }).then(res => {
        console.log("Request complete! response:", res);
      }).catch((error) => {
        console.error('Error:', error);
      });;
    }
});



// Like or unlike an article
var nodes = document.getElementsByClassName('like');
Array.prototype.map.call(nodes, function(node) {
    node.onclick = function() {
      var article_id = node.parentNode.parentNode.parentNode.getAttribute('data-article');
      var data;
      if (node.classList.contains("fa-heart")) {
          data = JSON.stringify({like: false});
          node.classList.remove('fa-heart');
          node.classList.add('fa-heart-o');
          if(window.location.pathname.indexOf('/favorites') != -1) {
              node.parentNode.parentNode.parentNode.remove();
          }
      }
      else {
          data = JSON.stringify({like: true})
          node.classList.remove('fa-heart-o');
          node.classList.add('fa-heart');
      }

        // sends the updates to the server
        $.ajax({
            type: 'PUT',
            // Provide correct Content-Type, so that Flask will know how to process it.
            contentType: 'application/json',
            // Encode your data as JSON.
            data: data,
            // This is the type of data you're expecting back from the server.
            url: API_ROOT + "article/" + article_id,
            success: function (result) {
                //console.log(result);
            },
            error: function(XMLHttpRequest, textStatus, errorThrown){
                console.log(XMLHttpRequest.responseText);
            }
        });
    });



    // Delete an article
    $('.delete').on('click', function() {
        var r = confirm('You are going to delete this article.');

        if (r == true) {
            var feed_id = $(this).parent().parent().parent().attr("data-feed");
            var article_id = $(this).parent().parent().parent().attr("data-article");
            $(this).parent().parent().parent().remove();

            // sends the updates to the server
            $.ajax({
                type: 'DELETE',
                url: API_ROOT + "article/" + article_id,
                success: function (result) {
                    change_unread_counter(feed_id, -1);
                },
                error: function(XMLHttpRequest, textStatus, errorThrown){
                    console.log(XMLHttpRequest.responseText);
                }
            });
        }
    });
      // sends the updates to the server
      fetch(API_ROOT + "article/" + article_id, {
        method: "PUT", 
        headers: {
          'Content-Type': 'application/json',
        },
        body: data
      }).then(res => {
        console.log("Request complete! response:", res);
      }).catch((error) => {
        console.error('Error:', error);
      });;
    }
});



    // Delete all duplicate articles (used in the page /duplicates)
    $('.delete-all').click(function(){
    var nodes = document.getElementsByClassName('delete-all');
    Array.prototype.map.call(nodes, function(node) {
        node.onclick = function() {
        var data = [];

        var columnNo = $(this).parent().index();
        $(this).closest("table")
        var columnNo = node.parentNode.index();
        node.closest("table")
            .find("tr td:nth-child(" + (columnNo+1) + ")")
            .each(function(line, column) {
                data.push(parseInt(column.id));


@@ 176,20 157,16 @@ function change_unread_counter(feed_id, increment) {
        data = JSON.stringify(data);

        // sends the updates to the server
        $.ajax({
            type: 'DELETE',
            // Provide correct Content-Type, so that Flask will know how to process it.
            contentType: 'application/json',
            data: data,
            url: API_ROOT + "articles",
            success: function (result) {
                //console.log(result);
            },
            error: function(XMLHttpRequest, textStatus, errorThrown){
                console.log(XMLHttpRequest.responseText);
            }
        });

    });

}(jQuery);
        fetch(API_ROOT + "articles", {
          method: "DELETE", 
          headers: {
            'Content-Type': 'application/json',
          },
          body: data
        }).then(res => {
          console.log("Request complete! response:", res);
        }).catch((error) => {
          console.error('Error:', error);
        });;
      }
  });

M newspipe/static/js/feed.js => newspipe/static/js/feed.js +47 -37
@@ 1,46 1,56 @@
$('.container').on('click', '#add-feed-filter-row', function() {
    $('#filters-container').append(
        '<div class="form-group">'
        + '    <input value="-" type="button" class="form-control del-feed-filter-row" />'
        + '    <select name="type" class="form-control">'
        + '        <option value="simple match" selected>simple match</option>'
        + '        <option value="regex">regex</option>'
        + '    </select>'
        + '    <input type="text" class="form-control"  name="pattern" />'
        + '    <select name="action_on" class="form-control">'
        + '        <option value="match" selected>match</option>'
        + '        <option value="no match">no match</option>'
        + '    </select>'
        + '    <select name="action" class="form-control">'
        + '        <option value="mark as read" selected>mark as read</option>'
        + '        <option value="mark as favorite">mark as favorite</option>'
        + '    </select>'
        + '</div>');
});
$('.container').on('click', '.del-feed-filter-row', function() {
    $(this).parent().remove();
});
var node = document.getElementById('add-feed-filter-row');
if (node != null) {
  node.onclick = function() {
    document.getElementById('filters-container').innerHTML = 
          '<div class="form-group">'
          + '    <input value="-" type="button" class="form-control del-feed-filter-row" />'
          + '    <select name="type" class="form-control">'
          + '        <option value="simple match" selected>simple match</option>'
          + '        <option value="regex">regex</option>'
          + '    </select>'
          + '    <input type="text" class="form-control"  name="pattern" />'
          + '    <select name="action_on" class="form-control">'
          + '        <option value="match" selected>match</option>'
          + '        <option value="no match">no match</option>'
          + '    </select>'
          + '    <select name="action" class="form-control">'
          + '        <option value="mark as read" selected>mark as read</option>'
          + '        <option value="mark as favorite">mark as favorite</option>'
          + '    </select>'
          + '</div>';  
  }  
}

var nodes = document.getElementsByClassName('del-feed-filter-row');
Array.prototype.map.call(nodes, function(node) {
    node.onclick = function() {
      node.parentNode.remove();
    }
})


// Delete a feed
$('.delete-feed').on('click', function() {
var nodes = document.getElementsByClassName('delete-feed');
Array.prototype.map.call(nodes, function(node) {
    node.onclick = function() {
    var r = confirm('You are going to delete this feed.');

    if (r == true) {
        var feed_id = $(this).parent().parent().parent().attr("data-feed");
        $(this).parent().parent().parent().remove();
        $('.feed-menu[data-feed='+feed_id+']').remove();
        var feed_id = node.parentNode.parentNode.parentNode.getAttribute("data-feed");
        node.parentNode.parentNode.parentNode.remove();
        // $('.feed-menu[data-feed='+feed_id+']').remove();

        // sends the updates to the server
        $.ajax({
            type: 'DELETE',
            url: API_ROOT + "feed/" + feed_id,
            success: function (result) {
                // change_unread_counter(feed_id, -1);
            },
            error: function(XMLHttpRequest, textStatus, errorThrown){
                console.log(XMLHttpRequest.responseText);
            }
        });
        fetch(API_ROOT + "feed/" + feed_id, {
          method: "DELETE", 
          headers: {
            'Content-Type': 'application/json',
          },
        }).then(res => {
          console.log("Request complete! response:", res);
        }).catch((error) => {
          console.error('Error:', error);
        });;
    }
});
  }
})

M newspipe/templates/categories.html => newspipe/templates/categories.html +1 -1
@@ 1,7 1,7 @@
{% extends "layout.html" %}
{% block content %}
<div class="container">
    <h1>{{ _("You have %(categories)d categories &middot; Add a %(start_link)scategory%(end_link)s", categories=categories|count, start_link=("<a href='%s'>" % url_for("category.form"))|safe, end_link="</a>"|safe) }}</h1>
    <h1>{{ _("You have %(categories)d categories &middot; %(start_link)sAdd%(end_link)s a category", categories=categories|count, start_link=("<a href='%s'>" % url_for("category.form"))|safe, end_link="</a>"|safe) }}</h1>
    {% if categories|count == 0 %}
        <h1>{{_("No category")}}</h1>
    {% else %}

M newspipe/templates/layout.html => newspipe/templates/layout.html +2 -6
@@ 10,22 10,17 @@
    <link rel="shortcut icon" href="{{ url_for("static", filename="img/favicon.ico") }}" />
    <!-- CSS -->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/bootstrap/dist/css/bootstrap.min.css') }}" media="screen" />
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/bootstrap-select/dist/css/bootstrap-select.css') }}" />
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/datatables.net-bs4/css/dataTables.bootstrap4.min.css') }}" />
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/fork-awesome/css/fork-awesome.min.css') }}" />
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/custom.css') }}" />
    <!-- JavaScript -->
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/jquery/dist/jquery.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/popper.js/dist/umd/popper.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/bootstrap/dist/js/bootstrap.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/bootstrap-select/dist/js/bootstrap-select.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/datatables.net/js/jquery.dataTables.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/datatables.net-bs4/js/dataTables.bootstrap4.min.js') }}"></script>
    {% endblock %}
  </head>
  <body>
    {% block menu %}
    <nav class="navbar navbar-expand-lg navbar-dark bg-newspipe-blue">
       <div class="container-fluid">
        <a class="navbar-brand" href="/">🗞&nbsp;Newspipe</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>


@@ 135,6 130,7 @@
            </ul>
        </div>
        {% endblock %}
      </div>
    </nav>
    {% endblock %}
    <br />

M newspipe/templates/management.html => newspipe/templates/management.html +9 -9
@@ 68,17 68,17 @@
                    <p>{{ _('Export feeds to OPML.') }}</p>
                    <form class="form" action="{{ url_for('feeds.export') }}" method="GET" id="formExportOPML">
                        <div class="form-group">
                            <div class="input-group">
                                <label>Include disabled feeds</label>
                                <input type="checkbox" class="form-control" name="includedisabled" checked />
                            <div class="form-check">
                              <input type="checkbox" class="form-check-input" name="includedisabled" checked />
                              <label class="form-check-label">Include disabled feeds</label>
                            </div>
                            <div class="input-group">
                                <label title="Newspipe encountered too much problems when retrieving these feeds.">Include dead feeds</label>
                                <input type="checkbox" class="form-control" name="includeexceedederrorcount" checked />
                            <div class="form-check">
                              <input type="checkbox" class="form-check-input" name="includeexceedederrorcount" checked />
                              <label class="form-check-label" title="Newspipe encountered too much problems when retrieving these feeds.">Include dead feeds</label>
                            </div>
                            <div class="input-group">
                                <label>Include private feeds</label>
                                <input type="checkbox" class="form-control" name="includeprivate" checked />
                            <div class="form-check">
                              <input type="checkbox" class="form-check-input" name="includeprivate" checked />
                              <label class="form-check-label">Include private feeds</label>  
                            </div>
                        </div>
                        <button class="btn btn-primary" type="submit">{{ _('Export') }}</button>

M package-lock.json => package-lock.json +6 -46
@@ 5,59 5,19 @@
  "requires": true,
  "dependencies": {
    "bootstrap": {
      "version": "4.5.0",
      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
      "integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA=="
    },
    "bootstrap-select": {
      "version": "1.13.17",
      "resolved": "https://registry.npmjs.org/bootstrap-select/-/bootstrap-select-1.13.17.tgz",
      "integrity": "sha512-LbzSQumoZNYGuoYMpShKIHbJ2qUWFLI0qVHVeKqw5+nfhtMxz+Gre1+IuI3X74bTzQfalBqDKc8fS8tZMdciWg=="
    },
    "datatables": {
      "version": "1.10.18",
      "resolved": "https://registry.npmjs.org/datatables/-/datatables-1.10.18.tgz",
      "integrity": "sha512-ntatMgS9NN6UMpwbmO+QkYJuKlVeMA2Mi0Gu/QxyIh+dW7ZjLSDhPT2tWlzjpIWEkDYgieDzS9Nu7bdQCW0sbQ==",
      "requires": {
        "jquery": ">=1.7"
      }
    },
    "datatables.net": {
      "version": "1.10.21",
      "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.21.tgz",
      "integrity": "sha512-/bSZtxmf3GTpYcvEmwZ8q26I1yhSx8qklR2B+s1K8+/51UW/zc2zTYwJMqr/Z+iCYixAc00ildj4g2x0Qamolw==",
      "requires": {
        "jquery": ">=1.7"
      }
    },
    "datatables.net-bs4": {
      "version": "1.10.21",
      "resolved": "https://registry.npmjs.org/datatables.net-bs4/-/datatables.net-bs4-1.10.21.tgz",
      "integrity": "sha512-F9zabYw8ZLXfjvj2S+BdnbkEUsL48bJwWxQFrA47cOXrIvsMhW8nmqPZcIMK4ko3k1i74nbpWLO1t+vueQKoXQ==",
      "requires": {
        "datatables.net": "1.10.21",
        "jquery": ">=1.7"
      }
      "version": "5.0.0-alpha1",
      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0-alpha1.tgz",
      "integrity": "sha512-iwKneP2pLXl8lN0YpnOuOARiNPTzmh/4cw+Un86u4OqrMLuQpyMC7nO07hvivvcg0B/ektJPjuPnS1s+YmRK9A=="
    },
    "fork-awesome": {
      "version": "1.1.7",
      "resolved": "https://registry.npmjs.org/fork-awesome/-/fork-awesome-1.1.7.tgz",
      "integrity": "sha512-IHI7XCSXrKfUIWslse8c/PaaVDT1oBaYge+ju40ihL2ooiQeBpTr4wvIXhgTd2NuhntlvX+M5jYHAPTzNlmv0g=="
    },
    "jquery": {
      "version": "3.5.1",
      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
      "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
    },
    "lodash": {
      "version": "4.17.15",
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
    },
    "moment": {
      "version": "2.26.0",
      "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz",
      "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw=="
      "version": "2.27.0",
      "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
      "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
    },
    "popper.js": {
      "version": "1.16.1",

M package.json => package.json +2 -7
@@ 4,14 4,9 @@
  "license": "AGPL-3.0",
  "private": true,
  "dependencies": {
    "bootstrap": "^4.5.0",
    "bootstrap-select": "^1.13.17",
    "datatables": "^1.10.18",
    "datatables.net-bs4": "^1.10.21",
    "bootstrap": "^5.0.0-alpha1",
    "fork-awesome": "^1.1.7",
    "jquery": "^3.5.1",
    "lodash": "^4.17.15",
    "moment": "^2.26.0",
    "moment": "^2.27.0",
    "popper.js": "^1.16.1"
  },
  "engines": {

M poetry.lock => poetry.lock +9 -9
@@ 100,7 100,7 @@ description = "Python package for providing Mozilla's CA Bundle."
name = "certifi"
optional = false
python-versions = "*"
version = "2020.4.5.2"
version = "2020.6.20"

[[package]]
category = "main"


@@ 132,7 132,7 @@ description = "the modular source code checker: pep8 pyflakes and co"
name = "flake8"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
version = "3.8.2"
version = "3.8.3"

[package.dependencies]
mccabe = ">=0.6.0,<0.7.0"


@@ 443,7 443,7 @@ description = "Python HTTP for Humans."
name = "requests"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.23.0"
version = "2.24.0"

[package.dependencies]
certifi = ">=2017.4.17"


@@ 614,8 614,8 @@ blinker = [
    {file = "blinker-1.4.tar.gz", hash = "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"},
]
certifi = [
    {file = "certifi-2020.4.5.2-py2.py3-none-any.whl", hash = "sha256:9cd41137dc19af6a5e03b630eefe7d1f458d964d406342dd3edf625839b944cc"},
    {file = "certifi-2020.4.5.2.tar.gz", hash = "sha256:5ad7e9a056d25ffa5082862e36f119f7f7cec6457fa07ee2f8c339814b80c9b1"},
    {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"},
    {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"},
]
chardet = [
    {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},


@@ 631,8 631,8 @@ feedparser = [
    {file = "feedparser-5.2.1.zip", hash = "sha256:cd2485472e41471632ed3029d44033ee420ad0b57111db95c240c9160a85831c"},
]
flake8 = [
    {file = "flake8-3.8.2-py2.py3-none-any.whl", hash = "sha256:ccaa799ef9893cebe69fdfefed76865aeaefbb94cb8545617b2298786a4de9a5"},
    {file = "flake8-3.8.2.tar.gz", hash = "sha256:c69ac1668e434d37a2d2880b3ca9aafd54b3a10a3ac1ab101d22f29e29cf8634"},
    {file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"},
    {file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"},
]
flask = [
    {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"},


@@ 843,8 843,8 @@ pytz = [
    {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"},
]
requests = [
    {file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"},
    {file = "requests-2.23.0.tar.gz", hash = "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"},
    {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
    {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
]
requests-futures = [
    {file = "requests-futures-1.0.0.tar.gz", hash = "sha256:35547502bf1958044716a03a2f47092a89efe8f9789ab0c4c528d9c9c30bc148"},