diff --git a/admin/base/settings/defaults.py b/admin/base/settings/defaults.py
index f0647492f67..579b920949e 100644
--- a/admin/base/settings/defaults.py
+++ b/admin/base/settings/defaults.py
@@ -199,22 +199,6 @@
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
NOSE_ARGS = ['--verbosity=2']
-# Keen.io settings in local.py
-KEEN_PROJECT_ID = osf_settings.KEEN['private']['project_id']
-KEEN_READ_KEY = osf_settings.KEEN['private']['read_key']
-KEEN_WRITE_KEY = osf_settings.KEEN['private']['write_key']
-
-KEEN_CREDENTIALS = {
- 'keen_ready': False
-}
-
-if KEEN_CREDENTIALS['keen_ready']:
- KEEN_CREDENTIALS.update({
- 'keen_project_id': KEEN_PROJECT_ID,
- 'keen_read_key': KEEN_READ_KEY,
- 'keen_write_key': KEEN_WRITE_KEY
- })
-
# Set in local.py
DESK_KEY = ''
diff --git a/admin/metrics/views.py b/admin/metrics/views.py
index 3628f8278e4..4255181c7ff 100644
--- a/admin/metrics/views.py
+++ b/admin/metrics/views.py
@@ -2,7 +2,6 @@
from django.contrib.auth.mixins import PermissionRequiredMixin
from admin.base import settings
-from admin.base.settings import KEEN_CREDENTIALS
class MetricsView(PermissionRequiredMixin, TemplateView):
@@ -11,7 +10,6 @@ class MetricsView(PermissionRequiredMixin, TemplateView):
raise_exception = True
def get_context_data(self, **kwargs):
- kwargs.update(KEEN_CREDENTIALS.copy())
api_report_url = f'{settings.API_DOMAIN}_/metrics/reports/'
kwargs.update({'metrics_url': api_report_url})
return super().get_context_data(**kwargs)
diff --git a/admin/nodes/views.py b/admin/nodes/views.py
index 2318778e369..fc16a3b0d05 100644
--- a/admin/nodes/views.py
+++ b/admin/nodes/views.py
@@ -643,7 +643,6 @@ def post(self, request, *args, **kwargs):
node = self.get_object()
node.is_public = False
- node.keenio_read_key = ''
# After set permissions callback
for addon in node.get_addons():
diff --git a/admin/preprints/views.py b/admin/preprints/views.py
index 14427df42f2..a936c27582e 100644
--- a/admin/preprints/views.py
+++ b/admin/preprints/views.py
@@ -18,6 +18,7 @@
from admin.preprints.forms import ChangeProviderForm, MachineStateForm
from api.share.utils import update_share
+from api.providers.workflows import Workflows
from osf.exceptions import PreprintStateError
@@ -43,6 +44,7 @@
)
from osf.utils.workflows import DefaultStates
from website import search
+from website.preprints.tasks import on_preprint_updated
class PreprintMixin(PermissionRequiredMixin):
@@ -558,7 +560,15 @@ class PreprintMakePublishedView(PreprintMixin, View):
def post(self, request, *args, **kwargs):
preprint = self.get_object()
- preprint.set_published(True, request, True)
+ preprint.set_published(
+ published=True,
+ auth=request,
+ save=True,
+ ignore_permission=True
+ )
+ if preprint.provider and preprint.provider.reviews_workflow == Workflows.POST_MODERATION.value:
+ on_preprint_updated.apply_async(kwargs={'preprint_id': preprint._id})
+
return redirect(self.get_success_url())
class PreprintUnwithdrawView(PreprintMixin, View):
diff --git a/admin/static/css/keen-dashboards.css b/admin/static/css/keen-dashboards.css
old mode 100755
new mode 100644
index b8e7a507681..2b6b8a01b32
--- a/admin/static/css/keen-dashboards.css
+++ b/admin/static/css/keen-dashboards.css
@@ -113,8 +113,4 @@ body.application .return-link:hover,
.hr-menu {
background-color: lightgray;
height: 1px;
-}
-
-#reload-node-logs {
- margin-left: 20px;
-}
+}
\ No newline at end of file
diff --git a/admin/static/js/metrics/metrics.es6.js b/admin/static/js/metrics/metrics.es6.js
index 42da8ffd8ac..10d6281646a 100644
--- a/admin/static/js/metrics/metrics.es6.js
+++ b/admin/static/js/metrics/metrics.es6.js
@@ -5,20 +5,9 @@ require('keen-dataviz/dist/keen-dataviz.min.css');
var c3 = require('c3/c3.js');
-var keen = require('keen-js');
var keenDataviz = require('keen-dataviz');
-var keenAnalysis = require('keen-analysis');
var $ = require('jquery');
-var client = new keenAnalysis({
- projectId: keenProjectId,
- readKey: keenReadKey
-});
-
-var publicClient = new keenAnalysis({
- projectId: keenPublicProjectId,
- readKey: keenPublicReadKey
-});
// Heights and Colors for the below rendered
// Keen and c3 Data visualizations
@@ -423,67 +412,6 @@ var getMetricTitle = function(metric, type) {
return title;
};
-var renderKeenMetric = function(element, type, query, height, colors, keenClient) {
- if (!keenClient) {
- keenClient = client;
- }
-
- var chart = new keenDataviz()
- .el(element)
- .height(height)
- .title(' ')
- .type(type)
- .prepare();
-
- if (colors) {
- chart.colors([colors]);
- }
-
- keenClient
- .run(query)
- .then(function(res){
- var metricChart = chart.data(res);
- metricChart.dataset.sortRows("desc", function(row) {
- return row[1]
- });
- metricChart.render();
- })
- .catch(function(err){
- chart.message(err.message);
- });
-};
-
-var renderNodeLogsForOneUserChart = function(user_id) {
- var chart = new keenDataviz()
- .el('#yesterdays-node-logs-by-user')
- .height(bigMetricHeight)
- .title('Individual Logs for ' + '' + user_id + '')
- .type('line')
- .prepare();
-
- client
- .query('count', {
- event_collection: "node_log_events",
- interval: "hourly",
- group_by: "action",
- filters: [{
- property_name: 'user_id',
- operator: 'eq',
- property_value: user_id
- }],
- timeframe: "previous_1_days",
- timezone: "UTC"
- })
- .then(function(res){
- chart
- .data(res)
- .render();
- })
- .catch(function(err){
- chart.message(err.message);
- });
-};
-
// called from pageview collection
var differenceGrowthBetweenMetrics = function(query1, query2, totalQuery, element, colors) {
@@ -837,46 +765,6 @@ var renderEmailDomainsChart = function() {
});
};
-var NodeLogsPerUser = function() {
- var chart = new keenDataviz()
- .el('#yesterdays-node-logs-by-user')
- .title(' ')
- .height(bigMetricHeight)
- .chartOptions({
- data: {
- onclick: function (d, element) {
- renderNodeLogsForOneUserChart(d.name);
- }
- }
- })
-
- .type('line')
- .prepare();
-
- client
- .query('count', {
- event_collection: 'node_log_events',
- group_by: "user_id",
- timeframe: 'previous_1_days',
- interval: 'hourly'
- })
- .then(function (res) {
- var chartWithData = chart.data(res);
- chartWithData.dataset.filterColumns(function (column, index) {
- var logThreshhold = 25;
- for (var i = 0; i < column.length; i++) {
- if (column[i] > logThreshhold && column[0] != 'null' && column[0] != 'uj57r') {
- return column;
- }
- }
- });
-
- chartWithData.render();
- })
- .catch(function (err) {
- chart.message(err.message);
- });
-};
var renderRawNodeMetrics = function() {
var propertiesAndElements = [
@@ -966,7 +854,6 @@ var UserGainMetrics = function() {
renderMainCounts();
renderWeeklyUserGainMetrics();
renderEmailDomainsChart();
- NodeLogsPerUser();
};
@@ -1084,36 +971,6 @@ function renderPreprintMetrics(timeframe) {
// ><+><+><+<+><+
var DownloadMetrics = function() {
-
- // ********** legacy keen **********
- var totalDownloadsQuery = new keenAnalysis.Query("count", {
- eventCollection: "file_stats",
- timeframe: 'previous_1_days',
- filters: [{
- property_name: 'action.type',
- operator: 'eq',
- property_value: 'download_file',
- timezone: "UTC"
- }]
- });
- renderKeenMetric("#number-of-downloads", "metric", totalDownloadsQuery,
- defaultHeight, defaultColor, publicClient);
-
- // ********** legacy keen **********
- var uniqueDownloadsQuery = new keenAnalysis.Query("count_unique", {
- eventCollection: "file_stats",
- timeframe: 'previous_1_days',
- target_property: 'file.resource',
- filters: [{
- property_name: 'action.type',
- operator: 'eq',
- property_value: 'download_file',
- timezone: "UTC"
- }]
- });
- renderKeenMetric("#number-of-unique-downloads", "metric", uniqueDownloadsQuery,
- defaultHeight, defaultColor, publicClient);
-
renderDownloadMetrics();
};
@@ -1138,13 +995,11 @@ function renderDownloadMetrics(timeframe) {
module.exports = {
UserGainMetrics: UserGainMetrics,
- NodeLogsPerUser: NodeLogsPerUser,
InstitutionMetrics: InstitutionMetrics,
RawNumberMetrics: RawNumberMetrics,
AddonMetrics: AddonMetrics,
PreprintMetrics: PreprintMetrics,
DownloadMetrics: DownloadMetrics,
- KeenRenderMetrics: renderKeenMetric,
RenderPreprintMetrics: renderPreprintMetrics,
RenderDownloadMetrics: renderDownloadMetrics,
};
diff --git a/admin/static/js/pages/metrics-page.js b/admin/static/js/pages/metrics-page.js
index dedd46e0570..50b8d3c1049 100644
--- a/admin/static/js/pages/metrics-page.js
+++ b/admin/static/js/pages/metrics-page.js
@@ -1,19 +1,13 @@
'use strict';
var $ = require('jquery');
-var keenAnalysis = require('keen-analysis');
var Metrics = require('js/metrics/metrics');
-keenAnalysis.ready(function() {
-
+$( document ).ready(function() {
Metrics.UserGainMetrics();
- $('#reload-node-logs')[0].onclick = function() {
- Metrics.NodeLogsPerUser();
- };
-
$('#institution-tab')[0].onclick = function() {
Metrics.InstitutionMetrics();
};
diff --git a/admin/static/js/sales_analytics/dashboard.js b/admin/static/js/sales_analytics/dashboard.js
deleted file mode 100644
index 19eb217c27d..00000000000
--- a/admin/static/js/sales_analytics/dashboard.js
+++ /dev/null
@@ -1,409 +0,0 @@
-require('bootstrap');
-require('jquery');
-require('c3/c3.css');
-
-var c3 = require('c3/c3.js');
-var keen = require('keen-js');
-var ss = require('simple-statistics');
-
-var SalesAnalytics = function() {
- // Metrics and visualization for the analytics dashboard.
- var self = this;
-
- self.keenClient = new keen({
- projectId: keenProjectId,
- readKey : keenReadKey
- });
-
- self.keenFilters = {
- nullUserFilter: {
- property_name: 'user.id',
- operator: 'ne',
- property_value: null
- },
- inactiveUserFilter: {
- property_name: 'user.id',
- operator: 'ne',
- property_value: ''
- }
- };
- self.getAverageUserSessionLength = function () {
- var query = new keen.Query('select_unique', {
- event_collection: 'pageviews',
- timeframe: 'previous_7_days',
- target_property: 'keen.timestamp',
- group_by: ['user.id', 'sessionId'],
- filters: [self.keenFilters.nullUserFilter]
- });
-
- var chart = self.prepareChart('keen-chart-average-session-user');
-
- self.keenClient.run(query, function(error, response) {
- if (error) {
- document.getElementById('keen-chart-average-session-user').innerHTML = 'Keen Query Failure';
- console.log(error);
- }
- else {
- var dataSet = self.extractDataSet(response);
- var result = ss.mean(dataSet);
- self.drawChart(chart, 'metric', 'Minutes', result/1000/60);
- }
- });
- };
-
- self.getAverageMAUSessionLength = function() {
- var query = new keen.Query('select_unique', {
- event_collection: 'pageviews',
- timeframe: 'previous_7_days',
- target_property: 'keen.timestamp',
- group_by: ['user.id', 'sessionId'],
- filters: [self.keenFilters.nullUserFilter, self.keenFilters.inactiveUserFilter]
- });
-
- var chart = self.prepareChart('keen-chart-average-session-mau');
-
- self.keenClient.run(query, function(error, response) {
- if (error) {
- document.getElementById('keen-chart-average-session-mau').innerHTML = 'Keen Query Failure.';
- console.log(error);
- }
- else {
- var dataSet = self.extractDataSet(response);
- var result = ss.mean(dataSet);
- self.drawChart(chart, 'metric', 'Minutes', result/1000/60);
- }
- });
- };
-
- self.getAverageUserSessionHistory = function(init, numberOfWeeks, weekEnd, weekStart, weeklyResult) {
- if (init === true) {
- var date = new Date();
- date.setHours(0, 0, 0, 0);
- date.setDate(date.getDate() - date.getDay());
- weekEnd = new Date(date);
- weekStart = new Date(date.setDate(date.getDate() - 7));
- weeklyResult = [];
- self.getAverageUserSessionHistory(false, numberOfWeeks, weekEnd, weekStart, weeklyResult);
- return;
- }
-
- if (numberOfWeeks === 0) {
- var chart = self.prepareChart('keen-chart-average-session-history');
- chart.attributes({title: 'Average User/MAU Session Length History of Past 12 Weeks', width: '100%', height: 450});
- chart.adapter({chartType: 'columnchart'});
- chart.chartOptions({
- hAxis: {title: 'Week'},
- vAxis: {title: 'Minutes'}
- });
- chart.parseRawData({result: weeklyResult}).render();
- return;
- }
-
- document.getElementById('keen-chart-average-session-history').innerHTML = 'loading ... : ' + (12 - numberOfWeeks) + ' / 12';
- var queryUser = new keen.Query('select_unique', {
- event_collection: 'pageviews',
- timeframe: {
- start: weekStart.toISOString(),
- end: weekEnd.toISOString()
- },
- target_property: 'keen.timestamp',
- group_by: ['user.id', 'sessionId'],
- filters: [self.keenFilters.nullUserFilter]
- });
- var queryMAU = new keen.Query('select_unique', {
- event_collection: 'pageviews',
- timeframe: {
- start: weekStart.toISOString(),
- end: weekEnd.toISOString()
- },
- target_property: 'keen.timestamp',
- group_by: ['user.id', 'sessionId'],
- filters: [self.keenFilters.nullUserFilter, self.keenFilters.inactiveUserFilter]
- });
-
- self.keenClient.run([queryUser, queryMAU], function (error, response) {
- var resultUser;
- var resultMAU;
- if (error) {
- document.getElementById('keen-chart-average-session-history').innerHTML = 'failure ... : ' + (12 - numberOfWeeks) + ' / 12';
- console.log(error);
- resultUser = resultMAU = -1;
- }
- else {
- var dataSetUser = self.extractDataSet(response[0]);
- var dataSetMAU = self.extractDataSet(response[1]);
- resultUser = ss.mean(dataSetUser);
- resultMAU = ss.mean(dataSetMAU);
- }
- var item = {
- timeframe: {
- start: weekStart.toISOString(),
- end: weekEnd.toISOString()
- },
- value: [
- { category: 'User', result: resultUser/1000/60 },
- { category: 'MAU', result: resultMAU/1000/60 }
- ]
- };
- weeklyResult.push(item);
-
- weekEnd = new Date(weekStart);
- weekStart.setDate(weekEnd.getDate() - 7);
- numberOfWeeks--;
- self.getAverageUserSessionHistory(false, numberOfWeeks, weekEnd, weekStart, weeklyResult);
- });
- };
-
- self.getOSFProductUsage = function() {
- var query = new Keen.Query('select_unique', {
- event_collection: 'pageviews',
- timeframe: 'previous_1_months',
- target_property: 'parsedPageUrl.path',
- group_by: ['user.id', 'parsedPageUrl.domain'],
- filters: [self.keenFilters.nullUserFilter, self.keenFilters.inactiveUserFilter]
- });
-
- var chartMoreThanTwo = self.prepareChart('keen-chart-osf-product-usage-mt2');
- var chartMeetings = self.prepareChart('keen-chart-osf-product-usage-mee');
- var chartInstitutions = self.prepareChart('keen-chart-osf-product-usage-ins');
-
- var request = self.keenClient.run(query, function(error, response) {
- if (error) {
- document.getElementById('keen-chart-osf-product-usage-mt2').innerHTML = 'Keen Query Failure';
- }
- else {
- var userProductMap = self.numberOfUsers(response);
- self.drawChart(chartMoreThanTwo, 'piechart', '', [
- {products: 'OSF Only', count: userProductMap.osf.length - userProductMap.moreThanTwo.length},
- {products: '2+ Products', count: userProductMap.moreThanTwo.length}
- ]);
- self.drawChart(chartMeetings, 'piechart', '', [
- {products: 'No Meetings', count: userProductMap.osf.length - userProductMap.meetings.length},
- {products: 'Meetings', count: userProductMap.meetings.length}
- ]);
- self.drawChart(chartInstitutions, 'piechart', '', [
- {products: 'No Institutions', count: userProductMap.osf.length - userProductMap.institutions.length},
- {products: 'Institutions', count: userProductMap.institutions.length}
- ]);
- }
- });
- };
- self.getUserCount = function(userCount) {
- // User count for each peroduct (osf, osf4m, institution)
- var chart = c3.generate({
- bindto: '#db-chart-user-count',
- data: {
- columns: [['Count'].concat(userCount.count)],
- type: 'bar',
- colors: {
- Count: '#ff5a5f',
- }
- },
- axis: {
- x: {
- type: 'category',
- categories: userCount.tags,
- },
- rotated: true
- },
- bar: {
- width: {
- ratio: 0.5 // this makes bar width 50% of length between ticks
- }
- }
- });
- };
-
- self.getUserPercentage = function(userCount) {
- var chart = c3.generate({
- bindto: '#db-chart-user-percent',
- data: {
- columns: [['Percentage'].concat(userCount.percent)],
- type: 'bar',
- colors: {
- Count: '#ff8083',
- }
- },
- axis: {
- x: {
- type: 'category',
- categories: userCount.tags,
- },
- rotated: true
- },
- bar: {
- width: {
- ratio: 0.5 // this makes bar width 50% of length between ticks
- }
- }
- });
- };
-
- self.getMultiProductCountYearly = function(multiProductMetricsYearly) {
- // Number of users that use 2+ products
- var chart = self.drawMetric('db-chart-multi-product-yearly',
- multiProductMetricsYearly.multi_product_count,
- '#e57fc2',
- 'Users');
- };
-
- self.getMultiProductCountMonthly = function(multiProductMetricsMonthly) {
- var chart = self.drawMetric('db-chart-multi-product-monthly',
- multiProductMetricsMonthly.multi_product_count,
- '#e57fc2',
- 'Users');
- };
-
- self.getCrossProductCountYearly = function(multiProductMetricsYearly) {
- // Number of users that use a product different from their entry points.
- var chart = self.drawMetric('db-chart-cross-product-yearly',
- multiProductMetricsYearly.cross_product_count,
- '#6ab975',
- 'Users');
- };
-
- self.getCrossProductCountMonthly = function(multiProductMetricsMonthly) {
- var chart = self.drawMetric('db-chart-cross-product-monthly',
- multiProductMetricsMonthly.cross_product_count,
- '#6ab975',
- 'Users');
- };
-
- self.getMultiActionCountYearly = function(multiProductMetricsYearly) {
- // Number of users that have more than one type of action.
- var chart = self.drawMetric('db-chart-multi-action-yearly',
- multiProductMetricsYearly.multi_action_count,
- '#4C72B7',
- 'Users');
- };
-
- self.getMultiActionCountMonthly = function(multiProductMetricsMonthly) {
- var chart = self.drawMetric('db-chart-multi-action-monthly',
- multiProductMetricsMonthly.multi_action_count,
- '#4C72B7',
- 'Users');
- };
-
- self.getRepeatActionCountMonthly = function(repeatActionUserMonthly) {
- var chart = self.drawMetric('db-chart-repeat-action-monthly',
- repeatActionUserMonthly.repeat_action_count,
- '#b75c4c',
- 'Users');
- };
-
- self.getTotalUserCount = function(userCount) {
- var chart = self.drawMetric('db-chart-total-user',
- userCount.total,
- '#005c66',
- 'Users');
- };
-
- self.prepareChart = function(elementId) {
- var chart = new keen.Dataviz();
- return chart.el(document.getElementById(elementId)).prepare();
- };
-
- self.drawChart = function(chart, type, title, result, color) {
- chart.attributes({title: title, width: '100%'});
- chart.adapter({chartType: type});
- chart.parseRawData({result: result});
- chart.render();
- };
-
- self.drawMetric = function(elementId, result, color, title) {
- var chart = new keen.Dataviz()
- .el(document.getElementById(elementId))
- .parseRawData({ result: result })
- .chartType('metric')
- .colors([color])
- .title(title)
- .render();
- };
-
- self.init = function() {
- console.log('init');
- keen.ready(self.run());
- };
-
- self.run = function() {
- console.log('run');
- self.getOSFProductUsage();
- self.getAverageUserSessionLength();
- self.getAverageMAUSessionLength();
- self.getAverageUserSessionHistory(true, 12, null, null, null);
- self.getUserCount(userCount);
- self.getUserPercentage(userCount);
- self.getMultiProductCountYearly(multiProductMetricsYearly);
- self.getMultiProductCountMonthly(multiProductMetricsMonthly);
- self.getCrossProductCountYearly(multiProductMetricsYearly);
- self.getCrossProductCountMonthly(multiProductMetricsMonthly);
- self.getMultiActionCountYearly(multiProductMetricsYearly);
- self.getMultiActionCountMonthly(multiProductMetricsMonthly);
- self.getRepeatActionCountMonthly(repeatActionUserMonthly);
- self.getTotalUserCount(userCount);
- };
-
- self.extractDataSet = function(keenResult) {
- if (!keenResult) {
- return 0;
- }
-
- var beginTime;
- var endTime;
- var deltaSet = [];
-
- for (var i in keenResult.result) {
- var session = keenResult.result[i];
- if (session.hasOwnProperty('result')) {
- if (session.result.length === 1) {
- // TODO: take care of the situation where there is only one 'keen.timestamp'
- continue;
- }
- beginTime = Date.parse(session.result[0]);
- endTime = Date.parse(session.result[session.result.length-1]);
- deltaSet.push(endTime - beginTime);
- }
- }
- return deltaSet;
- };
-
- self.numberOfUsers = function(keenResult, filters) {
- var userProductMap = {
- 'osf': [],
- 'meetings': [],
- 'institutions': [],
- 'moreThanTwo': []
- };
- for (var i in keenResult.result) {
- var session = keenResult.result[i];
- if (session.hasOwnProperty('result') && session.hasOwnProperty('user.id')) {
- if (session.hasOwnProperty('parsedPageUrl.domain') && session['parsedPageUrl.domain'] === 'staging.osf.io') {
- userProductMap.osf.push(session['user.id']);
- var paths = session.result;
- var numberOfProducts = 0;
- var meetings, institutions;
- meetings = institutions = false;
- for (var j in paths) {
- if (meetings === false && paths[j].startsWith('/meetings/')) {
- userProductMap.meetings.push(session['user.id']);
- meetings = true;
- numberOfProducts ++;
- }
- else if (institutions === false && paths[j].startsWith('/institutions/')) {
- userProductMap.institutions.push(session['user.id']);
- meetings = true;
- numberOfProducts ++;
- }
- }
- if (numberOfProducts > 0) {
- userProductMap.moreThanTwo.push(session['user.id']);
- }
- }
- }
- }
- return userProductMap;
- };
-};
-
-var salesAnalytics = new SalesAnalytics();
-salesAnalytics.init();
diff --git a/admin/templates/base_analytics.html b/admin/templates/base_analytics.html
deleted file mode 100644
index cc89be8c59e..00000000000
--- a/admin/templates/base_analytics.html
+++ /dev/null
@@ -1,38 +0,0 @@
-{% extends 'base.html' %}
-{% load static %}
-
-{% block top_includes %}
-
-
- {% block top_analytics %}
- {% endblock top_analytics %}
-
-{% endblock top_includes %}
-
-{% block title %}
-
The last day of node logs by user, if a user had over 100 node logs.
diff --git a/admin/templates/sales_analytics/dashboard.html b/admin/templates/sales_analytics/dashboard.html
deleted file mode 100644
index f3218b47a15..00000000000
--- a/admin/templates/sales_analytics/dashboard.html
+++ /dev/null
@@ -1,223 +0,0 @@
-{% extends 'base_analytics.html' %}
-{% load static %}
-{% load filters %}
-
-{% block top_analytics %}
-
-{% endblock top_analytics %}
-
-
-{% block title_analytics %}
-
-
-
-
-
-
-
-
- Average User/MAU Session Length History of past 12 weeks
-
-
-
- Notes about this chart
-
-
-
-
-
-
-
-
- User Count by Product
-
-
-
- User count/percentage for each peroduct (osf, osf4m, institution)
-
-
-
-
-
-
-
-
-
-
- OSF Products Users View in the Past Month
-
-
-
- Notes about this chart
-
-
-
-
-
-
- OSF Products Users View in the Past Month
-
-
-
- Notes about this chart
-
-
-
-
-
-
-
-
- Average User Session Length of Previous 7 days
-
-
-
-
-
- Average MAU Session Length of Previous 7 days
-
-
-
-
-
-
-
- Multi-product User Count (Year)
-
-
-
-
-
- Multi-product User Count (Month)
-
-
-
- Number of users that use 2+ products
-
-
-
-
-
-
- Cross-product User Count (Year)
-
-
-
-
-
- Cross-product User Count (Month)
-
-
-
- Number of users that use a product other than their entry points.
-
-
-
-
-
-
- Multi-action User Count (Year)
-
-
-
-
-
- Multi-action User Count (Month)
-
-
-
- Number of users that have more than one type of actions.
-
-
-
-
-
-
- Repetitive actions User Count (Month)
-
-
-
-
-
- Total User Count
-
-
-
- Total number of Users
-
-
-
-
-
-
-
-
-{% endblock content_analytics %}
-
-{% block bottom_js %}
-
-{% endblock %}
diff --git a/admin/webpack.admin.config.js b/admin/webpack.admin.config.js
index e9987f2ed9d..be4bdb6f53d 100644
--- a/admin/webpack.admin.config.js
+++ b/admin/webpack.admin.config.js
@@ -32,7 +32,6 @@ var config = Object.assign({}, common, {
entry: {
'admin-base-page': staticAdminPath('js/pages/base-page.js'),
'admin-registration-edit-page': staticAdminPath('js/pages/admin-registration-edit-page.js'),
- 'dashboard': staticAdminPath('js/sales_analytics/dashboard.js'),
'metrics-page': staticAdminPath('js/pages/metrics-page.js'),
'banners': staticAdminPath('js/banners/banners.js'),
'brands': staticAdminPath('js/brands/brands.js'),
diff --git a/admin_tests/preprints/test_views.py b/admin_tests/preprints/test_views.py
index c2f8ac40e6a..1fb9d68482d 100644
--- a/admin_tests/preprints/test_views.py
+++ b/admin_tests/preprints/test_views.py
@@ -21,6 +21,7 @@
from osf.models.admin_log_entry import AdminLogEntry
from osf.models.spam import SpamStatus
from osf.utils.workflows import DefaultStates, RequestTypes
+from osf.utils.permissions import ADMIN
from admin_tests.utilities import setup_view, setup_log_view, handle_post_view_request
@@ -768,3 +769,31 @@ def test_no_permission_raises_error(self, req, preprint):
request.user = req.user
with pytest.raises(PermissionDenied):
views.PreprintMachineStateView.as_view()(request, guid=preprint._id)
+
+
+@pytest.mark.urls('admin.base.urls')
+class TestPreprintMakePublishedView:
+
+ @pytest.fixture()
+ def plain_view(self):
+ return views.PreprintMakePublishedView
+
+ def test_admin_user_can_publish_preprint(self, user, preprint, plain_view):
+ admin_group = Group.objects.get(name='osf_admin')
+ preprint.is_published = False
+ preprint.save()
+
+ # user isn't admin contributor in the preprint
+ assert preprint.contributors.filter(id=user.id).exists() is False
+ assert preprint.has_permission(user, ADMIN) is False
+
+ request = RequestFactory().post(reverse('preprints:make-published', kwargs={'guid': preprint._id}))
+ request.user = user
+
+ admin_group.permissions.add(Permission.objects.get(codename='change_node'))
+ user.groups.add(admin_group)
+
+ plain_view.as_view()(request, guid=preprint._id)
+ preprint.reload()
+
+ assert preprint.is_published
diff --git a/api/collections/views.py b/api/collections/views.py
index 53d29fb9113..5935194e5b5 100644
--- a/api/collections/views.py
+++ b/api/collections/views.py
@@ -626,7 +626,6 @@ class LinkedRegistrationsList(BaseLinkedList, CollectionMixin):
pending_withdrawal boolean is this registration pending withdrawal?
pending_withdrawal_approval boolean is this registration pending approval?
pending_embargo_approval boolean is the associated Embargo awaiting approval by project admins?
- registered_meta dictionary registration supplementary information
registration_supplement string registration template
##Links
diff --git a/api/nodes/serializers.py b/api/nodes/serializers.py
index 879eff63d49..1c3632a3d52 100644
--- a/api/nodes/serializers.py
+++ b/api/nodes/serializers.py
@@ -274,7 +274,8 @@ class NodeSerializer(TaxonomizableSerializerMixin, JSONAPISerializer):
tags = ValuesListField(attr_name='name', child=ser.CharField(), required=False)
access_requests_enabled = ShowIfVersion(ser.BooleanField(read_only=False, required=False), min_version='2.0', max_version='2.8')
node_license = NodeLicenseSerializer(required=False, source='license')
- analytics_key = ShowIfAdminScopeOrAnonymous(ser.CharField(read_only=True, source='keenio_read_key'))
+ # 'analytics_key' can be removed after another version release.
+ analytics_key = ShowIfAdminScopeOrAnonymous(ser.CharField(read_only=True, default=''))
template_from = ser.CharField(
required=False, allow_blank=False, allow_null=False,
help_text='Specify a node id for a node you would like to use as a template for the '
@@ -1488,7 +1489,6 @@ class DraftRegistrationLegacySerializer(JSONAPISerializer):
id = IDField(source='_id', read_only=True)
type = TypeField()
# Will be eventually deprecated in favor of registration_responses
- registration_metadata = ser.DictField(required=False)
registration_responses = ser.DictField(required=False)
datetime_initiated = VersionedDateTimeField(read_only=True)
datetime_updated = VersionedDateTimeField(read_only=True)
@@ -1547,35 +1547,21 @@ def update_registration_responses(self, draft, registration_responses, required_
draft.update_registration_responses(registration_responses)
draft.save()
- def enforce_metadata_or_registration_responses(self, metadata=None, registration_responses=None):
- if metadata and registration_responses:
- raise exceptions.ValidationError(
- 'You cannot include both `registration_metadata` and `registration_responses` in your request. Please use' +
- ' `registration_responses` as `registration_metadata` will be deprecated in the future.',
- )
-
def get_node(self, validated_data=None):
return self.context['view'].get_node()
def create(self, validated_data):
initiator = get_user_auth(self.context['request']).user
node = self.get_node(validated_data)
- # Old workflow - deeply nested
- metadata = validated_data.pop('registration_metadata', None)
registration_responses = validated_data.pop('registration_responses', None)
schema = validated_data.pop('registration_schema')
provider = validated_data.pop('provider', None)
- self.enforce_metadata_or_registration_responses(metadata, registration_responses)
-
try:
draft = DraftRegistration.create_from_node(node=node, user=initiator, schema=schema, provider=provider)
except ValidationError as e:
raise exceptions.ValidationError(e.message)
- if metadata:
- self.update_metadata(draft, metadata)
-
if registration_responses:
self.update_registration_responses(draft, registration_responses)
@@ -1617,8 +1603,6 @@ def update(self, draft, validated_data):
metadata = validated_data.pop('registration_metadata', None)
registration_responses = validated_data.pop('registration_responses', None)
- self.enforce_metadata_or_registration_responses(metadata, registration_responses)
-
if metadata:
self.update_metadata(draft, metadata)
if registration_responses:
diff --git a/api/nodes/views.py b/api/nodes/views.py
index 7bc3ad929da..3048099ae91 100644
--- a/api/nodes/views.py
+++ b/api/nodes/views.py
@@ -2109,7 +2109,6 @@ class NodeLinkedRegistrationsList(BaseLinkedList, NodeMixin):
pending_withdrawal boolean is this registration pending withdrawal?
pending_withdrawal_approval boolean is this registration pending approval?
pending_embargo_approval boolean is the associated Embargo awaiting approval by project admins?
- registered_meta dictionary registration supplementary information
registration_supplement string registration template
##Links
diff --git a/api/preprints/serializers.py b/api/preprints/serializers.py
index 399d7bca343..2e802a438a0 100644
--- a/api/preprints/serializers.py
+++ b/api/preprints/serializers.py
@@ -49,6 +49,7 @@
NodeLicense,
)
from osf.utils import permissions as osf_permissions
+from osf.utils.workflows import DefaultStates
class PrimaryFileRelationshipField(RelationshipField):
@@ -625,6 +626,20 @@ class PreprintContributorDetailSerializer(NodeContributorDetailSerializer, Prepr
id = IDField(required=True, source='_id')
index = ser.IntegerField(required=False, read_only=False, source='_order')
+ def validate_permission(self, value):
+ preprint = self.context.get('resource')
+ user = self.context.get('user')
+ if (
+ user # if user is None then probably we're trying to make bulk update and this validation is not relevant
+ and preprint.machine_state == DefaultStates.INITIAL.value
+ and preprint.creator_id == user.id
+ ):
+ raise ValidationError(
+ 'You cannot change your permission setting at this time. '
+ 'Have another admin contributor edit your permission after you’ve submitted your preprint',
+ )
+ return value
+
class PreprintStorageProviderSerializer(NodeStorageProviderSerializer):
node = HideIfPreprint(ser.CharField(source='node_id', read_only=True))
diff --git a/api/preprints/views.py b/api/preprints/views.py
index 4b7d9b6c7f0..af1cb3af974 100644
--- a/api/preprints/views.py
+++ b/api/preprints/views.py
@@ -77,10 +77,10 @@ class PreprintOldVersionsImmutableMixin:
@staticmethod
def is_edit_allowed(preprint):
- if preprint.is_latest_version or preprint.machine_state == 'initial':
+ if preprint.is_latest_version or preprint.machine_state == DefaultStates.INITIAL.value:
return True
if preprint.provider.reviews_workflow == Workflows.PRE_MODERATION.value:
- if preprint.machine_state == 'pending' or preprint.machine_state == 'rejected':
+ if preprint.machine_state == DefaultStates.PENDING.value or preprint.machine_state == DefaultStates.REJECTED.value:
return True
return False
@@ -535,19 +535,10 @@ def get_object(self):
def get_serializer_context(self):
context = JSONAPIBaseView.get_serializer_context(self)
context['resource'] = self.get_preprint()
+ context['user'] = self.get_user()
context['default_email'] = 'preprint'
return context
- def patch(self, *args, **kwargs):
- preprint = self.get_resource()
- user = self.get_user()
- if preprint.machine_state == DefaultStates.INITIAL.value and preprint.creator_id == user.id:
- raise ValidationError(
- 'You cannot change your permission setting at this time. '
- 'Have another admin contributor edit your permission after you’ve submitted your preprint',
- )
- return super().patch(*args, **kwargs)
-
def perform_destroy(self, instance):
preprint = self.get_resource()
auth = get_user_auth(self.request)
diff --git a/api/registrations/serializers.py b/api/registrations/serializers.py
index 0e3542131fe..202a19f1233 100644
--- a/api/registrations/serializers.py
+++ b/api/registrations/serializers.py
@@ -1,5 +1,4 @@
import pytz
-import json
from website.archiver.utils import normalize_unicode_filenames
from packaging.version import Version
@@ -20,7 +19,7 @@
NodeLicenseRelationshipField,
NodeLinksSerializer,
NodeLicenseSerializer,
- NodeContributorsSerializer,
+ NodeContributorDetailSerializer,
NodeContributorsCreateSerializer,
RegistrationProviderRelationshipField,
get_license_details,
@@ -36,7 +35,6 @@
from framework.auth.core import Auth
from osf.exceptions import NodeStateError
from osf.models import Node
-from osf.utils.registrations import strip_registered_meta_comments
from osf.utils.workflows import ApprovalStates
class RegistrationSerializer(NodeSerializer):
@@ -62,7 +60,6 @@ class RegistrationSerializer(NodeSerializer):
'original_response',
'provider',
'provider_specific_metadata',
- 'registered_meta',
'registration_responses',
'registration_schema',
'registration_supplement',
@@ -185,12 +182,6 @@ class RegistrationSerializer(NodeSerializer):
has_supplements = HideIfWithdrawal(ser.BooleanField(read_only=True, required=False))
registration_supplement = ser.SerializerMethodField()
- # Will be deprecated in favor of registration_responses
- registered_meta = HideIfWithdrawal(
- ser.SerializerMethodField(
- help_text='A dictionary with supplemental registration questions and responses.',
- ),
- )
registration_responses = HideIfWithdrawal(
ser.SerializerMethodField(
help_text='A dictionary with supplemental registration questions and responses.',
@@ -510,17 +501,6 @@ def get_has_project(self, obj):
def get_absolute_url(self, obj):
return obj.get_absolute_url()
- def get_registered_meta(self, obj):
- if obj.registered_meta:
- meta_values = self.anonymize_registered_meta(obj)
- try:
- return json.loads(meta_values)
- except TypeError:
- return meta_values
- except ValueError:
- return meta_values
- return None
-
def get_registration_responses(self, obj):
latest_approved_response = obj.root.schema_responses.filter(
reviews_state=ApprovalStates.APPROVED.db_name,
@@ -572,15 +552,6 @@ def get_latest_response_id(self, obj):
return latest_approved._id
return None
- def anonymize_registered_meta(self, obj):
- """
- Looks at every question on every page of the schema, for any titles
- that have a contributor-input block type. If present, deletes that question's response
- from meta_values.
- """
- cleaned_registered_meta = strip_registered_meta_comments(list(obj.registered_meta.values())[0])
- return self.anonymize_fields(obj, cleaned_registered_meta)
-
def anonymize_registration_responses(self, obj):
"""
For any questions that have a `contributor-input` block type, delete
@@ -594,7 +565,7 @@ def anonymize_registration_responses(self, obj):
def anonymize_fields(self, obj, data):
"""
Consolidates logic to anonymize fields with contributor information
- on both registered_meta and registration_responses
+ on registration_responses
"""
if is_anonymized(self.context['request']):
anonymous_registration_response_keys = obj.get_contributor_registration_response_keys()
@@ -908,7 +879,7 @@ def get_absolute_url(self, obj):
)
-class RegistrationContributorsSerializer(NodeContributorsSerializer):
+class RegistrationContributorsSerializer(NodeContributorDetailSerializer):
def get_absolute_url(self, obj):
return absolute_reverse(
'registrations:registration-contributor-detail',
diff --git a/api/registrations/views.py b/api/registrations/views.py
index 29305d23ce2..66eca12294b 100644
--- a/api/registrations/views.py
+++ b/api/registrations/views.py
@@ -280,6 +280,8 @@ class RegistrationContributorsList(BaseContributorList, mixins.CreateModelMixin,
required_read_scopes = [CoreScopes.NODE_REGISTRATIONS_READ]
required_write_scopes = [CoreScopes.NODE_REGISTRATIONS_WRITE]
+ ordering = ('_order',)
+
permission_classes = (
ContributorDetailPermissions,
drf_permissions.IsAuthenticatedOrReadOnly,
@@ -800,7 +802,6 @@ class RegistrationLinkedRegistrationsList(NodeLinkedRegistrationsList, Registrat
pending_withdrawal boolean is this registration pending withdrawal?
pending_withdrawal_approval boolean is this registration pending approval?
pending_embargo_approval boolean is the associated Embargo awaiting approval by project admins?
- registered_meta dictionary registration supplementary information
registration_supplement string registration template
##Links
diff --git a/api/search/views.py b/api/search/views.py
index 2af2afd79a3..ebf7027ef2f 100644
--- a/api/search/views.py
+++ b/api/search/views.py
@@ -498,7 +498,6 @@ class SearchRegistrations(BaseSearchView):
pending_withdrawal boolean is this registration pending withdrawal?
pending_withdrawal_approval boolean is this registration pending approval?
pending_embargo_approval boolean is the associated Embargo awaiting approval by project admins?
- registered_meta dictionary registration supplementary information
registration_supplement string registration template
diff --git a/api/sparse/serializers.py b/api/sparse/serializers.py
index a085b7344ec..6d08bc97f02 100644
--- a/api/sparse/serializers.py
+++ b/api/sparse/serializers.py
@@ -167,7 +167,6 @@ def parse_sparse_fields(self):
'pending_registration_approval',
'pending_withdrawal',
'public',
- 'registered_meta',
'registration_schema',
'root',
'tags',
diff --git a/api_tests/collections/test_views.py b/api_tests/collections/test_views.py
index 3f7d414663f..025060cc4ee 100644
--- a/api_tests/collections/test_views.py
+++ b/api_tests/collections/test_views.py
@@ -21,6 +21,7 @@
from osf.utils.permissions import ADMIN, WRITE, READ
from website.project.signals import contributor_removed
from api_tests.utils import disconnected_from_listeners
+from api_tests.share._utils import mock_update_share
from website.views import find_bookmark_collection
@@ -4340,6 +4341,18 @@ def test_with_permissions(self, app, collection_with_three_collection_submission
assert len(res.json['data']) == 0
assert res.status_code == 200
+ def test_update_share_called(self, app, collection_with_zero_collection_submission, user_one, project_four, url, payload, subject_one):
+ collection_with_zero_collection_submission.is_public = True
+ collection_with_zero_collection_submission.save()
+
+ with mock_update_share() as _shmock:
+ res = app.post_json_api(
+ url.format(collection_with_zero_collection_submission._id),
+ payload(guid=project_four._id, subjects=[[subject_one._id]]),
+ auth=user_one.auth)
+ assert res.status_code == 201
+ _shmock.assert_called()
+
def test_filters(self, app, collection_with_one_collection_submission, collection_with_three_collection_submission, project_two, project_four, user_one, subject_one, url, payload):
res = app.get(f'{url.format(collection_with_three_collection_submission._id)}?filter[id]={project_two._id}', auth=user_one.auth)
assert res.status_code == 200
diff --git a/api_tests/nodes/serializers/test_serializers.py b/api_tests/nodes/serializers/test_serializers.py
index 1d6f7a14da7..d139652796b 100644
--- a/api_tests/nodes/serializers/test_serializers.py
+++ b/api_tests/nodes/serializers/test_serializers.py
@@ -48,7 +48,6 @@ def test_node_serializer(self, user):
assert attributes['registration'] == node.is_registration
assert attributes['fork'] == node.is_fork
assert attributes['collection'] == node.is_collection
- assert attributes['analytics_key'] == node.keenio_read_key
assert attributes['wiki_enabled'] == node.has_addon('wiki')
# Relationships
diff --git a/api_tests/preprints/views/test_preprint_contributors_detail.py b/api_tests/preprints/views/test_preprint_contributors_detail.py
index 170c75a99f8..16c05911d23 100644
--- a/api_tests/preprints/views/test_preprint_contributors_detail.py
+++ b/api_tests/preprints/views/test_preprint_contributors_detail.py
@@ -1022,6 +1022,33 @@ def test_patch_permission_only(self, app, user, preprint):
assert preprint.get_permissions(user_read_contrib) == [permissions.READ]
assert not preprint.get_visible(user_read_contrib)
+ def test_patch_admin_self_initial_preprint(self, app, user, preprint):
+ new_version = PreprintFactory.create_version(
+ create_from=preprint,
+ creator=user,
+ final_machine_state='initial',
+ is_published=False,
+ set_doi=False
+ )
+
+ contrib_id = f'{new_version._id}-{user._id}'
+ data = {
+ 'data': {
+ 'id': contrib_id,
+ 'type': 'contributors',
+ 'attributes': {
+ 'permission': permissions.WRITE,
+ 'bibliographic': True
+ }
+ }
+ }
+ url = f'/{API_BASE}preprints/{new_version._id}/contributors/{user._id}/'
+ res = app.patch_json_api(url, data, auth=user.auth, expect_errors=True)
+ assert res.status_code == 400
+
+ new_version.reload()
+ assert permissions.ADMIN in new_version.get_permissions(user)
+
@pytest.mark.django_db
class TestPreprintContributorDelete:
@@ -1190,6 +1217,28 @@ def test_remove_self_contributor_not_unique_admin(
preprint.reload()
assert user not in preprint.contributors
+ def test_remove_self_creator_not_unique_admin_initial_preprint(
+ self, app, user, user_write_contrib, preprint):
+ new_version = PreprintFactory.create_version(
+ create_from=preprint,
+ creator=user,
+ final_machine_state='initial',
+ is_published=False,
+ set_doi=False
+ )
+
+ new_version.add_permission(
+ user_write_contrib,
+ permissions.ADMIN,
+ save=True
+ )
+ assert len(new_version.contributors) == 2
+ assert new_version.creator_id == user.id
+
+ url = f'/{API_BASE}preprints/{new_version._id}/contributors/{user._id}/'
+ res = app.delete(url, auth=user.auth, expect_errors=True)
+ assert res.status_code == 400
+
# @assert_logs(PreprintLog.CONTRIB_REMOVED, 'preprint')
def test_can_remove_self_as_contributor_not_unique_admin(
self, app, user_write_contrib, preprint, url_user_write_contrib):
diff --git a/api_tests/registrations/views/test_registration_contributor_detail.py b/api_tests/registrations/views/test_registration_contributor_detail.py
new file mode 100644
index 00000000000..37ff567ab8d
--- /dev/null
+++ b/api_tests/registrations/views/test_registration_contributor_detail.py
@@ -0,0 +1,111 @@
+from tests.base import ApiTestCase
+
+from osf_tests.factories import (
+ ProjectFactory,
+ RegistrationFactory,
+ AuthUserFactory,
+)
+from api.base.settings.defaults import API_BASE
+
+
+class TestRegistrationContributorDetailTestCase(ApiTestCase):
+
+ def setUp(self):
+ super().setUp()
+ self.creator = AuthUserFactory()
+ self.read_contributor = AuthUserFactory()
+ self.write_contributor = AuthUserFactory()
+ self.public_project = ProjectFactory(
+ title='Public Project',
+ is_public=True,
+ creator=self.creator
+ )
+ self.public_registration = RegistrationFactory(project=self.public_project, creator=self.creator)
+
+ self.creator_id = f'{self.public_registration._id}-{self.creator._id}'
+ self.read_contributor_id = f'{self.public_registration._id}-{self.read_contributor._id}'
+ self.write_contributor_id = f'{self.public_registration._id}-{self.write_contributor._id}'
+
+ self.public_url = f'/{API_BASE}registrations/{self.public_registration._id}/'
+ self.add_contributor_url = f'/{API_BASE}registrations/{self.public_registration._id}/contributors/'
+ self.edit_contributor_url = f'/{API_BASE}registrations/{self.public_registration._id}/contributors/'
+
+ def form_contributors_create_payload(self, permission, contributor):
+ return {
+ 'data': {
+ 'type': 'contributors',
+ 'attributes': {
+ 'permission': permission,
+ 'bibliographic': True
+ },
+ 'relationships': {
+ 'users': {
+ 'data': {
+ 'type': 'users',
+ 'id': contributor._id
+ }
+ }
+ }
+ }
+ }
+
+ def form_contributor_edit_payload(self, contributor, index):
+ return {
+ 'data': {
+ 'id': f'{self.public_registration._id}-{contributor._id}',
+ 'attributes': {
+ 'index': index
+ },
+ 'relationships': {},
+ 'type': 'contributors'
+ }
+ }
+
+ def test_initial_contributors_ordering_is_correct(self):
+ read_payload = self.form_contributors_create_payload('read', self.read_contributor)
+ write_payload = self.form_contributors_create_payload('write', self.write_contributor)
+ self.app.post_json_api(
+ self.add_contributor_url,
+ read_payload,
+ auth=self.creator.auth
+ )
+ self.app.post_json_api(
+ self.add_contributor_url,
+ write_payload,
+ auth=self.creator.auth
+ )
+
+ contributors = self.app.get(self.add_contributor_url, auth=self.creator.auth)
+ # shift by 1 because creator is the first contributor
+ assert contributors.json['data'][1]['id'] == self.read_contributor_id
+ assert contributors.json['data'][2]['id'] == self.write_contributor_id
+
+ def test_contributors_reordering_works_correctly(self):
+ read_payload = self.form_contributors_create_payload('read', self.read_contributor)
+ write_payload = self.form_contributors_create_payload('write', self.write_contributor)
+ self.app.post_json_api(
+ self.add_contributor_url,
+ read_payload,
+ auth=self.creator.auth
+ )
+ self.app.post_json_api(
+ self.add_contributor_url,
+ write_payload,
+ auth=self.creator.auth
+ )
+
+ self.app.patch_json_api(
+ self.edit_contributor_url + f'{self.creator._id}/',
+ self.form_contributor_edit_payload(self.creator, 2),
+ auth=self.creator.auth
+ )
+ self.app.patch_json_api(
+ self.edit_contributor_url + f'{self.read_contributor._id}/',
+ self.form_contributor_edit_payload(self.read_contributor, 0),
+ auth=self.creator.auth
+ )
+
+ contributors = self.app.get(self.add_contributor_url, auth=self.creator.auth)
+ assert contributors.json['data'][0]['id'] == self.read_contributor_id
+ assert contributors.json['data'][1]['id'] == self.write_contributor_id
+ assert contributors.json['data'][2]['id'] == self.creator_id
diff --git a/api_tests/registrations/views/test_registration_list.py b/api_tests/registrations/views/test_registration_list.py
index 894c448b5b4..d095ea70b4c 100644
--- a/api_tests/registrations/views/test_registration_list.py
+++ b/api_tests/registrations/views/test_registration_list.py
@@ -27,7 +27,6 @@
InstitutionFactory,
)
from osf_tests.utils import get_default_test_schema
-from osf_tests.test_registrations import prereg_registration_responses
from rest_framework import exceptions
from tests.base import ApiTestCase
from website import settings
@@ -38,6 +37,77 @@
SCHEMA_VERSION = 2
+prereg_registration_responses = {
+ 'q12.uploader': [],
+ 'q7.question': 'data collection procedures',
+ 'q20': 'transforming and recoding',
+ 'q21': 'research plan follow up',
+ 'q22': 'criteria',
+ 'q23': 'this is how outliers will be handled',
+ 'q24': 'this is how I will deal with incomplete data.',
+ 'q25': 'this is my exploratory analysis',
+ 'q26': [],
+ 'q27': 'No additional comments',
+ 'q12.question': 'these are my measured variables',
+ 'q1': 'This is my title',
+ 'q3': 'research questions',
+ 'q5': 'Registration prior to creation of data',
+ 'q4': 'this is my hypothesis',
+ 'q6': 'Explanation of existing data',
+ 'q9': 'this is the rationale for my sample size',
+ 'q8': 'this is my sample size',
+ 'q13.question': 'these are my indices',
+ 'q19.uploader': [],
+ 'q11.uploader': [
+ {
+ 'file_name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
+ 'file_id': '5d6d22264d476c088fb8e01f',
+ 'file_urls': {
+ 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22264d476c088fb8e01f'),
+ },
+ 'file_hashes': {
+ 'sha256': 'sdf',
+ },
+ },
+ {
+ 'file_name': 'Alphabet.txt',
+ 'file_id': '5d6d22274d476c088fb8e021',
+ 'file_urls': {
+ 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021'),
+ },
+ 'file_hashes': {
+ 'sha256': 'dsdfds',
+ },
+ }
+ ],
+ 'q16.question': 'this is my study design',
+ 'q15': [
+ 'No blinding is involved in this study.',
+ 'For studies that involve human subjects, they will not know the treatment group to which they have been assigned.',
+ 'Research personnel who interact directly with the study subjects (either human or non-human subjects) will not be aware of the assigned treatments.'
+ ],
+ 'q14': '',
+ 'q17': 'this is my explanation of randomization',
+ 'q10': 'this is my stopping rule',
+ 'q11.question': 'these are my maniuplated variables',
+ 'q16.uploader': [],
+ 'q19.question': 'ANOVA',
+ 'q13.uploader': [],
+ 'q7.uploader': [
+ {
+ 'file_name': 'Alphabet.txt',
+ 'file_id': '5d6d22274d476c088fb8e021',
+ 'file_urls': {
+ 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021'),
+ },
+ 'file_hashes': {
+ 'sha256': 'dsdfds',
+ },
+ }
+ ]
+}
+
+
class TestRegistrationList(ApiTestCase):
def setUp(self):
diff --git a/api_tests/registrations/views/test_view_only_query_parameter.py b/api_tests/registrations/views/test_view_only_query_parameter.py
index 2d13ebe26e0..565fdc305c6 100644
--- a/api_tests/registrations/views/test_view_only_query_parameter.py
+++ b/api_tests/registrations/views/test_view_only_query_parameter.py
@@ -159,23 +159,11 @@ def registration_schema(self):
@pytest.fixture()
def reg_report(self, registration_schema, admin):
- registration = RegistrationFactory(schema=registration_schema, creator=admin, is_public=False)
- registration.registered_meta[registration_schema._id] = {
- 'q1': {
- 'comments': [],
- 'extra': [],
- 'value': 'This is the answer to a question'
- },
- 'q2': {
- 'comments': [],
- 'extra': [],
- 'value': 'Grapes McGee'
- }
-
- }
- registration.registration_responses = registration.flatten_registration_metadata()
- registration.save()
- return registration
+ return RegistrationFactory(
+ schema=registration_schema,
+ creator=admin,
+ is_public=False
+ )
@pytest.fixture()
def reg_report_anonymous_link(self, reg_report):
@@ -189,9 +177,6 @@ def test_author_questions_are_anonymous(self, app, base_url, reg_report, admin,
url = f'/{API_BASE}registrations/{reg_report._id}/'
res = app.get(url, auth=admin.auth)
assert res.status_code == 200
- meta = res.json['data']['attributes']['registered_meta']
- assert 'q1' in meta
- assert 'q2' in meta
reg_responses = res.json['data']['attributes']['registration_responses']
assert 'q1' in reg_responses
@@ -202,9 +187,6 @@ def test_author_questions_are_anonymous(self, app, base_url, reg_report, admin,
'view_only': reg_report_anonymous_link.key
})
assert res.status_code == 200
- meta = res.json['data']['attributes']['registered_meta']
- assert 'q1' in meta
- assert 'q2' not in meta
reg_responses = res.json['data']['attributes']['registration_responses']
assert 'q1' in reg_responses
diff --git a/api_tests/registrations/views/test_withdrawn_registrations.py b/api_tests/registrations/views/test_withdrawn_registrations.py
index 9cf582e7889..7308f3d4207 100644
--- a/api_tests/registrations/views/test_withdrawn_registrations.py
+++ b/api_tests/registrations/views/test_withdrawn_registrations.py
@@ -174,7 +174,6 @@ def test_withdrawn_registrations_display_limited_attributes_fields(
'pending_registration_approval': None,
'pending_embargo_approval': None,
'embargo_end_date': None,
- 'registered_meta': None,
'current_user_permissions': None,
'registration_supplement': registration.registered_schema.first().name}
diff --git a/api_tests/tokens/views/test_token_detail.py b/api_tests/tokens/views/test_token_detail.py
index 0f441efc108..c46a0dd6f8a 100644
--- a/api_tests/tokens/views/test_token_detail.py
+++ b/api_tests/tokens/views/test_token_detail.py
@@ -2,6 +2,7 @@
import pytest
from api.scopes.serializers import SCOPES_RELATIONSHIP_VERSION
+from framework.auth.cas import CasResponse
from osf_tests.factories import (
ApiOAuth2PersonalTokenFactory,
ApiOAuth2ScopeFactory,
@@ -10,36 +11,28 @@
from tests.base import assert_dict_contains_subset
from website.util import api_v2_url
+
@pytest.mark.django_db
-def post_payload(
- type_payload='tokens',
- scopes=None,
- name='A shiny updated token'):
- if not scopes:
- scopes = ApiOAuth2ScopeFactory().name
- return {
- 'data': {
- 'type': type_payload,
- 'attributes': {
- 'name': name,
- },
- 'relationships': {
- 'scopes': {
- 'data': [
- {
- 'type': 'scopes',
- 'id': scopes
- }
- ]
+class TestTokenDetailScopesAsAttributes:
+ def post_attributes_payload(self, type_payload='tokens', scopes='osf.full_write', name='A shiny updated token'):
+ return {
+ 'data': {
+ 'type': type_payload,
+ 'attributes': {
+ 'name': name,
+ 'scopes': scopes,
}
}
}
- }
+ @pytest.fixture()
+ def write_scope(self):
+ return ApiOAuth2ScopeFactory(name='osf.full_write')
-@pytest.mark.django_db
-class TestTokenDetailScopesAsRelationships:
+ @pytest.fixture()
+ def read_scope(self):
+ return ApiOAuth2ScopeFactory(name='osf.full_read')
@pytest.fixture()
def user_one(self):
@@ -53,46 +46,85 @@ def user_two(self):
def token_user_one(self, user_one):
return ApiOAuth2PersonalTokenFactory(owner=user_one)
+ @pytest.fixture()
+ def token_full_write(self, user_one, write_scope):
+ token = ApiOAuth2PersonalTokenFactory(
+ owner=user_one,
+ )
+ token.scopes.add(write_scope)
+ return token
+
@pytest.fixture()
def url_token_detail(self, user_one, token_user_one):
path = f'tokens/{token_user_one._id}/?version={SCOPES_RELATIONSHIP_VERSION}'
return api_v2_url(path, base_route='/')
@pytest.fixture()
- def url_token_list(self):
- return api_v2_url(f'tokens/?version={SCOPES_RELATIONSHIP_VERSION}', base_route='/')
+ def url_token_detail_full_write(self, user_one, token_full_write):
+ path = f'tokens/{token_full_write._id}/'
+ return api_v2_url(path, base_route='/')
@pytest.fixture()
- def read_scope(self):
- return ApiOAuth2ScopeFactory()
-
- def test_token_detail_who_can_view(
- self, app, url_token_detail, user_one,
- user_two, token_user_one):
+ def url_token_list(self):
+ return api_v2_url('tokens/', base_route='/')
- # test_owner_can_view
+ def test_owner_can_view(
+ self,
+ app,
+ url_token_detail,
+ user_one,
+ user_two,
+ token_user_one
+ ):
res = app.get(url_token_detail, auth=user_one.auth)
assert res.status_code == 200
assert res.json['data']['id'] == token_user_one._id
- # test_non_owner_cant_view
+ def test_non_owner_cant_view(
+ self,
+ app,
+ url_token_detail,
+ user_one,
+ user_two,
+ token_user_one
+ ):
res = app.get(url_token_detail, auth=user_two.auth, expect_errors=True)
assert res.status_code == 403
- # test_returns_401_when_not_logged_in
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_returns_401_when_not_logged_in(
+ self,
+ mock_method,
+ app,
+ url_token_detail,
+ user_one,
+ user_two,
+ token_user_one
+ ):
+ mock_method.return_value(True)
res = app.get(url_token_detail, expect_errors=True)
assert res.status_code == 401
@mock.patch('framework.auth.cas.CasClient.revoke_tokens')
def test_owner_can_delete(
- self, mock_method, app, user_one, url_token_detail):
+ self,
+ mock_method,
+ app,
+ user_one,
+ url_token_detail
+ ):
mock_method.return_value(True)
res = app.delete(url_token_detail, auth=user_one.auth)
assert res.status_code == 204
@mock.patch('framework.auth.cas.CasClient.revoke_tokens')
def test_deleting_tokens_makes_api_view_inaccessible(
- self, mock_method, app, url_token_detail, user_one):
+ self,
+ mock_method,
+ app,
+ url_token_detail,
+ user_one
+ ):
mock_method.return_value(True)
app.delete(url_token_detail, auth=user_one.auth)
res = app.get(url_token_detail, auth=user_one.auth, expect_errors=True)
@@ -100,9 +132,16 @@ def test_deleting_tokens_makes_api_view_inaccessible(
@mock.patch('framework.auth.cas.CasClient.revoke_tokens')
def test_updating_one_field_should_not_blank_others_on_patch_update(
- self, mock_revoke, app, token_user_one, url_token_detail, user_one, read_scope):
- mock_revoke.return_value = True
- user_one_token = token_user_one
+ self,
+ mock_method,
+ app,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ write_scope,
+ read_scope
+ ):
+ mock_method.return_value(True)
new_name = 'The token formerly known as Prince'
res = app.patch_json_api(
url_token_detail,
@@ -126,499 +165,354 @@ def test_updating_one_field_should_not_blank_others_on_patch_update(
}
}
},
- auth=user_one.auth)
- user_one_token.reload()
+ auth=user_one.auth
+ )
+ token_user_one.reload()
assert res.status_code == 200
-
assert_dict_contains_subset(
{
'name': new_name,
},
res.json['data']['attributes'])
- assert res.json['data']['id'] == user_one_token._id
- assert res.json['data']['relationships']['owner']['data']['id'] == user_one_token.owner._id
+ assert res.json['data']['id'] == token_user_one._id
+ assert res.json['data']['relationships']['owner']['data']['id'] == token_user_one.owner._id
assert len(res.json['data']['embeds']['scopes']['data']) == 1
assert res.json['data']['embeds']['scopes']['data'][0]['id'] == read_scope.name
@mock.patch('framework.auth.cas.CasClient.revoke_tokens')
def test_updating_an_instance_does_not_change_the_number_of_instances(
- self, mock_revoke, app, url_token_detail, url_token_list, token_user_one, user_one, read_scope):
- mock_revoke.return_value = True
- new_name = 'The token formerly known as Prince'
+ self,
+ mock_method,
+ app,
+ url_token_detail,
+ url_token_list,
+ token_user_one,
+ user_one,
+ write_scope
+ ):
+ mock_method.return_value(True)
res = app.patch_json_api(
url_token_detail,
- {'data': {
- 'attributes': {
- 'name': new_name,
- },
- 'relationships': {
- 'scopes': {
- 'data': [
- {
- 'type': 'scopes',
- 'id': read_scope.name
- }
- ]
-
- }
- },
- 'id': token_user_one._id,
- 'type': 'tokens'}},
- auth=user_one.auth)
+ {
+ 'data': {
+ 'attributes': {
+ 'name': 'The token formerly known as Prince',
+ 'scopes': 'osf.full_write'
+ },
+ 'id': token_user_one._id,
+ 'type': 'tokens'
+ }
+ },
+ auth=user_one.auth
+ )
assert res.status_code == 200
res = app.get(url_token_list, auth=user_one.auth)
assert res.status_code == 200
assert (len(res.json['data']) == 1)
+ assert res.json['data'][0]['attributes']['scopes'] == write_scope.name
@mock.patch('framework.auth.cas.CasClient.revoke_tokens')
def test_deleting_token_flags_instance_inactive(
- self, mock_method, app, url_token_detail, user_one, token_user_one):
+ self,
+ mock_method,
+ app,
+ url_token_detail,
+ user_one,
+ token_user_one
+ ):
mock_method.return_value(True)
app.delete(url_token_detail, auth=user_one.auth)
token_user_one.reload()
assert not token_user_one.is_active
- def test_read_does_not_return_token_id(
- self, app, url_token_detail, user_one):
+ def test_read_does_not_return_token_id(self, app, url_token_detail, user_one):
res = app.get(url_token_detail, auth=user_one.auth)
assert res.status_code == 200
assert 'token_id' not in res.json['data']['attributes']
@mock.patch('framework.auth.cas.CasClient.revoke_tokens')
def test_update_token_does_not_return_token_id(
- self, mock_revoke, app, url_token_detail, user_one):
- mock_revoke.return_value = True
- correct = post_payload()
+ self,
+ mock_method,
+ app,
+ url_token_detail,
+ user_one,
+ write_scope
+ ):
+ mock_method.return_value(True)
res = app.put_json_api(
url_token_detail,
- correct,
+ self.post_attributes_payload(scopes='osf.full_write'),
auth=user_one.auth,
- expect_errors=True)
+ expect_errors=True
+ )
assert res.status_code == 200
assert 'token_id' not in res.json['data']['attributes']
@mock.patch('framework.auth.cas.CasClient.revoke_tokens')
- def test_update_token(self, mock_revoke, app, user_one, url_token_detail):
- mock_revoke.return_value = True
- correct = post_payload()
- res = app.put_json_api(
+ def test_update_token(
+ self,
+ mock_method,
+ app,
+ user_one,
+ token_user_one,
url_token_detail,
- correct,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 200
-
- @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
- def test_update_token_add_scope(self, mock_revoke, app, user_one, token_user_one, url_token_detail):
- mock_revoke.return_value = True
- original_scope = token_user_one.scopes.first()
- scope = ApiOAuth2ScopeFactory()
- correct = post_payload(scopes=scope.name)
+ write_scope,
+ ):
+ mock_method.return_value(True)
res = app.put_json_api(
url_token_detail,
- correct,
+ self.post_attributes_payload(scopes=write_scope.name),
auth=user_one.auth,
- expect_errors=True)
+ expect_errors=True
+ )
assert res.status_code == 200
- scopes_data = res.json['data']['embeds']['scopes']['data']
- assert len(scopes_data) == 1
- assert scopes_data[0]['id'] == scope.name
- assert scope.name != original_scope.name
+ assert res.json['data']['relationships']['scopes']
+ token_user_one.reload()
+ assert token_user_one.scopes.all().first().name == write_scope.name
- @pytest.mark.enable_implicit_clean
@mock.patch('framework.auth.cas.CasClient.revoke_tokens')
- def test_token_detail_crud_with_wrong_payload(
- self, mock_revoke, app, url_token_list, url_token_detail,
- token_user_one, user_one, user_two):
- mock_revoke.return_value = True
-
- # test_non_owner_cant_delete
- res = app.delete(
+ @mock.patch('framework.auth.cas.CasClient.profile')
+ def test_owner_can_edit_scopes_via_pat(
+ self,
+ mock_cas,
+ mock_method,
+ app,
+ user_one,
+ token_user_one,
+ token_full_write,
url_token_detail,
- auth=user_two.auth,
- expect_errors=True)
- assert res.status_code == 403
-
- # test_create_with_nonexistant_scope_fails
- injected_scope = post_payload(
- name='A shiny invalid token',
- scopes='osf.admin')
- res = app.post_json_api(
- url_token_list,
- injected_scope,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 404
-
- # test_create_with_private_scope_fails
- fake_scope = ApiOAuth2ScopeFactory()
- fake_scope.is_public = False
- fake_scope.save()
- nonsense_scope = post_payload(
- name='A shiny invalid token',
- scopes=fake_scope.name)
- res = app.post_json_api(
- url_token_list,
- nonsense_scope,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 400
-
- # test_update_with_nonexistant_scope_fails
- injected_scope = post_payload(
- name='A shiny invalid token',
- scopes='osf.admin')
- res = app.put_json_api(
- url_token_detail,
- injected_scope,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 404
-
- # test_update_with_private_scope_fails
- nonsense_scope = post_payload(
- name='A shiny invalid token',
- scopes=fake_scope.name)
- res = app.put_json_api(
- url_token_detail,
- nonsense_scope,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 400
-
- # test_update_token_incorrect_type
- incorrect_type = post_payload(type_payload='Wrong type.')
- res = app.put_json_api(
- url_token_detail,
- incorrect_type,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 409
-
- # test_update_token_no_type
- missing_type = post_payload(type_payload='')
- res = app.put_json_api(
- url_token_detail,
- missing_type,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 400
-
- # test_update_token_no_attributes
- payload = {
- 'id': token_user_one._id,
- 'type': 'tokens',
- 'name': 'The token formerly known as Prince'
- }
- res = app.put_json_api(
- url_token_detail,
- payload,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 400
-
- # test_partial_update_token_incorrect_type
- incorrect_type = post_payload(type_payload='Wrong type.')
- res = app.patch_json_api(
- url_token_detail,
- incorrect_type,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 409
-
- # test_partial_update_token_no_type
- missing_type = post_payload(type_payload='')
- res = app.patch_json_api(
- url_token_detail,
- missing_type,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 400
-
-
-def post_attributes_payload(
- type_payload='tokens',
- scopes='osf.full_write',
- name='A shiny updated token'):
- return {
- 'data': {
- 'type': type_payload,
- 'attributes': {
- 'name': name,
- 'scopes': scopes,
- }
- }
- }
-
-
-@pytest.mark.django_db
-class TestTokenDetailScopesAsAttributes:
-
- @pytest.fixture()
- def write_scope(self):
- return ApiOAuth2ScopeFactory(name='osf.full_write')
-
- @pytest.fixture()
- def user_one(self):
- return AuthUserFactory()
-
- @pytest.fixture()
- def user_two(self):
- return AuthUserFactory()
-
- @pytest.fixture()
- def token_user_one(self, user_one):
- return ApiOAuth2PersonalTokenFactory(owner=user_one)
-
- @pytest.fixture()
- def url_token_detail(self, user_one, token_user_one):
- path = f'tokens/{token_user_one._id}/'
- return api_v2_url(path, base_route='/')
-
- @pytest.fixture()
- def url_token_list(self):
- return api_v2_url('tokens/', base_route='/')
-
- def test_token_detail_who_can_view(
- self, app, url_token_detail, user_one,
- user_two, token_user_one):
-
- # test_owner_can_view
- res = app.get(url_token_detail, auth=user_one.auth)
- assert res.status_code == 200
- assert res.json['data']['id'] == token_user_one._id
- assert res.json['data']['attributes']['scopes'] == token_user_one.scopes.first().name
-
- # test_non_owner_cant_view
- res = app.get(url_token_detail, auth=user_two.auth, expect_errors=True)
- assert res.status_code == 403
-
- # test_returns_401_when_not_logged_in
- res = app.get(url_token_detail, expect_errors=True)
- assert res.status_code == 401
-
- @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
- def test_owner_can_delete(
- self, mock_method, app, user_one, url_token_detail):
- mock_method.return_value(True)
- res = app.delete(url_token_detail, auth=user_one.auth)
- assert res.status_code == 204
-
- @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
- def test_deleting_tokens_makes_api_view_inaccessible(
- self, mock_method, app, url_token_detail, user_one):
+ write_scope
+ ):
mock_method.return_value(True)
- app.delete(url_token_detail, auth=user_one.auth)
- res = app.get(url_token_detail, auth=user_one.auth, expect_errors=True)
- assert res.status_code == 404
+ mock_cas.return_value = CasResponse(
+ authenticated=True,
+ user=user_one._id,
+ attributes={'accessTokenScope': [write_scope.name,]}
+ )
- @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
- def test_updating_one_field_should_not_blank_others_on_patch_update(
- self, mock_revoke, app, token_user_one, url_token_detail, user_one, write_scope):
- mock_revoke.return_value = True
- user_one_token = token_user_one
- new_name = 'The token formerly known as Prince'
- res = app.patch_json_api(
+ res = app.put_json_api(
url_token_detail,
{
'data': {
'attributes': {
- 'name': new_name,
- 'scopes': 'osf.full_write'
+ 'name': token_user_one.name,
+ 'scopes': write_scope.name
},
- 'id': token_user_one._id,
+ 'id': token_full_write._id,
'type': 'tokens'
}
},
- auth=user_one.auth)
- user_one_token.reload()
- assert res.status_code == 200
- assert_dict_contains_subset(
- {
- 'owner': user_one_token.owner._id,
- 'name': new_name,
- 'scopes': f'{write_scope.name}',
- },
- res.json['data']['attributes'])
- assert res.json['data']['id'] == user_one_token._id
-
- @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
- def test_updating_an_instance_does_not_change_the_number_of_instances(
- self, mock_revoke, app, url_token_detail, url_token_list, token_user_one, user_one, write_scope):
- mock_revoke.return_value = True
- new_name = 'The token formerly known as Prince'
- res = app.patch_json_api(
- url_token_detail,
- {'data': {
- 'attributes': {
- 'name': new_name,
- 'scopes': 'osf.full_write'
- },
- 'id': token_user_one._id,
- 'type': 'tokens'}},
- auth=user_one.auth)
- assert res.status_code == 200
-
- res = app.get(url_token_list, auth=user_one.auth)
- assert res.status_code == 200
- assert (len(res.json['data']) == 1)
- assert res.json['data'][0]['attributes']['scopes'] == write_scope.name
-
- @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
- def test_deleting_token_flags_instance_inactive(
- self, mock_method, app, url_token_detail, user_one, token_user_one):
- mock_method.return_value(True)
- app.delete(url_token_detail, auth=user_one.auth)
- token_user_one.reload()
- assert not token_user_one.is_active
-
- def test_read_does_not_return_token_id(
- self, app, url_token_detail, user_one):
- res = app.get(url_token_detail, auth=user_one.auth)
+ headers={'Authorization': f'Bearer {token_full_write.token_id}'},
+ )
assert res.status_code == 200
- assert 'token_id' not in res.json['data']['attributes']
- assert 'scopes' in res.json['data']['attributes']
+ token_full_write.refresh_from_db()
+ assert write_scope in token_full_write.scopes.all()
- @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
- def test_update_token_does_not_return_token_id(
- self, mock_revoke, app, url_token_detail, user_one, write_scope):
- mock_revoke.return_value = True
- correct = post_attributes_payload(scopes='osf.full_write')
- res = app.put_json_api(
- url_token_detail,
- correct,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 200
- assert 'token_id' not in res.json['data']['attributes']
- assert res.json['data']['attributes']['scopes'] == write_scope.name
-
- @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
- def test_update_token(self, mock_revoke, app, user_one, url_token_detail, write_scope):
- mock_revoke.return_value = True
- correct = post_attributes_payload(scopes='osf.full_write')
- res = app.put_json_api(
+ def test_non_owner_cant_delete(
+ self,
+ app,
+ url_token_list,
url_token_detail,
- correct,
- auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 200
- assert res.json['data']['attributes']['scopes'] == write_scope.name
-
- def test_token_detail_crud_with_wrong_payload(
- self, app, url_token_list, url_token_detail,
- token_user_one, user_one, user_two):
-
- # test_non_owner_cant_delete
+ token_user_one,
+ user_one,
+ user_two
+ ):
res = app.delete(
url_token_detail,
auth=user_two.auth,
- expect_errors=True)
+ expect_errors=True
+ )
assert res.status_code == 403
- # test_create_with_admin_scope_fails
+ def test_create_with_admin_scope_fails(
+ self,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
admin_token = ApiOAuth2ScopeFactory(name='osf.admin')
admin_token.is_public = False
admin_token.save()
- injected_scope = post_attributes_payload(
+ injected_scope = self.post_attributes_payload(
name='A shiny invalid token',
- scopes='osf.admin')
+ scopes='osf.admin'
+ )
res = app.post_json_api(
url_token_list,
injected_scope,
auth=user_one.auth,
- expect_errors=True)
+ expect_errors=True
+ )
assert res.status_code == 400
- # test_create_with_fake_scope_fails
- nonsense_scope = post_attributes_payload(
+ def test_create_with_fake_scope_fails(
+ self,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
+ nonsense_scope = self.post_attributes_payload(
name='A shiny invalid token',
- scopes='osf.nonsense')
+ scopes='osf.nonsense'
+ )
res = app.post_json_api(
url_token_list,
nonsense_scope,
auth=user_one.auth,
- expect_errors=True)
+ expect_errors=True
+ )
assert res.status_code == 404
- # test_update_with_admin_scope_fails
- injected_scope = post_attributes_payload(
- name='A shiny invalid token',
- scopes='osf.admin')
+ def test_update_with_admin_scope_fails(
+ self,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
res = app.put_json_api(
url_token_detail,
- injected_scope,
+ self.post_attributes_payload(
+ name='A shiny invalid token',
+ scopes='osf.admin'
+ ),
auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 400
+ expect_errors=True
+ )
+ assert res.status_code == 404
- # test_update_with_fake_scope_fails
- nonsense_scope = post_attributes_payload(
- name='A shiny invalid token',
- scopes='osf.nonsense')
+ def test_update_with_fake_scope_fails(
+ self,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
res = app.put_json_api(
url_token_detail,
- nonsense_scope,
+ self.post_attributes_payload(
+ name='A shiny invalid token',
+ scopes='osf.nonsense'
+ ),
auth=user_one.auth,
expect_errors=True)
assert res.status_code == 404
- # test_update_token_incorrect_type
- incorrect_type = post_attributes_payload(type_payload='Wrong type.')
+ def test_update_token_incorrect_type(
+ self,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
res = app.put_json_api(
url_token_detail,
- incorrect_type,
+ self.post_attributes_payload(type_payload='Wrong type.'),
auth=user_one.auth,
- expect_errors=True)
+ expect_errors=True
+ )
assert res.status_code == 409
- # test_update_token_no_type
- missing_type = post_attributes_payload(type_payload='')
+ def test_update_token_no_type(
+ self,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
res = app.put_json_api(
url_token_detail,
- missing_type,
+ self.post_attributes_payload(type_payload=''),
auth=user_one.auth,
- expect_errors=True)
+ expect_errors=True
+ )
assert res.status_code == 400
- # test_update_token_no_attributes
- payload = {
- 'id': token_user_one._id,
- 'type': 'tokens',
- 'name': 'The token formerly known as Prince'
- }
+ def test_update_token_no_attributes(
+ self,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
res = app.put_json_api(
url_token_detail,
- payload,
+ {
+ 'id': token_user_one._id,
+ 'type': 'tokens',
+ 'name': 'The token formerly known as Prince'
+ },
auth=user_one.auth,
- expect_errors=True)
+ expect_errors=True
+ )
assert res.status_code == 400
- # test_partial_update_token_incorrect_type
- incorrect_type = post_attributes_payload(type_payload='Wrong type.')
+ def test_partial_update_token_incorrect_type(
+ self,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
res = app.patch_json_api(
url_token_detail,
- incorrect_type,
+ self.post_attributes_payload(type_payload='Wrong type.'),
auth=user_one.auth,
- expect_errors=True)
+ expect_errors=True
+ )
assert res.status_code == 409
- # test_partial_update_token_no_type
- missing_type = post_attributes_payload(type_payload='')
+ def test_partial_update_token_no_type(
+ self,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
res = app.patch_json_api(
url_token_detail,
- missing_type,
+ self.post_attributes_payload(type_payload=''),
auth=user_one.auth,
- expect_errors=True)
+ expect_errors=True
+ )
assert res.status_code == 400
- # test token too long
- payload = post_payload(name='A' * 101)
+ def test_token_too_long(
+ self,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
res = app.put_json_api(
url_token_detail,
- payload,
+ self.post_attributes_payload(name='A' * 101),
auth=user_one.auth,
- expect_errors=True)
- assert res.status_code == 400
+ expect_errors=True
+ )
+ assert res.status_code == 404
diff --git a/api_tests/tokens/views/test_token_detail_relationships.py b/api_tests/tokens/views/test_token_detail_relationships.py
new file mode 100644
index 00000000000..b8f3c01de6a
--- /dev/null
+++ b/api_tests/tokens/views/test_token_detail_relationships.py
@@ -0,0 +1,495 @@
+from unittest import mock
+import pytest
+
+from api.scopes.serializers import SCOPES_RELATIONSHIP_VERSION
+from osf_tests.factories import (
+ ApiOAuth2PersonalTokenFactory,
+ ApiOAuth2ScopeFactory,
+ AuthUserFactory,
+)
+from tests.base import assert_dict_contains_subset
+from website.util import api_v2_url
+
+@pytest.mark.django_db
+class TestTokenDetailScopesAsRelationships:
+
+ def post_payload(self, type_payload='tokens', scopes=None, name='A shiny updated token'):
+ if not scopes:
+ scopes = ApiOAuth2ScopeFactory().name
+
+ return {
+ 'data': {
+ 'type': type_payload,
+ 'attributes': {
+ 'name': name,
+ },
+ 'relationships': {
+ 'scopes': {
+ 'data': [
+ {
+ 'type': 'scopes',
+ 'id': scopes
+ }
+ ]
+ }
+ }
+ }
+ }
+
+ @pytest.fixture()
+ def user_one(self):
+ return AuthUserFactory()
+
+ @pytest.fixture()
+ def user_two(self):
+ return AuthUserFactory()
+
+ @pytest.fixture()
+ def token_user_one(self, user_one):
+ return ApiOAuth2PersonalTokenFactory(owner=user_one)
+
+ @pytest.fixture()
+ def url_token_detail(self, user_one, token_user_one):
+ path = f'tokens/{token_user_one._id}/?version={SCOPES_RELATIONSHIP_VERSION}'
+ return api_v2_url(path, base_route='/')
+
+ @pytest.fixture()
+ def url_token_list(self):
+ return api_v2_url(f'tokens/?version={SCOPES_RELATIONSHIP_VERSION}', base_route='/')
+
+ @pytest.fixture()
+ def read_scope(self):
+ return ApiOAuth2ScopeFactory(name='osf.full_read')
+
+ @pytest.fixture()
+ def private_scope(self):
+ return ApiOAuth2ScopeFactory(is_public=False)
+
+ def test_owner_can_view(
+ self,
+ app,
+ url_token_detail,
+ user_one,
+ user_two,
+ token_user_one
+ ):
+ res = app.get(url_token_detail, auth=user_one.auth)
+ assert res.status_code == 200
+ assert res.json['data']['id'] == token_user_one._id
+
+ def test_non_owner_cant_view(
+ self,
+ app,
+ url_token_detail,
+ user_one,
+ user_two,
+ token_user_one
+ ):
+ res = app.get(url_token_detail, auth=user_two.auth, expect_errors=True)
+ assert res.status_code == 403
+
+ def test_returns_401_when_not_logged_in(
+ self,
+ app,
+ url_token_detail,
+ user_one,
+ user_two,
+ token_user_one
+ ):
+ res = app.get(url_token_detail, expect_errors=True)
+ assert res.status_code == 401
+
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_owner_can_delete(
+ self,
+ mock_method,
+ app,
+ user_one,
+ url_token_detail
+ ):
+ mock_method.return_value(True)
+ res = app.delete(url_token_detail, auth=user_one.auth)
+ assert res.status_code == 204
+
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_deleting_tokens_makes_api_view_inaccessible(
+ self,
+ mock_method,
+ app,
+ url_token_detail,
+ user_one
+ ):
+ mock_method.return_value(True)
+ app.delete(url_token_detail, auth=user_one.auth)
+ res = app.get(url_token_detail, auth=user_one.auth, expect_errors=True)
+ assert res.status_code == 404
+
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_updating_one_field_should_not_blank_others_on_patch_update(
+ self,
+ mock_revoke,
+ app,
+ token_user_one,
+ url_token_detail,
+ user_one,
+ read_scope
+ ):
+ mock_revoke.return_value = True
+ user_one_token = token_user_one
+ new_name = 'The token formerly known as Prince'
+ res = app.patch_json_api(
+ url_token_detail,
+ {
+ 'data': {
+ 'attributes': {
+ 'name': new_name,
+ },
+ 'id': token_user_one._id,
+ 'type': 'tokens',
+ 'relationships': {
+ 'scopes': {
+ 'data': [
+ {
+ 'type': 'scopes',
+ 'id': read_scope.name
+ }
+ ]
+
+ }
+ }
+ }
+ },
+ auth=user_one.auth
+ )
+ user_one_token.reload()
+ assert res.status_code == 200
+
+ assert_dict_contains_subset(
+ {'name': new_name},
+ res.json['data']['attributes']
+ )
+ assert res.json['data']['id'] == user_one_token._id
+ assert res.json['data']['relationships']['owner']['data']['id'] == user_one_token.owner._id
+ assert len(res.json['data']['embeds']['scopes']['data']) == 1
+ assert res.json['data']['embeds']['scopes']['data'][0]['id'] == read_scope.name
+
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_updating_an_instance_does_not_change_the_number_of_instances(
+ self,
+ mock_revoke,
+ app,
+ url_token_detail,
+ url_token_list,
+ token_user_one,
+ user_one,
+ read_scope
+ ):
+ mock_revoke.return_value = True
+ res = app.patch_json_api(
+ url_token_detail,
+ {
+ 'data': {
+ 'attributes': {
+ 'name': 'The token formerly known as Prince',
+ },
+ 'relationships': {
+ 'scopes': {
+ 'data': [
+ {
+ 'type': 'scopes',
+ 'id': read_scope.name
+ }
+ ]
+ }
+ },
+ 'id': token_user_one._id,
+ 'type': 'tokens'
+ }
+ },
+ auth=user_one.auth
+ )
+ assert res.status_code == 200
+
+ res = app.get(url_token_list, auth=user_one.auth)
+ assert res.status_code == 200
+ assert (len(res.json['data']) == 1)
+
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_deleting_token_flags_instance_inactive(
+ self,
+ mock_method,
+ app,
+ url_token_detail,
+ user_one,
+ token_user_one
+ ):
+ mock_method.return_value(True)
+ app.delete(url_token_detail, auth=user_one.auth)
+ token_user_one.reload()
+ assert not token_user_one.is_active
+
+ def test_read_does_not_return_token_id(
+ self,
+ app,
+ url_token_detail,
+ user_one
+ ):
+ res = app.get(url_token_detail, auth=user_one.auth)
+ assert res.status_code == 200
+ assert 'token_id' not in res.json['data']['attributes']
+
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_update_token_does_not_return_token_id(self, mock_revoke, app, url_token_detail, user_one):
+ mock_revoke.return_value = True
+ res = app.put_json_api(
+ url_token_detail,
+ self.post_payload(),
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 200
+ assert 'token_id' not in res.json['data']['attributes']
+ assert 'token_id' not in res.json['data']['attributes']
+
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_update_token(self, mock_revoke, app, user_one, url_token_detail):
+ mock_revoke.return_value = True
+ res = app.put_json_api(
+ url_token_detail,
+ self.post_payload(),
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 200
+
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_update_token_add_scope(self, mock_revoke, app, user_one, token_user_one, url_token_detail):
+ mock_revoke.return_value = True
+ original_scope = token_user_one.scopes.first()
+ scope = ApiOAuth2ScopeFactory()
+ res = app.put_json_api(
+ url_token_detail,
+ self.post_payload(scopes=scope.name),
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 200
+ scopes_data = res.json['data']['embeds']['scopes']['data']
+ assert len(scopes_data) == 1
+ assert scopes_data[0]['id'] == scope.name
+ assert scope.name != original_scope.name
+
+ @pytest.mark.enable_implicit_clean
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_non_owner_cant_delete(
+ self,
+ mock_revoke,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
+ mock_revoke.return_value = True
+ res = app.delete(
+ url_token_detail,
+ auth=user_two.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 403
+
+ @pytest.mark.enable_implicit_clean
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_create_with_nonexistant_scope_fails(
+ self,
+ mock_revoke,
+ url_token_list,
+ user_one,
+ app,
+ ):
+ mock_revoke.return_value = True
+
+ injected_scope = self.post_payload(
+ name='A shiny invalid token',
+ scopes='osf.admin'
+ )
+ res = app.post_json_api(
+ url_token_list,
+ injected_scope,
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 404
+
+ @pytest.mark.enable_implicit_clean
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_create_with_private_scope_fails(
+ self,
+ mock_revoke,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two,
+ private_scope
+ ):
+ assert not private_scope.is_public
+ nonsense_scope = self.post_payload(
+ scopes=private_scope.name
+ )
+ res = app.post_json_api(
+ url_token_list,
+ nonsense_scope,
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 400
+
+ @pytest.mark.enable_implicit_clean
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_update_with_nonexistant_scope_fails(
+ self,
+ mock_revoke,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
+ injected_scope = self.post_payload(
+ name='A shiny invalid token',
+ scopes='osf.admin'
+ )
+ res = app.put_json_api(
+ url_token_detail,
+ injected_scope,
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 404
+
+ @pytest.mark.enable_implicit_clean
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_update_with_private_scope_fails(
+ self,
+ mock_revoke,
+ url_token_detail,
+ user_one,
+ app,
+ private_scope
+ ):
+ private_scope = self.post_payload(
+ name='A shiny invalid token',
+ scopes=private_scope.name
+ )
+ res = app.put_json_api(
+ url_token_detail,
+ private_scope,
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 400
+
+ @pytest.mark.enable_implicit_clean
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_update_token_incorrect_type(
+ self,
+ mock_revoke,
+ app,
+ url_token_detail,
+ user_one,
+ ):
+ res = app.put_json_api(
+ url_token_detail,
+ self.post_payload(type_payload='Wrong type.'),
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 409
+
+ @pytest.mark.enable_implicit_clean
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_update_token_no_type(
+ self,
+ mock_revoke,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
+ res = app.put_json_api(
+ url_token_detail,
+ self.post_payload(type_payload=''),
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 400
+
+ @pytest.mark.enable_implicit_clean
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_update_token_no_attributes(
+ self,
+ mock_revoke,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
+ res = app.put_json_api(
+ url_token_detail,
+ {
+ 'id': token_user_one._id,
+ 'type': 'tokens',
+ 'name': 'The token formerly known as Prince'
+ },
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 400
+
+ @pytest.mark.enable_implicit_clean
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_partial_update_token_incorrect_type(
+ self,
+ mock_revoke,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
+ res = app.patch_json_api(
+ url_token_detail,
+ self.post_payload(type_payload='Wrong type.'),
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 409
+
+ @pytest.mark.enable_implicit_clean
+ @mock.patch('framework.auth.cas.CasClient.revoke_tokens')
+ def test_partial_update_token_no_type(
+ self,
+ mock_revoke,
+ app,
+ url_token_list,
+ url_token_detail,
+ token_user_one,
+ user_one,
+ user_two
+ ):
+ res = app.patch_json_api(
+ url_token_detail,
+ self.post_payload(type_payload=''),
+ auth=user_one.auth,
+ expect_errors=True
+ )
+ assert res.status_code == 400
diff --git a/framework/auth/oauth_scopes.py b/framework/auth/oauth_scopes.py
index 67644050383..796023228df 100644
--- a/framework/auth/oauth_scopes.py
+++ b/framework/auth/oauth_scopes.py
@@ -342,6 +342,7 @@ class ComposedScopes:
+ REVIEWS_WRITE\
+ PREPRINT_ALL_WRITE\
+ GROUP_WRITE\
+ + TOKENS_WRITE\
+ (
CoreScopes.CEDAR_METADATA_RECORD_WRITE,
CoreScopes.WRITE_COLLECTION_SUBMISSION_ACTION,
diff --git a/osf/management/commands/change_node_region.py b/osf/management/commands/change_node_region.py
index abce28672bf..5af09e73da9 100644
--- a/osf/management/commands/change_node_region.py
+++ b/osf/management/commands/change_node_region.py
@@ -41,7 +41,6 @@ def _update_blocks(file_block_map, original_id, cloned_id):
def _update_schema_meta(node):
logger.info('Updating legacy schema information...')
node.registration_responses = node.schema_responses.latest('-created').all_responses
- node.registered_meta[node.registration_schema._id] = node.expand_registration_responses()
node.save()
logger.info('Updated legacy schema information.')
diff --git a/osf/management/commands/cumulative_plos_metrics.py b/osf/management/commands/cumulative_plos_metrics.py
deleted file mode 100644
index 27a6aa8a47e..00000000000
--- a/osf/management/commands/cumulative_plos_metrics.py
+++ /dev/null
@@ -1,162 +0,0 @@
-import csv
-import io
-from datetime import datetime
-import logging
-import requests
-import tempfile
-
-from django.core.management.base import BaseCommand
-from keen import KeenClient
-from requests_oauthlib import OAuth2
-
-from addons.osfstorage.models import OsfStorageFile
-from framework.celery_tasks import app as celery_app
-from osf.metrics import PreprintView, PreprintDownload
-from osf.models import Guid, Node, OSFUser, Preprint, \
- Registration, TrashedFile
-from osf.utils.fields import ensure_str
-from website.settings import (
- KEEN,
- PLOS_METRICS_BASE_FOLDER,
- PLOS_METRICS_INITIAL_FILE_DOWNLOAD_URL,
- PLOS_METRICS_OSF_TOKEN,
-)
-
-
-DEFAULT_API_VERSION = '2.20'
-TEMP_FOLDER = tempfile.mkdtemp(suffix='/')
-COL_HEADERS = [
- 'id',
- 'doi',
- 'link',
- 'guid',
- 'type',
- 'views',
- 'downloads',
-]
-
-logger = logging.getLogger(__name__)
-
-keen_client = None
-keen_project = KEEN['public']['project_id']
-keen_key = KEEN['public']['read_key']
-if keen_project and keen_key:
- keen_client = KeenClient(
- project_id=keen_project,
- read_key=keen_key
- )
-
-def bearer_token_auth(token):
- token_dict = {
- 'token_type': 'Bearer',
- 'access_token': token
- }
- return OAuth2(token=token_dict)
-
-def upload_metrics_file(file_path, params):
- with open(file_path, 'rb') as metrics_file:
- requests.put(
- url=PLOS_METRICS_BASE_FOLDER,
- headers={'Accept': f'application/vnd.api+json;version={DEFAULT_API_VERSION}'},
- params=params,
- data=metrics_file,
- auth=bearer_token_auth(PLOS_METRICS_OSF_TOKEN),
- )
-
-def parse_base_file():
- r = requests.get(
- url=PLOS_METRICS_INITIAL_FILE_DOWNLOAD_URL,
- headers={'Content-type': 'application/CSV'},
- auth=bearer_token_auth(PLOS_METRICS_OSF_TOKEN)
- )
- csvr = csv.DictReader(ensure_str(r.content).splitlines())
- assert csvr.fieldnames == COL_HEADERS, f'Unexpected headers: expected {COL_HEADERS}, got {csvr.fieldnames}'
- return csvr
-
-def fetch_metric_data_by_guid(guid):
- """return: tuple(
, tuple(, ))
- """
- g = Guid.load(guid)
- if not g:
- logger.error(f'Unable to find Guid {guid}')
- return None, (0, 0)
- obj = Guid.load(guid).referent
- if isinstance(obj, Node):
- return 'node', fetch_node_metric_data(obj)
- elif isinstance(obj, Registration):
- return 'registration', fetch_node_metric_data(obj)
- elif isinstance(obj, Preprint):
- return 'preprint', fetch_preprint_metric_data(obj)
- elif isinstance(obj, OsfStorageFile):
- return 'file', fetch_file_metric_data(obj)
- elif isinstance(obj, TrashedFile):
- return 'trashedfile', fetch_file_metric_data(obj)
- elif isinstance(obj, OSFUser):
- return 'user', (0, 0)
- else:
- logger.error(f'Unknown type for {guid}')
- return 'Unknown', (0, 0)
-
-def fetch_node_metric_data(node):
- """return: tuple(, )
- """
- views = keen_client.count(
- f'pageviews-{node._id[0]}',
- timeframe='this_20_years',
- filters=[{
- 'property_name': 'node.id',
- 'operator': 'eq',
- 'property_value': node._id
- }]
- )
- downs = 0 # AbstractNodes do not download
- return views, downs
-
-
-def fetch_preprint_metric_data(preprint):
- """return: tuple(, )
- """
- views = PreprintView.get_count_for_preprint(preprint)
- downs = PreprintDownload.get_count_for_preprint(preprint)
- return views, downs
-
-def fetch_file_metric_data(file_):
- """return: tuple(, )
- """
- views = file_.get_view_count()
- downs = file_.get_download_count()
- return views, downs
-
-@celery_app.task(name='osf.management.commands.cumulative_plos_metrics')
-def cumulative_plos_metrics():
- today = datetime.today()
- file_name = f'{today.year}_{today.month}_{today.day}_PLOS_Metrics.csv'
- file_path = f'{TEMP_FOLDER}{file_name}'
- logger.info('Parsing initial file...')
- init_data_iter = parse_base_file()
- output = io.StringIO()
- writer = csv.DictWriter(output, COL_HEADERS)
- writer.writeheader()
- logger.info('Gathering data...')
- for r in init_data_iter:
- r['type'], (r['views'], r['downloads']) = fetch_metric_data_by_guid(r['guid'])
- writer.writerow(r)
- params = {
- 'kind': 'file',
- 'name': file_name
- }
- logger.info('Writing data...')
- with open(file_path, 'w') as writeFile:
- writeFile.write(output.getvalue())
- logger.info('Uploading file...')
- upload_metrics_file(file_path=file_path, params=params)
- logger.info('Done.')
-
-
-class Command(BaseCommand):
- def handle(self, *args, **kwargs):
- start_time = datetime.now()
- logger.info(f'Script start time: {start_time}')
- cumulative_plos_metrics()
- end_time = datetime.now()
- logger.info(f'Script end time: {end_time}')
diff --git a/osf/management/commands/export_user_account.py b/osf/management/commands/export_user_account.py
index deb299c004a..f1f64de49b5 100644
--- a/osf/management/commands/export_user_account.py
+++ b/osf/management/commands/export_user_account.py
@@ -53,7 +53,6 @@
REGISTRATION_EXPORT_FIELDS = NODE_EXPORT_FIELDS + [
'registered_data',
- 'registered_meta'
]
logging.getLogger('urllib3').setLevel(logging.WARNING)
diff --git a/osf/management/commands/fix_registration_file_domains.py b/osf/management/commands/fix_registration_file_domains.py
index 72529cf3f87..9d8d74817a6 100644
--- a/osf/management/commands/fix_registration_file_domains.py
+++ b/osf/management/commands/fix_registration_file_domains.py
@@ -48,10 +48,7 @@ def fix_registration_response_file_links(registration):
urls['download'] = urls['download'].replace(BAD_DOMAIN, DOMAIN)
block.save()
- # Re-set registration_responses and registered_meta bssed on the *latest* schema_response,
- # which will have inherited these fixes if not already explicitly updated
registration.registration_responses = registration.schema_responses.first().all_responses
- registration.registered_meta[schema._id] = registration.expand_registration_responses()
registration.save()
diff --git a/osf/management/commands/migrate_registration_responses.py b/osf/management/commands/migrate_registration_responses.py
index 009dee81c4d..74812e3a796 100644
--- a/osf/management/commands/migrate_registration_responses.py
+++ b/osf/management/commands/migrate_registration_responses.py
@@ -9,7 +9,6 @@
from framework import sentry
from osf.exceptions import SchemaBlockConversionError
-from osf.utils.registrations import flatten_registration_metadata
logger = logging.getLogger(__name__)
@@ -20,9 +19,6 @@ def get_nested_responses(registration_or_draft, schema_id):
'registration_metadata',
None,
)
- if nested_responses is None:
- registered_meta = registration_or_draft.registered_meta or {}
- nested_responses = registered_meta.get(schema_id, None)
return nested_responses
# because Registrations and DraftRegistrations are different
@@ -82,11 +78,6 @@ def migrate_responses(model, resources, resource_name, dry_run=False, rows='all'
errors_to_save = []
for resource in resources:
try:
- schema = get_registration_schema(resource)
- resource.registration_responses = flatten_registration_metadata(
- schema,
- get_nested_responses(resource, schema._id),
- )
resource.registration_responses_migrated = True
successes_to_save.append(resource)
except SchemaBlockConversionError as e:
diff --git a/osf/management/commands/sync_collection_provider_indices.py b/osf/management/commands/sync_collection_provider_indices.py
index 33e1eb015f4..54adaa6677a 100644
--- a/osf/management/commands/sync_collection_provider_indices.py
+++ b/osf/management/commands/sync_collection_provider_indices.py
@@ -26,7 +26,7 @@ def sync_collection_provider_indices(cp_ids=None, only_remove=False):
submission.remove_from_index()
remove_ct += 1
elif not only_remove:
- submission.update_index()
+ submission.update_search()
add_ct += 1
logger.info(f'{remove_ct} submissions removed from {prov._id}')
logger.info(f'{add_ct} submissions synced to {prov._id}')
diff --git a/osf/metadata/serializers/google_dataset_json_ld.py b/osf/metadata/serializers/google_dataset_json_ld.py
index 04deebede00..896e80acc7c 100644
--- a/osf/metadata/serializers/google_dataset_json_ld.py
+++ b/osf/metadata/serializers/google_dataset_json_ld.py
@@ -76,10 +76,12 @@ def metadata_as_dict(self) -> dict:
def format_creators(basket):
creator_data = []
- for creator_iri in basket[DCTERMS.creator]:
+ for creator in basket.focus.dbmodel.contributors.all():
creator_data.append({
'@type': 'Person',
- 'name': next(basket[creator_iri:FOAF.name]),
+ 'name': creator.fullname,
+ 'givenName': creator.given_name,
+ 'familyName': creator.family_name
})
return creator_data
diff --git a/osf/migrations/0029_remove_abstractnode_keenio_read_key.py b/osf/migrations/0029_remove_abstractnode_keenio_read_key.py
new file mode 100644
index 00000000000..e9e1ca0791b
--- /dev/null
+++ b/osf/migrations/0029_remove_abstractnode_keenio_read_key.py
@@ -0,0 +1,17 @@
+# Generated by Django 4.2.15 on 2025-02-21 13:08
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('osf', '0028_collection_grade_levels_choices_and_more'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='abstractnode',
+ name='keenio_read_key',
+ ),
+ ]
diff --git a/osf/migrations/0030_remove_abstractnode_registered_metadata.py b/osf/migrations/0030_remove_abstractnode_registered_metadata.py
new file mode 100644
index 00000000000..ac3e29948d9
--- /dev/null
+++ b/osf/migrations/0030_remove_abstractnode_registered_metadata.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.2.13 on 2025-04-11 13:36
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('osf', '0029_remove_abstractnode_keenio_read_key'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='abstractnode',
+ name='registered_meta',
+ ),
+ migrations.RemoveField(
+ model_name='draftregistration',
+ name='registration_metadata',
+ ),
+ ]
diff --git a/osf/models/collection_submission.py b/osf/models/collection_submission.py
index d8ef9193fb0..893533d85d1 100644
--- a/osf/models/collection_submission.py
+++ b/osf/models/collection_submission.py
@@ -2,6 +2,7 @@
from django.db import models
from django.utils.functional import cached_property
+from framework import sentry
from framework.exceptions import PermissionsError
from .base import BaseModel
@@ -382,13 +383,19 @@ def absolute_api_v2_url(self):
path = f'/collections/{self.collection._id}/collection_submissions/{self.guid._id}/'
return api_v2_url(path)
- def update_index(self):
+ def update_search(self):
if self.collection.is_public:
+ from api.share.utils import update_share
from website.search.search import update_collected_metadata
+
+ # It will automatically determine if a referent is part of the collection
+ update_share(self.guid.referent)
+
try:
update_collected_metadata(self.guid._id, collection_id=self.collection.id)
except SearchUnavailableError as e:
logger.exception(e)
+ sentry.log_exception(e)
def remove_from_index(self):
from website.search.search import update_collected_metadata
@@ -396,10 +403,11 @@ def remove_from_index(self):
update_collected_metadata(self.guid._id, collection_id=self.collection.id, op='delete')
except SearchUnavailableError as e:
logger.exception(e)
+ sentry.log_exception(e)
def save(self, *args, **kwargs):
ret = super().save(*args, **kwargs)
- self.update_index()
+ self.update_search()
return ret
diff --git a/osf/models/mixins.py b/osf/models/mixins.py
index 9027a284f7c..0e614ea1483 100644
--- a/osf/models/mixins.py
+++ b/osf/models/mixins.py
@@ -44,7 +44,6 @@
)
from osf.utils.permissions import ADMIN, REVIEW_GROUPS, READ, WRITE
-from osf.utils.registrations import flatten_registration_metadata, expand_registration_responses
from osf.utils.workflows import (
DefaultStates,
DefaultTriggers,
@@ -2286,37 +2285,6 @@ def file_storage_resource(self):
# Where the original files were stored (the node)
raise NotImplementedError()
- def flatten_registration_metadata(self):
- """
- Extracts questions/nested registration_responses - makes use of schema block `registration_response_key`
- and block_type to assemble flattened registration_responses.
-
- For example, if the registration_response_key = "description-methods.planned-sample.question7b",
- this will recurse through the registered_meta, looking for each key, starting with "description-methods",
- then "planned-sample", and finally "question7b", returning the most deeply nested value corresponding
- with the final key to flatten the dictionary.
- :self, DraftRegistration or Registration
- :returns dictionary, registration_responses, flattened dictionary with registration_response_keys
- top-level
- """
- schema = self.registration_schema
- registered_meta = self.get_registration_metadata(schema)
- return flatten_registration_metadata(schema, registered_meta)
-
- def expand_registration_responses(self):
- """
- Expanding `registration_responses` into Draft.registration_metadata or
- Registration.registered_meta. registration_responses are more flat;
- "registration_response_keys" are top level. Registration_metadata/registered_meta
- will have a more deeply nested format.
- :returns registration_metadata, dictionary
- """
- return expand_registration_responses(
- self.registration_schema,
- self.registration_responses,
- self.file_storage_resource,
- )
-
class Meta:
abstract = True
diff --git a/osf/models/node.py b/osf/models/node.py
index d06af182e47..f83209bc827 100644
--- a/osf/models/node.py
+++ b/osf/models/node.py
@@ -20,7 +20,6 @@
from django.dispatch import receiver
from django.utils import timezone
from django.utils.functional import cached_property
-from keen import scoped_keys
from psycopg2._psycopg import AsIs
from typedmodels.models import TypedModel, TypedModelManager
from guardian.models import (
@@ -57,7 +56,7 @@
translations as gv_translations,
)
from osf.utils.datetime_aware_jsonfield import DateTimeAwareJSONField
-from osf.utils.fields import NonNaiveDateTimeField, ensure_str
+from osf.utils.fields import NonNaiveDateTimeField
from osf.utils.requests import get_request_and_user_id, string_type_request_headers, get_current_request
from osf.utils.workflows import CollectionSubmissionStates
from osf.utils import sanitize
@@ -479,8 +478,6 @@ def linked_nodes(self):
identifiers = GenericRelation(Identifier, related_query_name='nodes')
- keenio_read_key = models.CharField(max_length=1000, null=True, blank=True)
-
def __init__(self, *args, **kwargs):
self._parent = kwargs.pop('parent', None)
self._is_templated_clone = False
@@ -1260,7 +1257,6 @@ def set_privacy(self, permissions, auth=None, log=True, save=True, meeting_creat
self.request_embargo_termination(auth.user)
return False
self.is_public = True
- self.keenio_read_key = self.generate_keenio_read_key()
elif permissions == 'private' and self.is_public:
if self.is_registration and not self.is_pending_embargo and not force:
raise NodeStateError('Public registrations must be withdrawn, not made private.')
@@ -1268,7 +1264,6 @@ def set_privacy(self, permissions, auth=None, log=True, save=True, meeting_creat
self.check_privacy_change_viability(auth)
self.is_public = False
- self.keenio_read_key = ''
self._remove_from_associated_collections(auth, force=force)
else:
return False
@@ -1305,17 +1300,6 @@ def set_privacy(self, permissions, auth=None, log=True, save=True, meeting_creat
project_signals.privacy_set_public.send(auth.user, node=self, meeting_creation=meeting_creation)
return True
- def generate_keenio_read_key(self):
- encrypted_read_key = scoped_keys.encrypt(settings.KEEN['public']['master_key'], options={
- 'filters': [{
- 'property_name': 'node.id',
- 'operator': 'eq',
- 'property_value': str(self._id)
- }],
- 'allowed_operations': [READ]
- })
- return ensure_str(encrypted_read_key)
-
@property
def private_links_active(self):
if self.is_spammy:
@@ -1439,9 +1423,6 @@ def register_node(self, schema, auth, draft_registration, parent=None, child_ids
registered.registered_user = auth.user
registered.registered_from = original
registered.provider = provider
- if not registered.registered_meta:
- registered.registered_meta = {}
- registered.registered_meta[schema._id] = draft_registration.registration_metadata
registered.forked_from = self.forked_from
registered.creator = self.creator
diff --git a/osf/models/preprint.py b/osf/models/preprint.py
index ab30cbff39f..162ab8b00a8 100644
--- a/osf/models/preprint.py
+++ b/osf/models/preprint.py
@@ -785,8 +785,8 @@ def set_primary_file(self, preprint_file, auth, save=False):
self.save()
update_or_enqueue_on_preprint_updated(preprint_id=self._id, saved_fields=['primary_file'])
- def set_published(self, published, auth, save=False):
- if not self.has_permission(auth.user, ADMIN):
+ def set_published(self, published, auth, save=False, ignore_permission=False):
+ if not ignore_permission and not self.has_permission(auth.user, ADMIN):
raise PermissionsError('Only admins can publish a preprint.')
if self.is_published and not published:
diff --git a/osf/models/registrations.py b/osf/models/registrations.py
index f7b017d9ddf..0db8ff630a0 100644
--- a/osf/models/registrations.py
+++ b/osf/models/registrations.py
@@ -114,7 +114,6 @@ class Registration(AbstractNode):
# TODO: Consider making this a FK, as there can be one per Registration
registered_schema = models.ManyToManyField(RegistrationSchema)
- registered_meta = DateTimeAwareJSONField(default=dict, blank=True)
registered_from = models.ForeignKey('self',
related_name='registrations',
on_delete=models.SET_NULL,
@@ -198,11 +197,6 @@ def registration_schema(self):
return self.registered_schema.first()
return None
- def get_registration_metadata(self, schema):
- # Overrides RegistrationResponseMixin
- registered_meta = self.registered_meta or {}
- return registered_meta.get(schema._id, None)
-
@property
def file_storage_resource(self):
# Overrides RegistrationResponseMixin
@@ -989,7 +983,6 @@ class DraftRegistration(ObjectIDMixin, RegistrationResponseMixin, DirtyFieldsMix
# 'value':
# }
# }
- registration_metadata = DateTimeAwareJSONField(default=dict, blank=True)
registration_schema = models.ForeignKey('RegistrationSchema', null=True, on_delete=models.CASCADE)
registered_node = models.ForeignKey('Registration', null=True, blank=True,
related_name='draft_registration', on_delete=models.CASCADE)
@@ -1029,10 +1022,6 @@ def __repr__(self):
return ('').format(self=self)
- def get_registration_metadata(self, schema):
- # Overrides RegistrationResponseMixin
- return self.registration_metadata
-
@property
def file_storage_resource(self):
# Overrides RegistrationResponseMixin
@@ -1282,7 +1271,6 @@ def create_from_node(cls, user, schema, node=None, data=None, provider=None):
initiator=user,
branched_from=branched_from,
registration_schema=schema,
- registration_metadata=data or {},
provider=provider,
)
draft.save()
@@ -1331,14 +1319,6 @@ def copy_contributors_from(self, resource):
self.add_permission(contrib.user, permission, save=True)
DraftRegistrationContributor.objects.bulk_create(contribs)
- def update_metadata(self, metadata):
- # Prevent comments on approved drafts
- self.registration_metadata.update(metadata)
-
- # Write to registration_responses also (new workflow)
- registration_responses = self.flatten_registration_metadata()
- self.registration_responses.update(registration_responses)
-
def update_registration_responses(self, registration_responses):
"""
New workflow - update_registration_responses. This should have been
@@ -1347,8 +1327,6 @@ def update_registration_responses(self, registration_responses):
"""
registration_responses = self.unescape_registration_file_names(registration_responses)
self.registration_responses.update(registration_responses)
- registration_metadata = self.expand_registration_responses()
- self.registration_metadata = registration_metadata
return
def unescape_registration_file_names(self, registration_responses):
diff --git a/osf/utils/registrations.py b/osf/utils/registrations.py
index 3e8adfa1069..71e247acf38 100644
--- a/osf/utils/registrations.py
+++ b/osf/utils/registrations.py
@@ -1,49 +1,9 @@
-import copy
import re
from urllib.parse import urljoin
-from osf.exceptions import SchemaBlockConversionError
from website import settings
-
-def strip_registered_meta_comments(messy_dict_or_list, in_place=False):
- """Removes Prereg Challenge comments from a given `registered_meta` dict.
-
- Nothing publicly exposed needs these comments:
- ```
- {
- "registered_meta": {
- "q20": {
- "comments": [ ... ], <~~~ THIS
- "value": "foo",
- "extra": []
- },
- }
- }
- ```
-
- If `in_place` is truthy, modifies `messy_dict_or_list` and returns it.
- Else, returns a deep copy without modifying the given `messy_dict_or_list`
- """
- obj = messy_dict_or_list if in_place else copy.deepcopy(messy_dict_or_list)
-
- if isinstance(obj, list):
- for nested_obj in obj:
- strip_registered_meta_comments(nested_obj, in_place=True)
- elif isinstance(obj, dict):
- comments = obj.get('comments', None)
-
- # some schemas have a question named "comments" -- those will have a dict value
- if isinstance(comments, list):
- del obj['comments']
-
- # dig into the deeply nested structure
- for nested_obj in obj.values():
- strip_registered_meta_comments(nested_obj, in_place=True)
- return obj
-
-
"""
Old workflow uses DraftRegistration.registration_metadata and Registration.registered_meta.
New workflow uses DraftRegistration.registration_responses and Registration.registration_responses.
@@ -62,296 +22,3 @@ def strip_registered_meta_comments(messy_dict_or_list, in_place=False):
# use absolute URLs in new 'flattened' format
FILE_HTML_URL_TEMPLATE = urljoin(settings.DOMAIN, '/project/{node_id}/files/osfstorage/{file_id}')
FILE_DOWNLOAD_URL_TEMPLATE = urljoin(settings.DOMAIN, '/download/{file_id}')
-
-def flatten_registration_metadata(schema, registered_meta):
- """
- Extracts questions/nested registration_responses - makes use of schema block `registration_response_key`
- and block_type to assemble flattened registration_responses.
-
- For example, if the registration_response_key = "description-methods.planned-sample.question7b",
- this will recurse through the registered_meta, looking for each key, starting with "description-methods",
- then "planned-sample", and finally "question7b", returning the most deeply nested value corresponding
- with the final key to flatten the dictionary.
- :schema, RegistrationSchema instance
- :registered_meta, dict containing the legacy "nested" registered_meta/registration_metadata
- :returns dictionary, registration_responses, flattened dictionary with registration_response_keys
- top-level
- """
- registration_responses = {}
- registration_response_keys = schema.schema_blocks.filter(
- registration_response_key__isnull=False
- ).values(
- 'registration_response_key',
- 'block_type'
- )
-
- for registration_response_key_dict in registration_response_keys:
- key = registration_response_key_dict['registration_response_key']
- registration_responses[key] = get_nested_answer(
- registered_meta,
- registration_response_key_dict['block_type'],
- key.split('.')
- )
- return registration_responses
-
-# For flatten_registration_metadata
-def build_file_ref(file):
- """
- Extracts name, file_id, and sha256 from the nested "extras" dictionary.
- Pulling name from selectedFileName and the file_id from the viewUrl.
-
- Some weird data here...such as {u'selectedFileName': u'No file selected'}
- Raises SchemaBlockConversionError if it's too weird to handle.
-
- :returns dictionary formatted as below
- {
- file_id: ,
- file_name: ,
- file_hashes: {
- sha256: ,
- },
- file_urls: {
- html: ,
- download: ,
- },
- }
- """
- file_data = file.get('data')
-
- # on a Registration, viewUrl is the only place the file/node ids are accurate.
- # the others refer to the original file on the node, not the file that was archived on the reg
- view_url = file.get('viewUrl')
- file_id = None
- node_id = None
- if view_url:
- url_match = FILE_VIEW_URL_REGEX.search(view_url)
- if not url_match:
- raise SchemaBlockConversionError(f'Unexpected file viewUrl: {view_url}')
- groupdict = url_match.groupdict()
- file_id = groupdict['file_id']
- node_id = groupdict['node_id']
- elif file_data:
- # this data is bad and it should feel bad
- id_from_path = file_data.get('path', '').lstrip('/')
- file_id = id_from_path or file.get('fileId')
- node_id = file.get('nodeId')
-
- if not (file_id and node_id):
- raise SchemaBlockConversionError(f'Could not find file and node ids in file info: {file}')
-
- file_name = file.get('selectedFileName')
- if file_data and not file_name:
- file_name = file_data.get('name')
-
- sha256 = file.get('sha256')
- if file_data and not sha256:
- sha256 = file_data.get('extra', {}).get('hashes', {}).get('sha256')
-
- return {
- 'file_id': file_id,
- 'file_name': file_name,
- 'file_hashes': {'sha256': sha256} if sha256 else {},
- 'file_urls': {
- 'html': FILE_HTML_URL_TEMPLATE.format(file_id=file_id, node_id=node_id),
- 'download': FILE_DOWNLOAD_URL_TEMPLATE.format(file_id=file_id),
- },
- }
-
-# For flatten_registration_metadata
-def build_file_refs(messy_file_infos):
- for file_info in messy_file_infos:
- if not file_info:
- continue
- if len(file_info) == 1 and file_info.get('selectedFileName') == 'No file selected':
- continue
- yield build_file_ref(file_info)
-
-# For flatten_registration_metadata
-def get_value_or_extra(nested_response, block_type, key, keys):
- """
- Sometimes the relevant information is stored under "extra" for files,
- otherwise, "value".
-
- :params, nested dictionary
- :block_type, string, current block type
- :key, particular key in question
- :keys, array of keys remaining to recurse through to find the user's answer
- :returns array (files or multi-response answers) or a string IF deepest level of nesting,
- otherwise, returns a dictionary to get the next level of nesting.
- """
- keyed_value = nested_response.get(key, '')
- # No guarantee that the key exists in the dictionary
- if isinstance(keyed_value, str):
- return keyed_value
-
- # If we are on the most deeply nested key (no more keys left in array),
- # and the block type is "file-input", the information we want is
- # stored under extra
- if block_type == 'file-input' and not keys:
- extra = keyed_value.get('extra', [])
- extra_list = extra if isinstance(extra, list) else [extra]
- return list(build_file_refs(extra_list))
-
- value = keyed_value.get('value')
- if value is None:
- return ''
- if isinstance(value, bool):
- return str(value)
- return value
-
-# For flatten_registration_metadata
-def get_nested_answer(nested_response, block_type, keys):
- """
- Recursively fetches the nested response in registered_meta.
-
- :params nested_response dictionary
- :params keys array, of nested question_ids: ["recommended-analysis", "specify", "question11c"]
- :returns array (files or multi-response answers) or a string
- """
- if isinstance(nested_response, dict):
- if not keys:
- raise SchemaBlockConversionError('Unexpected nested object (expected list or string)', nested_response)
- key = keys.pop(0)
- # Returns the value associated with the given key
- value = get_value_or_extra(nested_response, block_type, key, keys)
- return get_nested_answer(value, block_type, keys)
- else:
- # Once we've drilled down through the entire dictionary, our nested_response
- # should be an array or a string
- if not isinstance(nested_response, (list, str)):
- raise SchemaBlockConversionError('Unexpected value type (expected list or string)', nested_response)
- return nested_response
-
-def expand_registration_responses(schema, registration_responses, file_storage_resource):
- """
- Expanding `registration_responses` into Draft.registration_metadata or
- Registration.registered_meta. registration_responses are more flat;
- "registration_response_keys" are top level. Registration_metadata/registered_meta
- will have a more deeply nested format.
- :returns registration_metadata, dictionary
- """
- registration_responses = copy.deepcopy(registration_responses)
-
- # Pull out all registration_response_keys and their block types
- registration_response_keys = schema.schema_blocks.filter(
- registration_response_key__isnull=False
- ).values(
- 'registration_response_key',
- 'block_type'
- )
-
- metadata = {}
-
- for registration_response_key_dict in registration_response_keys:
- response_key = str(registration_response_key_dict['registration_response_key'])
- # Turns "confirmatory-analyses-further.further.question2c" into
- # ['confirmatory-analyses-further', 'value', 'further', 'value', 'question2c']
- nested_keys = response_key.replace('.', '.value.').split('.')
- block_type = registration_response_key_dict['block_type']
-
- # Continues to add to metadata with every registration_response_key
- metadata = build_registration_metadata_dict(
- nested_keys,
- metadata=metadata,
- value=build_answer_block(
- block_type,
- registration_responses.get(response_key, ''),
- file_storage_resource=file_storage_resource
- )
- )
- return metadata
-
-# For expanding registration_responses
-def set_nested_values(nested_dictionary, keys, value):
- """
- Drills down through the nested dictionary, accessing each key in the array,
- and sets the last key equal to the passed in value, if this key doesn't already exist.
-
- Assumes all keys are present, except for potentially the final key.
-
- :param nested_dictionary, dictionary
- :param keys, array
- :param value, object, array, or string
- """
- for key in keys[:-1]:
- nested_dictionary = nested_dictionary.get(key, None)
-
- final_key = keys[-1]
- if not nested_dictionary.get(final_key):
- nested_dictionary[final_key] = value
-
-# For expanding registration_responses
-def build_extra_file_dict(file_ref):
- url_match = FILE_VIEW_URL_REGEX.search(file_ref['file_urls']['html'])
- if not url_match:
- raise SchemaBlockConversionError('Expected `file_urls.html` in format `/project//files/osfstorage/`')
-
- groups = url_match.groupdict()
- node_id = groups['node_id']
- file_id = groups['file_id']
-
- file_name = file_ref['file_name']
- sha256 = file_ref['file_hashes']['sha256']
-
- # viewUrl, selectedFileName, and sha256 are everything needed for the return trip
- # (see `osf.utils.build_file_ref`)
- return {
- 'viewUrl': FILE_VIEW_URL_TEMPLATE.format(node_id=node_id, file_id=file_id),
- 'selectedFileName': file_name,
- 'sha256': sha256,
- 'nodeId': node_id, # Used in _find_orphan_files
- 'data': {
- 'name': file_name, # What legacy FE needs for rendering file on the draft
- }
- }
-
-# For expanding registration_responses
-def build_answer_block(block_type, value, file_storage_resource=None):
- extra = []
- if block_type == 'file-input':
- extra = list(map(build_extra_file_dict, value))
- value = ''
- return {
- 'comments': [],
- 'value': value,
- 'extra': extra
- }
-
-# For expanding registration_responses
-def build_registration_metadata_dict(keys, current_index=0, metadata={}, value={}):
- """
- Function will recursively loop through each key in the list, checking if it exists in metadata, if not,
- adding another nested level in the dictionary.
-
- For example, calling build_registration_metadata_dict(
- ["recommended-analysis", "value", "specify", "value", "question11c"], value='hello'), yields,
-
- {
- 'recommended-analysis': {
- 'value': {
- 'specify': {
- 'value': {
- 'question11c': 'hello'
- }
- }
- }
- }
- }
- :param keys array, of nested question_ids: ["recommended-analysis", "value", "specify", "value", "question11c"]
- :param current_index, call initially with 0
- :param metadata - registration_metadata
- :param value, provide most deeply nested value.
- :returns partial registration_metadata
- """
- if current_index == len(keys):
- # We've iterated through all the keys, so we exit.
- return metadata
- else:
- # All keys from left to right including the current key.
- current_chain = keys[0:current_index + 1]
- # If we're on the final key, use the passed in value
- val = value if current_index == len(keys) - 1 else {}
- # set_nested_values incrementally adds another layer of nesting to the dictionary,
- # until we get to the deepest level where we can set the value equal to the user's response
- set_nested_values(metadata, current_chain, val)
- return build_registration_metadata_dict(keys, current_index + 1, metadata, value)
diff --git a/osf_tests/factories.py b/osf_tests/factories.py
index 7ad8885e1ad..37ca8096f02 100644
--- a/osf_tests/factories.py
+++ b/osf_tests/factories.py
@@ -587,7 +587,6 @@ def _create(cls, *args, **kwargs):
draft.title = title
if description:
draft.description = description
- draft.registration_responses = draft.flatten_registration_metadata()
draft.save()
return draft
diff --git a/osf_tests/management_commands/test_migration_registration_responses.py b/osf_tests/management_commands/test_migration_registration_responses.py
deleted file mode 100644
index 880590717d1..00000000000
--- a/osf_tests/management_commands/test_migration_registration_responses.py
+++ /dev/null
@@ -1,2084 +0,0 @@
-import pytest
-from urllib.parse import urljoin
-
-from osf.models import RegistrationSchema
-from osf_tests.factories import DraftRegistrationFactory, RegistrationFactory
-
-from osf.management.commands.migrate_registration_responses import (
- migrate_draft_registrations,
- migrate_registrations
-)
-from website import settings
-
-"""
-Regression test to prevent the migration of 'registration_metadata' behavior
-from breaking
-"""
-
-prereg_registration_responses = {
- 'q12.uploader': [],
- 'q7.question': 'data collection procedures',
- 'q20': 'transforming and recoding',
- 'q21': 'research plan follow up',
- 'q22': 'criteria',
- 'q23': 'this is how outliers will be handled',
- 'q24': 'this is how I will deal with incomplete data.',
- 'q25': 'this is my exploratory analysis',
- 'q26': [],
- 'q27': 'No additional comments',
- 'q12.question': 'these are my measured variables',
- 'q1': 'This is my title',
- 'q3': 'research questions',
- 'q5': 'Registration prior to creation of data',
- 'q4': 'this is my hypothesis',
- 'q6': 'Explanation of existing data',
- 'q9': 'this is the rationale for my sample size',
- 'q8': 'this is my sample size',
- 'q13.question': 'these are my indices',
- 'q19.uploader': [],
- 'q11.uploader': [
- {
- 'file_name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'file_id': '5d6d22264d476c088fb8e01f',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22264d476c088fb8e01f'),
- },
- 'file_hashes': {
- 'sha256': 'sdf',
- },
- },
- {
- 'file_name': 'Alphabet.txt',
- 'file_id': '5d6d22274d476c088fb8e021',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021'),
- },
- 'file_hashes': {
- 'sha256': 'dsdfds',
- },
- }
- ],
- 'q16.question': 'this is my study design',
- 'q15': [
- 'No blinding is involved in this study.',
- 'For studies that involve human subjects, they will not know the treatment group to which they have been assigned.',
- 'Research personnel who interact directly with the study subjects (either human or non-human subjects) will not be aware of the assigned treatments.'
- ],
- 'q14': '',
- 'q17': 'this is my explanation of randomization',
- 'q10': 'this is my stopping rule',
- 'q11.question': 'these are my maniuplated variables',
- 'q16.uploader': [],
- 'q19.question': 'ANOVA',
- 'q13.uploader': [],
- 'q7.uploader': [
- {
- 'file_name': 'Alphabet.txt',
- 'file_id': '5d6d22274d476c088fb8e021',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021'),
- },
- 'file_hashes': {
- 'sha256': 'dsdfds',
- },
- }
- ]
-}
-
-prereg_registration_metadata = {
- 'q20': {
- 'comments': [],
- 'value': 'transforming and recoding',
- 'extra': []
- },
- 'q21': {
- 'comments': [],
- 'value': 'research plan follow up',
- 'extra': []
- },
- 'q22': {
- 'comments': [],
- 'value': 'criteria',
- 'extra': []
- },
- 'q23': {
- 'comments': [],
- 'value': 'this is how outliers will be handled',
- 'extra': []
- },
- 'q24': {
- 'comments': [],
- 'value': 'this is how I will deal with incomplete data.',
- 'extra': []
- },
- 'q25': {
- 'comments': [],
- 'value': 'this is my exploratory analysis',
- 'extra': []
- },
- 'q26': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'q27': {
- 'comments': [],
- 'value': 'No additional comments',
- 'extra': []
- },
- 'q1': {
- 'comments': [],
- 'value': 'This is my title',
- 'extra': []
- },
- 'q3': {
- 'comments': [],
- 'value': 'research questions',
- 'extra': []
- },
- 'q5': {
- 'comments': [],
- 'value': 'Registration prior to creation of data',
- 'extra': []
- },
- 'q4': {
- 'comments': [],
- 'value': 'this is my hypothesis',
- 'extra': []
- },
- 'q7': {
- 'comments': [],
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'data collection procedures',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': 'Alphabet.txt',
- 'extra': [
- {
- 'descriptionValue': '',
- 'nodeId': '9bknu',
- 'viewUrl': '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021/',
- 'selectedFileName': 'Alphabet.txt',
- 'sha256': 'dsdfds',
- 'data': {
- 'contentType': '',
- 'sizeInt': 3169,
- 'kind': 'file',
- 'resource': '9bknu',
- 'name': 'Alphabet.txt',
- 'extra': {
- 'downloads': 0,
- 'latestVersionSeen': '',
- 'version': 1,
- 'hashes': {
- 'sha256': 'dsdfds',
- 'md5': 'sdfsef'
- },
- 'guid': '',
- 'checkout': ''
- },
- 'materialized': '/Alphabet.txt',
- 'created_utc': '',
- 'nodeId': '9bknu',
- 'modified': '2019-09-02T14:04:14.776301+00:00',
- 'etag': 'b529584199f707abda42a42ec2f3c5a052d280e56fb3382f07efc77933053159',
- 'provider': 'osfstorage',
- 'path': '/5d6d215b4d476c088fb8e000',
- 'modified_utc': '2019-09-02T14:04:14+00:00',
- 'size': 3169
- },
- 'fileId': '7ry42'
- }
- ]
- }
- },
- 'extra': []
- },
- 'q6': {
- 'comments': [],
- 'value': 'Explanation of existing data',
- 'extra': []
- },
- 'q9': {
- 'comments': [],
- 'value': 'this is the rationale for my sample size',
- 'extra': []
- },
- 'q8': {
- 'comments': [],
- 'value': 'this is my sample size',
- 'extra': []
- },
- 'q15': {
- 'comments': [],
- 'value': [
- 'No blinding is involved in this study.',
- 'For studies that involve human subjects, they will not know the treatment group to which they have been assigned.',
- 'Research personnel who interact directly with the study subjects (either human or non-human subjects) will not be aware of the assigned treatments.'
- ],
- 'extra': []
- },
- 'q14': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'q17': {
- 'comments': [],
- 'value': 'this is my explanation of randomization',
- 'extra': []
- },
- 'q16': {
- 'comments': [],
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'this is my study design',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- },
- 'extra': []
- },
- 'q11': {
- 'comments': [],
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'these are my maniuplated variables',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'extra': [
- {
- 'descriptionValue': '',
- 'nodeId': '9bknu',
- 'viewUrl': '/project/57zbh/files/osfstorage/5d6d22264d476c088fb8e01f/',
- 'selectedFileName': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'sha256': 'sdf',
- 'data': {
- 'contentType': '',
- 'sizeInt': 78257,
- 'kind': 'file',
- 'resource': '9bknu',
- 'name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'extra': {
- 'downloads': 0,
- 'latestVersionSeen': '',
- 'version': 1,
- 'hashes': {
- 'sha256': 'sdf',
- 'md5': 'asdf'
- },
- 'guid': '',
- 'checkout': ''
- },
- 'materialized': '/Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'created_utc': '',
- 'nodeId': '9bknu',
- 'modified': '2019-09-02T14:04:47.793337+00:00',
- 'etag': 'd114466ad8f1e04c03e678fdaf642988c1accd1526bb6e81b34bf35612a77cbb',
- 'provider': 'osfstorage',
- 'path': '/5d6d217f4d476c088fb8e00d',
- 'modified_utc': '2019-09-02T14:04:47+00:00',
- 'size': 78257
- },
- 'fileId': 'nc8qy'
- },
- {
- 'descriptionValue': '',
- 'nodeId': '9bknu',
- 'viewUrl': '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021/',
- 'selectedFileName': 'Alphabet.txt',
- 'sha256': 'asdf',
- 'data': {
- 'links': {
- 'download': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d215b4d476c088fb8e000',
- 'move': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d215b4d476c088fb8e000',
- 'upload': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d215b4d476c088fb8e000?kind=file',
- 'delete': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d215b4d476c088fb8e000'
- },
- 'extra': {
- 'downloads': 0,
- 'latestVersionSeen': '',
- 'version': 1,
- 'hashes': {
- 'sha256': 'asdf',
- 'md5': 'sdfs'
- },
- 'guid': '',
- 'checkout': ''
- },
- 'accept': {
- 'acceptedFiles': 'True',
- 'maxSize': 5120
- },
- 'id': 'osfstorage/5d6d215b4d476c088fb8e000',
- 'size': 3169,
- 'nodeApiUrl': '/api/v1/project/9bknu/',
- 'nodeId': '9bknu',
- 'etag': 'b529584199f707abda42a42ec2f3c5a052d280e56fb3382f07efc77933053159',
- 'provider': 'osfstorage',
- 'type': 'files',
- 'nodeUrl': '/9bknu/',
- 'sizeInt': 3169,
- 'contentType': '',
- 'path': '/5d6d215b4d476c088fb8e000',
- 'permissions': {
- 'edit': 'True',
- 'view': 'True'
- },
- 'waterbutlerURL': 'http://localhost:7777',
- 'kind': 'file',
- 'resource': '9bknu',
- 'name': 'Alphabet.txt',
- 'materialized': '/Alphabet.txt',
- 'created_utc': '2019-09-02T14:04:14.776301+00:00',
- 'modified': '2019-09-02T14:04:14.776301+00:00',
- 'modified_utc': '2019-09-02T14:04:14.776301+00:00'
- },
- 'fileId': '7ry42'
- }
- ]
- }
- },
- 'extra': []
- },
- 'q10': {
- 'comments': [],
- 'value': 'this is my stopping rule',
- 'extra': []
- },
- 'q13': {
- 'comments': [],
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'these are my indices',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- },
- 'extra': []
- },
- 'q12': {
- 'comments': [],
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'these are my measured variables',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- },
- 'extra': []
- },
- 'q19': {
- 'comments': [],
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'ANOVA',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- },
- 'extra': []
- }
-}
-
-prereg_registration_metadata_built = {
- 'q20': {
- 'comments': [],
- 'value': 'transforming and recoding',
- 'extra': []
- },
- 'q21': {
- 'comments': [],
- 'value': 'research plan follow up',
- 'extra': []
- },
- 'q22': {
- 'comments': [],
- 'value': 'criteria',
- 'extra': []
- },
- 'q23': {
- 'comments': [],
- 'value': 'this is how outliers will be handled',
- 'extra': []
- },
- 'q24': {
- 'comments': [],
- 'value': 'this is how I will deal with incomplete data.',
- 'extra': []
- },
- 'q25': {
- 'comments': [],
- 'value': 'this is my exploratory analysis',
- 'extra': []
- },
- 'q26': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'q27': {
- 'comments': [],
- 'value': 'No additional comments',
- 'extra': []
- },
- 'q1': {
- 'comments': [],
- 'value': 'This is my title',
- 'extra': []
- },
- 'q3': {
- 'comments': [],
- 'value': 'research questions',
- 'extra': []
- },
- 'q5': {
- 'comments': [],
- 'value': 'Registration prior to creation of data',
- 'extra': []
- },
- 'q4': {
- 'comments': [],
- 'value': 'this is my hypothesis',
- 'extra': []
- },
- 'q7': {
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'data collection procedures',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': '',
- 'extra': [
- {
- 'viewUrl': '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021',
- 'selectedFileName': 'Alphabet.txt',
- 'nodeId': '57zbh',
- 'sha256': 'dsdfds',
- 'data': {
- 'name': 'Alphabet.txt'
- }
- }
- ]
- }
- },
- },
- 'q6': {
- 'comments': [],
- 'value': 'Explanation of existing data',
- 'extra': []
- },
- 'q9': {
- 'comments': [],
- 'value': 'this is the rationale for my sample size',
- 'extra': []
- },
- 'q8': {
- 'comments': [],
- 'value': 'this is my sample size',
- 'extra': []
- },
- 'q15': {
- 'comments': [],
- 'value': [
- 'No blinding is involved in this study.',
- 'For studies that involve human subjects, they will not know the treatment group to which they have been assigned.',
- 'Research personnel who interact directly with the study subjects (either human or non-human subjects) will not be aware of the assigned treatments.'
- ],
- 'extra': []
- },
- 'q14': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'q17': {
- 'comments': [],
- 'value': 'this is my explanation of randomization',
- 'extra': []
- },
- 'q16': {
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'this is my study design',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- },
- },
- 'q11': {
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'these are my maniuplated variables',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': '',
- 'extra': [
- {
- 'selectedFileName': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'viewUrl': '/project/57zbh/files/osfstorage/5d6d22264d476c088fb8e01f',
- 'nodeId': '57zbh',
- 'sha256': 'sdf',
- 'data': {
- 'name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png'
- }
- },
- {
- 'selectedFileName': 'Alphabet.txt',
- 'viewUrl': '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021',
- 'nodeId': '57zbh',
- 'sha256': 'dsdfds',
- 'data': {
- 'name': 'Alphabet.txt'
- }
- }
- ]
- }
- },
- },
- 'q10': {
- 'comments': [],
- 'value': 'this is my stopping rule',
- 'extra': []
- },
- 'q13': {
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'these are my indices',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- },
- },
- 'q12': {
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'these are my measured variables',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- },
- },
- 'q19': {
- 'value': {
- 'question': {
- 'comments': [],
- 'value': 'ANOVA',
- 'extra': []
- },
- 'uploader': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- },
- }
-}
-
-veer_registration_metadata = {
- 'dataCollectionDates': {
- 'comments': [],
- 'value': '2020 - 2030',
- 'extra': []
- },
- 'description-methods': {
- 'comments': [],
- 'value': {
- 'exclusion-criteria': {
- 'comments': [],
- 'value': {
- 'question8b': {
- 'comments': [],
- 'value': 'these are failing check-tests',
- 'extra': []
- }
- },
- 'extra': []
- },
- 'design': {
- 'comments': [],
- 'value': {
- 'question2a': {
- 'comments': [],
- 'value': 'a. whether they are between participants',
- 'extra': []
- },
- 'question2b': {
- 'comments': [],
- 'value': 'these are my dependent variables',
- 'extra': []
- },
- 'question3b': {
- 'comments': [],
- 'value': 'These variables are acting as covariates.',
- 'extra': []
- }
- },
- 'extra': []
- },
- 'procedure': {
- 'comments': [],
- 'value': {
- 'question10b': {
- 'comments': [],
- 'value': 'describe all manipulations',
- 'extra': []
- }
- },
- 'extra': []
- },
- 'planned-sample': {
- 'comments': [],
- 'value': {
- 'question4b': {
- 'comments': [],
- 'value': 'these are the preselection rults',
- 'extra': []
- },
- 'question7b': {
- 'comments': [],
- 'value': 'here is my data collection termination rule',
- 'extra': []
- },
- 'question5b': {
- 'comments': [],
- 'value': 'here is how the data will be collected',
- 'extra': []
- },
- 'question6b-upload': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question6b': {
- 'comments': [],
- 'value': 'this is my planned sample size',
- 'extra': []
- }
- },
- 'extra': []
- }
- },
- 'extra': []
- },
- 'recommended-analysis': {
- 'comments': [],
- 'value': {
- 'specify': {
- 'comments': [],
- 'value': {
- 'question6c': {
- 'comments': [],
- 'value': 'I used a method of correction for multiple tests',
- 'extra': []
- },
- 'question8c': {
- 'comments': [],
- 'value': 'reliability criteria',
- 'extra': []
- },
- 'question9c': {
- 'comments': [],
- 'value': 'these are the anticipated data transformations',
- 'extra': []
- },
- 'question7c': {
- 'comments': [],
- 'value': 'method of missing data handling',
- 'extra': []
- },
- 'question11c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question10c': {
- 'comments': [],
- 'value': 'assumptions of analysses',
- 'extra': []
- }
- },
- 'extra': []
- }
- },
- 'extra': []
- },
- 'description-hypothesis': {
- 'comments': [],
- 'value': {
- 'question2a': {
- 'comments': [],
- 'value': 'expected interaction shape',
- 'extra': []
- },
- 'question1a': {
- 'comments': [],
- 'value': 'These are the essential elements',
- 'extra': []
- },
- 'question3a': {
- 'comments': [],
- 'value': 'predictions for successful checks',
- 'extra': []
- }
- },
- 'extra': []
- },
- 'confirmatory-analyses-first': {
- 'comments': [],
- 'value': {
- 'first': {
- 'comments': [],
- 'value': {
- 'question4c': {
- 'comments': [],
- 'value': 'this the covariate rationale',
- 'extra': []
- },
- 'question5c': {
- 'comments': [],
- 'value': 'these are techniques for null hypo testing',
- 'extra': []
- },
- 'question2c': {
- 'comments': [],
- 'value': 'this is the statistical technicque',
- 'extra': []
- },
- 'question3c': {
- 'comments': [],
- 'value': 'this is each variable role',
- 'extra': []
- },
- 'question1c': {
- 'comments': [],
- 'value': 'these are the relevant variables',
- 'extra': []
- }
- },
- 'extra': []
- }
- },
- 'extra': []
- },
- 'confirmatory-analyses-third': {
- 'comments': [],
- 'value': {
- 'third': {
- 'comments': [],
- 'value': {
- 'question4c': {
- 'comments': [],
- 'value': 'here was the rationale',
- 'extra': []
- },
- 'question5c': {
- 'comments': [],
- 'value': 'I used BAYESIAN STATISTICS',
- 'extra': []
- },
- 'question2c': {
- 'comments': [],
- 'value': 't-test',
- 'extra': []
- },
- 'question3c': {
- 'comments': [],
- 'value': 't-test informed covariate',
- 'extra': []
- },
- 'question1c': {
- 'comments': [],
- 'value': '3rd prediction',
- 'extra': []
- }
- },
- 'extra': []
- }
- },
- 'extra': []
- },
- 'datacompletion': {
- 'comments': [],
- 'value': 'No, data collection has not begun',
- 'extra': []
- },
- 'looked': {
- 'comments': [],
- 'value': 'Yes',
- 'extra': []
- },
- 'confirmatory-analyses-fourth': {
- 'comments': [],
- 'value': {
- 'fourth': {
- 'comments': [],
- 'value': {
- 'question4c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question5c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question2c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question3c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question1c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- },
- 'extra': []
- }
- },
- 'extra': []
- },
- 'confirmatory-analyses-further': {
- 'comments': [],
- 'value': {
- 'further': {
- 'comments': [],
- 'value': {
- 'question4c': {
- 'comments': [],
- 'value': 'this was the rationale',
- 'extra': []
- },
- 'question5c': {
- 'comments': [],
- 'value': 'also Bayesian',
- 'extra': []
- },
- 'question2c': {
- 'comments': [],
- 'value': 'i used a common statistical technique',
- 'extra': []
- },
- 'question3c': {
- 'comments': [],
- 'value': 'this was the independent variable',
- 'extra': []
- },
- 'question1c': {
- 'comments': [],
- 'value': 'FURTHER PREdictions:',
- 'extra': []
- }
- },
- 'extra': []
- }
- },
- 'extra': []
- },
- 'recommended-hypothesis': {
- 'comments': [],
- 'value': {
- 'question5a': {
- 'comments': [],
- 'value': 'This is the hypotheses that was tested.',
- 'extra': []
- },
- 'question4a': {
- 'comments': [],
- 'value': 'Alphabet.txt',
- 'extra': [
- {
- 'descriptionValue': '',
- 'nodeId': '9bknu',
- 'viewUrl': '/project/85qku/files/osfstorage/5d6d25024d476c088fb8e03b/',
- 'selectedFileName': 'Alphabet.txt',
- 'sha256': 'asdf',
- 'data': {
- 'links': {
- 'download': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d215b4d476c088fb8e000',
- 'move': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d215b4d476c088fb8e000',
- 'upload': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d215b4d476c088fb8e000?kind=file',
- 'delete': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d215b4d476c088fb8e000'
- },
- 'extra': {
- 'downloads': 0,
- 'latestVersionSeen': '',
- 'version': 1,
- 'hashes': {
- 'sha256': 'asdf',
- 'md5': 'asd'
- },
- 'guid': '',
- 'checkout': ''
- },
- 'accept': {
- 'acceptedFiles': 'True',
- 'maxSize': 5120
- },
- 'id': 'osfstorage/5d6d215b4d476c088fb8e000',
- 'size': 3169,
- 'nodeApiUrl': '/api/v1/project/9bknu/',
- 'nodeId': '9bknu',
- 'etag': 'b529584199f707abda42a42ec2f3c5a052d280e56fb3382f07efc77933053159',
- 'provider': 'osfstorage',
- 'type': 'files',
- 'nodeUrl': '/9bknu/',
- 'sizeInt': 3169,
- 'contentType': '',
- 'path': '/5d6d215b4d476c088fb8e000',
- 'permissions': {
- 'edit': 'True',
- 'view': 'True'
- },
- 'waterbutlerURL': 'http://localhost:7777',
- 'kind': 'file',
- 'resource': '9bknu',
- 'name': 'Alphabet.txt',
- 'materialized': '/Alphabet.txt',
- 'created_utc': '2019-09-02T14:04:14.776301+00:00',
- 'modified': '2019-09-02T14:04:14.776301+00:00',
- 'modified_utc': '2019-09-02T14:04:14.776301+00:00'
- },
- 'fileId': '7ry42'
- },
- {
- 'descriptionValue': '',
- 'nodeId': '9bknu',
- 'viewUrl': '/project/85qku/files/osfstorage/5d6d25014d476c088fb8e038/',
- 'selectedFileName': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'sha256': 'asdf',
- 'data': {
- 'links': {
- 'download': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d217f4d476c088fb8e00d',
- 'move': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d217f4d476c088fb8e00d',
- 'upload': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d217f4d476c088fb8e00d?kind=file',
- 'delete': 'http://localhost:7777/v1/resources/9bknu/providers/osfstorage/5d6d217f4d476c088fb8e00d'
- },
- 'extra': {
- 'downloads': 0,
- 'latestVersionSeen': '',
- 'version': 1,
- 'hashes': {
- 'sha256': 'asdf',
- 'md5': 'asd'
- },
- 'guid': '',
- 'checkout': ''
- },
- 'accept': {
- 'acceptedFiles': 'True',
- 'maxSize': 5120
- },
- 'id': 'osfstorage/5d6d217f4d476c088fb8e00d',
- 'size': 78257,
- 'nodeApiUrl': '/api/v1/project/9bknu/',
- 'nodeId': '9bknu',
- 'etag': 'd114466ad8f1e04c03e678fdaf642988c1accd1526bb6e81b34bf35612a77cbb',
- 'provider': 'osfstorage',
- 'type': 'files',
- 'nodeUrl': '/9bknu/',
- 'sizeInt': 78257,
- 'contentType': '',
- 'path': '/5d6d217f4d476c088fb8e00d',
- 'permissions': {
- 'edit': 'True',
- 'view': 'True'
- },
- 'waterbutlerURL': 'http://localhost:7777',
- 'kind': 'file',
- 'resource': '9bknu',
- 'name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'materialized': '/Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'created_utc': '2019-09-02T14:04:47.793337+00:00',
- 'modified': '2019-09-02T14:04:47.793337+00:00',
- 'modified_utc': '2019-09-02T14:04:47.793337+00:00'
- },
- 'fileId': 'nc8qy'
- }
- ]
- },
- 'question6a': {
- 'comments': [],
- 'value': 'this is the outcome that would be predicted by each theory',
- 'extra': []
- }
- },
- 'extra': []
- },
- 'additionalComments': {
- 'comments': [],
- 'value': 'no additional comments',
- 'extra': []
- },
- 'confirmatory-analyses-second': {
- 'comments': [],
- 'value': {
- 'second': {
- 'comments': [],
- 'value': {
- 'question4c': {
- 'comments': [],
- 'value': 'here is the rationale',
- 'extra': []
- },
- 'question5c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question2c': {
- 'comments': [],
- 'value': 'ANOVA test',
- 'extra': []
- },
- 'question3c': {
- 'comments': [],
- 'value': 'it was the covariate',
- 'extra': []
- },
- 'question1c': {
- 'comments': [],
- 'value': 'how 2nd prediction calculated',
- 'extra': []
- }
- },
- 'extra': []
- }
- },
- 'extra': []
- },
- 'recommended-methods': {
- 'comments': [],
- 'value': {
- 'procedure': {
- 'comments': [],
- 'value': {
- 'question9b-file': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question9b': {
- 'comments': [],
- 'value': 'set fail-safe levels of exclusions',
- 'extra': []
- }
- },
- 'extra': []
- }
- },
- 'extra': []
- }
-}
-
-veer_condensed = {
- 'dataCollectionDates': {
- 'comments': [],
- 'value': '2020 - 2030',
- 'extra': []
- },
- 'description-methods': {
- 'value': {
- 'exclusion-criteria': {
- 'value': {
- 'question8b': {
- 'comments': [],
- 'value': 'these are failing check-tests',
- 'extra': []
- }
- },
- },
- 'design': {
- 'value': {
- 'question2a': {
- 'comments': [],
- 'value': 'a. whether they are between participants',
- 'extra': []
- },
- 'question2b': {
- 'comments': [],
- 'value': 'these are my dependent variables',
- 'extra': []
- },
- 'question3b': {
- 'comments': [],
- 'value': 'These variables are acting as covariates.',
- 'extra': []
- }
- },
- },
- 'procedure': {
- 'value': {
- 'question10b': {
- 'comments': [],
- 'value': 'describe all manipulations',
- 'extra': []
- }
- },
- },
- 'planned-sample': {
- 'value': {
- 'question4b': {
- 'comments': [],
- 'value': 'these are the preselection rults',
- 'extra': []
- },
- 'question7b': {
- 'comments': [],
- 'value': 'here is my data collection termination rule',
- 'extra': []
- },
- 'question5b': {
- 'comments': [],
- 'value': 'here is how the data will be collected',
- 'extra': []
- },
- 'question6b-upload': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question6b': {
- 'comments': [],
- 'value': 'this is my planned sample size',
- 'extra': []
- }
- },
- }
- },
- },
- 'recommended-analysis': {
- 'value': {
- 'specify': {
- 'value': {
- 'question6c': {
- 'comments': [],
- 'value': 'I used a method of correction for multiple tests',
- 'extra': []
- },
- 'question8c': {
- 'comments': [],
- 'value': 'reliability criteria',
- 'extra': []
- },
- 'question9c': {
- 'comments': [],
- 'value': 'these are the anticipated data transformations',
- 'extra': []
- },
- 'question7c': {
- 'comments': [],
- 'value': 'method of missing data handling',
- 'extra': []
- },
- 'question11c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question10c': {
- 'comments': [],
- 'value': 'assumptions of analysses',
- 'extra': []
- }
- },
- }
- },
- },
- 'description-hypothesis': {
- 'value': {
- 'question2a': {
- 'comments': [],
- 'value': 'expected interaction shape',
- 'extra': []
- },
- 'question1a': {
- 'comments': [],
- 'value': 'These are the essential elements',
- 'extra': []
- },
- 'question3a': {
- 'comments': [],
- 'value': 'predictions for successful checks',
- 'extra': []
- }
- },
- },
- 'confirmatory-analyses-first': {
- 'value': {
- 'first': {
- 'value': {
- 'question4c': {
- 'comments': [],
- 'value': 'this the covariate rationale',
- 'extra': []
- },
- 'question5c': {
- 'comments': [],
- 'value': 'these are techniques for null hypo testing',
- 'extra': []
- },
- 'question2c': {
- 'comments': [],
- 'value': 'this is the statistical technicque',
- 'extra': []
- },
- 'question3c': {
- 'comments': [],
- 'value': 'this is each variable role',
- 'extra': []
- },
- 'question1c': {
- 'comments': [],
- 'value': 'these are the relevant variables',
- 'extra': []
- }
- },
- }
- },
- },
- 'confirmatory-analyses-third': {
- 'value': {
- 'third': {
- 'value': {
- 'question4c': {
- 'comments': [],
- 'value': 'here was the rationale',
- 'extra': []
- },
- 'question5c': {
- 'comments': [],
- 'value': 'I used BAYESIAN STATISTICS',
- 'extra': []
- },
- 'question2c': {
- 'comments': [],
- 'value': 't-test',
- 'extra': []
- },
- 'question3c': {
- 'comments': [],
- 'value': 't-test informed covariate',
- 'extra': []
- },
- 'question1c': {
- 'comments': [],
- 'value': '3rd prediction',
- 'extra': []
- }
- },
- }
- },
- },
- 'datacompletion': {
- 'comments': [],
- 'value': 'No, data collection has not begun',
- 'extra': []
- },
- 'looked': {
- 'comments': [],
- 'value': 'Yes',
- 'extra': []
- },
- 'confirmatory-analyses-fourth': {
- 'value': {
- 'fourth': {
- 'value': {
- 'question4c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question5c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question2c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question3c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question1c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- },
- }
- },
- },
- 'confirmatory-analyses-further': {
- 'value': {
- 'further': {
- 'value': {
- 'question4c': {
- 'comments': [],
- 'value': 'this was the rationale',
- 'extra': []
- },
- 'question5c': {
- 'comments': [],
- 'value': 'also Bayesian',
- 'extra': []
- },
- 'question2c': {
- 'comments': [],
- 'value': 'i used a common statistical technique',
- 'extra': []
- },
- 'question3c': {
- 'comments': [],
- 'value': 'this was the independent variable',
- 'extra': []
- },
- 'question1c': {
- 'comments': [],
- 'value': 'FURTHER PREdictions:',
- 'extra': []
- }
- },
- }
- },
- },
- 'recommended-hypothesis': {
- 'value': {
- 'question5a': {
- 'comments': [],
- 'value': 'This is the hypotheses that was tested.',
- 'extra': []
- },
- 'question4a': {
- 'comments': [],
- 'value': '',
- 'extra': [
- {
- 'selectedFileName': 'Alphabet.txt',
- 'viewUrl': '/project/85qku/files/osfstorage/5d6d25024d476c088fb8e03b',
- 'sha256': 'asdf',
- 'nodeId': '85qku',
- 'data': {
- 'name': 'Alphabet.txt',
- },
- },
- {
- 'selectedFileName': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'viewUrl': '/project/85qku/files/osfstorage/5d6d25014d476c088fb8e038',
- 'sha256': 'asdf',
- 'nodeId': '85qku',
- 'data': {
- 'name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png'
- }
- }
- ]
- },
- 'question6a': {
- 'comments': [],
- 'value': 'this is the outcome that would be predicted by each theory',
- 'extra': []
- }
- },
- },
- 'additionalComments': {
- 'comments': [],
- 'value': 'no additional comments',
- 'extra': []
- },
- 'confirmatory-analyses-second': {
- 'value': {
- 'second': {
- 'value': {
- 'question4c': {
- 'comments': [],
- 'value': 'here is the rationale',
- 'extra': []
- },
- 'question5c': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question2c': {
- 'comments': [],
- 'value': 'ANOVA test',
- 'extra': []
- },
- 'question3c': {
- 'comments': [],
- 'value': 'it was the covariate',
- 'extra': []
- },
- 'question1c': {
- 'comments': [],
- 'value': 'how 2nd prediction calculated',
- 'extra': []
- }
- },
- }
- },
- },
- 'recommended-methods': {
- 'value': {
- 'procedure': {
- 'value': {
- 'question9b-file': {
- 'comments': [],
- 'value': '',
- 'extra': []
- },
- 'question9b': {
- 'comments': [],
- 'value': 'set fail-safe levels of exclusions',
- 'extra': []
- }
- },
- }
- },
- }
-}
-
-veer_registration_responses = {
- 'confirmatory-analyses-third.third.question4c': 'here was the rationale',
- 'recommended-hypothesis.question5a': 'This is the hypotheses that was tested.',
- 'confirmatory-analyses-further.further.question4c': 'this was the rationale',
- 'confirmatory-analyses-fourth.fourth.question5c': '',
- 'description-methods.design.question3b': 'These variables are acting as covariates.',
- 'confirmatory-analyses-further.further.question2c': 'i used a common statistical technique',
- 'description-methods.exclusion-criteria.question8b': 'these are failing check-tests',
- 'description-methods.planned-sample.question4b': 'these are the preselection rults',
- 'confirmatory-analyses-second.second.question3c': 'it was the covariate',
- 'dataCollectionDates': '2020 - 2030',
- 'recommended-analysis.specify.question6c': 'I used a method of correction for multiple tests',
- 'confirmatory-analyses-first.first.question2c': 'this is the statistical technicque',
- 'description-methods.procedure.question10b': 'describe all manipulations',
- 'recommended-analysis.specify.question11c': [],
- 'recommended-methods.procedure.question9b': 'set fail-safe levels of exclusions',
- 'description-hypothesis.question2a': 'expected interaction shape',
- 'confirmatory-analyses-second.second.question5c': '',
- 'confirmatory-analyses-first.first.question4c': 'this the covariate rationale',
- 'description-methods.planned-sample.question6b': 'this is my planned sample size',
- 'confirmatory-analyses-third.third.question1c': '3rd prediction',
- 'confirmatory-analyses-fourth.fourth.question1c': '',
- 'recommended-analysis.specify.question9c': 'these are the anticipated data transformations',
- 'confirmatory-analyses-fourth.fourth.question2c': '',
- 'confirmatory-analyses-third.third.question3c': 't-test informed covariate',
- 'recommended-hypothesis.question6a': 'this is the outcome that would be predicted by each theory',
- 'confirmatory-analyses-fourth.fourth.question4c': '',
- 'confirmatory-analyses-second.second.question4c': 'here is the rationale',
- 'recommended-hypothesis.question4a': [
- {
- 'file_name': 'Alphabet.txt',
- 'file_id': '5d6d25024d476c088fb8e03b',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/85qku/files/osfstorage/5d6d25024d476c088fb8e03b'),
- },
- 'file_hashes': {
- 'sha256': 'asdf',
- },
- },
- {
- 'file_name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'file_id': '5d6d25014d476c088fb8e038',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/85qku/files/osfstorage/5d6d25014d476c088fb8e038'),
- },
- 'file_hashes': {
- 'sha256': 'asdf',
- },
- }
- ],
- 'confirmatory-analyses-third.third.question5c': 'I used BAYESIAN STATISTICS',
- 'description-methods.design.question2b': 'these are my dependent variables',
- 'description-methods.design.question2a': 'a. whether they are between participants',
- 'datacompletion': 'No, data collection has not begun',
- 'description-methods.planned-sample.question5b': 'here is how the data will be collected',
- 'confirmatory-analyses-further.further.question3c': 'this was the independent variable',
- 'confirmatory-analyses-further.further.question1c': 'FURTHER PREdictions:',
- 'confirmatory-analyses-second.second.question2c': 'ANOVA test',
- 'additionalComments': 'no additional comments',
- 'confirmatory-analyses-first.first.question1c': 'these are the relevant variables',
- 'recommended-analysis.specify.question7c': 'method of missing data handling',
- 'confirmatory-analyses-first.first.question3c': 'this is each variable role',
- 'description-hypothesis.question3a': 'predictions for successful checks',
- 'confirmatory-analyses-further.further.question5c': 'also Bayesian',
- 'recommended-analysis.specify.question10c': 'assumptions of analysses',
- 'recommended-methods.procedure.question9b-file': [],
- 'description-hypothesis.question1a': 'These are the essential elements',
- 'description-methods.planned-sample.question7b': 'here is my data collection termination rule',
- 'confirmatory-analyses-first.first.question5c': 'these are techniques for null hypo testing',
- 'looked': 'Yes',
- 'confirmatory-analyses-second.second.question1c': 'how 2nd prediction calculated',
- 'confirmatory-analyses-third.third.question2c': 't-test',
- 'confirmatory-analyses-fourth.fourth.question3c': '',
- 'recommended-analysis.specify.question8c': 'reliability criteria',
- 'description-methods.planned-sample.question6b-upload': []
-}
-
-
-@pytest.fixture()
-def osf_standard_schema():
- return RegistrationSchema.objects.get(
- name='OSF-Standard Pre-Data Collection Registration',
- schema_version=2
- )
-
-@pytest.fixture()
-def prereg_schema():
- return RegistrationSchema.objects.get(
- name='Prereg Challenge',
- schema_version=2
- )
-
-@pytest.fixture()
-def veer_schema():
- return RegistrationSchema.objects.get(
- name__icontains='Pre-Registration in Social Psychology',
- schema_version=2
- )
-
-
-@pytest.mark.django_db
-class TestMigrateDraftRegistrationRegistrationResponses:
-
- @pytest.fixture()
- def draft_osf_standard(self, osf_standard_schema):
- draft = DraftRegistrationFactory(
- registration_schema=osf_standard_schema,
- registration_metadata={
- 'looked': {
- 'comments': [],
- 'value': 'Yes',
- 'extra': []
- },
- 'datacompletion': {
- 'comments': [],
- 'value': 'No, data collection has not begun',
- 'extra': []
- },
- 'comments': {
- 'comments': [],
- 'value': 'more comments',
- 'extra': []
- }
- }
- )
- draft.registration_responses = {}
- draft.save()
- return draft
-
- @pytest.fixture()
- def empty_draft_osf_standard(self, osf_standard_schema):
- draft = DraftRegistrationFactory(
- registration_schema=osf_standard_schema,
- registration_metadata={}
- )
- draft.registration_responses = {}
- draft.registration_responses_migrated = False
- draft.save()
- return draft
-
- @pytest.fixture()
- def draft_prereg(self, prereg_schema):
- draft = DraftRegistrationFactory(
- registration_schema=prereg_schema,
- registration_metadata=prereg_registration_metadata
- )
- draft.registration_responses = {}
- draft.registration_responses_migrated = False
- draft.save()
- return draft
-
- @pytest.fixture()
- def draft_veer(self, veer_schema):
- draft = DraftRegistrationFactory(
- registration_schema=veer_schema,
- registration_metadata=veer_registration_metadata
- )
- draft.registration_responses = {}
- draft.registration_responses_migrated = False
- draft.save()
- return draft
-
- def test_migrate_empty_draft(self, app, empty_draft_osf_standard):
- assert empty_draft_osf_standard.registration_responses == {}
- assert empty_draft_osf_standard.registration_responses_migrated is False
-
- migrate_draft_registrations(dry_run=False)
-
- empty_draft_osf_standard.reload()
- assert empty_draft_osf_standard.registration_responses_migrated is True
- responses = empty_draft_osf_standard.registration_responses
- assert responses['looked'] == ''
- assert responses['datacompletion'] == ''
- assert responses['comments'] == ''
-
- def test_migrate_draft_registrations(self, app, draft_osf_standard, draft_prereg, draft_veer):
- drafts = [
- draft_osf_standard,
- draft_prereg,
- draft_veer
- ]
-
- for draft in drafts:
- assert draft.registration_responses == {}
- draft.registration_responses_migrated = False
- draft.save()
-
- migrate_draft_registrations(dry_run=False)
-
- # Verifying OSF Standard Migration
- draft_osf_standard.reload()
- assert draft_osf_standard.registration_responses_migrated is True
- responses = draft_osf_standard.registration_responses
- assert responses['looked'] == 'Yes'
- assert responses['datacompletion'] == 'No, data collection has not begun'
- assert responses['comments'] == 'more comments'
-
- # Verifying Prereg Migration
- draft_prereg.reload()
- assert draft_prereg.registration_responses_migrated is True
- responses = draft_prereg.registration_responses
- assert responses['q12.uploader'] == []
- assert responses['q7.question'] == 'data collection procedures'
- assert responses['q21'] == 'research plan follow up'
- assert responses['q22'] == 'criteria'
- assert responses['q23'] == 'this is how outliers will be handled'
- assert responses['q24'] == 'this is how I will deal with incomplete data.'
- assert responses['q25'] == 'this is my exploratory analysis'
- assert responses['q26'] == []
- assert responses['q27'] == 'No additional comments'
- assert responses['q12.question'] == 'these are my measured variables'
- assert responses['q1'] == 'This is my title'
- assert responses['q3'] == 'research questions'
- assert responses['q5'] == 'Registration prior to creation of data'
- assert responses['q4'] == 'this is my hypothesis'
- assert responses['q6'] == 'Explanation of existing data'
- assert responses['q9'] == 'this is the rationale for my sample size'
- assert responses['q8'] == 'this is my sample size'
- assert responses['q13.question'] == 'these are my indices'
- assert responses['q19.uploader'] == []
- assert responses['q11.uploader'] == [
- {
- 'file_name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'file_id': '5d6d22264d476c088fb8e01f',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22264d476c088fb8e01f'),
- 'download': urljoin(settings.DOMAIN, '/download/5d6d22264d476c088fb8e01f'),
- },
- 'file_hashes': {
- 'sha256': 'sdf',
- },
- },
- {
- 'file_name': 'Alphabet.txt',
- 'file_id': '5d6d22274d476c088fb8e021',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021'),
- 'download': urljoin(settings.DOMAIN, '/download/5d6d22274d476c088fb8e021'),
- },
- 'file_hashes': {
- 'sha256': 'asdf',
- },
- }
- ]
- assert responses['q16.question'] == 'this is my study design'
- assert responses['q15'] == [
- 'No blinding is involved in this study.',
- 'For studies that involve human subjects, they will not know the treatment group to which they have been assigned.',
- 'Research personnel who interact directly with the study subjects (either human or non-human subjects) will not be aware of the assigned treatments.'
- ]
- assert responses['q14'] == ''
- assert responses['q17'] == 'this is my explanation of randomization'
- assert responses['q10'] == 'this is my stopping rule'
- assert responses['q11.question'] == 'these are my maniuplated variables'
- assert responses['q16.uploader'] == []
- assert responses['q19.question'] == 'ANOVA'
- assert responses['q13.uploader'] == []
- assert responses['q7.uploader'] == [
- {
- 'file_name': 'Alphabet.txt',
- 'file_id': '5d6d22274d476c088fb8e021',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021'),
- 'download': urljoin(settings.DOMAIN, '/download/5d6d22274d476c088fb8e021'),
- },
- 'file_hashes': {
- 'sha256': 'dsdfds'
- },
- }
- ]
-
- # Verifying Veer Migration
- draft_veer.reload()
- assert draft_veer.registration_responses_migrated is True
- responses = draft_veer.registration_responses
- assert responses['confirmatory-analyses-third.third.question4c'] == 'here was the rationale'
- assert responses['recommended-hypothesis.question5a'] == 'This is the hypotheses that was tested.'
- assert responses['confirmatory-analyses-further.further.question4c'] == 'this was the rationale'
- assert responses['confirmatory-analyses-fourth.fourth.question5c'] == ''
- assert responses['description-methods.design.question3b'] == 'These variables are acting as covariates.'
- assert responses['confirmatory-analyses-second.second.question1c'] == 'how 2nd prediction calculated'
- assert responses['description-methods.exclusion-criteria.question8b'] == 'these are failing check-tests'
- assert responses['description-methods.planned-sample.question4b'] == 'these are the preselection rults'
- assert responses['confirmatory-analyses-second.second.question3c'] == 'it was the covariate'
- assert responses['recommended-analysis.specify.question6c'] == 'I used a method of correction for multiple tests'
- assert responses['confirmatory-analyses-first.first.question2c'] == 'this is the statistical technicque'
- assert responses['description-methods.procedure.question10b'] == 'describe all manipulations'
- assert responses['recommended-analysis.specify.question11c'] == []
- assert responses['recommended-methods.procedure.question9b'] == 'set fail-safe levels of exclusions'
- assert responses['description-hypothesis.question2a'] == 'expected interaction shape'
- assert responses['confirmatory-analyses-second.second.question5c'] == ''
- assert responses['confirmatory-analyses-first.first.question4c'] == 'this the covariate rationale'
- assert responses['description-methods.planned-sample.question6b'] == 'this is my planned sample size'
- assert responses['confirmatory-analyses-third.third.question1c'] == '3rd prediction'
- assert responses['recommended-analysis.specify.question9c'] == 'these are the anticipated data transformations'
- assert responses['confirmatory-analyses-fourth.fourth.question2c'] == ''
- assert responses['confirmatory-analyses-third.third.question3c'] == 't-test informed covariate'
- assert responses['recommended-hypothesis.question6a'] == 'this is the outcome that would be predicted by each theory'
- assert responses['confirmatory-analyses-fourth.fourth.question4c'] == ''
- assert responses['confirmatory-analyses-second.second.question4c'] == 'here is the rationale'
- assert responses['recommended-hypothesis.question4a'] == [
- {
- 'file_name': 'Alphabet.txt',
- 'file_id': '5d6d25024d476c088fb8e03b',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/85qku/files/osfstorage/5d6d25024d476c088fb8e03b'),
- 'download': urljoin(settings.DOMAIN, '/download/5d6d25024d476c088fb8e03b'),
- },
- 'file_hashes': {
- 'sha256': 'asdf',
- },
- },
- {
- 'file_name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'file_id': '5d6d25014d476c088fb8e038',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/85qku/files/osfstorage/5d6d25014d476c088fb8e038'),
- 'download': urljoin(settings.DOMAIN, '/download/5d6d25014d476c088fb8e038'),
- },
- 'file_hashes': {
- 'sha256': 'asdf',
- },
- }
- ]
- assert responses['confirmatory-analyses-third.third.question5c'] == 'I used BAYESIAN STATISTICS'
- assert responses['description-methods.design.question2b'] == 'these are my dependent variables'
- assert responses['description-methods.design.question2a'] == 'a. whether they are between participants'
- assert responses['datacompletion'] == 'No, data collection has not begun'
- assert responses['description-methods.planned-sample.question5b'] == 'here is how the data will be collected'
- assert responses['confirmatory-analyses-further.further.question3c'] == 'this was the independent variable'
- assert responses['confirmatory-analyses-further.further.question1c'] == 'FURTHER PREdictions:'
- assert responses['confirmatory-analyses-second.second.question2c'] == 'ANOVA test'
- assert responses['additionalComments'] == 'no additional comments'
- assert responses['looked'] == 'Yes'
- assert responses['confirmatory-analyses-first.first.question1c'] == 'these are the relevant variables'
- assert responses['recommended-analysis.specify.question7c'] == 'method of missing data handling'
- assert responses['confirmatory-analyses-first.first.question3c'] == 'this is each variable role'
- assert responses['description-hypothesis.question3a'] == 'predictions for successful checks'
- assert responses['confirmatory-analyses-further.further.question2c'] == 'i used a common statistical technique'
- assert responses['confirmatory-analyses-further.further.question5c'] == 'also Bayesian'
- assert responses['recommended-analysis.specify.question10c'] == 'assumptions of analysses'
- assert responses['recommended-methods.procedure.question9b-file'] == []
- assert responses['description-hypothesis.question1a'] == 'These are the essential elements'
- assert responses['description-methods.planned-sample.question7b'] == 'here is my data collection termination rule'
- assert responses['confirmatory-analyses-first.first.question5c'] == 'these are techniques for null hypo testing'
- assert responses['dataCollectionDates'] == '2020 - 2030'
- assert responses['confirmatory-analyses-fourth.fourth.question1c'] == ''
- assert responses['confirmatory-analyses-third.third.question2c'] == 't-test'
- assert responses['confirmatory-analyses-fourth.fourth.question3c'] == ''
- assert responses['recommended-analysis.specify.question8c'] == 'reliability criteria'
- assert responses['description-methods.planned-sample.question6b-upload'] == []
-
-
-@pytest.mark.django_db
-class TestMigrateRegistrationRegistrationResponses:
-
- @pytest.fixture()
- def reg_osf_standard(self, osf_standard_schema):
- draft = DraftRegistrationFactory(
- registration_schema=osf_standard_schema,
- registration_metadata={
- 'looked': {
- 'comments': [],
- 'value': 'Yes',
- 'extra': []
- },
- 'datacompletion': {
- 'comments': [],
- 'value': 'No, data collection has not begun',
- 'extra': []
- },
- 'comments': {
- 'comments': [],
- 'value': 'more comments',
- 'extra': []
- }
- }
- )
- return RegistrationFactory(
- schema=osf_standard_schema,
- draft_registration=draft,
- project=draft.branched_from
- )
-
- @pytest.fixture()
- def reg_prereg(self, prereg_schema):
- draft = DraftRegistrationFactory(
- registration_schema=prereg_schema,
- registration_metadata=prereg_registration_metadata
- )
- return RegistrationFactory(
- schema=prereg_schema,
- draft_registration=draft,
- project=draft.branched_from
- )
-
- @pytest.fixture()
- def reg_veer(self, veer_schema):
- draft = DraftRegistrationFactory(
- registration_metadata=veer_registration_metadata,
- registration_schema=veer_schema,
- )
- return RegistrationFactory(
- schema=veer_schema,
- draft_registration=draft,
- project=draft.branched_from
- )
-
- def test_migrate_registrations(self, app, reg_osf_standard, reg_prereg, reg_veer):
- regs = [
- reg_osf_standard,
- reg_prereg,
- reg_veer
- ]
-
- for reg in regs:
- reg.registration_responses = {}
- reg.registration_responses_migrated = False
- reg.save()
-
- migrate_registrations(dry_run=False)
-
- # Verifying OSF Standard Migration
- reg_osf_standard.reload()
- assert reg_osf_standard.registration_responses_migrated is True
- responses = reg_osf_standard.registration_responses
- assert responses['looked'] == 'Yes'
- assert responses['datacompletion'] == 'No, data collection has not begun'
- assert responses['comments'] == 'more comments'
-
- # Verifying Prereg Migration
- reg_prereg.reload()
- assert reg_prereg.registration_responses_migrated is True
- responses = reg_prereg.registration_responses
- assert responses['q12.uploader'] == []
- assert responses['q7.question'] == 'data collection procedures'
- assert responses['q21'] == 'research plan follow up'
- assert responses['q22'] == 'criteria'
- assert responses['q23'] == 'this is how outliers will be handled'
- assert responses['q24'] == 'this is how I will deal with incomplete data.'
- assert responses['q25'] == 'this is my exploratory analysis'
- assert responses['q26'] == []
- assert responses['q27'] == 'No additional comments'
- assert responses['q12.question'] == 'these are my measured variables'
- assert responses['q1'] == 'This is my title'
- assert responses['q3'] == 'research questions'
- assert responses['q5'] == 'Registration prior to creation of data'
- assert responses['q4'] == 'this is my hypothesis'
- assert responses['q6'] == 'Explanation of existing data'
- assert responses['q9'] == 'this is the rationale for my sample size'
- assert responses['q8'] == 'this is my sample size'
- assert responses['q13.question'] == 'these are my indices'
- assert responses['q19.uploader'] == []
- assert responses['q11.uploader'] == [
- {
- 'file_name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'file_id': '5d6d22264d476c088fb8e01f',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22264d476c088fb8e01f'),
- 'download': urljoin(settings.DOMAIN, '/download/5d6d22264d476c088fb8e01f'),
- },
- 'file_hashes': {
- 'sha256': 'sdf',
- },
- },
- {
- 'file_name': 'Alphabet.txt',
- 'file_id': '5d6d22274d476c088fb8e021',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021'),
- 'download': urljoin(settings.DOMAIN, '/download/5d6d22274d476c088fb8e021'),
- },
- 'file_hashes': {
- 'sha256': 'asdf',
- },
- }
- ]
- assert responses['q16.question'] == 'this is my study design'
- assert responses['q15'] == [
- 'No blinding is involved in this study.',
- 'For studies that involve human subjects, they will not know the treatment group to which they have been assigned.',
- 'Research personnel who interact directly with the study subjects (either human or non-human subjects) will not be aware of the assigned treatments.'
- ]
- assert responses['q14'] == ''
- assert responses['q17'] == 'this is my explanation of randomization'
- assert responses['q10'] == 'this is my stopping rule'
- assert responses['q11.question'] == 'these are my maniuplated variables'
- assert responses['q16.uploader'] == []
- assert responses['q19.question'] == 'ANOVA'
- assert responses['q13.uploader'] == []
- assert responses['q7.uploader'] == [
- {
- 'file_name': 'Alphabet.txt',
- 'file_id': '5d6d22274d476c088fb8e021',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/57zbh/files/osfstorage/5d6d22274d476c088fb8e021'),
- 'download': urljoin(settings.DOMAIN, '/download/5d6d22274d476c088fb8e021'),
- },
- 'file_hashes': {
- 'sha256': 'dsdfds',
- },
- }
- ]
-
- # Verifying Veer Migration
- reg_veer.reload()
- assert reg_veer.registration_responses_migrated is True
- responses = reg_veer.registration_responses
- assert responses['confirmatory-analyses-third.third.question4c'] == 'here was the rationale'
- assert responses['recommended-hypothesis.question5a'] == 'This is the hypotheses that was tested.'
- assert responses['confirmatory-analyses-further.further.question4c'] == 'this was the rationale'
- assert responses['confirmatory-analyses-fourth.fourth.question5c'] == ''
- assert responses['description-methods.design.question3b'] == 'These variables are acting as covariates.'
- assert responses['confirmatory-analyses-second.second.question1c'] == 'how 2nd prediction calculated'
- assert responses['description-methods.exclusion-criteria.question8b'] == 'these are failing check-tests'
- assert responses['description-methods.planned-sample.question4b'] == 'these are the preselection rults'
- assert responses['confirmatory-analyses-second.second.question3c'] == 'it was the covariate'
- assert responses['recommended-analysis.specify.question6c'] == 'I used a method of correction for multiple tests'
- assert responses['confirmatory-analyses-first.first.question2c'] == 'this is the statistical technicque'
- assert responses['description-methods.procedure.question10b'] == 'describe all manipulations'
- assert responses['recommended-analysis.specify.question11c'] == []
- assert responses['recommended-methods.procedure.question9b'] == 'set fail-safe levels of exclusions'
- assert responses['description-hypothesis.question2a'] == 'expected interaction shape'
- assert responses['confirmatory-analyses-second.second.question5c'] == ''
- assert responses['confirmatory-analyses-first.first.question4c'] == 'this the covariate rationale'
- assert responses['description-methods.planned-sample.question6b'] == 'this is my planned sample size'
- assert responses['confirmatory-analyses-third.third.question1c'] == '3rd prediction'
- assert responses['recommended-analysis.specify.question9c'] == 'these are the anticipated data transformations'
- assert responses['confirmatory-analyses-fourth.fourth.question2c'] == ''
- assert responses['confirmatory-analyses-third.third.question3c'] == 't-test informed covariate'
- assert responses['recommended-hypothesis.question6a'] == 'this is the outcome that would be predicted by each theory'
- assert responses['confirmatory-analyses-fourth.fourth.question4c'] == ''
- assert responses['confirmatory-analyses-second.second.question4c'] == 'here is the rationale'
- assert responses['recommended-hypothesis.question4a'] == [
- {
- 'file_name': 'Alphabet.txt',
- 'file_id': '5d6d25024d476c088fb8e03b',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/85qku/files/osfstorage/5d6d25024d476c088fb8e03b'),
- 'download': urljoin(settings.DOMAIN, '/download/5d6d25024d476c088fb8e03b'),
- },
- 'file_hashes': {
- 'sha256': 'asdf',
- },
- },
- {
- 'file_name': 'Screen Shot 2019-08-30 at 9.04.01 AM.png',
- 'file_id': '5d6d25014d476c088fb8e038',
- 'file_urls': {
- 'html': urljoin(settings.DOMAIN, '/project/85qku/files/osfstorage/5d6d25014d476c088fb8e038'),
- 'download': urljoin(settings.DOMAIN, '/download/5d6d25014d476c088fb8e038'),
- },
- 'file_hashes': {
- 'sha256': 'asdf',
- },
- }
- ]
- assert responses['confirmatory-analyses-third.third.question5c'] == 'I used BAYESIAN STATISTICS'
- assert responses['description-methods.design.question2b'] == 'these are my dependent variables'
- assert responses['description-methods.design.question2a'] == 'a. whether they are between participants'
- assert responses['datacompletion'] == 'No, data collection has not begun'
- assert responses['description-methods.planned-sample.question5b'] == 'here is how the data will be collected'
- assert responses['confirmatory-analyses-further.further.question3c'] == 'this was the independent variable'
- assert responses['confirmatory-analyses-further.further.question1c'] == 'FURTHER PREdictions:'
- assert responses['confirmatory-analyses-second.second.question2c'] == 'ANOVA test'
- assert responses['additionalComments'] == 'no additional comments'
- assert responses['looked'] == 'Yes'
- assert responses['confirmatory-analyses-first.first.question1c'] == 'these are the relevant variables'
- assert responses['recommended-analysis.specify.question7c'] == 'method of missing data handling'
- assert responses['confirmatory-analyses-first.first.question3c'] == 'this is each variable role'
- assert responses['description-hypothesis.question3a'] == 'predictions for successful checks'
- assert responses['confirmatory-analyses-further.further.question2c'] == 'i used a common statistical technique'
- assert responses['confirmatory-analyses-further.further.question5c'] == 'also Bayesian'
- assert responses['recommended-analysis.specify.question10c'] == 'assumptions of analysses'
- assert responses['recommended-methods.procedure.question9b-file'] == []
- assert responses['description-hypothesis.question1a'] == 'These are the essential elements'
- assert responses['description-methods.planned-sample.question7b'] == 'here is my data collection termination rule'
- assert responses['confirmatory-analyses-first.first.question5c'] == 'these are techniques for null hypo testing'
- assert responses['dataCollectionDates'] == '2020 - 2030'
- assert responses['confirmatory-analyses-fourth.fourth.question1c'] == ''
- assert responses['confirmatory-analyses-third.third.question2c'] == 't-test'
- assert responses['confirmatory-analyses-fourth.fourth.question3c'] == ''
- assert responses['recommended-analysis.specify.question8c'] == 'reliability criteria'
- assert responses['description-methods.planned-sample.question6b-upload'] == []
diff --git a/osf_tests/test_archiver.py b/osf_tests/test_archiver.py
index 3855d169acb..70bd476f9e2 100644
--- a/osf_tests/test_archiver.py
+++ b/osf_tests/test_archiver.py
@@ -656,8 +656,8 @@ def test_archive_success_different_name_same_sha(self):
with mock.patch.object(BaseStorageAddon, '_get_file_tree', mock.Mock(return_value=file_tree)):
job = factories.ArchiveJobFactory(initiator=registration.creator)
archive_success(registration._id, job._id)
- for key, question in registration.registered_meta[schema._id].items():
- assert question['extra'][0]['selectedFileName'] == fake_file['name']
+ # for key, question in registration.registered_meta[schema._id].items():
+ # assert question['extra'][0]['selectedFileName'] == fake_file['name']
def test_archive_failure_different_name_same_sha(self):
file_tree = file_tree_factory(0, 0, 0)
@@ -712,8 +712,8 @@ def test_archive_success_same_file_in_component(self):
archive_success(registration._id, job._id)
registration.reload()
child_reg = registration.nodes[0]
- for key, question in registration.registered_meta[schema._id].items():
- assert child_reg._id in question['extra'][0]['viewUrl']
+ for rr in registration.registered_responses.all():
+ assert child_reg._id in rr.url # rr question['extra'][0]['viewUrl']
class TestArchiverUtils(ArchiverTestCase):
diff --git a/osf_tests/test_draft_registration.py b/osf_tests/test_draft_registration.py
index 225719efdd4..fd9d2bdda91 100644
--- a/osf_tests/test_draft_registration.py
+++ b/osf_tests/test_draft_registration.py
@@ -92,40 +92,6 @@ def test_register_no_title_fails(self):
assert str(e.value) == 'Draft Registration must have title to be registered'
- def test_update_metadata_updates_registration_responses(self, project):
- schema = RegistrationSchema.objects.get(
- name='OSF-Standard Pre-Data Collection Registration',
- schema_version=2
- )
- draft = factories.DraftRegistrationFactory(registration_schema=schema, branched_from=project)
- new_metadata = {
- 'looked': {
- 'comments': [],
- 'value': 'Yes',
- 'extra': []
- },
- 'datacompletion': {
- 'comments': [],
- 'value': 'No, data collection has not begun',
- 'extra': []
- },
- 'comments': {
- 'comments': [],
- 'value': '',
- 'extra': []
- }
- }
- draft.update_metadata(new_metadata)
- draft.save()
- # To preserve both workflows, if update_metadata is called,
- # a flattened version of that metadata is stored in
- # registration_responses
- assert draft.registration_responses == {
- 'looked': 'Yes',
- 'datacompletion': 'No, data collection has not begun',
- 'comments': ''
- }
-
def test_update_registration_responses(self, project):
schema = RegistrationSchema.objects.get(
name='OSF-Standard Pre-Data Collection Registration',
@@ -571,6 +537,33 @@ def test_update_contributor(self, draft_registration, auth):
assert draft_registration.get_permissions(new_contrib) == [READ]
assert draft_registration.get_visible(new_contrib) is False
+ def test_remove_admin_permission(self, draft_registration, auth):
+ admin_user = auth.user
+ assert len(draft_registration.contributors) == 1
+ assert draft_registration.has_permission(admin_user, ADMIN) is True
+
+ with pytest.raises(DraftRegistrationStateError) as exc_info:
+ draft_registration.update_contributor(
+ admin_user,
+ WRITE,
+ draft_registration.get_visible(admin_user),
+ auth=auth
+ )
+ assert str(exc_info.value) == f"{admin_user.fullname} is the only admin."
+
+ # user should be able to remove their own admin permission if there're 2+ admin contributors
+ new_contrib = factories.AuthUserFactory()
+ draft_registration.add_contributor(new_contrib, permissions=ADMIN, auth=auth)
+ assert draft_registration.has_permission(new_contrib, ADMIN) is True
+
+ draft_registration.update_contributor(
+ admin_user,
+ WRITE,
+ draft_registration.get_visible(admin_user),
+ auth=auth,
+ )
+ assert draft_registration.has_permission(admin_user, ADMIN) is False
+
def test_update_contributor_non_admin_raises_error(self, draft_registration, auth):
non_admin = factories.AuthUserFactory()
draft_registration.add_contributor(
diff --git a/osf_tests/test_node.py b/osf_tests/test_node.py
index 4e88d1c2bf7..b4cd13d9eb4 100644
--- a/osf_tests/test_node.py
+++ b/osf_tests/test_node.py
@@ -711,7 +711,6 @@ def test_project_factory(self):
assert hasattr(node, 'forked_date')
assert bool(node.title)
assert hasattr(node, 'description')
- assert hasattr(node, 'registered_meta')
assert hasattr(node, 'registered_user')
assert hasattr(node, 'registered_schema')
assert bool(node.creator)
@@ -2003,7 +2002,6 @@ def test_register_node_propagates_schema_and_data_to_children(self, mock_signal,
r1 = reg.nodes[0]
r1a = r1.nodes[0]
for r in [reg, r1, r1a]:
- assert r.registered_meta[meta_schema._id] == data
assert r.registration_responses == expected_flat_data
assert r.registered_schema.first() == meta_schema
@@ -2101,7 +2099,6 @@ def test_register_node_contributor_questions(self, mock_signal, user, auth):
# so there aren't discrepancies
# assert that other registration_metadata not overridden
- assert registration.registered_meta[registration.registration_schema._id]['q3']['value'] == 'research questions'
assert registration.registration_responses['q3'] == 'research questions'
@@ -2254,11 +2251,9 @@ def test_set_privacy(self, node, auth):
assert last_logged_before_method_call != node.last_logged
node.save()
assert bool(node.is_public) is True
- assert node.keenio_read_key != ''
node.set_privacy('private', auth=auth)
node.save()
assert bool(node.is_public) is False
- assert node.keenio_read_key == ''
assert node.logs.first().action == NodeLog.MADE_PRIVATE
assert last_logged_before_method_call != node.last_logged
diff --git a/osf_tests/test_registrations.py b/osf_tests/test_registrations.py
index 970b3670a3e..ef3e9742168 100644
--- a/osf_tests/test_registrations.py
+++ b/osf_tests/test_registrations.py
@@ -20,12 +20,6 @@
from api.providers.workflows import Workflows
from osf.migrations import update_provider_auth_groups
from osf.models.action import RegistrationAction
-from osf_tests.management_commands.test_migration_registration_responses import (
- prereg_registration_responses,
- prereg_registration_metadata_built,
- veer_registration_responses,
- veer_condensed
-)
from osf.utils.workflows import (
RegistrationModerationStates,
RegistrationModerationTriggers,
@@ -615,25 +609,6 @@ def veer_schema(self):
schema_version=2
)
- def test_expand_registration_responses(self, draft_prereg):
- draft_prereg.registration_responses = prereg_registration_responses
- draft_prereg.save()
- assert draft_prereg.registration_metadata == {}
-
- registration_metadata = draft_prereg.expand_registration_responses()
-
- assert registration_metadata == prereg_registration_metadata_built
-
- def test_expand_registration_responses_veer(self, draft_veer):
- draft_veer.registration_responses = veer_registration_responses
- draft_veer.save()
- assert draft_veer.registration_metadata == {}
-
- registration_metadata = draft_veer.expand_registration_responses()
-
- assert registration_metadata == veer_condensed
-
-
class TestRegistationModerationStates:
@pytest.fixture
diff --git a/package.json b/package.json
index e97d5265722..b199a03a62e 100644
--- a/package.json
+++ b/package.json
@@ -33,9 +33,7 @@
"js-cookie": "3.0.1",
"js-md5": "^0.7.3",
"jstimezonedetect": "^1.0.6",
- "keen-analysis": "^1.1.0",
"keen-dataviz": "^1.0.2",
- "keen-tracking": "1.0.3",
"knockout": "~3.4.2",
"knockout.validation": "^2.0.2",
"less": "^4.1.2",
diff --git a/poetry.lock b/poetry.lock
index e9f17c95c03..27061024317 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,141 +1,5 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
-[[package]]
-name = "aiohappyeyeballs"
-version = "2.4.0"
-description = "Happy Eyeballs for asyncio"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd"},
- {file = "aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2"},
-]
-
-[[package]]
-name = "aiohttp"
-version = "3.10.5"
-description = "Async http client/server framework (asyncio)"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"},
- {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"},
- {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"},
- {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"},
- {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"},
- {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"},
- {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"},
- {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"},
- {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"},
- {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"},
- {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"},
- {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"},
- {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"},
- {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"},
- {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"},
- {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"},
- {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"},
- {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"},
- {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"},
- {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"},
- {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"},
- {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"},
- {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"},
- {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"},
- {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"},
- {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"},
- {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"},
- {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"},
- {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"},
- {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"},
- {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"},
- {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"},
- {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"},
- {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"},
- {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"},
- {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"},
- {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"},
- {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"},
- {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"},
- {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"},
- {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"},
- {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"},
- {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"},
- {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"},
- {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"},
- {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"},
- {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"},
- {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"},
- {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"},
- {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"},
- {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"},
- {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"},
- {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"},
- {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"},
- {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"},
- {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"},
- {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"},
- {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"},
- {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"},
- {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"},
- {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569"},
- {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a"},
- {file = "aiohttp-3.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc"},
- {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3"},
- {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf"},
- {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe"},
- {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5"},
- {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471"},
- {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589"},
- {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae"},
- {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d"},
- {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f"},
- {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511"},
- {file = "aiohttp-3.10.5-cp38-cp38-win32.whl", hash = "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a"},
- {file = "aiohttp-3.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8"},
- {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"},
- {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"},
- {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"},
- {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"},
- {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"},
- {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"},
- {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"},
- {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"},
- {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"},
- {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"},
- {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"},
- {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"},
- {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"},
- {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"},
- {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"},
- {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"},
-]
-
-[package.dependencies]
-aiohappyeyeballs = ">=2.3.0"
-aiosignal = ">=1.1.2"
-attrs = ">=17.3.0"
-frozenlist = ">=1.1.1"
-multidict = ">=4.5,<7.0"
-yarl = ">=1.0,<2.0"
-
-[package.extras]
-speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"]
-
-[[package]]
-name = "aiosignal"
-version = "1.3.1"
-description = "aiosignal: a list of registered asynchronous callbacks"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"},
- {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"},
-]
-
-[package.dependencies]
-frozenlist = ">=1.1.0"
-
[[package]]
name = "amqp"
version = "5.2.0"
@@ -1249,7 +1113,6 @@ python-versions = "*"
files = [
{file = "django-sendgrid-v5-1.2.3.tar.gz", hash = "sha256:3887aafbb10d5b808efc2c1031dcd96fd357d542eb5affe38fef07cc0f3cfae9"},
{file = "django_sendgrid_v5-1.2.3-py2.py3-none-any.whl", hash = "sha256:2d2fa8a085d21c95e5f97fc60b61f199ccc57a27df8da90cd3f29a5702346dc6"},
- {file = "django_sendgrid_v5-1.2.3-py3-none-any.whl", hash = "sha256:f6a44ee37c1c3cc7d683a43c55ead530417be1849a8a41bde02b158009559d9d"},
]
[package.dependencies]
@@ -1694,92 +1557,6 @@ Werkzeug = ">=3.0.0"
async = ["asgiref (>=3.2)"]
dotenv = ["python-dotenv"]
-[[package]]
-name = "frozenlist"
-version = "1.4.1"
-description = "A list-like structure which implements collections.abc.MutableSequence"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"},
- {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"},
- {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"},
- {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"},
- {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"},
- {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"},
- {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"},
- {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"},
- {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"},
- {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"},
- {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"},
- {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"},
- {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"},
- {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"},
- {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"},
- {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"},
- {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"},
- {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"},
- {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"},
- {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"},
- {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"},
- {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"},
- {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"},
- {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"},
- {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"},
- {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"},
- {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"},
- {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"},
- {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"},
- {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"},
- {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"},
- {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"},
- {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"},
- {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"},
- {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"},
- {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"},
- {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"},
- {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"},
- {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"},
- {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"},
- {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"},
- {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"},
- {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"},
- {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"},
- {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"},
- {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"},
- {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"},
- {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"},
- {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"},
- {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"},
- {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"},
- {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"},
- {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"},
- {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"},
- {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"},
- {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"},
- {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"},
- {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"},
- {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"},
- {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"},
- {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"},
- {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"},
- {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"},
- {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"},
- {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"},
- {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"},
- {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"},
- {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"},
- {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"},
- {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"},
- {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"},
- {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"},
- {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"},
- {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"},
- {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"},
- {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"},
- {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"},
-]
-
[[package]]
name = "furl"
version = "2.1.3"
@@ -1795,22 +1572,6 @@ files = [
orderedmultidict = ">=1.0.1"
six = ">=1.8.0"
-[[package]]
-name = "geoip2"
-version = "4.7.0"
-description = "MaxMind GeoIP2 API"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "geoip2-4.7.0-py2.py3-none-any.whl", hash = "sha256:078fcd4cce26ea029b1e3252a0f0ec20a1f42e7ab0f19b7be3864f20f4db2b51"},
- {file = "geoip2-4.7.0.tar.gz", hash = "sha256:3bdde4994f6bc917eafab5b51e772d737b2ae00037a5b85001fb06dc68f779df"},
-]
-
-[package.dependencies]
-aiohttp = ">=3.6.2,<4.0.0"
-maxminddb = ">=2.3.0,<3.0.0"
-requests = ">=2.24.0,<3.0.0"
-
[[package]]
name = "gevent"
version = "24.2.1"
@@ -2450,21 +2211,6 @@ files = [
[package.dependencies]
referencing = ">=0.31.0"
-[[package]]
-name = "keen"
-version = "0.7.0"
-description = "Python Client for Keen IO"
-optional = false
-python-versions = "*"
-files = [
- {file = "keen-0.7.0.tar.gz", hash = "sha256:141fad22abd52dbe9e84a21866a7affcbc8b46f04885a5598618c5992bd6667c"},
-]
-
-[package.dependencies]
-pycryptodome = ">=3.4"
-requests = ">=2.5,<3.0"
-six = ">=1.10,<2.0"
-
[[package]]
name = "kombu"
version = "5.3.5"
@@ -2735,84 +2481,6 @@ files = [
[package.dependencies]
traitlets = "*"
-[[package]]
-name = "maxminddb"
-version = "2.6.2"
-description = "Reader for the MaxMind DB format"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "maxminddb-2.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cfdf5c29a2739610700b9fea7f8d68ce81dcf30bb8016f1a1853ef889a2624b"},
- {file = "maxminddb-2.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05e873eb82281cef6e787bd40bd1d58b2e496a21b3689346f0d0420988b3cbb1"},
- {file = "maxminddb-2.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2b85ffc9fb2e192321c2f0b34d0b291b8e82de6e51a6ec7534645663678e835"},
- {file = "maxminddb-2.6.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28a2eaf9769262c05c486e777016771f3367c843b053c43cd5fde1108755753d"},
- {file = "maxminddb-2.6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96a1fa38322bce1d587bb6ce39a0e6ca4c1b824f48fbc5739a5ec507f63aa889"},
- {file = "maxminddb-2.6.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:eb534333f5fd7180e35c0207b3d95d621e4b9be3b8c1709995d0feb6c752b6f4"},
- {file = "maxminddb-2.6.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b281c0eec3601dde1f169a1c04e2615751c66368141aded9f03131fe635450b"},
- {file = "maxminddb-2.6.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a771df92e599ad867c16ae4acb08cc3763c9d1028f4ca772c0571da97f7f86d2"},
- {file = "maxminddb-2.6.2-cp310-cp310-win32.whl", hash = "sha256:f412a54f87ef9083911c334267188d3d1b14f2591eac94b94ca32528f21d5f25"},
- {file = "maxminddb-2.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:7e5a90a1cb0c7fd6226aa44e18a87b26fa85b6eebae36d529d7582f93e8dfbd1"},
- {file = "maxminddb-2.6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38941a38278491bf95e5ca544969782c7ab33326802f6a93816867289c3f6401"},
- {file = "maxminddb-2.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eef1c26210155c7b94c4ca28fef65eb44a5ca1584427b1fbdeec1cd3c81e25c5"},
- {file = "maxminddb-2.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4d9cd7ddd02ee123a44d0d7821166d31540ea85352deb06b29d55e802f32781"},
- {file = "maxminddb-2.6.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8101291e5b92bd272a050c25822a5e30860d453dde16b4fffed9d751f0483a82"},
- {file = "maxminddb-2.6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5c7c520d06d335b288d06a00b786cea9b7e023bd588efb1a6ef485e94ccc7244"},
- {file = "maxminddb-2.6.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:58bfd2c55c96aaaa7c4996c704edabfb1bd369dfc1592cedf8957a24062178b1"},
- {file = "maxminddb-2.6.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:886af3ba4aa26214ff39214565f53152b62a5abdb6ef9e00c76c194dbfd79231"},
- {file = "maxminddb-2.6.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:93691c8b4b4c448babb37bedc6f3d51523a3f06ab11bdd171da7ffc4005a7897"},
- {file = "maxminddb-2.6.2-cp311-cp311-win32.whl", hash = "sha256:e9013076deca5d136c260510cd05e82ec2b4ddb9476d63e2180a13ddfd305c3e"},
- {file = "maxminddb-2.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:47170ec0e1e76787cc5882301c487f495d67f3146318f2f4e2adc281951a96ef"},
- {file = "maxminddb-2.6.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eacd65e38bdf4efdf42bbc15cfa734b09eb818ecfef76b7b36e64be382be4c83"},
- {file = "maxminddb-2.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:20662878bc9514e90b0b4c4eb1a76622ecc7504d012e76bad9cdb7372fc0ef96"},
- {file = "maxminddb-2.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7607e45f7eca991fa34d57c03a791a1dfbe774ddd9250d0f35cdcc6f17142a15"},
- {file = "maxminddb-2.6.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0970b661c4fac6624b9128057ed5fe35a2d95aa60359272289cd4c7207c9a6d"},
- {file = "maxminddb-2.6.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12207f0becf3f2bf14e7a4bf86efcaa6e90d665a918915ae228c4e77792d7151"},
- {file = "maxminddb-2.6.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:826a1858b93b193df7fa71e3caca65c3051db20545df0020444f55c02e8ed2c3"},
- {file = "maxminddb-2.6.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e63649a82926f1d93acdd3df5f7be66dc9473653350afe73f365bb25e5b34368"},
- {file = "maxminddb-2.6.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ebf9fdf8a8e55862aabb8b2c34a4af31a8a5b686007288eeb561fa20ef348378"},
- {file = "maxminddb-2.6.2-cp312-cp312-win32.whl", hash = "sha256:2aaefb62f881151960bb67e5aeb302c159a32bd2d623cf72dad688bda1020869"},
- {file = "maxminddb-2.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:78c3aa70c62be68ace23f819e7f23258545f2bfbd92cd6c33ee398cd261f6b84"},
- {file = "maxminddb-2.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e1e40449bd278fdca1f351df442f391e72fd3d98b054ccac1672f27d70210642"},
- {file = "maxminddb-2.6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:80d7f943f6b8bc437eaae5da778a83d8f38e4b7463756fdee04833e1be0bdea2"},
- {file = "maxminddb-2.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:058ca89789bc1770fe58d02a88272ca91dabeef9f3fe0011fe506484355f1804"},
- {file = "maxminddb-2.6.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80d20683afe01b4d41bad1c1829f87ab12f3d19c68ec230f83318a2fd13871a7"},
- {file = "maxminddb-2.6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd90c3798e6c347d48d5d9a9c95dc678b52a5a965f1fb72152067fdf52b994da"},
- {file = "maxminddb-2.6.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:add1e55620033516c5f0734b1d9d03848859192d9f3825aabe720dfa8a783958"},
- {file = "maxminddb-2.6.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:8cb992da535264177b380e7b81943c884d57dcbfad6b3335d7f633967144746e"},
- {file = "maxminddb-2.6.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:86048ff328793599e584bcc2fc8278c2b7c5d3a4005c70403613449ec93817ef"},
- {file = "maxminddb-2.6.2-cp38-cp38-win32.whl", hash = "sha256:f2e326a99eaa924ff2fb09d6e44127983a43016228e7780888f15e9ba171d7b3"},
- {file = "maxminddb-2.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a2671e8f4161130803cf226cd9cb8b93ec5c4b2493f83a902986177052d95d3"},
- {file = "maxminddb-2.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a50bc348c699d8f6a5f0aa35e5096515d642ca2f38b944bd71c3dedda3d3588"},
- {file = "maxminddb-2.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc9f1203eb2b139252aa08965960fe13c36cc8b80b536490b94b05c31aa1fca9"},
- {file = "maxminddb-2.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ccca5327cb4e706f669456ec6d556badfa92c0fdacd57a15076f3cdc061560"},
- {file = "maxminddb-2.6.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3987e103396e925edebbef4877e94515822f63b3b436027a0b164b500622fccd"},
- {file = "maxminddb-2.6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b31ecf3083b78c77624783bfdf6177e6ac73ae14684ef182855eb5569bc78e7c"},
- {file = "maxminddb-2.6.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:cd4530b9604d66cfa5e37eb94c671e54feff87769f8ba7fa997cce959e0cb241"},
- {file = "maxminddb-2.6.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ecce0b2d125691e2311f94dbd564c2d61c36c5033d082919431a21e6c694fa3f"},
- {file = "maxminddb-2.6.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:34b6e8d667d724f60d52635f3d959f793ab4e5d57d78b27fe66f02752d8c6b08"},
- {file = "maxminddb-2.6.2-cp39-cp39-win32.whl", hash = "sha256:d15414d251513748cb646d284a2829a5f4c69d8c90963a6e6da53a1a6d0accf7"},
- {file = "maxminddb-2.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:7c1220838ba9b0bcdaa0c5846f9da70a2304df2ac255fe518370f8faf8c18316"},
- {file = "maxminddb-2.6.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39eab93ddd75fd02f8d5ad6b1bd3f8d894828d91d6f6c1a96bb9e87c34e94aaa"},
- {file = "maxminddb-2.6.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:aa8cb54b01a29a23a0ea6659fbb38deec6f35453588c5decdbf8669feb53b624"},
- {file = "maxminddb-2.6.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c096dfd20926c4de7d7fd5b5e75c756eddd4bdac5ab7aafd4bb67d000b13743"},
- {file = "maxminddb-2.6.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dc2b511c7255f7cbbb01e8ba01ba82e62e9c1213e382d36f9d9b0ee45c2f6b2"},
- {file = "maxminddb-2.6.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80d7495565d30260c630afbe74d61522b13dd31ed05b8916003ec5b127109a12"},
- {file = "maxminddb-2.6.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9dccd7a438f81e3df84dfc31a75af4c8d29adefb6082329385bfde604c9ea01b"},
- {file = "maxminddb-2.6.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b0a3b9cab1a94cc633df3da85c6567f0188f10165e3338ec9a6c421de9fe53b9"},
- {file = "maxminddb-2.6.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb38aa94e76a87785b654c035f9f3ee39b74a98e9beea9a10b1aa62abdcc4cbd"},
- {file = "maxminddb-2.6.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9e9e893f7c0fa44cfdd5ab819a07d93f63ee398c28b792cedd50b94dcfea7c0"},
- {file = "maxminddb-2.6.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28af9470f28fce2ccb945478235f53fb52d98a505653b1bf4028e34df6149a06"},
- {file = "maxminddb-2.6.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a74b60cdc61a69b967ec44201c6259fbc48ef2eab2e885fbdc50ec1accaad545"},
- {file = "maxminddb-2.6.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:485c0778f6801e1437c2efd6e3b964a7ae71c8819f063e0b5460c3267d977040"},
- {file = "maxminddb-2.6.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0b480a31589750da4e36d1ba04b77ee3ac3853ac7b94d63f337b9d4d0403043f"},
- {file = "maxminddb-2.6.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:85fc9406f42c1311ce8ea9f2c820db5d7ac687a39ab5d932708dc783607378ef"},
- {file = "maxminddb-2.6.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fd1a612110ff182a559d8010e7615e5d05ef9d2c234b5f7de124ee8fdf1ecb9"},
- {file = "maxminddb-2.6.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cd7f525eb2331cf05181c5ba562cc3edec3de4b41dbb18a5fee9ad24884b499"},
- {file = "maxminddb-2.6.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d32266792b349f5507b0369d3277d45318fcd346a16dcc98b484aadc208e4d74"},
- {file = "maxminddb-2.6.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5662386db91872d5505fde9e7bb0b9530b6aab7a6f3ece7df59a2b43a7b45d17"},
- {file = "maxminddb-2.6.2.tar.gz", hash = "sha256:7d842d32e2620abc894b7d79a5a1007a69df2c6cf279a06b94c9c3913f66f264"},
-]
-
[[package]]
name = "mccabe"
version = "0.7.0"
@@ -2929,107 +2597,6 @@ files = [
{file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"},
]
-[[package]]
-name = "multidict"
-version = "6.1.0"
-description = "multidict implementation"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"},
- {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"},
- {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"},
- {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"},
- {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"},
- {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"},
- {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"},
- {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"},
- {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"},
- {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"},
- {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"},
- {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"},
- {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"},
- {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"},
- {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"},
- {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"},
- {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"},
- {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"},
- {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"},
- {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"},
- {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"},
- {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"},
- {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"},
- {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"},
- {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"},
- {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"},
- {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"},
- {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"},
- {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"},
- {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"},
- {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"},
- {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"},
- {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"},
- {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"},
- {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"},
- {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"},
- {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"},
- {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"},
- {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"},
- {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"},
- {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"},
- {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"},
- {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"},
- {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"},
- {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"},
- {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"},
- {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"},
- {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"},
- {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"},
- {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"},
- {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"},
- {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"},
- {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"},
- {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"},
- {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"},
- {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"},
- {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"},
- {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"},
- {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"},
- {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"},
- {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"},
- {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"},
- {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"},
- {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"},
- {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"},
- {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"},
- {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"},
- {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"},
- {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"},
- {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"},
- {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"},
- {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"},
- {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"},
- {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"},
- {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"},
- {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"},
- {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"},
- {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"},
- {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"},
- {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"},
- {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"},
- {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"},
- {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"},
- {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"},
- {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"},
- {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"},
- {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"},
- {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"},
- {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"},
- {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"},
- {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"},
- {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"},
-]
-
[[package]]
name = "nameparser"
version = "1.1.3"
@@ -3468,47 +3035,6 @@ files = [
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
-[[package]]
-name = "pycryptodome"
-version = "3.20.0"
-description = "Cryptographic library for Python"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-files = [
- {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"},
- {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"},
- {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"},
- {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"},
- {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"},
- {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"},
- {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"},
- {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"},
- {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"},
- {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"},
- {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"},
- {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"},
- {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"},
- {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"},
- {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"},
- {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"},
- {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"},
- {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"},
- {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"},
- {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"},
- {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"},
- {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"},
- {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"},
- {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"},
- {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"},
- {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"},
-]
-
[[package]]
name = "pydevd"
version = "3.0.3"
@@ -4894,111 +4420,6 @@ markupsafe = "*"
[package.extras]
email = ["email-validator"]
-[[package]]
-name = "yarl"
-version = "1.11.1"
-description = "Yet another URL library"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:400cd42185f92de559d29eeb529e71d80dfbd2f45c36844914a4a34297ca6f00"},
- {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8258c86f47e080a258993eed877d579c71da7bda26af86ce6c2d2d072c11320d"},
- {file = "yarl-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2164cd9725092761fed26f299e3f276bb4b537ca58e6ff6b252eae9631b5c96e"},
- {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08ea567c16f140af8ddc7cb58e27e9138a1386e3e6e53982abaa6f2377b38cc"},
- {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:768ecc550096b028754ea28bf90fde071c379c62c43afa574edc6f33ee5daaec"},
- {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2909fa3a7d249ef64eeb2faa04b7957e34fefb6ec9966506312349ed8a7e77bf"},
- {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01a8697ec24f17c349c4f655763c4db70eebc56a5f82995e5e26e837c6eb0e49"},
- {file = "yarl-1.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e286580b6511aac7c3268a78cdb861ec739d3e5a2a53b4809faef6b49778eaff"},
- {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4179522dc0305c3fc9782549175c8e8849252fefeb077c92a73889ccbcd508ad"},
- {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27fcb271a41b746bd0e2a92182df507e1c204759f460ff784ca614e12dd85145"},
- {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd"},
- {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c92261eb2ad367629dc437536463dc934030c9e7caca861cc51990fe6c565f26"},
- {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d95b52fbef190ca87d8c42f49e314eace4fc52070f3dfa5f87a6594b0c1c6e46"},
- {file = "yarl-1.11.1-cp310-cp310-win32.whl", hash = "sha256:489fa8bde4f1244ad6c5f6d11bb33e09cf0d1d0367edb197619c3e3fc06f3d91"},
- {file = "yarl-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:476e20c433b356e16e9a141449f25161e6b69984fb4cdbd7cd4bd54c17844998"},
- {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68"},
- {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe"},
- {file = "yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675"},
- {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25861303e0be76b60fddc1250ec5986c42f0a5c0c50ff57cc30b1be199c00e63"},
- {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4b53f73077e839b3f89c992223f15b1d2ab314bdbdf502afdc7bb18e95eae27"},
- {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:327c724b01b8641a1bf1ab3b232fb638706e50f76c0b5bf16051ab65c868fac5"},
- {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4307d9a3417eea87715c9736d050c83e8c1904e9b7aada6ce61b46361b733d92"},
- {file = "yarl-1.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a28bed68ab8fb7e380775f0029a079f08a17799cb3387a65d14ace16c12e2b"},
- {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:067b961853c8e62725ff2893226fef3d0da060656a9827f3f520fb1d19b2b68a"},
- {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8215f6f21394d1f46e222abeb06316e77ef328d628f593502d8fc2a9117bde83"},
- {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:498442e3af2a860a663baa14fbf23fb04b0dd758039c0e7c8f91cb9279799bff"},
- {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:69721b8effdb588cb055cc22f7c5105ca6fdaa5aeb3ea09021d517882c4a904c"},
- {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e969fa4c1e0b1a391f3fcbcb9ec31e84440253325b534519be0d28f4b6b533e"},
- {file = "yarl-1.11.1-cp311-cp311-win32.whl", hash = "sha256:7d51324a04fc4b0e097ff8a153e9276c2593106a811704025bbc1d6916f45ca6"},
- {file = "yarl-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:15061ce6584ece023457fb8b7a7a69ec40bf7114d781a8c4f5dcd68e28b5c53b"},
- {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a4264515f9117be204935cd230fb2a052dd3792789cc94c101c535d349b3dab0"},
- {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f41fa79114a1d2eddb5eea7b912d6160508f57440bd302ce96eaa384914cd265"},
- {file = "yarl-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02da8759b47d964f9173c8675710720b468aa1c1693be0c9c64abb9d8d9a4867"},
- {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9361628f28f48dcf8b2f528420d4d68102f593f9c2e592bfc842f5fb337e44fd"},
- {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91044952da03b6f95fdba398d7993dd983b64d3c31c358a4c89e3c19b6f7aef"},
- {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74db2ef03b442276d25951749a803ddb6e270d02dda1d1c556f6ae595a0d76a8"},
- {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e975a2211952a8a083d1b9d9ba26472981ae338e720b419eb50535de3c02870"},
- {file = "yarl-1.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aef97ba1dd2138112890ef848e17d8526fe80b21f743b4ee65947ea184f07a2"},
- {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7915ea49b0c113641dc4d9338efa9bd66b6a9a485ffe75b9907e8573ca94b84"},
- {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:504cf0d4c5e4579a51261d6091267f9fd997ef58558c4ffa7a3e1460bd2336fa"},
- {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3de5292f9f0ee285e6bd168b2a77b2a00d74cbcfa420ed078456d3023d2f6dff"},
- {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a34e1e30f1774fa35d37202bbeae62423e9a79d78d0874e5556a593479fdf239"},
- {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66b63c504d2ca43bf7221a1f72fbe981ff56ecb39004c70a94485d13e37ebf45"},
- {file = "yarl-1.11.1-cp312-cp312-win32.whl", hash = "sha256:a28b70c9e2213de425d9cba5ab2e7f7a1c8ca23a99c4b5159bf77b9c31251447"},
- {file = "yarl-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:17b5a386d0d36fb828e2fb3ef08c8829c1ebf977eef88e5367d1c8c94b454639"},
- {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1fa2e7a406fbd45b61b4433e3aa254a2c3e14c4b3186f6e952d08a730807fa0c"},
- {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:750f656832d7d3cb0c76be137ee79405cc17e792f31e0a01eee390e383b2936e"},
- {file = "yarl-1.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b8486f322d8f6a38539136a22c55f94d269addb24db5cb6f61adc61eabc9d93"},
- {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce4da3703ee6048ad4138fe74619c50874afe98b1ad87b2698ef95bf92c96d"},
- {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed653638ef669e0efc6fe2acb792275cb419bf9cb5c5049399f3556995f23c7"},
- {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18ac56c9dd70941ecad42b5a906820824ca72ff84ad6fa18db33c2537ae2e089"},
- {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:688654f8507464745ab563b041d1fb7dab5d9912ca6b06e61d1c4708366832f5"},
- {file = "yarl-1.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4973eac1e2ff63cf187073cd4e1f1148dcd119314ab79b88e1b3fad74a18c9d5"},
- {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:964a428132227edff96d6f3cf261573cb0f1a60c9a764ce28cda9525f18f7786"},
- {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6d23754b9939cbab02c63434776df1170e43b09c6a517585c7ce2b3d449b7318"},
- {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c2dc4250fe94d8cd864d66018f8344d4af50e3758e9d725e94fecfa27588ff82"},
- {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09696438cb43ea6f9492ef237761b043f9179f455f405279e609f2bc9100212a"},
- {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:999bfee0a5b7385a0af5ffb606393509cfde70ecca4f01c36985be6d33e336da"},
- {file = "yarl-1.11.1-cp313-cp313-win32.whl", hash = "sha256:ce928c9c6409c79e10f39604a7e214b3cb69552952fbda8d836c052832e6a979"},
- {file = "yarl-1.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:501c503eed2bb306638ccb60c174f856cc3246c861829ff40eaa80e2f0330367"},
- {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dae7bd0daeb33aa3e79e72877d3d51052e8b19c9025ecf0374f542ea8ec120e4"},
- {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3ff6b1617aa39279fe18a76c8d165469c48b159931d9b48239065767ee455b2b"},
- {file = "yarl-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3257978c870728a52dcce8c2902bf01f6c53b65094b457bf87b2644ee6238ddc"},
- {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f351fa31234699d6084ff98283cb1e852270fe9e250a3b3bf7804eb493bd937"},
- {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8aef1b64da41d18026632d99a06b3fefe1d08e85dd81d849fa7c96301ed22f1b"},
- {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7175a87ab8f7fbde37160a15e58e138ba3b2b0e05492d7351314a250d61b1591"},
- {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba444bdd4caa2a94456ef67a2f383710928820dd0117aae6650a4d17029fa25e"},
- {file = "yarl-1.11.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ea9682124fc062e3d931c6911934a678cb28453f957ddccf51f568c2f2b5e05"},
- {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8418c053aeb236b20b0ab8fa6bacfc2feaaf7d4683dd96528610989c99723d5f"},
- {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:61a5f2c14d0a1adfdd82258f756b23a550c13ba4c86c84106be4c111a3a4e413"},
- {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f3a6d90cab0bdf07df8f176eae3a07127daafcf7457b997b2bf46776da2c7eb7"},
- {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:077da604852be488c9a05a524068cdae1e972b7dc02438161c32420fb4ec5e14"},
- {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:15439f3c5c72686b6c3ff235279630d08936ace67d0fe5c8d5bbc3ef06f5a420"},
- {file = "yarl-1.11.1-cp38-cp38-win32.whl", hash = "sha256:238a21849dd7554cb4d25a14ffbfa0ef380bb7ba201f45b144a14454a72ffa5a"},
- {file = "yarl-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:67459cf8cf31da0e2cbdb4b040507e535d25cfbb1604ca76396a3a66b8ba37a6"},
- {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:884eab2ce97cbaf89f264372eae58388862c33c4f551c15680dd80f53c89a269"},
- {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a336eaa7ee7e87cdece3cedb395c9657d227bfceb6781295cf56abcd3386a26"},
- {file = "yarl-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87f020d010ba80a247c4abc335fc13421037800ca20b42af5ae40e5fd75e7909"},
- {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:637c7ddb585a62d4469f843dac221f23eec3cbad31693b23abbc2c366ad41ff4"},
- {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48dfd117ab93f0129084577a07287376cc69c08138694396f305636e229caa1a"},
- {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e0ae31fb5ccab6eda09ba1494e87eb226dcbd2372dae96b87800e1dcc98804"},
- {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f46f81501160c28d0c0b7333b4f7be8983dbbc161983b6fb814024d1b4952f79"},
- {file = "yarl-1.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04293941646647b3bfb1719d1d11ff1028e9c30199509a844da3c0f5919dc520"},
- {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:250e888fa62d73e721f3041e3a9abf427788a1934b426b45e1b92f62c1f68366"},
- {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e8f63904df26d1a66aabc141bfd258bf738b9bc7bc6bdef22713b4f5ef789a4c"},
- {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:aac44097d838dda26526cffb63bdd8737a2dbdf5f2c68efb72ad83aec6673c7e"},
- {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:267b24f891e74eccbdff42241c5fb4f974de2d6271dcc7d7e0c9ae1079a560d9"},
- {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6907daa4b9d7a688063ed098c472f96e8181733c525e03e866fb5db480a424df"},
- {file = "yarl-1.11.1-cp39-cp39-win32.whl", hash = "sha256:14438dfc5015661f75f85bc5adad0743678eefee266ff0c9a8e32969d5d69f74"},
- {file = "yarl-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:94d0caaa912bfcdc702a4204cd5e2bb01eb917fc4f5ea2315aa23962549561b0"},
- {file = "yarl-1.11.1-py3-none-any.whl", hash = "sha256:72bf26f66456baa0584eff63e44545c9f0eaed9b73cb6601b647c91f14c11f38"},
- {file = "yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53"},
-]
-
-[package.dependencies]
-idna = ">=2.0"
-multidict = ">=4.0"
-
[[package]]
name = "zope-event"
version = "5.0"
@@ -5071,4 +4492,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
-content-hash = "26b5234a40ced450784309969babc41b8cd042e2c6eccda2958d93e978cb5dc6"
+content-hash = "81b3fc071f1be070d1072d4cfe1a45c8c44815e803c4ba17cf6da85a6b7b3894"
diff --git a/pyproject.toml b/pyproject.toml
index 51805194f39..5e74a6defb3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -75,9 +75,6 @@ django-guardian = "2.4.0"
django-webpack-loader = {git = "https://github.com/CenterForOpenScience/django-webpack-loader.git", rev = "6b62fef7d6bc9d25d7b7b7a303f4580ad24831a6"} # branch is feature/v1-webpack-stats
django-sendgrid-v5 = "1.2.3" # metadata says python 3.10 not supported, but tests pass
-# Analytics requirements
-keen = "0.7.0"
-geoip2 = "4.7.0"
# OSF models
django-typed-models = "0.14.0"
django-storages = "1.14.3"
diff --git a/scripts/populate_popular_projects_and_registrations.py b/scripts/populate_popular_projects_and_registrations.py
deleted file mode 100644
index 0bf52709d19..00000000000
--- a/scripts/populate_popular_projects_and_registrations.py
+++ /dev/null
@@ -1,75 +0,0 @@
-"""
-This will update node links on POPULAR_LINKS_NODE, POPULAR_LINKS_REGISTRATIONS and NEW_AND_NOTEWORTHY_LINKS_NODE.
-"""
-import sys
-import logging
-
-from django.db import transaction
-
-from website.app import init_app
-from framework.auth.core import Auth
-from scripts import utils as script_utils
-from framework.celery_tasks import app as celery_app
-from website.settings import POPULAR_LINKS_NODE, POPULAR_LINKS_REGISTRATIONS
-
-logger = logging.getLogger(__name__)
-
-
-def update_node_links(designated_node, target_nodes, description):
- """ Takes designated node, removes current node links and replaces them with node links to target nodes """
- if len(target_nodes) == 0:
- logger.info('No target nodes specified - no node links will be added!')
- else:
- logger.info(f'Repopulating {designated_node._id} with latest {description} nodes.')
- user = designated_node.creator
- auth = Auth(user)
-
- for pointer in designated_node.nodes_pointer:
- designated_node.rm_pointer(pointer, auth)
-
- for node in target_nodes:
- designated_node.add_pointer(node, auth, save=True)
- logger.info(f'Added node link {node} to {designated_node}')
-
-def main(dry_run=True):
- init_app(routes=False)
- from osf.models import AbstractNode
- from website.project.utils import activity
-
- popular_activity = activity()
-
- popular_nodes = popular_activity['popular_public_projects']
- popular_links_node = AbstractNode.objects.get(guids___id=POPULAR_LINKS_NODE, guids___id__isnull=False)
- popular_registrations = popular_activity['popular_public_registrations']
- popular_links_registrations = AbstractNode.objects.get(guids___id=POPULAR_LINKS_REGISTRATIONS)
-
- update_node_links(popular_links_node, popular_nodes, 'popular')
- update_node_links(popular_links_registrations, popular_registrations, 'popular registrations')
- try:
- popular_links_node.save()
- logger.info(f'Node links on {popular_links_node._id} updated.')
- except (KeyError, RuntimeError) as error:
- logger.error('Could not migrate popular nodes due to error')
- logger.exception(error)
-
- try:
- popular_links_registrations.save()
- logger.info(f'Node links for registrations on {popular_links_registrations._id} updated.')
- except (KeyError, RuntimeError) as error:
- logger.error('Could not migrate popular nodes for registrations due to error')
- logger.exception(error)
-
- if dry_run:
- raise RuntimeError('Dry run -- transaction rolled back.')
-
-
-@celery_app.task(name='scripts.populate_popular_projects_and_registrations')
-def run_main(dry_run=True):
- if not dry_run:
- script_utils.add_file_logger(logger, __file__)
- with transaction.atomic():
- main(dry_run=dry_run)
-
-if __name__ == '__main__':
- dry_run = '--dry' in sys.argv
- run_main(dry_run=dry_run)
diff --git a/scripts/tests/test_populate_popular_projects_and_registrations.py b/scripts/tests/test_populate_popular_projects_and_registrations.py
deleted file mode 100644
index b1cf38c651d..00000000000
--- a/scripts/tests/test_populate_popular_projects_and_registrations.py
+++ /dev/null
@@ -1,95 +0,0 @@
-from unittest import mock
-
-from tests.base import OsfTestCase
-from osf_tests.factories import ProjectFactory, RegistrationFactory
-
-from website.settings import POPULAR_LINKS_NODE, POPULAR_LINKS_REGISTRATIONS, NEW_AND_NOTEWORTHY_LINKS_NODE
-
-from scripts import populate_popular_projects_and_registrations as script
-
-
-class TestPopulateNewAndNoteworthy(OsfTestCase):
-
- def setUp(self):
- super().setUp()
- self.pop1 = ProjectFactory(is_public=True)
- self.pop2 = ProjectFactory(is_public=True)
-
- self.popreg1 = RegistrationFactory(is_public=True)
- self.popreg2 = RegistrationFactory(is_public=True)
-
- def tearDown(self):
- super().tearDown()
-
- @mock.patch('website.project.utils.get_keen_activity')
- def test_populate_popular_nodes_and_registrations(self, mock_client):
-
- # only for setup, not used
- self.new_noteworthy_node = ProjectFactory()
- self.new_noteworthy_node._id = NEW_AND_NOTEWORTHY_LINKS_NODE
- self.new_noteworthy_node.save()
-
- self.popular_links_node = ProjectFactory()
- self.popular_links_node._id = POPULAR_LINKS_NODE
- self.popular_links_node.save()
-
- self.popular_links_registrations = ProjectFactory()
- self.popular_links_registrations._id = POPULAR_LINKS_REGISTRATIONS
- self.popular_links_registrations.save()
-
- popular_nodes = [self.pop1, self.pop2]
- popular_registrations = [self.popreg1, self.popreg2]
-
- node_pageviews = [
- {
- 'result': 5,
- 'node.id': self.pop1._id
- },
- {
- 'result': 5,
- 'node.id': self.pop2._id
- },
- {
- 'result': 5,
- 'node.id': self.popreg1._id
- },
- {
- 'result': 5,
- 'node.id': self.popreg2._id
- }
- ]
-
- node_visits = [
- {
- 'result': 2,
- 'node.id': self.pop1._id
- },
- {
- 'result': 2,
- 'node.id': self.pop2._id
- },
- {
- 'result': 2,
- 'node.id': self.popreg1._id
- },
- {
- 'result': 2,
- 'node.id': self.popreg2._id
- }
- ]
-
- mock_client.return_value = {'node_pageviews': node_pageviews, 'node_visits': node_visits}
-
- assert len(self.popular_links_node.nodes) == 0
- assert len(self.popular_links_registrations.nodes) == 0
-
- script.main(dry_run=False)
-
- self.popular_links_node.reload()
- self.popular_links_registrations.reload()
-
- assert len(self.popular_links_node.nodes) == 2
- assert len(self.popular_links_registrations.nodes) == 2
-
- assert popular_nodes == self.popular_links_node.nodes
- assert popular_registrations == self.popular_links_registrations.nodes
diff --git a/tests/test_preprints.py b/tests/test_preprints.py
index c975203fdde..a213c961659 100644
--- a/tests/test_preprints.py
+++ b/tests/test_preprints.py
@@ -2739,69 +2739,3 @@ def test_ember_redirect_to_versioned_guid(self):
location_with_no_guid = res.location.replace(guid_with_version, '')
# check if location has not wrong format https://osf.io/preprints/socarxiv/3rhyz/3rhyz_v1
assert location_with_no_guid == location_with_no_guid.replace(guid_with_no_version, '')
-
-@pytest.mark.django_db
-class TestPreprintCreatorStateChangings:
-
- @pytest.fixture()
- def creator(self):
- return AuthUserFactory()
-
- @pytest.fixture()
- def unpublished_preprint_pre_mod(self):
- return PreprintFactory(reviews_workflow='pre-moderation', is_published=False)
-
- def test_preprint_initial_creator_removal(self, creator, unpublished_preprint_pre_mod):
- new_version = PreprintFactory.create_version(
- create_from=unpublished_preprint_pre_mod,
- creator=creator,
- final_machine_state='initial',
- is_published=False,
- set_doi=False
- )
- request = RequestFactory().delete('/fake_path')
- request.user = creator
- request.query_params = {}
- request.parser_context = {
- 'kwargs': {
- 'preprint_id': new_version._id,
- 'user_id': creator._id
- },
- }
- view = PreprintContributorDetail()
- view = setup_view(view, request, preprint_id=new_version._id, user_id=creator._id)
- try:
- view.perform_destroy(request)
- except Exception as error:
- assert error.args[0] == ('You cannot delete yourself at this time. '
- 'Have another admin contributor do that after you’ve submitted your preprint')
- else:
- assert False
-
- def test_preprint_initial_creator_update(self, creator, unpublished_preprint_pre_mod):
- new_version = PreprintFactory.create_version(
- create_from=unpublished_preprint_pre_mod,
- creator=creator,
- final_machine_state='initial',
- is_published=False,
- set_doi=False
- )
- request = RequestFactory().patch('/fake_path')
- request.user = creator
- request.query_params = {}
- request.parser_context = {
- 'kwargs': {
- 'preprint_id': new_version._id,
- 'user_id': creator._id
- },
- }
- view = PreprintContributorDetail()
- view = setup_view(view, request, preprint_id=new_version._id, user_id=creator._id)
- try:
- view.patch(request)
- except Exception as error:
- assert error.args[0] == ('You cannot change your permission setting at this time. '
- 'Have another admin contributor edit your permission '
- 'after you’ve submitted your preprint')
- else:
- assert False
diff --git a/website/archiver/utils.py b/website/archiver/utils.py
index 44cd7517413..60d2c093871 100644
--- a/website/archiver/utils.py
+++ b/website/archiver/utils.py
@@ -265,7 +265,6 @@ def migrate_file_metadata(dst):
response_block.set_response(updated_response)
dst.registration_responses = dst.schema_responses.get().all_responses
- dst.registered_meta[schema._id] = dst.expand_registration_responses()
dst.save()
diff --git a/website/profile/utils.py b/website/profile/utils.py
index 5b1e22e8916..468fa42fd4a 100644
--- a/website/profile/utils.py
+++ b/website/profile/utils.py
@@ -31,6 +31,7 @@ def serialize_user(user, node=None, admin=False, full=False, is_profile=False, i
'id': str(user._id),
'registered': user.is_registered,
'surname': user.family_name,
+ 'given_name': user.given_name,
'fullname': fullname,
'shortname': fullname if len(fullname) < 50 else fullname[:23] + '...' + fullname[-23:],
'profile_image_url': user.profile_image_url(size=settings.PROFILE_IMAGE_MEDIUM),
diff --git a/website/project/utils.py b/website/project/utils.py
index 173219d5558..e8eb0eaf526 100644
--- a/website/project/utils.py
+++ b/website/project/utils.py
@@ -1,14 +1,8 @@
"""Various node-related utilities."""
-from string import ascii_lowercase, digits
-
from django.apps import apps
from django.db.models import Q
from website import settings
-
-from keen import KeenClient
-
-
# Alias the project serializer
def serialize_node(*args, **kwargs):
@@ -28,47 +22,6 @@ def recent_public_registrations(n=10):
).get_roots().order_by('-registered_date')[:n]
-def get_keen_activity():
- client = KeenClient(
- project_id=settings.KEEN['public']['project_id'],
- read_key=settings.KEEN['public']['read_key'],
- )
-
- node_pageviews = []
- node_visits = []
- for character in list(digits + ascii_lowercase):
- partial_node_pageviews = client.count(
- event_collection=f'pageviews-{character}',
- timeframe='this_7_days',
- group_by='node.id',
- filters=[
- {
- 'property_name': 'node.id',
- 'operator': 'exists',
- 'property_value': True
- }
- ]
- )
- node_pageviews += partial_node_pageviews
-
- partial_node_visits = client.count_unique(
- event_collection=f'pageviews-{character}',
- target_property='anon.id',
- timeframe='this_7_days',
- group_by='node.id',
- filters=[
- {
- 'property_name': 'node.id',
- 'operator': 'exists',
- 'property_value': True
- }
- ]
- )
- node_visits += partial_node_visits
-
- return {'node_pageviews': node_pageviews, 'node_visits': node_visits}
-
-
def activity():
"""Generate analytics for most popular public projects and registrations.
Called by `scripts/update_populate_projects_and_registrations`
@@ -76,33 +29,8 @@ def activity():
Node = apps.get_model('osf.AbstractNode')
popular_public_projects = []
popular_public_registrations = []
- max_projects_to_display = settings.MAX_POPULAR_PROJECTS
-
- if settings.KEEN['public']['read_key']:
- keen_activity = get_keen_activity()
- node_visits = keen_activity['node_visits']
-
- node_data = [{'node': x['node.id'], 'views': x['result']} for x in node_visits]
- node_data.sort(key=lambda datum: datum['views'], reverse=True)
-
- node_data = [node_dict['node'] for node_dict in node_data]
-
- for nid in node_data:
- node = Node.load(nid)
- if node is None:
- continue
- if node.is_public and not node.is_registration and not node.is_deleted:
- if len(popular_public_projects) < max_projects_to_display:
- popular_public_projects.append(node)
- elif node.is_public and node.is_registration and not node.is_deleted and not node.is_retracted:
- if len(popular_public_registrations) < max_projects_to_display:
- popular_public_registrations.append(node)
- if len(popular_public_projects) >= max_projects_to_display and len(popular_public_registrations) >= max_projects_to_display:
- break
-
# New and Noteworthy projects are updated manually
new_and_noteworthy_projects = list(Node.objects.get(guids___id=settings.NEW_AND_NOTEWORTHY_LINKS_NODE, guids___id__isnull=False).nodes_pointer)
-
return {
'new_and_noteworthy_projects': new_and_noteworthy_projects,
'recent_public_registrations': recent_public_registrations(),
@@ -110,7 +38,6 @@ def activity():
'popular_public_registrations': popular_public_registrations
}
-
# Credit to https://gist.github.com/cizixs/be41bbede49a772791c08491801c396f
def sizeof_fmt(num, suffix='B'):
for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
diff --git a/website/project/views/drafts.py b/website/project/views/drafts.py
index e4ee762c992..d477f8f988d 100644
--- a/website/project/views/drafts.py
+++ b/website/project/views/drafts.py
@@ -1,22 +1,18 @@
import functools
from rest_framework import status as http_status
import itertools
-import html
from operator import itemgetter
from dateutil.parser import parse as parse_date
from django.utils import timezone
-from flask import request, redirect
+from flask import request
import pytz
from framework.database import autoload
from framework.exceptions import HTTPError
-from osf import features
-from osf.utils.sanitize import strip_html
from osf.utils.permissions import ADMIN
-from osf.utils.functional import rapply
from osf.models import RegistrationSchema, DraftRegistration
from website.project.decorators import (
@@ -25,24 +21,13 @@
must_have_permission,
)
from website import settings
-from website.ember_osf_web.decorators import ember_flag_is_active
-from website.project import utils
from website.project.metadata.schemas import METASCHEMA_ORDERING
from website.project.metadata.utils import serialize_meta_schema, serialize_draft_registration
from website.project.utils import serialize_node
autoload_draft = functools.partial(autoload, DraftRegistration, 'draft_id', 'draft')
-def get_schema_or_fail(schema_name, schema_version):
- try:
- meta_schema = RegistrationSchema.objects.get(name=schema_name, schema_version=schema_version)
- except RegistrationSchema.DoesNotExist:
- raise HTTPError(http_status.HTTP_404_NOT_FOUND, data=dict(
- message_long='No RegistrationSchema record matching that query could be found'
- ))
- return meta_schema
-
def must_be_branched_from_node(func):
@autoload_draft
@must_be_valid_project
@@ -150,95 +135,6 @@ def get_draft_registrations(auth, node, *args, **kwargs):
'drafts': sorted_serialized_drafts
}, http_status.HTTP_200_OK
-@must_have_permission(ADMIN)
-@must_be_valid_project
-@must_be_contributor_and_not_group_member
-@ember_flag_is_active(features.EMBER_CREATE_DRAFT_REGISTRATION)
-def new_draft_registration(auth, node, *args, **kwargs):
- """Create a new draft registration for the node
-
- :return: Redirect to the new draft's edit page
- :rtype: flask.redirect
- :raises: HTTPError
- """
- if node.is_registration:
- raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={
- 'message_short': "Can't create draft",
- 'message_long': 'Creating draft registrations on registered projects is not allowed.'
- })
- data = request.values
-
- schema_name = data.get('schema_name')
- if not schema_name:
- raise HTTPError(
- http_status.HTTP_400_BAD_REQUEST,
- data={
- 'message_short': 'Must specify a schema_name',
- 'message_long': 'Please specify a schema_name'
- }
- )
-
- schema_version = data.get('schema_version', 2)
-
- meta_schema = get_schema_or_fail(schema_name, int(schema_version))
- draft = DraftRegistration.create_from_node(
- node=node,
- user=auth.user,
- schema=meta_schema,
- data={}
- )
- return redirect(node.web_url_for('edit_draft_registration_page', draft_id=draft._id, _guid=True))
-
-
-@must_have_permission(ADMIN)
-@must_be_contributor_and_not_group_member
-@ember_flag_is_active(features.EMBER_EDIT_DRAFT_REGISTRATION)
-@must_be_branched_from_node
-def edit_draft_registration_page(auth, node, draft, **kwargs):
- """Draft registration editor
-
- :return: serialized DraftRegistration
- :rtype: dict
- """
- check_draft_state(draft)
- ret = utils.serialize_node(node, auth, primary=True)
- ret['draft'] = serialize_draft_registration(draft, auth)
- return ret
-
-@must_have_permission(ADMIN)
-@must_be_contributor_and_not_group_member
-@must_be_branched_from_node
-def update_draft_registration(auth, node, draft, *args, **kwargs):
- """Update an existing draft registration
-
- :return: serialized draft registration
- :rtype: dict
- :raises: HTTPError
- """
- check_draft_state(draft)
- data = request.get_json()
-
- schema_data = data.get('schema_data', {})
- schema_data = rapply(schema_data, strip_html)
-
- # Unencodes HTML special characters in filenames
- if schema_data.get('uploader', {}).get('value'):
- schema_data['uploader']['value'] = html.unescape(schema_data['uploader']['value'])
- if schema_data.get('uploader', {}).get('extra'):
- for extra in schema_data['uploader']['extra']:
- extra['selectedFileName'] = html.unescape(extra['selectedFileName'])
-
- schema_name = data.get('schema_name')
- schema_version = data.get('schema_version', 1)
- if schema_name:
- meta_schema = get_schema_or_fail(schema_name, schema_version)
- existing_schema = draft.registration_schema
- if (existing_schema.name, existing_schema.schema_version) != (meta_schema.name, meta_schema.schema_version):
- draft.registration_schema = meta_schema
-
- draft.update_metadata(schema_data)
- draft.save()
- return serialize_draft_registration(draft, auth), http_status.HTTP_200_OK
@must_have_permission(ADMIN)
@must_be_contributor_and_not_group_member
diff --git a/website/project/views/node.py b/website/project/views/node.py
index 9bad3713d3b..5a10ef00853 100644
--- a/website/project/views/node.py
+++ b/website/project/views/node.py
@@ -24,7 +24,6 @@
from osf.models.nodelog import NodeLog
from osf.models.user import OSFUser
from osf.utils.functional import rapply
-from osf.utils.registrations import strip_registered_meta_comments
from osf.utils import sanitize
from osf import features
@@ -805,7 +804,6 @@ def _view_project(node, auth, primary=False,
'registered_from_url': node.registered_from.url if is_registration else '',
'registered_date': iso8601format(node.registered_date) if is_registration else '',
'root_id': node.root._id if node.root else None,
- 'registered_meta': strip_registered_meta_comments(node.registered_meta),
'registered_schemas': serialize_meta_schemas(
list(node.registered_schema.all())) if is_registration else False,
'is_fork': node.is_fork,
diff --git a/website/routes.py b/website/routes.py
index 227d68302e3..2e7fc37f5be 100644
--- a/website/routes.py
+++ b/website/routes.py
@@ -15,8 +15,6 @@
from django.conf import settings as api_settings
from django.utils.encoding import smart_str
from werkzeug.http import dump_cookie
-from geoip2.database import Reader
-from geoip2.errors import AddressNotFoundError
from framework import status
from framework import sentry
@@ -89,11 +87,6 @@ def get_globals():
user = _get_current_user()
set_status_message(user)
user_institutions = [{'id': inst._id, 'name': inst.name, 'logo_path': inst.logo_path_rounded_corners} for inst in user.get_affiliated_institutions()] if user else []
- try:
- location = Reader('GeoLite2-City.mmdb').city(request.remote_addr)
- # TODO: replace with adequate error handling during keen removal
- except (FileNotFoundError, AddressNotFoundError):
- location = None
if request.host_url != settings.DOMAIN:
try:
@@ -118,10 +111,6 @@ def get_globals():
'user_entry_point': metrics.get_entry_point(user) if user else '',
'user_institutions': user_institutions if user else None,
'display_name': user.fullname if user else '',
- 'anon': {
- 'continent': (location or {}).get('continent', {}).get('code', None),
- 'country': (location or {}).get('country', {}).get('iso_code', None),
- },
'use_cdn': settings.USE_CDN_FOR_CLIENT_LIBS,
'sentry_dsn_js': settings.SENTRY_DSN_JS if sentry.enabled else None,
'dev_mode': settings.DEV_MODE,
@@ -150,16 +139,6 @@ def get_globals():
'profile_url': cas.get_profile_url(),
'enable_institutions': settings.ENABLE_INSTITUTIONS,
'page_route_name': request.url_rule.endpoint.replace('__', '.'),
- 'keen': {
- 'public': {
- 'project_id': settings.KEEN['public']['project_id'],
- 'write_key': settings.KEEN['public']['write_key'],
- },
- 'private': {
- 'project_id': settings.KEEN['private']['project_id'],
- 'write_key': settings.KEEN['private']['write_key'],
- },
- },
'institutional_landing_flag': flag_is_active(request, features.INSTITUTIONAL_LANDING_FLAG),
'maintenance': maintenance.get_maintenance(),
'recaptcha_site_key': settings.RECAPTCHA_SITE_KEY,
@@ -1271,22 +1250,6 @@ def make_url_map(app):
project_views.node.node_registrations,
notemplate,
),
- Rule(
- [
- '/project//registrations/',
- '/project//node//registrations/',
- ],
- 'post',
- project_views.drafts.new_draft_registration,
- OsfWebRenderer('project/edit_draft_registration.mako', trust=False)),
- Rule(
- [
- '/project//drafts//',
- '/project//node//drafts//',
- ],
- 'get',
- project_views.drafts.edit_draft_registration_page,
- OsfWebRenderer('project/edit_draft_registration.mako', trust=False)),
Rule(
[
'/project//drafts//register/',
@@ -1527,9 +1490,6 @@ def make_url_map(app):
Rule([
'/project//drafts//',
], 'get', project_views.drafts.get_draft_registration, json_renderer),
- Rule([
- '/project//drafts//',
- ], 'put', project_views.drafts.update_draft_registration, json_renderer),
Rule([
'/project//drafts//',
], 'delete', project_views.drafts.delete_draft_registration, json_renderer),
diff --git a/website/settings/defaults.py b/website/settings/defaults.py
index 70867dd901e..d891e886873 100644
--- a/website/settings/defaults.py
+++ b/website/settings/defaults.py
@@ -275,20 +275,6 @@ def parent_dir(path):
'node': [],
}
-KEEN = {
- 'public': {
- 'project_id': None,
- 'master_key': 'changeme',
- 'write_key': '',
- 'read_key': '',
- },
- 'private': {
- 'project_id': '',
- 'write_key': '',
- 'read_key': '',
- },
-}
-
SENTRY_DSN = None
SENTRY_DSN_JS = None
@@ -296,7 +282,6 @@ def parent_dir(path):
# Most Popular and New and Noteworthy Nodes
POPULAR_LINKS_NODE = None # TODO Override in local.py in production.
-POPULAR_LINKS_REGISTRATIONS = None # TODO Override in local.py in production.
NEW_AND_NOTEWORTHY_LINKS_NODE = None # TODO Override in local.py in production.
MAX_POPULAR_PROJECTS = 10
@@ -433,7 +418,6 @@ class CeleryConfig:
'scripts.osfstorage.usage_audit',
'scripts.stuck_registration_audit',
'scripts.populate_new_and_noteworthy_projects',
- 'scripts.populate_popular_projects_and_registrations',
'website.search.elastic_search',
'scripts.generate_sitemap',
'osf.management.commands.clear_expired_sessions',
@@ -450,7 +434,6 @@ class CeleryConfig:
'osf.management.commands.sync_datacite_doi_metadata',
'osf.management.commands.update_institution_project_counts',
'osf.management.commands.populate_branched_from',
- 'osf.management.commands.cumulative_plos_metrics',
'osf.management.commands.spam_metrics',
'osf.management.commands.daily_reporters_go',
'osf.management.commands.monthly_reporters_go',
@@ -530,7 +513,6 @@ class CeleryConfig:
'website.search.search',
'website.project.tasks',
'scripts.populate_new_and_noteworthy_projects',
- 'scripts.populate_popular_projects_and_registrations',
'scripts.refresh_addon_tokens',
'scripts.retract_registrations',
'scripts.embargo_registrations',
@@ -555,7 +537,6 @@ class CeleryConfig:
'osf.management.commands.delete_legacy_quickfiles_nodes',
'osf.management.commands.fix_quickfiles_waterbutler_logs',
'osf.management.commands.sync_doi_metadata',
- 'osf.management.commands.cumulative_plos_metrics',
'api.providers.tasks',
'osf.management.commands.daily_reporters_go',
'osf.management.commands.monthly_reporters_go',
@@ -644,11 +625,6 @@ class CeleryConfig:
'schedule': crontab(minute=0, hour=7, day_of_week=6), # Saturday 2:00 a.m.
'kwargs': {'dry_run': False}
},
- 'update_popular_nodes': {
- 'task': 'scripts.populate_popular_projects_and_registrations',
- 'schedule': crontab(minute=0, hour=7), # Daily 2:00 a.m.
- 'kwargs': {'dry_run': False}
- },
'registration_schema_metrics': {
'task': 'management.commands.registration_schema_metrics',
'schedule': crontab(minute=45, hour=7, day_of_month=3), # Third day of month 2:45 a.m.
@@ -744,11 +720,6 @@ class CeleryConfig:
# 'schedule': crontab(minute=0, hour=11), # Daily 6 a.m
# 'kwargs': {},
# },
- # 'cumulative_plos_metrics': {
- # 'task': 'osf.management.commands.cumulative_plos_metrics',
- # 'schedule': crontab(day_of_month=1, minute=30, hour=9), # First of the month at 4:30 a.m.
- # 'kwargs': {},
- # },
# })
@@ -2089,9 +2060,6 @@ class CeleryConfig:
DS_METRICS_BASE_FOLDER = None
REG_METRICS_OSF_TOKEN = None
REG_METRICS_BASE_FOLDER = None
-PLOS_METRICS_BASE_FOLDER = None
-PLOS_METRICS_INITIAL_FILE_DOWNLOAD_URL = None
-PLOS_METRICS_OSF_TOKEN = None
STORAGE_WARNING_THRESHOLD = .9 # percent of maximum storage used before users get a warning message
STORAGE_LIMIT_PUBLIC = 50
diff --git a/website/settings/local-ci.py b/website/settings/local-ci.py
index 582541525a9..8bf283b6338 100644
--- a/website/settings/local-ci.py
+++ b/website/settings/local-ci.py
@@ -82,23 +82,8 @@ class CeleryConfig(defaults.CeleryConfig):
# if ENABLE_VARNISH isn't set in python read it from the env var and set it
locals().setdefault('ENABLE_VARNISH', os.environ.get('ENABLE_VARNISH') == 'True')
-KEEN = {
- 'public': {
- 'project_id': '123456789abcdef101112131415161718191a1b1c1d1e1f20212223242526272',
- 'master_key': '123456789abcdef101112131415161718191a1b1c1d1e1f20212223242526272',
- 'write_key': '123456789abcdef101112131415161718191a1b1c1d1e1f20212223242526272',
- 'read_key': '123456789abcdef101112131415161718191a1b1c1d1e1f20212223242526272',
- },
- 'private': {
- 'project_id': '123456789abcdef101112131415161718191a1b1c1d1e1f20212223242526272',
- 'write_key': '123456789abcdef101112131415161718191a1b1c1d1e1f20212223242526272',
- 'read_key': '123456789abcdef101112131415161718191a1b1c1d1e1f20212223242526272',
- },
-}
-
NEW_AND_NOTEWORTHY_LINKS_NODE = 'helloo'
POPULAR_LINKS_NODE = 'hiyah'
-POPULAR_LINKS_REGISTRATIONS = 'woooo'
logging.getLogger('celery.app.trace').setLevel(logging.FATAL)
diff --git a/website/static/js/keen.js b/website/static/js/keen.js
deleted file mode 100644
index b716bc84c79..00000000000
--- a/website/static/js/keen.js
+++ /dev/null
@@ -1,275 +0,0 @@
-'use strict';
-
-
-var $ = require('jquery');
-var md5 = require('js-md5');
-var Raven = require('raven-js');
-var Cookie = require('js-cookie');
-var lodashGet = require('lodash.get');
-var keenTracking = require('keen-tracking');
-var $osf = require('js/osfHelpers');
-
-var KeenTracker = (function() {
-
- function _nowUTC() {
- var now = new Date();
- return new Date(
- now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(),
- now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds()
- );
- }
-
- function _createOrUpdateKeenSession() {
- var expDate = new Date();
- var expiresInMinutes = 25;
- expDate.setTime(expDate.getTime() + (expiresInMinutes * 60 * 1000));
- var currentSessionId = Cookie.get('keenSessionId') || keenTracking.helpers.getUniqueId();
- Cookie.set('keenSessionId', currentSessionId, {expires: expDate, path: '/'});
- return currentSessionId;
- }
-
- function _getOrCreateKeenId() {
- if (!Cookie.get('keenUserId')) {
- Cookie.set('keenUserId', keenTracking.helpers.getUniqueId(), {expires: 365, path: '/'});
- }
- return Cookie.get('keenUserId');
- }
-
-
- function _defaultKeenPayload() {
- _createOrUpdateKeenSession();
-
- var user = window.contextVars.currentUser;
- var node = window.contextVars.node;
- var pageMeta = lodashGet(window, 'contextVars.analyticsMeta.pageMeta', {});
- return {
- page: {
- title: document.title,
- url: document.URL,
- meta: pageMeta,
- info: {},
- },
- referrer: {
- url: document.referrer,
- info: {},
- },
- time: {
- local: keenTracking.helpers.getDatetimeIndex(),
- utc: keenTracking.helpers.getDatetimeIndex(_nowUTC()),
- },
- node: {
- id: lodashGet(node, 'id'),
- title: lodashGet(node, 'title'),
- type: lodashGet(node, 'category'),
- tags: lodashGet(node, 'tags'),
- },
- anon: {
- id: md5(Cookie.get('keenSessionId')),
- continent: user.anon.continent,
- country: user.anon.country,
- },
- meta: {
- epoch: 1, // version of pageview event schema
- },
- keen: {
- addons: [
- {
- name: 'keen:url_parser',
- input: {
- url: 'page.url',
- },
- output: 'page.info',
- },
- {
- name: 'keen:url_parser',
- input: {
- url: 'referrer.url',
- },
- output: 'referrer.info',
- },
- {
- name: 'keen:referrer_parser',
- input: {
- referrer_url: 'referrer.url',
- page_url: 'page.url',
- },
- output: 'referrer.info',
- },
- ]
- },
- };
- } // end _defaultKeenPayload
-
- function _trackCustomEvent(client, collection, eventData) {
- if (client === null) {
- return;
- }
- client.recordEvent(collection, eventData, function (err) {
- if (err) {
- // If google analytics is inaccessible keen will throw errors
- var adBlockError = document.getElementsByTagName('iframe').item(0) === null;
- var uselessError = 'An error occurred!' === err;
- if(!adBlockError || !uselessError) {
- Raven.captureMessage('Error sending Keen data to ' + collection + ': <' + err + '>', {
- extra: {payload: eventData}
- });
- }
- }
- });
- }
-
- function _pageIsPublic() {
- return Boolean(
- lodashGet(window, 'contextVars.node.isPublic', false) &&
- lodashGet(window, 'contextVars.analyticsMeta.pageMeta.public', false)
- );
- }
-
- function _getActionLabels() {
- const actionLabelMap = {
- 'web': true,
- 'view': Boolean(lodashGet(window, 'contextVars.analyticsMeta.itemGuid')),
- 'search': Boolean(lodashGet(window, 'contextVars.analyticsMeta.searchProviderId')),
- };
- return (
- Object.keys(actionLabelMap)
- .filter(label => Boolean(actionLabelMap[label]))
- );
- }
-
- function _logPageview() {
- const url = new URL('/_/metrics/events/counted_usage/', window.contextVars.apiV2Domain);
- const sessionId = _createOrUpdateKeenSession();
- const data = {
- type: 'counted-usage',
- attributes: {
- client_session_id: sessionId ? md5(sessionId) : null,
- provider_id: lodashGet(window, 'contextVars.analyticsMeta.searchProviderId'),
- item_guid: lodashGet(window, 'contextVars.analyticsMeta.itemGuid'),
- item_public: _pageIsPublic(),
- action_labels: _getActionLabels(),
- pageview_info: {
- referer_url: document.referrer,
- page_url: document.URL,
- page_title: document.title,
- route_name: lodashGet(window, 'contextVars.analyticsMeta.pageMeta.routeName'),
- },
- },
- };
-
- $osf.ajaxJSON('POST', url.toString(), {
- isCors: true,
- data: {data},
- });
- }
-
- function KeenTracker() {
- if (instance) {
- throw new Error('Cannot instantiate another KeenTracker instance.');
- } else {
- var self = this;
-
- self._publicClient = null;
- self._privateClient = null;
-
- self.init = function _initKeentracker(params) {
- var self = this;
-
- if (params === undefined) {
- return self;
- }
-
- self._publicClient = new keenTracking({
- projectId: params.public.projectId,
- writeKey: params.public.writeKey,
- });
- self._publicClient.extendEvents(_defaultPublicKeenPayload);
-
- self._privateClient = new keenTracking({
- projectId: params.private.projectId,
- writeKey: params.private.writeKey,
- });
- self._privateClient.extendEvents(_defaultPrivateKeenPayload);
-
- return self;
- };
-
- var _defaultPublicKeenPayload = function() { return _defaultKeenPayload(); };
- var _defaultPrivateKeenPayload = function() {
- var payload = _defaultKeenPayload();
- var user = window.contextVars.currentUser;
- payload.visitor = {
- id: _getOrCreateKeenId(),
- session: Cookie.get('keenSessionId'),
- returning: Boolean(Cookie.get('keenUserId')),
- };
- payload.tech = {
- browser: keenTracking.helpers.getBrowserProfile(),
- ua: '${keen.user_agent}',
- ip: '${keen.ip}',
- info: {},
- };
- payload.user = {
- id: user.id,
- entry_point: user.entryPoint,
- institutions: user.institutions,
- locale: user.locale,
- timezone: user.timezone,
- };
- payload.keen.addons.push({
- name: 'keen:ip_to_geo',
- input: {
- ip: 'tech.ip',
- remove_ip_property: true
- },
- output: 'geo',
- });
- payload.keen.addons.push({
- name: 'keen:ua_parser',
- input: {
- ua_string: 'tech.ua'
- },
- output: 'tech.info',
- });
-
- return payload;
- };
-
- self.trackPageView = function () {
- _logPageview();
-
- var self = this;
- var guid;
- if (_pageIsPublic()) {
- guid = lodashGet(window, 'contextVars.node.id', null);
- if (guid) {
- var partitioned_collection = 'pageviews-' + guid.charAt(0);
- self.trackPublicEvent(partitioned_collection, {});
- }
- }
- self.trackPrivateEvent('pageviews', {});
- };
-
- self.trackPrivateEvent = function(collection, event) {
- return _trackCustomEvent(self._privateClient, collection, event);
- };
-
- self.trackPublicEvent = function(collection, event) {
- return _trackCustomEvent(self._publicClient, collection, event);
- };
- }
- }
-
- var instance = null;
- return {
- getInstance: function() {
- if (!instance) {
- instance = new KeenTracker();
- instance.init(window.contextVars.keen);
- }
- return instance;
- }
- };
-})();
-
-module.exports = KeenTracker;
diff --git a/website/static/js/metrics.js b/website/static/js/metrics.js
new file mode 100644
index 00000000000..a9f86eb811f
--- /dev/null
+++ b/website/static/js/metrics.js
@@ -0,0 +1,106 @@
+'use strict';
+
+
+var md5 = require('js-md5');
+var Cookie = require('js-cookie');
+var lodashGet = require('lodash.get');
+var $osf = require('js/osfHelpers');
+
+var MetricsTracker = (function() {
+
+ // approach taken from: https://github.com/keen/keen-tracking.js/blob/master/lib/helpers/getUniqueId.js
+ // which originally came from: http://stackoverflow.com/a/2117523/2511985
+ function getUniqueId() {
+ if (typeof window.crypto !== 'undefined' && window.crypto.getRandomValues) {
+ return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
+ (c ^ window.crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
+ );
+ } else {
+ let str = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
+ return str.replace(/[xy]/g, function(c) {
+ let r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
+ return v.toString(16);
+ });
+ }
+ }
+
+ function _createOrUpdateMetricsSession() {
+ var expDate = new Date();
+ var expiresInMinutes = 25;
+ expDate.setTime(expDate.getTime() + (expiresInMinutes * 60 * 1000));
+ var currentSessionId = Cookie.get('osfMetricsSessionId') || getUniqueId();
+ Cookie.set('osfMetricsSessionId', currentSessionId, {expires: expDate, path: '/'});
+ return currentSessionId;
+ }
+
+
+ function _pageIsPublic() {
+ return Boolean(
+ lodashGet(window, 'contextVars.node.isPublic', false) &&
+ lodashGet(window, 'contextVars.analyticsMeta.pageMeta.public', false)
+ );
+ }
+
+ function _getActionLabels() {
+ const actionLabelMap = {
+ 'web': true,
+ 'view': Boolean(lodashGet(window, 'contextVars.analyticsMeta.itemGuid')),
+ 'search': Boolean(lodashGet(window, 'contextVars.analyticsMeta.searchProviderId')),
+ };
+ return (
+ Object.keys(actionLabelMap)
+ .filter(label => Boolean(actionLabelMap[label]))
+ );
+ }
+
+ function _logPageview() {
+ const url = new URL('/_/metrics/events/counted_usage/', window.contextVars.apiV2Domain);
+ const sessionId = _createOrUpdateMetricsSession();
+ const data = {
+ type: 'counted-usage',
+ attributes: {
+ client_session_id: sessionId ? md5(sessionId) : null,
+ provider_id: lodashGet(window, 'contextVars.analyticsMeta.searchProviderId'),
+ item_guid: lodashGet(window, 'contextVars.analyticsMeta.itemGuid'),
+ item_public: _pageIsPublic(),
+ action_labels: _getActionLabels(),
+ pageview_info: {
+ referer_url: document.referrer,
+ page_url: document.URL,
+ page_title: document.title,
+ route_name: lodashGet(window, 'contextVars.analyticsMeta.pageMeta.routeName'),
+ },
+ },
+ };
+
+ $osf.ajaxJSON('POST', url.toString(), {
+ isCors: true,
+ data: {data},
+ });
+ }
+
+ function MetricsTracker() {
+ if (instance) {
+ throw new Error('Cannot instantiate another MetricsTracker instance.');
+ } else {
+ var self = this;
+
+ self.trackPageView = function () {
+ _logPageview();
+ };
+
+ }
+ }
+
+ var instance = null;
+ return {
+ getInstance: function() {
+ if (!instance) {
+ instance = new MetricsTracker();
+ }
+ return instance;
+ }
+ };
+})();
+
+module.exports = MetricsTracker;
diff --git a/website/static/js/pages/base-page.js b/website/static/js/pages/base-page.js
index 079e0f9a74e..877239bd271 100644
--- a/website/static/js/pages/base-page.js
+++ b/website/static/js/pages/base-page.js
@@ -19,7 +19,7 @@ var NavbarControl = require('js/navbarControl');
var AlertManager = require('js/alertsManager');
var Raven = require('raven-js');
var moment = require('moment');
-var KeenTracker = require('js/keen');
+var MetricsTracker = require('js/metrics');
var DevModeControls = require('js/devModeControls');
var bootbox = require('bootbox');
@@ -252,9 +252,9 @@ $(function() {
}
}
- //Don't track PhantomJS visits with KeenIO
+ //Don't track PhantomJS visits with metrics
if (!(/PhantomJS/.test(navigator.userAgent))){
- KeenTracker.getInstance().trackPageView();
+ MetricsTracker.getInstance().trackPageView();
}
confirmEmails(window.contextVars.currentUser.emailsToAdd);
diff --git a/website/templates/base.mako b/website/templates/base.mako
index fceb1842931..642f8ffa03c 100644
--- a/website/templates/base.mako
+++ b/website/templates/base.mako
@@ -75,8 +75,8 @@
- %for author in self.authors_meta()[:10]:
-
+ %for author, creator in list(zip(self.authors_meta()[:10], self.creator_meta()[:10])):
+
%endfor
%for tag in self.keywords_meta()[:10]:
@@ -258,7 +258,6 @@
entryPoint: ${ user_entry_point | sjson, n },
institutions: ${ user_institutions | sjson, n},
emailsToAdd: ${ user_email_verifications | sjson, n },
- anon: ${ anon | sjson, n },
},
maintenance: ${ maintenance | sjson, n},
analyticsMeta: {
@@ -271,23 +270,6 @@
});
- % if keen['public']['project_id']:
-
- % endif
-
% if settings.DATACITE_TRACKER_REPO_ID:
% endif
-
${self.javascript_bottom()}